@routedock/routedock 0.1.2 → 0.3.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 +532 -4
  3. package/dist/MppSessionHandler-1EL8QBAn.d.cts +215 -0
  4. package/dist/MppSessionHandler-Bq4S-JTE.d.ts +215 -0
  5. package/dist/ProviderRegistry-CA_TdREG.d.ts +142 -0
  6. package/dist/ProviderRegistry-D-0rqK0N.d.cts +142 -0
  7. package/dist/RouteDockClient-Bh86bVly.d.ts +469 -0
  8. package/dist/RouteDockClient-go9aopfF.d.cts +469 -0
  9. package/dist/SessionReconciler-Bw-PoBVl.d.ts +134 -0
  10. package/dist/SessionReconciler-Cz2nuOlJ.d.cts +134 -0
  11. package/dist/SessionStore-B4-Hi4H8.d.cts +143 -0
  12. package/dist/SessionStore-BaYiegP0.d.ts +143 -0
  13. package/dist/client.cjs +2746 -217
  14. package/dist/client.d.cts +51 -81
  15. package/dist/client.d.ts +51 -81
  16. package/dist/client.js +2690 -218
  17. package/dist/index.cjs +3634 -420
  18. package/dist/index.d.cts +12 -3
  19. package/dist/index.d.ts +12 -3
  20. package/dist/index.js +3569 -419
  21. package/dist/logger-D8_Q2ytp.d.cts +467 -0
  22. package/dist/logger-D8_Q2ytp.d.ts +467 -0
  23. package/dist/provider/fastify.cjs +1099 -0
  24. package/dist/provider/fastify.d.cts +40 -0
  25. package/dist/provider/fastify.d.ts +40 -0
  26. package/dist/provider/fastify.js +1097 -0
  27. package/dist/provider/hono.cjs +1152 -0
  28. package/dist/provider/hono.d.cts +101 -0
  29. package/dist/provider/hono.d.ts +101 -0
  30. package/dist/provider/hono.js +1142 -0
  31. package/dist/provider.cjs +1105 -147
  32. package/dist/provider.d.cts +38 -27
  33. package/dist/provider.d.ts +38 -27
  34. package/dist/provider.js +1079 -148
  35. package/dist/react.cjs +2931 -0
  36. package/dist/react.d.cts +107 -0
  37. package/dist/react.d.ts +107 -0
  38. package/dist/react.js +2924 -0
  39. package/dist/routedockMiddleware-57gUaEMf.d.ts +64 -0
  40. package/dist/routedockMiddleware-DlyLrxp7.d.cts +64 -0
  41. package/dist/schema.cjs +404 -0
  42. package/dist/schema.d.cts +3 -0
  43. package/dist/schema.d.ts +3 -0
  44. package/dist/schema.js +399 -0
  45. package/dist/sign-AKH67sft.d.cts +53 -0
  46. package/dist/sign-BEq-OsSq.d.ts +53 -0
  47. package/dist/testing.cjs +59 -0
  48. package/dist/testing.d.cts +114 -0
  49. package/dist/testing.d.ts +114 -0
  50. package/dist/testing.js +56 -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,215 @@
1
+ import { g as RouteDockLogger, h as RouteDockManifest } from './logger-D8_Q2ytp.cjs';
2
+ import { RequestHandler } from 'express';
3
+ import { Store } from 'mppx';
4
+
5
+ /**
6
+ * Idempotency store for payment settlement.
7
+ *
8
+ * `x402Handler` and `MppChargeHandler` settle on every request carrying a
9
+ * payment header. An agent that retries after a post-settlement network
10
+ * timeout resends the *same* signed payment, which would settle a second time
11
+ * and invoke `onSettled` twice — double-counting in billing.
12
+ *
13
+ * Before settling, a handler derives an idempotency key from the inbound
14
+ * payment header(s) *together with the resource being paid for* and checks
15
+ * this store. On a hit it replays the cached settlement response and skips
16
+ * both the on-chain settle and `onSettled`. A hit older than
17
+ * {@link SETTLEMENT_REPLAY_WINDOW_MS}, or one replayed more than
18
+ * {@link MAX_SETTLEMENT_REPLAYS} times, is rejected instead of replayed so a
19
+ * leaked header cannot buy unlimited requests.
20
+ *
21
+ * `claimReplay` and {@link SettlementRecord.createdAt} are optional so a
22
+ * custom store written against the original two-method (`get`/`set`) contract
23
+ * keeps compiling. See {@link checkSettlementReplay} for how a store that
24
+ * supplies neither bounds a replay.
25
+ *
26
+ * The default {@link InMemorySeenTxStore} is per-handler and per-process. For
27
+ * multi-instance deployments, supply a shared implementation backed by Redis,
28
+ * Supabase, etc.
29
+ */
30
+ /** The resource a settlement idempotency key is scoped to. */
31
+ interface SettlementScope {
32
+ /** HTTP method of the paid request (e.g. `GET`). */
33
+ method: string;
34
+ /** Path of the paid request, without query string (e.g. `/price`). */
35
+ path: string;
36
+ /** Charge amount as declared by the route's requirements. */
37
+ amount: string;
38
+ /** Payee address the route settles to. */
39
+ payTo: string;
40
+ }
41
+ /** Cached outcome of a settlement, replayed on a duplicate payment. */
42
+ interface SettlementRecord {
43
+ /** On-chain transaction hash (or settlement reference), if known. */
44
+ txHash: string | null;
45
+ /** Response headers to re-apply on a replay (e.g. `X-Payment-Response`). */
46
+ headers?: Record<string, string>;
47
+ /**
48
+ * Epoch milliseconds at which the settlement was recorded. Optional for
49
+ * backwards compatibility with custom stores that predate the replay bound;
50
+ * a record without it can only be bounded by the replay count.
51
+ */
52
+ createdAt?: number;
53
+ }
54
+ interface SeenTxStore {
55
+ /** Returns the cached settlement for a key, or `undefined` if unseen. */
56
+ get(key: string): Promise<SettlementRecord | undefined> | SettlementRecord | undefined;
57
+ /** Records the settlement outcome for a key. */
58
+ set(key: string, record: SettlementRecord): Promise<void> | void;
59
+ /**
60
+ * Optionally, atomically record one replay of `key` and return the new
61
+ * replay count. Returns `Infinity` when the key is unknown (e.g. evicted
62
+ * between a `get` and this call), so the caller treats the payment as spent
63
+ * rather than replaying an unbounded number of times.
64
+ *
65
+ * Optional so a custom store written against the original two-method
66
+ * (`get`/`set`) contract keeps compiling. A store that omits it is still
67
+ * bounded by {@link SETTLEMENT_REPLAY_WINDOW_MS} when its records carry
68
+ * {@link SettlementRecord.createdAt}.
69
+ */
70
+ claimReplay?(key: string): Promise<number> | number;
71
+ }
72
+ interface InMemorySeenTxStoreOptions {
73
+ /** Maximum number of entries before FIFO eviction. Defaults to 10,000. */
74
+ maxEntries?: number;
75
+ /** Log a startup warning about non-durability. Defaults to true. */
76
+ warn?: boolean;
77
+ /** Log sink for the non-durability warning. Defaults to a console-backed logger. */
78
+ logger?: RouteDockLogger;
79
+ }
80
+ /**
81
+ * How long after a settlement a byte-identical retry may still replay the
82
+ * cached response. Matches the `maxTimeoutSeconds: 60` both x402 requirement
83
+ * objects declare, so past this point the payment is expired anyway.
84
+ */
85
+ declare const SETTLEMENT_REPLAY_WINDOW_MS = 60000;
86
+ /**
87
+ * How many times one settled payment may be replayed beyond the original
88
+ * settlement. The SDK's own clients never resend a byte-identical header —
89
+ * they re-sign on every retry — so a tight limit costs nothing while stopping
90
+ * a leaked header from buying unlimited requests.
91
+ */
92
+ declare const MAX_SETTLEMENT_REPLAYS = 1;
93
+ /** Outcome of checking whether an idempotency key may replay a settlement. */
94
+ type SettlementReplayCheck = {
95
+ kind: 'miss';
96
+ } | {
97
+ kind: 'replay';
98
+ record: SettlementRecord;
99
+ } | {
100
+ kind: 'spent';
101
+ };
102
+ /**
103
+ * Decide what a payment bearing `key` should do.
104
+ *
105
+ * - `miss` — no cached settlement; fall through to decode and settle.
106
+ * - `replay`— a fresh settlement within the replay window and under the replay
107
+ * limit; re-apply the recorded headers and skip settling.
108
+ * - `spent` — the settlement is older than the replay window, or has already
109
+ * been replayed the maximum number of times; respond 402 without
110
+ * decoding, settling, or invoking callbacks.
111
+ *
112
+ * The age check runs first so an expired record never touches the replay
113
+ * counter.
114
+ *
115
+ * Custom stores may predate this function and implement only `get`/`set`:
116
+ *
117
+ * - a record that carries `createdAt` but a store without `claimReplay` is
118
+ * bounded by {@link SETTLEMENT_REPLAY_WINDOW_MS} alone;
119
+ * - a record without `createdAt` offers no window to check, so a store's own
120
+ * `claimReplay` is the only bound available; a store that supplies neither
121
+ * replays as it did before this function existed.
122
+ */
123
+ declare function checkSettlementReplay(store: SeenTxStore, key: string, now?: number): Promise<SettlementReplayCheck>;
124
+ /**
125
+ * Default in-memory {@link SeenTxStore}. Bounded by `maxEntries` with FIFO
126
+ * eviction so a long-running process cannot grow without limit.
127
+ *
128
+ * **Not durable.** On serverless/edge runtimes each request may land on a
129
+ * fresh isolate with an empty cache, which causes duplicate settlements.
130
+ * Supply a persistent implementation (Supabase, Redis, …) in production.
131
+ */
132
+ declare class InMemorySeenTxStore implements SeenTxStore {
133
+ private readonly map;
134
+ private readonly replayCounts;
135
+ private readonly order;
136
+ constructor(maxEntriesOrOptions?: number | InMemorySeenTxStoreOptions);
137
+ private readonly maxEntries;
138
+ get(key: string): SettlementRecord | undefined;
139
+ set(key: string, record: SettlementRecord): void;
140
+ claimReplay(key: string): number;
141
+ }
142
+ /**
143
+ * Derive a collision-resistant idempotency key from the inbound
144
+ * payment-bearing headers, scoped to the resource being paid for. Uses
145
+ * SHA-256 instead of 32-bit FNV-1a (which has a 2^32 keyspace — birthday
146
+ * collisions become likely well before the 10_000-entry default store is
147
+ * full).
148
+ *
149
+ * x402 clients send the signed payment in `payment-signature` / `x-payment`;
150
+ * mppx clients send it in `authorization` (the `Payment` scheme). A retry
151
+ * resends byte-identical headers, so hashing the first present one yields a
152
+ * stable key.
153
+ *
154
+ * The `scope` (method, path, amount, payTo) is hashed alongside the header so
155
+ * a payment settled for one route can never replay against another route,
156
+ * even when the same store is shared across routes. Returns `null` when no
157
+ * payment header is present (nothing to dedupe — e.g. the initial 402
158
+ * challenge request).
159
+ */
160
+ declare function paymentIdempotencyKey(getHeader: (name: string) => string | undefined, scope: SettlementScope): Promise<string | null>;
161
+
162
+ /** Store shape extended with optional atomic update operation. */
163
+ type ChannelStore = Store.Store & {
164
+ update?: (key: string, fn: (prev: unknown) => unknown) => Promise<unknown>;
165
+ };
166
+ /** Expected payload shape for voucher cumulative amount entries. */
167
+ interface VoucherStoreValue {
168
+ amount: string;
169
+ [key: string]: unknown;
170
+ }
171
+ /** Validates that a stored store value is an object containing a numeric string amount. */
172
+ declare function isVoucherStoreValue(value: unknown): value is VoucherStoreValue;
173
+ type Network = 'testnet' | 'mainnet';
174
+ /** Why a live session was flagged as orphaned (never cleanly closed). */
175
+ type OrphanReason = 'connection-closed' | 'idle-timeout';
176
+ /** Latest signed state of an orphaned session, enough for the reconciler to settle. */
177
+ interface OrphanedSessionInfo {
178
+ /** Cumulative amount of the highest voucher seen (human-readable, 7 dp). */
179
+ cumulativeAmount: string;
180
+ /** Hex-encoded signature of the highest voucher seen. */
181
+ lastSignature: string;
182
+ /** Number of vouchers received before teardown. */
183
+ voucherCount: number;
184
+ /** What triggered the orphan flag. */
185
+ reason: OrphanReason;
186
+ }
187
+ interface MppSessionHandlerOptions {
188
+ payeeSecretKey: string;
189
+ network: Network;
190
+ channelFactory: string;
191
+ rate: string;
192
+ assetContract?: string;
193
+ manifest: RouteDockManifest;
194
+ commitmentPublicKey: string;
195
+ onSettled?: (txHash: string, totalPaid: string, mode: string, payer: string | null) => Promise<void>;
196
+ onSessionOpen?: (channelId: string, payer: string | null) => Promise<void>;
197
+ onVoucher?: (channelId: string, voucherIndex: number, cumulativeAmount: string, signature: string) => Promise<void>;
198
+ onCallbackError?: (err: unknown, cb: string) => void;
199
+ /**
200
+ * Called when the client connection drops mid-session or the session goes
201
+ * idle, before a clean close. Persist the session as `closing` so the
202
+ * SessionReconciler can settle it with the latest signed voucher.
203
+ */
204
+ onOrphaned?: (channelId: string, info: OrphanedSessionInfo) => Promise<void>;
205
+ /**
206
+ * Flag the session orphaned after this many milliseconds with no voucher
207
+ * activity. Disabled when unset.
208
+ */
209
+ idleTimeoutMs?: number;
210
+ /** Log sink for internal error paths. Defaults to a console-backed logger. */
211
+ logger?: RouteDockLogger;
212
+ }
213
+ declare function createMppSessionHandler(opts: MppSessionHandlerOptions): RequestHandler;
214
+
215
+ export { type ChannelStore as C, InMemorySeenTxStore as I, MAX_SETTLEMENT_REPLAYS as M, type OrphanedSessionInfo as O, type SeenTxStore as S, type VoucherStoreValue as V, type SettlementRecord as a, type InMemorySeenTxStoreOptions as b, type MppSessionHandlerOptions as c, type OrphanReason as d, SETTLEMENT_REPLAY_WINDOW_MS as e, type SettlementReplayCheck as f, type SettlementScope as g, checkSettlementReplay as h, createMppSessionHandler as i, isVoucherStoreValue as j, paymentIdempotencyKey as p };
@@ -0,0 +1,215 @@
1
+ import { g as RouteDockLogger, h as RouteDockManifest } from './logger-D8_Q2ytp.js';
2
+ import { RequestHandler } from 'express';
3
+ import { Store } from 'mppx';
4
+
5
+ /**
6
+ * Idempotency store for payment settlement.
7
+ *
8
+ * `x402Handler` and `MppChargeHandler` settle on every request carrying a
9
+ * payment header. An agent that retries after a post-settlement network
10
+ * timeout resends the *same* signed payment, which would settle a second time
11
+ * and invoke `onSettled` twice — double-counting in billing.
12
+ *
13
+ * Before settling, a handler derives an idempotency key from the inbound
14
+ * payment header(s) *together with the resource being paid for* and checks
15
+ * this store. On a hit it replays the cached settlement response and skips
16
+ * both the on-chain settle and `onSettled`. A hit older than
17
+ * {@link SETTLEMENT_REPLAY_WINDOW_MS}, or one replayed more than
18
+ * {@link MAX_SETTLEMENT_REPLAYS} times, is rejected instead of replayed so a
19
+ * leaked header cannot buy unlimited requests.
20
+ *
21
+ * `claimReplay` and {@link SettlementRecord.createdAt} are optional so a
22
+ * custom store written against the original two-method (`get`/`set`) contract
23
+ * keeps compiling. See {@link checkSettlementReplay} for how a store that
24
+ * supplies neither bounds a replay.
25
+ *
26
+ * The default {@link InMemorySeenTxStore} is per-handler and per-process. For
27
+ * multi-instance deployments, supply a shared implementation backed by Redis,
28
+ * Supabase, etc.
29
+ */
30
+ /** The resource a settlement idempotency key is scoped to. */
31
+ interface SettlementScope {
32
+ /** HTTP method of the paid request (e.g. `GET`). */
33
+ method: string;
34
+ /** Path of the paid request, without query string (e.g. `/price`). */
35
+ path: string;
36
+ /** Charge amount as declared by the route's requirements. */
37
+ amount: string;
38
+ /** Payee address the route settles to. */
39
+ payTo: string;
40
+ }
41
+ /** Cached outcome of a settlement, replayed on a duplicate payment. */
42
+ interface SettlementRecord {
43
+ /** On-chain transaction hash (or settlement reference), if known. */
44
+ txHash: string | null;
45
+ /** Response headers to re-apply on a replay (e.g. `X-Payment-Response`). */
46
+ headers?: Record<string, string>;
47
+ /**
48
+ * Epoch milliseconds at which the settlement was recorded. Optional for
49
+ * backwards compatibility with custom stores that predate the replay bound;
50
+ * a record without it can only be bounded by the replay count.
51
+ */
52
+ createdAt?: number;
53
+ }
54
+ interface SeenTxStore {
55
+ /** Returns the cached settlement for a key, or `undefined` if unseen. */
56
+ get(key: string): Promise<SettlementRecord | undefined> | SettlementRecord | undefined;
57
+ /** Records the settlement outcome for a key. */
58
+ set(key: string, record: SettlementRecord): Promise<void> | void;
59
+ /**
60
+ * Optionally, atomically record one replay of `key` and return the new
61
+ * replay count. Returns `Infinity` when the key is unknown (e.g. evicted
62
+ * between a `get` and this call), so the caller treats the payment as spent
63
+ * rather than replaying an unbounded number of times.
64
+ *
65
+ * Optional so a custom store written against the original two-method
66
+ * (`get`/`set`) contract keeps compiling. A store that omits it is still
67
+ * bounded by {@link SETTLEMENT_REPLAY_WINDOW_MS} when its records carry
68
+ * {@link SettlementRecord.createdAt}.
69
+ */
70
+ claimReplay?(key: string): Promise<number> | number;
71
+ }
72
+ interface InMemorySeenTxStoreOptions {
73
+ /** Maximum number of entries before FIFO eviction. Defaults to 10,000. */
74
+ maxEntries?: number;
75
+ /** Log a startup warning about non-durability. Defaults to true. */
76
+ warn?: boolean;
77
+ /** Log sink for the non-durability warning. Defaults to a console-backed logger. */
78
+ logger?: RouteDockLogger;
79
+ }
80
+ /**
81
+ * How long after a settlement a byte-identical retry may still replay the
82
+ * cached response. Matches the `maxTimeoutSeconds: 60` both x402 requirement
83
+ * objects declare, so past this point the payment is expired anyway.
84
+ */
85
+ declare const SETTLEMENT_REPLAY_WINDOW_MS = 60000;
86
+ /**
87
+ * How many times one settled payment may be replayed beyond the original
88
+ * settlement. The SDK's own clients never resend a byte-identical header —
89
+ * they re-sign on every retry — so a tight limit costs nothing while stopping
90
+ * a leaked header from buying unlimited requests.
91
+ */
92
+ declare const MAX_SETTLEMENT_REPLAYS = 1;
93
+ /** Outcome of checking whether an idempotency key may replay a settlement. */
94
+ type SettlementReplayCheck = {
95
+ kind: 'miss';
96
+ } | {
97
+ kind: 'replay';
98
+ record: SettlementRecord;
99
+ } | {
100
+ kind: 'spent';
101
+ };
102
+ /**
103
+ * Decide what a payment bearing `key` should do.
104
+ *
105
+ * - `miss` — no cached settlement; fall through to decode and settle.
106
+ * - `replay`— a fresh settlement within the replay window and under the replay
107
+ * limit; re-apply the recorded headers and skip settling.
108
+ * - `spent` — the settlement is older than the replay window, or has already
109
+ * been replayed the maximum number of times; respond 402 without
110
+ * decoding, settling, or invoking callbacks.
111
+ *
112
+ * The age check runs first so an expired record never touches the replay
113
+ * counter.
114
+ *
115
+ * Custom stores may predate this function and implement only `get`/`set`:
116
+ *
117
+ * - a record that carries `createdAt` but a store without `claimReplay` is
118
+ * bounded by {@link SETTLEMENT_REPLAY_WINDOW_MS} alone;
119
+ * - a record without `createdAt` offers no window to check, so a store's own
120
+ * `claimReplay` is the only bound available; a store that supplies neither
121
+ * replays as it did before this function existed.
122
+ */
123
+ declare function checkSettlementReplay(store: SeenTxStore, key: string, now?: number): Promise<SettlementReplayCheck>;
124
+ /**
125
+ * Default in-memory {@link SeenTxStore}. Bounded by `maxEntries` with FIFO
126
+ * eviction so a long-running process cannot grow without limit.
127
+ *
128
+ * **Not durable.** On serverless/edge runtimes each request may land on a
129
+ * fresh isolate with an empty cache, which causes duplicate settlements.
130
+ * Supply a persistent implementation (Supabase, Redis, …) in production.
131
+ */
132
+ declare class InMemorySeenTxStore implements SeenTxStore {
133
+ private readonly map;
134
+ private readonly replayCounts;
135
+ private readonly order;
136
+ constructor(maxEntriesOrOptions?: number | InMemorySeenTxStoreOptions);
137
+ private readonly maxEntries;
138
+ get(key: string): SettlementRecord | undefined;
139
+ set(key: string, record: SettlementRecord): void;
140
+ claimReplay(key: string): number;
141
+ }
142
+ /**
143
+ * Derive a collision-resistant idempotency key from the inbound
144
+ * payment-bearing headers, scoped to the resource being paid for. Uses
145
+ * SHA-256 instead of 32-bit FNV-1a (which has a 2^32 keyspace — birthday
146
+ * collisions become likely well before the 10_000-entry default store is
147
+ * full).
148
+ *
149
+ * x402 clients send the signed payment in `payment-signature` / `x-payment`;
150
+ * mppx clients send it in `authorization` (the `Payment` scheme). A retry
151
+ * resends byte-identical headers, so hashing the first present one yields a
152
+ * stable key.
153
+ *
154
+ * The `scope` (method, path, amount, payTo) is hashed alongside the header so
155
+ * a payment settled for one route can never replay against another route,
156
+ * even when the same store is shared across routes. Returns `null` when no
157
+ * payment header is present (nothing to dedupe — e.g. the initial 402
158
+ * challenge request).
159
+ */
160
+ declare function paymentIdempotencyKey(getHeader: (name: string) => string | undefined, scope: SettlementScope): Promise<string | null>;
161
+
162
+ /** Store shape extended with optional atomic update operation. */
163
+ type ChannelStore = Store.Store & {
164
+ update?: (key: string, fn: (prev: unknown) => unknown) => Promise<unknown>;
165
+ };
166
+ /** Expected payload shape for voucher cumulative amount entries. */
167
+ interface VoucherStoreValue {
168
+ amount: string;
169
+ [key: string]: unknown;
170
+ }
171
+ /** Validates that a stored store value is an object containing a numeric string amount. */
172
+ declare function isVoucherStoreValue(value: unknown): value is VoucherStoreValue;
173
+ type Network = 'testnet' | 'mainnet';
174
+ /** Why a live session was flagged as orphaned (never cleanly closed). */
175
+ type OrphanReason = 'connection-closed' | 'idle-timeout';
176
+ /** Latest signed state of an orphaned session, enough for the reconciler to settle. */
177
+ interface OrphanedSessionInfo {
178
+ /** Cumulative amount of the highest voucher seen (human-readable, 7 dp). */
179
+ cumulativeAmount: string;
180
+ /** Hex-encoded signature of the highest voucher seen. */
181
+ lastSignature: string;
182
+ /** Number of vouchers received before teardown. */
183
+ voucherCount: number;
184
+ /** What triggered the orphan flag. */
185
+ reason: OrphanReason;
186
+ }
187
+ interface MppSessionHandlerOptions {
188
+ payeeSecretKey: string;
189
+ network: Network;
190
+ channelFactory: string;
191
+ rate: string;
192
+ assetContract?: string;
193
+ manifest: RouteDockManifest;
194
+ commitmentPublicKey: string;
195
+ onSettled?: (txHash: string, totalPaid: string, mode: string, payer: string | null) => Promise<void>;
196
+ onSessionOpen?: (channelId: string, payer: string | null) => Promise<void>;
197
+ onVoucher?: (channelId: string, voucherIndex: number, cumulativeAmount: string, signature: string) => Promise<void>;
198
+ onCallbackError?: (err: unknown, cb: string) => void;
199
+ /**
200
+ * Called when the client connection drops mid-session or the session goes
201
+ * idle, before a clean close. Persist the session as `closing` so the
202
+ * SessionReconciler can settle it with the latest signed voucher.
203
+ */
204
+ onOrphaned?: (channelId: string, info: OrphanedSessionInfo) => Promise<void>;
205
+ /**
206
+ * Flag the session orphaned after this many milliseconds with no voucher
207
+ * activity. Disabled when unset.
208
+ */
209
+ idleTimeoutMs?: number;
210
+ /** Log sink for internal error paths. Defaults to a console-backed logger. */
211
+ logger?: RouteDockLogger;
212
+ }
213
+ declare function createMppSessionHandler(opts: MppSessionHandlerOptions): RequestHandler;
214
+
215
+ export { type ChannelStore as C, InMemorySeenTxStore as I, MAX_SETTLEMENT_REPLAYS as M, type OrphanedSessionInfo as O, type SeenTxStore as S, type VoucherStoreValue as V, type SettlementRecord as a, type InMemorySeenTxStoreOptions as b, type MppSessionHandlerOptions as c, type OrphanReason as d, SETTLEMENT_REPLAY_WINDOW_MS as e, type SettlementReplayCheck as f, type SettlementScope as g, checkSettlementReplay as h, createMppSessionHandler as i, isVoucherStoreValue as j, paymentIdempotencyKey as p };
@@ -0,0 +1,142 @@
1
+ import { usdcToStroops } from '@routedock/nulth-sdk';
2
+ import { l as SpendStore, a as DailySpend, R as RetryPolicy } from './RouteDockClient-Bh86bVly.js';
3
+ import { ClientStellarSigner } from '@x402/stellar';
4
+ import { h as RouteDockManifest, c as PaymentResult } from './logger-D8_Q2ytp.js';
5
+ import { Keypair } from '@stellar/stellar-sdk';
6
+ import { SupabaseClient } from '@supabase/supabase-js';
7
+
8
+ /**
9
+ * USDC conversion — canonical implementation lives in @routedock/nulth-sdk and is
10
+ * re-exported here so SDK consumers (client, provider) share one converter.
11
+ *
12
+ * @routedock/routedock already depends on @routedock/nulth-sdk, so re-exporting
13
+ * (rather than defining a parallel helper) keeps a single source of truth.
14
+ */
15
+
16
+ /**
17
+ * @deprecated Use {@link usdcToStroops} — the same validated bigint converter.
18
+ * Kept as a thin alias so existing call sites keep working during migration.
19
+ */
20
+ declare const usdcToUnits: typeof usdcToStroops;
21
+
22
+ /**
23
+ * File-backed durable {@link SpendStore}.
24
+ *
25
+ * Persists the daily spend accumulator to a JSON file on disk, preserving
26
+ * cumulative spending totals across process restarts, crashes, and redeploys.
27
+ *
28
+ * ### Error semantics
29
+ * - File absent (ENOENT): returns null — legitimately no spend recorded yet.
30
+ * - File present but unreadable or malformed: throws — a safety control must
31
+ * fail closed, not assume nothing was spent.
32
+ *
33
+ * ### Write atomicity
34
+ * Writes go to `<filePath>.tmp` first, then `fs.rename` onto the target.
35
+ * `rename` is atomic within a filesystem, so there is no window where the
36
+ * target file is truncated but not yet fully written.
37
+ */
38
+ declare class FileSpendStore implements SpendStore {
39
+ readonly filePath: string;
40
+ constructor(filePath: string);
41
+ read(): Promise<DailySpend | null>;
42
+ write(state: DailySpend): Promise<void>;
43
+ }
44
+
45
+ type Network = 'testnet' | 'mainnet';
46
+ declare class X402Client {
47
+ private readonly network;
48
+ private readonly retryPolicy?;
49
+ private readonly httpClient;
50
+ private readonly signer;
51
+ constructor(secretKeyOrSigner: string | ClientStellarSigner, network: Network, retryPolicy?: RetryPolicy | undefined);
52
+ /** Replace signer (e.g. swap to Nulth ZK account payer before pay) */
53
+ withSigner(signer: ClientStellarSigner): X402Client;
54
+ pay(url: string, manifest: RouteDockManifest): Promise<PaymentResult>;
55
+ }
56
+
57
+ declare class MppChargeClient {
58
+ private readonly keypair;
59
+ private readonly network;
60
+ private readonly retryPolicy?;
61
+ constructor(keypair: Keypair, network: 'testnet' | 'mainnet', retryPolicy?: RetryPolicy | undefined);
62
+ pay(url: string, manifest: RouteDockManifest): Promise<PaymentResult>;
63
+ }
64
+
65
+ interface OnChainProviderInfo {
66
+ account: string;
67
+ endpoint: string;
68
+ tags: string[];
69
+ }
70
+ declare class OnChainRegistry {
71
+ private readonly horizon;
72
+ private readonly knownAccounts;
73
+ private readonly timeoutMs;
74
+ constructor(options: {
75
+ horizonUrl: string;
76
+ knownAccounts: string[];
77
+ timeoutMs?: number;
78
+ });
79
+ /**
80
+ * stellar-sdk 14.6.1 has no way to cancel the underlying Horizon request, so
81
+ * we stop waiting on it instead: the promise returned here rejects once
82
+ * `timeoutMs` elapses, even though the request may still be in flight. The
83
+ * timer is always cleared, including on the error path.
84
+ */
85
+ private loadAccountWithTimeout;
86
+ listProviders(): Promise<OnChainProviderInfo[]>;
87
+ }
88
+
89
+ interface ProviderRecord {
90
+ name: string;
91
+ description: string | undefined;
92
+ base_url: string;
93
+ modes: string[];
94
+ tags: string[];
95
+ network: string;
96
+ payee: string;
97
+ source: 'supabase' | 'onchain';
98
+ /**
99
+ * True only when the provider was verified out-of-band — for on-chain
100
+ * records the payee is the account that published the endpoint, for
101
+ * supabase records only rows flagged `verified` are returned.
102
+ */
103
+ verified: boolean;
104
+ }
105
+ interface ProviderRegistryConfig {
106
+ supabase?: SupabaseClient;
107
+ onChain: {
108
+ horizonUrl: string;
109
+ knownAccounts: string[];
110
+ /**
111
+ * Optional explicit network override. When omitted it is derived from the
112
+ * Horizon URL: URLs containing `testnet` map to 'testnet', everything else
113
+ * defaults to 'mainnet'.
114
+ */
115
+ network?: 'testnet' | 'mainnet';
116
+ /**
117
+ * Per-account timeout for on-chain account loads, in milliseconds.
118
+ * Defaults to 10000 ms.
119
+ */
120
+ timeoutMs?: number;
121
+ };
122
+ }
123
+ declare class ProviderRegistry {
124
+ private readonly supabase;
125
+ private readonly onChain;
126
+ private readonly network;
127
+ constructor(config: ProviderRegistryConfig);
128
+ listProviders(): Promise<ProviderRecord[]>;
129
+ private trySupabase;
130
+ private tryOnChain;
131
+ /**
132
+ * Fetch a provider's manifest bound to the record's payee. The record's
133
+ * `payee` (from supabase `verified` rows or the on-chain registry account)
134
+ * is the out-of-band trust anchor: the fetched manifest's `payee` must
135
+ * equal it, otherwise `RouteDockSignatureError` is thrown. This closes the
136
+ * gap where the registry never cross-checked the on-chain payee against
137
+ * the manifest payee.
138
+ */
139
+ fetchProviderManifest(record: ProviderRecord): Promise<RouteDockManifest>;
140
+ }
141
+
142
+ export { FileSpendStore as F, MppChargeClient as M, type OnChainProviderInfo as O, type ProviderRecord as P, X402Client as X, OnChainRegistry as a, ProviderRegistry as b, type ProviderRegistryConfig as c, usdcToUnits as u };