@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.
Files changed (58) hide show
  1. package/README.md +113 -12
  2. package/dist/{MppSessionHandler-1EL8QBAn.d.cts → MppSessionHandler-3QfCHI5x.d.ts} +40 -2
  3. package/dist/{MppSessionHandler-Bq4S-JTE.d.ts → MppSessionHandler-xu4Kxu6N.d.cts} +40 -2
  4. package/dist/{ProviderRegistry-w4_hkI_1.d.ts → ProviderRegistry-D9KqyuH-.d.cts} +16 -36
  5. package/dist/{ProviderRegistry-CyZ5eryO.d.cts → ProviderRegistry-DVi2uzgk.d.ts} +16 -36
  6. package/dist/{RouteDockClient-CjM2VyOd.d.ts → RouteDockClient-6rmgkbYb.d.cts} +124 -144
  7. package/dist/{RouteDockClient-CXvWXix5.d.cts → RouteDockClient-CnAkxwrW.d.ts} +124 -144
  8. package/dist/{SessionReconciler-Bw-PoBVl.d.ts → SessionReconciler-Cce9MXO1.d.ts} +10 -3
  9. package/dist/{SessionReconciler-Cz2nuOlJ.d.cts → SessionReconciler-Dpe9qXdD.d.cts} +10 -3
  10. package/dist/{SessionStore-BaYiegP0.d.ts → SessionStore-DkLBxFQu.d.cts} +28 -3
  11. package/dist/{SessionStore-B4-Hi4H8.d.cts → SessionStore-_ZkoBzFY.d.ts} +28 -3
  12. package/dist/SpendStore-BUclV02i.d.ts +73 -0
  13. package/dist/SpendStore-s_dfcemj.d.cts +73 -0
  14. package/dist/client.cjs +767 -482
  15. package/dist/client.d.cts +80 -10
  16. package/dist/client.d.ts +80 -10
  17. package/dist/client.js +733 -463
  18. package/dist/index.cjs +1295 -585
  19. package/dist/index.d.cts +13 -10
  20. package/dist/index.d.ts +13 -10
  21. package/dist/index.js +1247 -554
  22. package/dist/logger-DVa7VT4c.d.cts +41 -0
  23. package/dist/logger-DVa7VT4c.d.ts +41 -0
  24. package/dist/network.cjs +57 -0
  25. package/dist/network.d.cts +72 -0
  26. package/dist/network.d.ts +72 -0
  27. package/dist/network.js +49 -0
  28. package/dist/node.cjs +166 -0
  29. package/dist/node.d.cts +77 -0
  30. package/dist/node.d.ts +77 -0
  31. package/dist/node.js +159 -0
  32. package/dist/provider/fastify.cjs +847 -62
  33. package/dist/provider/fastify.d.cts +10 -3
  34. package/dist/provider/fastify.d.ts +10 -3
  35. package/dist/provider/fastify.js +848 -63
  36. package/dist/provider/hono.cjs +936 -60
  37. package/dist/provider/hono.d.cts +23 -8
  38. package/dist/provider/hono.d.ts +23 -8
  39. package/dist/provider/hono.js +937 -61
  40. package/dist/provider.cjs +894 -70
  41. package/dist/provider.d.cts +9 -8
  42. package/dist/provider.d.ts +9 -8
  43. package/dist/provider.js +895 -72
  44. package/dist/react.cjs +736 -431
  45. package/dist/react.d.cts +7 -5
  46. package/dist/react.d.ts +7 -5
  47. package/dist/react.js +725 -420
  48. package/dist/{routedockMiddleware-57gUaEMf.d.ts → routedockMiddleware-Cq9HOJEa.d.ts} +9 -2
  49. package/dist/{routedockMiddleware-DlyLrxp7.d.cts → routedockMiddleware-CxsWkSKa.d.cts} +9 -2
  50. package/dist/schema.cjs +7 -0
  51. package/dist/schema.js +7 -0
  52. package/dist/{sign-AKH67sft.d.cts → sign-Bq08SeFq.d.ts} +2 -2
  53. package/dist/{sign-BEq-OsSq.d.ts → sign-DqGE0Plv.d.cts} +2 -2
  54. package/dist/testing.d.cts +4 -3
  55. package/dist/testing.d.ts +4 -3
  56. package/dist/{logger-D8_Q2ytp.d.cts → types-DXaS5Wip.d.cts} +85 -47
  57. package/dist/{logger-D8_Q2ytp.d.ts → types-DXaS5Wip.d.ts} +85 -47
  58. package/package.json +50 -4
@@ -1,15 +1,22 @@
1
1
  import { Keypair } from '@stellar/stellar-sdk';
2
- import { g as RouteDockLogger, P as PaymentMode, h as RouteDockManifest, d as PreflightResult, c as PaymentResult, b as EstimateCostResult, n as SessionOptions, m as SessionHandle } from './logger-D8_Q2ytp.js';
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.cjs';
3
+ import { b as RouteDockLogger } from './logger-DVa7VT4c.cjs';
3
4
  import { ClientStellarSigner } from '@x402/stellar';
4
- import { SignAuthEntry } from '@stellar/stellar-sdk/contract';
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-s_dfcemj.cjs';
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
- /** Maximum jittered backoff delay in ms. Default: 30000. */
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. `pay()` never learns the HTTP method, so a path shared by more
42
- * than one descriptor (e.g. GET and POST on `/infer`) is refused only when
43
- * every matching descriptor has sunset. Paths the manifest does not list keep
44
- * today's behavior. Query strings and hashes are ignored because only
45
- * `URL.pathname` is compared.
46
- */
47
- declare function assertEndpointActive(manifest: RouteDockManifest, url: string, logger?: RouteDockLogger): void;
48
- /**
49
- * Validate semantic constraints for manifest fields beyond JSON schema syntax.
50
- * Enforces:
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 assertManifestValid(manifest: RouteDockManifest, baseUrl?: string): void;
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
- interface NulthPolicyState {
127
- dailyCapStroops: bigint;
128
- allowlistCommitment: string;
129
- allowedPayees: readonly string[];
130
- dailySpendStroops: bigint;
131
- dayBucket: number;
132
- witnessSecret: string;
133
- expiryLedger?: number;
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
- interface NulthProof {
143
- version: 1;
144
- proof: string;
145
- publicInputs: {
146
- authDigest: string;
147
- allowlistCommitment: string;
148
- capCommitment: string;
149
- payeeHash: string;
150
- amountStroops: string;
151
- dayBucket: string;
152
- };
153
- }
154
- interface NulthAuthSignature {
155
- nulth: 'zk-v1';
156
- proof: NulthProof;
157
- }
158
- interface NulthClientConfig {
159
- nulthAccount: string;
160
- network: 'testnet' | 'mainnet';
161
- prover?: 'mock';
162
- policy: NulthPolicyState;
163
- verifierContract?: string;
164
- /** Log sink for the mock-prover warning. Defaults to a console-backed logger. */
165
- logger?: RouteDockLogger;
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
- /** Maximum total USDC spend per day (decimal string, e.g. "1.00") */
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 that the payer has a trustline for the payment asset and surface
380
- * manifest metadata (regions, latency hints, capabilities). Safe to
381
- * call before committing to a payment — for approval gates and manual
382
- * trustline remediation.
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 { selectMode as A, setNulthPaymentContext as B, usdcToMicros as C, DEFAULT_RETRY_POLICY as D, withRetry as E, 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 SpendStore as l, assertEndpointActive as m, assertManifestValid as n, assertNulthVaultManifest as o, backoffDelayMs as p, configureManifestCache as q, createNulthSigner as r, createPolicyState as s, decodeAuthSignature as t, fetchLedgerSequence as u, fetchManifest as v, invalidateManifest as w, paymentContextFromManifest as x, prepareNulthSigner as y, rankProvidersByLatency as z };
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 };