@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
package/dist/schema.js ADDED
@@ -0,0 +1,399 @@
1
+ // src/schemas/routedock.schema.json
2
+ var routedock_schema_default = {
3
+ $schema: "http://json-schema.org/draft-07/schema#",
4
+ $id: "https://routedock.xyz/schemas/routedock.schema.json",
5
+ title: "RouteDock Manifest",
6
+ description: "Discovery manifest served at /.well-known/routedock.json on every RouteDock provider endpoint",
7
+ type: "object",
8
+ required: [
9
+ "routedock",
10
+ "name",
11
+ "description",
12
+ "modes",
13
+ "network",
14
+ "asset",
15
+ "asset_contract",
16
+ "payee",
17
+ "pricing",
18
+ "endpoints",
19
+ "tags"
20
+ ],
21
+ additionalProperties: false,
22
+ properties: {
23
+ routedock: {
24
+ type: "string",
25
+ pattern: "^[0-9]+\\.[0-9]+$",
26
+ description: "Manifest schema version, e.g. '1.0', '1.1', '2.0'."
27
+ },
28
+ min_client_version: {
29
+ type: "string",
30
+ pattern: "^[0-9]+\\.[0-9]+$",
31
+ description: "Minimum SDK client version (major.minor) required to use this manifest. Clients below this version must refuse to pay."
32
+ },
33
+ deprecated_modes: {
34
+ type: "array",
35
+ items: {
36
+ $ref: "#/definitions/PaymentMode"
37
+ },
38
+ uniqueItems: true,
39
+ description: "Payment modes still listed in 'modes' for backwards compatibility but deprecated for new integrations."
40
+ },
41
+ sunset_at: {
42
+ type: "string",
43
+ format: "date-time",
44
+ description: "ISO 8601 timestamp after which this manifest version is no longer supported by the provider."
45
+ },
46
+ name: {
47
+ type: "string",
48
+ minLength: 1,
49
+ maxLength: 120,
50
+ description: "Human-readable provider name"
51
+ },
52
+ description: {
53
+ type: "string",
54
+ minLength: 1,
55
+ maxLength: 500,
56
+ description: "Human-readable description of the provider's data or service"
57
+ },
58
+ modes: {
59
+ type: "array",
60
+ minItems: 1,
61
+ uniqueItems: true,
62
+ items: {
63
+ $ref: "#/definitions/PaymentMode"
64
+ },
65
+ description: "Payment modes supported by this provider"
66
+ },
67
+ network: {
68
+ type: "string",
69
+ enum: ["testnet", "mainnet"],
70
+ description: "Stellar network this provider operates on"
71
+ },
72
+ asset: {
73
+ type: "string",
74
+ description: "Asset ticker symbol (e.g. 'USDC')"
75
+ },
76
+ asset_contract: {
77
+ type: "string",
78
+ description: "Stellar Asset Contract (SAC) address for the payment asset"
79
+ },
80
+ assets: {
81
+ type: "array",
82
+ minItems: 1,
83
+ items: {
84
+ $ref: "#/definitions/AssetConfig"
85
+ },
86
+ description: "Payment assets accepted by this provider. Each asset can be scoped to specific modes and endpoints. When present, assets[0] must match the root-level asset and asset_contract."
87
+ },
88
+ payee: {
89
+ type: "string",
90
+ description: "Stellar address (G...) that receives payments"
91
+ },
92
+ pricing: {
93
+ type: "object",
94
+ description: "Pricing configuration per supported payment mode",
95
+ additionalProperties: false,
96
+ properties: {
97
+ x402: {
98
+ $ref: "#/definitions/PricingConfig"
99
+ },
100
+ "mpp-charge": {
101
+ $ref: "#/definitions/PricingConfig"
102
+ },
103
+ "mpp-session": {
104
+ $ref: "#/definitions/SessionPricingConfig"
105
+ },
106
+ "mpp-session-ws": {
107
+ $ref: "#/definitions/SessionPricingConfig",
108
+ description: "WebSocket transport variant of mpp-session: channel opened and first voucher negotiated over HTTP, then the connection upgrades to WebSocket for push-based streaming."
109
+ }
110
+ },
111
+ minProperties: 1
112
+ },
113
+ endpoints: {
114
+ type: "object",
115
+ description: "Map of endpoint name to endpoint descriptors",
116
+ additionalProperties: {
117
+ $ref: "#/definitions/EndpointDescriptor"
118
+ },
119
+ minProperties: 1
120
+ },
121
+ sla: {
122
+ $ref: "#/definitions/SLAConfig"
123
+ },
124
+ tags: {
125
+ type: "array",
126
+ items: {
127
+ type: "string",
128
+ minLength: 1,
129
+ maxLength: 64
130
+ },
131
+ uniqueItems: true,
132
+ description: "Capability tags indexed with trigram search in the provider registry"
133
+ },
134
+ vault: {
135
+ $ref: "#/definitions/VaultMode"
136
+ },
137
+ nulth_account: {
138
+ type: "string",
139
+ description: "Optional Nulth smart account address associated with this provider"
140
+ },
141
+ capabilities: {
142
+ type: "object",
143
+ description: "Optional protocol features this provider supports",
144
+ properties: {
145
+ streaming: {
146
+ type: "array",
147
+ items: {
148
+ type: "string",
149
+ enum: ["sse", "websocket"]
150
+ },
151
+ description: "Supported streaming transports"
152
+ },
153
+ webhooks: {
154
+ type: "boolean",
155
+ description: "Whether webhook callbacks are supported"
156
+ },
157
+ idempotency_keys: {
158
+ type: "boolean",
159
+ description: "Whether idempotency keys are supported"
160
+ },
161
+ content_types: {
162
+ type: "array",
163
+ items: {
164
+ type: "string"
165
+ },
166
+ description: "Supported content types for request/response"
167
+ }
168
+ },
169
+ additionalProperties: false
170
+ },
171
+ regions: {
172
+ type: "array",
173
+ items: {
174
+ type: "string",
175
+ minLength: 2,
176
+ maxLength: 10
177
+ },
178
+ uniqueItems: true,
179
+ description: "IATA airport codes or UN/LOCODE identifiers for regions where this provider has infrastructure (e.g. ['IAD', 'AMS']). Used by agents to select the nearest provider."
180
+ },
181
+ latency_hints: {
182
+ type: "object",
183
+ additionalProperties: {
184
+ type: "number",
185
+ minimum: 0
186
+ },
187
+ description: "Provider-declared p50 latency in milliseconds per region code. Keys must be a subset of 'regions'. Example: { 'IAD': 14, 'AMS': 22 }."
188
+ },
189
+ signature: {
190
+ type: "string",
191
+ description: "Base64 Ed25519 signature (payee keypair) over the SHA-256 hash of this manifest with the signature field omitted. Clients must verify before trusting any routing field."
192
+ },
193
+ signature_version: {
194
+ type: "string",
195
+ const: "2",
196
+ description: "Manifest signature format. Version 2 recursively includes every nested manifest field in the signed digest."
197
+ },
198
+ categories: {
199
+ type: "array",
200
+ items: {
201
+ type: "string",
202
+ minLength: 1,
203
+ maxLength: 128
204
+ },
205
+ uniqueItems: true,
206
+ description: "Optional hierarchical categories from a published taxonomy (e.g. data/price/crypto)"
207
+ }
208
+ },
209
+ definitions: {
210
+ VaultMode: {
211
+ type: "string",
212
+ enum: ["local-key", "agent-vault", "nulth"],
213
+ description: "Agent vault / custody mode for payments from this provider"
214
+ },
215
+ PaymentMode: {
216
+ type: "string",
217
+ enum: ["x402", "mpp-charge", "mpp-session", "mpp-session-ws"],
218
+ description: "A supported RouteDock payment mode"
219
+ },
220
+ AssetConfig: {
221
+ type: "object",
222
+ description: "Payment asset configuration with optional mode and endpoint scoping",
223
+ required: ["asset", "asset_contract"],
224
+ additionalProperties: false,
225
+ properties: {
226
+ asset: {
227
+ type: "string",
228
+ description: "Asset ticker symbol (e.g. 'USDC' or 'XLM')"
229
+ },
230
+ asset_contract: {
231
+ type: "string",
232
+ description: "Stellar Asset Contract (SAC) address for the payment asset"
233
+ },
234
+ modes: {
235
+ type: "array",
236
+ items: {
237
+ $ref: "#/definitions/PaymentMode"
238
+ },
239
+ uniqueItems: true,
240
+ description: "Optional: restrict this asset to specific payment modes. If omitted, available for all modes."
241
+ },
242
+ endpoints: {
243
+ type: "array",
244
+ items: {
245
+ type: "string"
246
+ },
247
+ uniqueItems: true,
248
+ description: "Optional: restrict this asset to specific endpoints (by name). If omitted, available for all endpoints."
249
+ }
250
+ }
251
+ },
252
+ PricingConfig: {
253
+ type: "object",
254
+ description: "Per-request pricing for x402 or mpp-charge modes",
255
+ required: ["amount", "per"],
256
+ additionalProperties: false,
257
+ properties: {
258
+ amount: {
259
+ type: "string",
260
+ description: "Cost per request in the payment asset (decimal string, e.g. '0.001')"
261
+ },
262
+ per: {
263
+ type: "string",
264
+ const: "request",
265
+ description: "Billing unit \u2014 always 'request' for non-session modes"
266
+ },
267
+ facilitator: {
268
+ type: "string",
269
+ description: "x402 facilitator URL (required for x402 mode). OpenZeppelin Channels endpoint."
270
+ },
271
+ payee: {
272
+ type: "string",
273
+ description: "Optional per-mode payee override (Stellar G... address). Directs settlements for this mode to a separate account for treasury separation. Falls back to the top-level 'payee' when omitted."
274
+ }
275
+ }
276
+ },
277
+ SessionPricingConfig: {
278
+ type: "object",
279
+ description: "Per-voucher pricing for mpp-session (one-way-channel) mode",
280
+ required: ["rate", "per", "channel_factory", "min_deposit", "refund_waiting_period_ledgers"],
281
+ additionalProperties: false,
282
+ properties: {
283
+ rate: {
284
+ type: "string",
285
+ description: "Cost per voucher in the payment asset (decimal string, e.g. '0.0001')"
286
+ },
287
+ per: {
288
+ type: "string",
289
+ const: "voucher",
290
+ description: "Billing unit \u2014 always 'voucher' for session mode"
291
+ },
292
+ channel_factory: {
293
+ type: "string",
294
+ description: "Stable Soroban factory address (C...) used to deploy a per-session channel"
295
+ },
296
+ min_deposit: {
297
+ type: "string",
298
+ description: "Minimum initial deposit to open a channel (decimal string)"
299
+ },
300
+ refund_waiting_period_ledgers: {
301
+ type: "integer",
302
+ minimum: 1,
303
+ description: "Ledgers the server has to settle before the funder can reclaim. Default: 17280 (~24h). Do not reduce in production."
304
+ }
305
+ }
306
+ },
307
+ SLAConfig: {
308
+ type: "object",
309
+ description: "Service Level Agreement for the provider",
310
+ required: ["uptime_30d_percent", "p95_latency_ms"],
311
+ additionalProperties: false,
312
+ properties: {
313
+ uptime_30d_percent: {
314
+ type: "number",
315
+ description: "Uptime percentage over the last 30 days"
316
+ },
317
+ p95_latency_ms: {
318
+ type: "number",
319
+ description: "P95 latency in milliseconds"
320
+ },
321
+ maintenance_windows: {
322
+ type: "array",
323
+ items: {
324
+ type: "object",
325
+ required: ["cron", "duration_minutes"],
326
+ additionalProperties: false,
327
+ properties: {
328
+ cron: {
329
+ type: "string",
330
+ description: "Cron expression for the maintenance window"
331
+ },
332
+ duration_minutes: {
333
+ type: "number",
334
+ description: "Duration of the maintenance window in minutes"
335
+ }
336
+ }
337
+ }
338
+ }
339
+ }
340
+ },
341
+ EndpointDescriptor: {
342
+ type: "object",
343
+ description: "Descriptor for a provider endpoint",
344
+ required: ["method", "path"],
345
+ additionalProperties: false,
346
+ properties: {
347
+ method: {
348
+ type: "string",
349
+ description: "HTTP method for the endpoint"
350
+ },
351
+ path: {
352
+ type: "string",
353
+ description: "Path for the endpoint"
354
+ },
355
+ deprecated: {
356
+ type: "boolean",
357
+ description: "Whether this endpoint is retained only for backwards compatibility"
358
+ },
359
+ sunset_at: {
360
+ type: "string",
361
+ format: "date-time",
362
+ description: "ISO 8601 timestamp after which callers should stop using this endpoint"
363
+ },
364
+ headers: {
365
+ type: "object",
366
+ additionalProperties: {
367
+ type: "string"
368
+ },
369
+ description: "Expected request headers"
370
+ },
371
+ request_schema: {
372
+ description: "JSON schema for the request body"
373
+ },
374
+ response_schema: {
375
+ description: "JSON schema for the response body"
376
+ },
377
+ rate_limit: {
378
+ type: "object",
379
+ required: ["requests", "window_seconds"],
380
+ additionalProperties: false,
381
+ properties: {
382
+ requests: {
383
+ type: "number"
384
+ },
385
+ window_seconds: {
386
+ type: "number"
387
+ }
388
+ }
389
+ }
390
+ }
391
+ }
392
+ }
393
+ };
394
+
395
+ // src/schema.ts
396
+ var schema = routedock_schema_default;
397
+ var schema_default = schema;
398
+
399
+ export { schema_default as default, schema };
@@ -0,0 +1,53 @@
1
+ import { h as RouteDockManifest } from './logger-D8_Q2ytp.cjs';
2
+
3
+ /**
4
+ * Ed25519 manifest signing and verification.
5
+ *
6
+ * The canonical message is the SHA-256 hash of the manifest JSON with the
7
+ * `signature` field omitted and object keys sorted recursively at every depth.
8
+ * Array order and JSON primitive semantics are preserved. The payee Stellar
9
+ * keypair signs this hash.
10
+ *
11
+ * TRUST MODEL: the `payee` field is part of the document being signed, so a
12
+ * valid signature only proves that whoever signed it controlled the signing
13
+ * key declared in `payee`. It does NOT, by itself, prove the manifest was
14
+ * served by that entity — any host serving `/.well-known/routedock.json` can
15
+ * substitute its own payee and self-sign.
16
+ *
17
+ * Verification must therefore be bound to an out-of-band trust anchor. Callers
18
+ * that already know the expected payee (e.g. the account a provider registered
19
+ * in the on-chain registry, or a pinned/allowlisted key) MUST pass it as
20
+ * `expectedPayee` so a substituted document is rejected before any signature
21
+ * work is trusted.
22
+ *
23
+ * Providers call `signManifest` once at startup to embed the signature.
24
+ */
25
+
26
+ declare const MANIFEST_SIGNATURE_VERSION: "2";
27
+ /** Produce the canonical SHA-256 digest of the manifest (signature field excluded). */
28
+ declare function manifestDigest(manifest: RouteDockManifest): Buffer;
29
+ /**
30
+ * Sign a manifest with the payee's Stellar secret key.
31
+ * Returns a new manifest object with the `signature` field populated.
32
+ */
33
+ declare function signManifest(manifest: RouteDockManifest, payeeSecretKey: string): RouteDockManifest & {
34
+ signature_version: typeof MANIFEST_SIGNATURE_VERSION;
35
+ signature: string;
36
+ };
37
+ /**
38
+ * Verify the manifest's Ed25519 signature.
39
+ *
40
+ * `expectedPayee` binds the manifest to an out-of-band trust anchor: when
41
+ * provided, the (attacker-controllable) `manifest.payee` must equal it,
42
+ * otherwise `RouteDockSignatureError` is thrown before signature verification.
43
+ * Callers that have a registry/pinned payee MUST pass it; omitting it leaves
44
+ * only self-signed authenticity, which is insufficient for routing trust.
45
+ *
46
+ * Throws `RouteDockSignatureError` if the signature is missing, the payee does
47
+ * not match `expectedPayee`, or the signature is invalid.
48
+ */
49
+ declare function verifyManifestSignature(manifest: RouteDockManifest & {
50
+ signature?: string;
51
+ }, expectedPayee?: string): void;
52
+
53
+ export { manifestDigest as m, signManifest as s, verifyManifestSignature as v };
@@ -0,0 +1,53 @@
1
+ import { h as RouteDockManifest } from './logger-D8_Q2ytp.js';
2
+
3
+ /**
4
+ * Ed25519 manifest signing and verification.
5
+ *
6
+ * The canonical message is the SHA-256 hash of the manifest JSON with the
7
+ * `signature` field omitted and object keys sorted recursively at every depth.
8
+ * Array order and JSON primitive semantics are preserved. The payee Stellar
9
+ * keypair signs this hash.
10
+ *
11
+ * TRUST MODEL: the `payee` field is part of the document being signed, so a
12
+ * valid signature only proves that whoever signed it controlled the signing
13
+ * key declared in `payee`. It does NOT, by itself, prove the manifest was
14
+ * served by that entity — any host serving `/.well-known/routedock.json` can
15
+ * substitute its own payee and self-sign.
16
+ *
17
+ * Verification must therefore be bound to an out-of-band trust anchor. Callers
18
+ * that already know the expected payee (e.g. the account a provider registered
19
+ * in the on-chain registry, or a pinned/allowlisted key) MUST pass it as
20
+ * `expectedPayee` so a substituted document is rejected before any signature
21
+ * work is trusted.
22
+ *
23
+ * Providers call `signManifest` once at startup to embed the signature.
24
+ */
25
+
26
+ declare const MANIFEST_SIGNATURE_VERSION: "2";
27
+ /** Produce the canonical SHA-256 digest of the manifest (signature field excluded). */
28
+ declare function manifestDigest(manifest: RouteDockManifest): Buffer;
29
+ /**
30
+ * Sign a manifest with the payee's Stellar secret key.
31
+ * Returns a new manifest object with the `signature` field populated.
32
+ */
33
+ declare function signManifest(manifest: RouteDockManifest, payeeSecretKey: string): RouteDockManifest & {
34
+ signature_version: typeof MANIFEST_SIGNATURE_VERSION;
35
+ signature: string;
36
+ };
37
+ /**
38
+ * Verify the manifest's Ed25519 signature.
39
+ *
40
+ * `expectedPayee` binds the manifest to an out-of-band trust anchor: when
41
+ * provided, the (attacker-controllable) `manifest.payee` must equal it,
42
+ * otherwise `RouteDockSignatureError` is thrown before signature verification.
43
+ * Callers that have a registry/pinned payee MUST pass it; omitting it leaves
44
+ * only self-signed authenticity, which is insufficient for routing trust.
45
+ *
46
+ * Throws `RouteDockSignatureError` if the signature is missing, the payee does
47
+ * not match `expectedPayee`, or the signature is invalid.
48
+ */
49
+ declare function verifyManifestSignature(manifest: RouteDockManifest & {
50
+ signature?: string;
51
+ }, expectedPayee?: string): void;
52
+
53
+ export { manifestDigest as m, signManifest as s, verifyManifestSignature as v };
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+
3
+ // src/testing/createMockRoutedockMiddleware.ts
4
+ var DEFAULT_TX_HASH = "0000000000000000000000000000000000000000000000000000000000000000";
5
+ var DEFAULT_CHANNEL_ID = "CCK4XOW3YKQUEZFONUTINKMSNW7SNMRQZURME5U3UP7E6WNGK7UHUCAH";
6
+ var DEFAULT_PAYER = "GAIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCF6M";
7
+ function format7(scaled) {
8
+ return (Number(scaled) / 1e7).toFixed(7);
9
+ }
10
+ function toScaled(decimal) {
11
+ const [whole, frac = ""] = decimal.split(".");
12
+ const fracPadded = (frac + "0000000").slice(0, 7);
13
+ return BigInt(whole + fracPadded);
14
+ }
15
+ async function runMockSettlement(opts) {
16
+ const mode = opts.mode ?? "x402";
17
+ if ((opts.payment ?? "auto-pass") === "auto-fail") return null;
18
+ const s = opts.synthetic ?? {};
19
+ const txHash = s.txHash ?? DEFAULT_TX_HASH;
20
+ const payer = s.payer === void 0 ? DEFAULT_PAYER : s.payer;
21
+ if (mode === "mpp-session" || mode === "mpp-session-ws") {
22
+ const channelId = s.channelId ?? DEFAULT_CHANNEL_ID;
23
+ const rate = toScaled(s.rate ?? "0.0001");
24
+ const count = s.voucherCount ?? 3;
25
+ if (opts.onSessionOpen) await opts.onSessionOpen(channelId, payer);
26
+ const vouchers = [];
27
+ for (let i = 1; i <= count; i++) {
28
+ const cumulativeAmount = format7(rate * BigInt(i));
29
+ vouchers.push({ index: i, cumulativeAmount });
30
+ if (opts.onVoucher) await opts.onVoucher(channelId, i, cumulativeAmount, "0000000000000000000000000000000000000000000000000000000000000000");
31
+ }
32
+ const totalPaid = format7(rate * BigInt(count));
33
+ if (opts.onSettled) await opts.onSettled(txHash, totalPaid, mode, payer);
34
+ return { mode, txHash, amount: totalPaid, channelId, vouchers };
35
+ }
36
+ const amount = s.amount ?? "0.001";
37
+ if (opts.onSettled) await opts.onSettled(txHash, amount, mode, payer);
38
+ return { mode, txHash, amount };
39
+ }
40
+ function createMockRoutedockMiddleware(opts = {}) {
41
+ const payment = opts.payment ?? "auto-pass";
42
+ const failStatus = opts.failStatus ?? 402;
43
+ return (req, res, next) => {
44
+ if (payment === "auto-fail") {
45
+ res.status(failStatus).json({
46
+ error: "payment required",
47
+ detail: "mock routedock middleware: payment=auto-fail"
48
+ });
49
+ return;
50
+ }
51
+ void runMockSettlement(opts).then((record) => {
52
+ if (record) req.routedock = record;
53
+ next();
54
+ }).catch((err) => next(err));
55
+ };
56
+ }
57
+
58
+ exports.createMockRoutedockMiddleware = createMockRoutedockMiddleware;
59
+ exports.runMockSettlement = runMockSettlement;
@@ -0,0 +1,114 @@
1
+ import { RequestHandler } from 'express';
2
+ import { P as PaymentMode } from './logger-D8_Q2ytp.cjs';
3
+ import { R as RouteDockMiddlewareOptions } from './routedockMiddleware-DlyLrxp7.cjs';
4
+ import 'mppx';
5
+ import './MppSessionHandler-1EL8QBAn.cjs';
6
+
7
+ /**
8
+ * Loosen a production callback's return type so a plain sync test spy works,
9
+ * while keeping its exact parameter list (the real adapter's signature).
10
+ */
11
+ type TestCallback<F> = F extends (...args: infer A) => unknown ? (...args: A) => void | Promise<void> : never;
12
+ /**
13
+ * Provider settlement callbacks under test. Same parameter list as the real
14
+ * `RouteDockMiddlewareOptions` callbacks — including `payer` — but they accept
15
+ * sync or async fns so a plain test spy works without wrapping it in a Promise.
16
+ */
17
+ interface MockRoutedockCallbacks {
18
+ /** Called after each (synthetic) on-chain settlement. */
19
+ onSettled?: TestCallback<NonNullable<RouteDockMiddlewareOptions['onSettled']>>;
20
+ /** Called once when a synthetic session is opened (first voucher). */
21
+ onSessionOpen?: TestCallback<NonNullable<RouteDockMiddlewareOptions['onSessionOpen']>>;
22
+ /** Called for each synthetic voucher in a session (1-based index). */
23
+ onVoucher?: TestCallback<NonNullable<RouteDockMiddlewareOptions['onVoucher']>>;
24
+ }
25
+ /** Synthetic settlement values handed to the callbacks. All optional — sensible defaults per mode. */
26
+ interface SyntheticPayment {
27
+ /** On-chain tx hash passed to onSettled. Default: a fixed 64-hex string. */
28
+ txHash?: string;
29
+ /** Settled amount (x402 / mpp-charge) passed to onSettled. Default: '0.001'. */
30
+ amount?: string;
31
+ /** Channel address (session modes) passed to onSessionOpen. Default: a fixed C... address. */
32
+ channelId?: string;
33
+ /** Per-voucher rate (session modes), in decimal string. Default: '0.0001'. */
34
+ rate?: string;
35
+ /** Number of vouchers to emit before close (session modes). Default: 3. */
36
+ voucherCount?: number;
37
+ /**
38
+ * Payer passed to onSettled (4th arg) and onSessionOpen (2nd arg). Default: a
39
+ * valid Stellar G... account. Pass `null` to exercise the address-unavailable
40
+ * path; `null` is passed through untouched (an explicit null is not replaced
41
+ * by the default).
42
+ */
43
+ payer?: string | null;
44
+ }
45
+ interface MockRoutedockOptions extends MockRoutedockCallbacks {
46
+ /** Payment mode the synthetic payment uses. Default: 'x402'. */
47
+ mode?: PaymentMode;
48
+ /**
49
+ * 'auto-pass' (default): synthesize a successful settlement, invoke the
50
+ * configured callbacks with synthetic data, then call the route handler.
51
+ * 'auto-fail': respond with `failStatus` and skip both callbacks and handler.
52
+ */
53
+ payment?: 'auto-pass' | 'auto-fail';
54
+ /** Override the synthetic data handed to callbacks. */
55
+ synthetic?: SyntheticPayment;
56
+ /** HTTP status used on 'auto-fail'. Default: 402. */
57
+ failStatus?: number;
58
+ }
59
+ /** Synthetic settlement record attached to `req.routedock` for assertions. */
60
+ interface MockSettlementRecord {
61
+ mode: PaymentMode;
62
+ txHash: string;
63
+ amount: string;
64
+ channelId?: string;
65
+ vouchers?: Array<{
66
+ index: number;
67
+ cumulativeAmount: string;
68
+ }>;
69
+ }
70
+ /**
71
+ * Drive a provider's settlement callbacks with synthetic data, with no Express
72
+ * request involved. Useful for unit-testing an `onSettled` (e.g. a Supabase
73
+ * write) in complete isolation. Returns the synthetic settlement record.
74
+ *
75
+ * Honors `payment: 'auto-fail'` by invoking no callbacks and returning null.
76
+ */
77
+ declare function runMockSettlement(opts: MockRoutedockOptions): Promise<MockSettlementRecord | null>;
78
+ /**
79
+ * Express middleware that mocks RouteDock payment verification — the testing
80
+ * counterpart to `routedock()`. Drop it in front of a route to drive that
81
+ * route's settlement callbacks with synthetic data, without any on-chain
82
+ * settlement, facilitator, or wallet. Think `msw` for RouteDock providers.
83
+ *
84
+ * On `payment: 'auto-pass'` (default) it invokes the configured callbacks with
85
+ * synthetic data and then calls the next handler (your route). On
86
+ * `payment: 'auto-fail'` it responds with `failStatus` (402 by default) and
87
+ * skips both the callbacks and the route handler — exactly as a real payment
88
+ * rejection would.
89
+ *
90
+ * The synthetic settlement is attached to `req.routedock` for assertions.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * import express from 'express'
95
+ * import request from 'supertest'
96
+ * import { createMockRoutedockMiddleware } from '@routedock/routedock/testing'
97
+ *
98
+ * const onSettled = vi.fn()
99
+ * const app = express()
100
+ * app.use('/price', createMockRoutedockMiddleware({ mode: 'x402', payment: 'auto-pass', onSettled }))
101
+ * app.get('/price', (_req, res) => res.json({ price: '42' }))
102
+ *
103
+ * await request(app).get('/price').expect(200)
104
+ * expect(onSettled).toHaveBeenCalledWith(
105
+ * expect.any(String),
106
+ * '0.001',
107
+ * 'x402',
108
+ * expect.any(String), // payer
109
+ * )
110
+ * ```
111
+ */
112
+ declare function createMockRoutedockMiddleware(opts?: MockRoutedockOptions): RequestHandler;
113
+
114
+ export { type MockRoutedockCallbacks, type MockRoutedockOptions, type MockSettlementRecord, type SyntheticPayment, createMockRoutedockMiddleware, runMockSettlement };