@bosonprotocol/x402-server 0.1.0-alpha-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 (53) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +65 -0
  3. package/dist/cjs/challenge/index.d.ts +64 -0
  4. package/dist/cjs/challenge/index.js +96 -0
  5. package/dist/cjs/challenge/index.js.map +1 -0
  6. package/dist/cjs/client-tnrpqVMW.d.ts +36 -0
  7. package/dist/cjs/config-CBr9qMps.d.ts +419 -0
  8. package/dist/cjs/facilitator/index.d.ts +26 -0
  9. package/dist/cjs/facilitator/index.js +136 -0
  10. package/dist/cjs/facilitator/index.js.map +1 -0
  11. package/dist/cjs/handlers/index.d.ts +267 -0
  12. package/dist/cjs/handlers/index.js +1182 -0
  13. package/dist/cjs/handlers/index.js.map +1 -0
  14. package/dist/cjs/index.d.ts +72 -0
  15. package/dist/cjs/index.js +1643 -0
  16. package/dist/cjs/index.js.map +1 -0
  17. package/dist/cjs/onchain/index.d.ts +73 -0
  18. package/dist/cjs/onchain/index.js +87 -0
  19. package/dist/cjs/onchain/index.js.map +1 -0
  20. package/dist/cjs/package.json +3 -0
  21. package/dist/cjs/validate/index.d.ts +67 -0
  22. package/dist/cjs/validate/index.js +429 -0
  23. package/dist/cjs/validate/index.js.map +1 -0
  24. package/dist/esm/challenge/index.js +3 -0
  25. package/dist/esm/challenge/index.js.map +1 -0
  26. package/dist/esm/chunk-4NQ3VKC3.js +84 -0
  27. package/dist/esm/chunk-4NQ3VKC3.js.map +1 -0
  28. package/dist/esm/chunk-CGVXQX5V.js +426 -0
  29. package/dist/esm/chunk-CGVXQX5V.js.map +1 -0
  30. package/dist/esm/chunk-DJMCSCBE.js +3 -0
  31. package/dist/esm/chunk-DJMCSCBE.js.map +1 -0
  32. package/dist/esm/chunk-EAKQ4EFZ.js +124 -0
  33. package/dist/esm/chunk-EAKQ4EFZ.js.map +1 -0
  34. package/dist/esm/chunk-GBPU373M.js +93 -0
  35. package/dist/esm/chunk-GBPU373M.js.map +1 -0
  36. package/dist/esm/chunk-PU5Y7FZG.js +14 -0
  37. package/dist/esm/chunk-PU5Y7FZG.js.map +1 -0
  38. package/dist/esm/chunk-WU7QJ7YP.js +3 -0
  39. package/dist/esm/chunk-WU7QJ7YP.js.map +1 -0
  40. package/dist/esm/chunk-Y5HFLCAT.js +657 -0
  41. package/dist/esm/chunk-Y5HFLCAT.js.map +1 -0
  42. package/dist/esm/facilitator/index.js +4 -0
  43. package/dist/esm/facilitator/index.js.map +1 -0
  44. package/dist/esm/handlers/index.js +6 -0
  45. package/dist/esm/handlers/index.js.map +1 -0
  46. package/dist/esm/index.js +261 -0
  47. package/dist/esm/index.js.map +1 -0
  48. package/dist/esm/onchain/index.js +4 -0
  49. package/dist/esm/onchain/index.js.map +1 -0
  50. package/dist/esm/package.json +3 -0
  51. package/dist/esm/validate/index.js +4 -0
  52. package/dist/esm/validate/index.js.map +1 -0
  53. package/package.json +88 -0
@@ -0,0 +1,419 @@
1
+ import * as _bosonprotocol_x402_facilitator from '@bosonprotocol/x402-facilitator';
2
+ import * as _bosonprotocol_x402_actions from '@bosonprotocol/x402-actions';
3
+ import { ChannelRegistry } from '@bosonprotocol/x402-actions';
4
+ import { EvmNetwork, Address } from '@bosonprotocol/x402-core/schemes/escrow';
5
+ import { Hex } from 'viem';
6
+ import { z } from 'zod';
7
+ import { CoreSDK } from '@bosonprotocol/core-sdk';
8
+ import { ExchangeReader } from './onchain/index.js';
9
+
10
+ /** Funds entity returned by `coreSdk.getFunds` — narrowed to the fields the handler reshapes. */
11
+ interface CoreSdkFundsEntity {
12
+ id?: string;
13
+ accountId: string;
14
+ availableAmount: string;
15
+ token: {
16
+ address: string;
17
+ decimals: string;
18
+ symbol: string;
19
+ name: string;
20
+ };
21
+ }
22
+ /** Seller entity returned by `coreSdk.getSellersByAddress` — only the id is consumed. */
23
+ interface CoreSdkSellerEntity {
24
+ id: string;
25
+ }
26
+ /** Buyer entity returned by `coreSdk.getBuyers` — only the id is consumed. */
27
+ interface CoreSdkBuyerEntity {
28
+ id: string;
29
+ }
30
+ /**
31
+ * The subset of `CoreSDK` the read-only handlers actually use. Both the
32
+ * real `CoreSDK` and a hand-rolled stub for tests satisfy this shape.
33
+ */
34
+ interface CoreSdkReadAdapter {
35
+ getFunds(queryVars: {
36
+ fundsFilter: {
37
+ accountId: string;
38
+ };
39
+ }): Promise<CoreSdkFundsEntity[]>;
40
+ getSellersByAddress(address: string): Promise<CoreSdkSellerEntity[]>;
41
+ getBuyers(queryVars: {
42
+ buyersFilter: {
43
+ wallet: string;
44
+ };
45
+ }): Promise<CoreSdkBuyerEntity[]>;
46
+ }
47
+ /**
48
+ * Cast a full `CoreSDK` to the narrowed adapter shape. Structural-only;
49
+ * does not capture or wrap anything.
50
+ */
51
+ declare function asCoreSdkReadAdapter(coreSdk: CoreSDK): CoreSdkReadAdapter;
52
+
53
+ /**
54
+ * Minimal signing surface needed by `signFullOffer`. Structurally
55
+ * compatible with viem's `Account` (in particular `LocalAccount` from
56
+ * `privateKeyToAccount`), but kept narrow so consumers can plug in
57
+ * HSM- / KMS- / ERC-1271-backed signers without depending on viem's
58
+ * internal account types.
59
+ */
60
+ interface SellerSigner {
61
+ /** Address that the EIP-712 signature must recover to (`creator` in `BosonOfferRef`). */
62
+ readonly address: Address;
63
+ /** EIP-712 typed-data signer. Accepts the same shape viem's `signTypedData` does. */
64
+ signTypedData: (params: {
65
+ domain: Record<string, unknown>;
66
+ types: Record<string, readonly {
67
+ name: string;
68
+ type: string;
69
+ }[]>;
70
+ primaryType: string;
71
+ message: Record<string, unknown>;
72
+ }) => Promise<Hex>;
73
+ }
74
+ /**
75
+ * Per-server configuration. Validated by `x402bServerConfigSchema` at
76
+ * `createX402bServer` time so bad config fails fast at boot rather than
77
+ * later inside a 402 response.
78
+ */
79
+ interface X402bServerConfig {
80
+ /** CAIP-2 EVM network the server advertises (e.g. `eip155:8453`). */
81
+ network: EvmNetwork;
82
+ /** EVM chain id matching `network` — passed straight to the EIP-712 builder's salt. */
83
+ chainId: number;
84
+ /** Address of the Boson Diamond — the EIP-712 `verifyingContract` and the wire-level `escrowAddress`. */
85
+ escrow: Address;
86
+ /** Seller signer (signs the FullOffer EIP-712 typed-data). */
87
+ signer: SellerSigner;
88
+ /** Facilitator service the server forwards meta-tx envelopes to. The URL is also advertised in `nextActions[].endpoints.facilitator`. */
89
+ facilitator: {
90
+ url: string;
91
+ };
92
+ /** Per-seller channel + endpoint registry consumed by `deriveNextActions`. */
93
+ channelRegistry: ChannelRegistry;
94
+ /**
95
+ * Reader the convenience handlers use to verify the post-settle /
96
+ * post-perform-action exchange state. Required at runtime when any
97
+ * write handler is invoked; the `signOffer` / `buildPaymentRequirements`
98
+ * read-only paths don't touch it. See `./onchain/verify-exchange.ts`
99
+ * for the interface.
100
+ */
101
+ exchangeReader?: ExchangeReader;
102
+ /**
103
+ * URL of the Boson Protocol subgraph for this network. Used by the
104
+ * read-only `available-funds` handler and the address→entityId
105
+ * resolver shared with `withdraw-funds`. Optional at config time
106
+ * (legacy commit/redeem handlers don't need it); required at runtime
107
+ * by `handlers.withdrawFunds` / `handlers.getAvailableFunds`.
108
+ */
109
+ subgraphUrl?: string;
110
+ /**
111
+ * Pre-built read-only core-sdk client. If omitted, the server
112
+ * constructs one internally from `subgraphUrl` + `escrow` + `chainId`
113
+ * with a throwing `Web3LibAdapter` stub. Provide your own for tests
114
+ * or when you want to share a single core-sdk instance with the
115
+ * rest of your service.
116
+ */
117
+ coreSdkRead?: CoreSdkReadAdapter;
118
+ /**
119
+ * Server-side store of the fulfillment option ids advertised for
120
+ * each Flow A exchange (keyed by `exchangeId`). Populated from the
121
+ * original `PaymentRequirements` after commit verification and read
122
+ * at redeem time so the buyer's redeem-time fulfillment choice
123
+ * cannot switch to a channel the offer never advertised.
124
+ *
125
+ * Optional in the config: when omitted, `createX402bServer` wires up
126
+ * an in-memory `Map`. Hosts running multiple instances should plug
127
+ * in their own cross-process `Map`-shaped backing store.
128
+ */
129
+ exchangeFulfillmentOptionStore?: Map<string, readonly string[]>;
130
+ /**
131
+ * Pending fulfillment updates that reached REDEEMED on-chain but
132
+ * failed the server-side `channel.onCommit(...)` upsert. The commit
133
+ * handler records Flow B updates here before attempting the channel
134
+ * write; the redeem handler does the same for Flow A. Both delete the
135
+ * record on success and leave it behind with the error message on
136
+ * failure so the host can replay/reconcile out of band.
137
+ */
138
+ fulfillmentRecoveryStore?: Map<string, FulfillmentRecoveryEntry>;
139
+ /**
140
+ * Fulfillment channels the server accepts at redeem time when the
141
+ * client re-submits delivery data. Structurally a subset of
142
+ * `@bosonprotocol/x402-fulfillment`'s `FulfillmentChannel` — only
143
+ * `id`, `validate`, and `onCommit` are read here, so existing
144
+ * channel instances pass through directly. Required iff a host
145
+ * wants to accept redeem-time fulfillment updates; absent means
146
+ * redeem requests carrying `fulfillment` are rejected with
147
+ * `FULFILLMENT_CHANNELS_NOT_CONFIGURED`.
148
+ */
149
+ fulfillmentChannels?: readonly RedeemFulfillmentChannel[];
150
+ }
151
+ /**
152
+ * Minimal structural slice of `FulfillmentChannel` the redeem
153
+ * handler needs. Kept inline so `@bosonprotocol/x402-server` does
154
+ * not depend on `@bosonprotocol/x402-fulfillment` (avoids a hard
155
+ * coupling between the resource-server SDK and a specific channel
156
+ * package); any real channel implementation is type-compatible.
157
+ */
158
+ interface RedeemFulfillmentChannel {
159
+ readonly id: string;
160
+ validate(data: Record<string, unknown> | null): {
161
+ ok: true;
162
+ } | {
163
+ ok: false;
164
+ reason: string;
165
+ };
166
+ onCommit(exchangeId: string, data: Record<string, unknown> | null): Promise<void>;
167
+ }
168
+ interface FulfillmentRecoveryEntry {
169
+ exchangeId: string;
170
+ option: string;
171
+ data: Record<string, unknown> | null;
172
+ redeemer: Address;
173
+ recordedAt: number;
174
+ error?: string;
175
+ }
176
+ /**
177
+ * zod validator for `X402bServerConfig`. Shallow on the signer +
178
+ * exchange reader (viem account types bring their own structural
179
+ * validators; the user-supplied reader impl is structurally typed);
180
+ * strict on the scalar fields we own here, and on the channel
181
+ * registry via `@bosonprotocol/x402-actions`.
182
+ */
183
+ declare const x402bServerConfigSchema: z.ZodEffects<z.ZodObject<{
184
+ network: z.ZodString;
185
+ chainId: z.ZodNumber;
186
+ escrow: z.ZodString;
187
+ signer: z.ZodObject<{
188
+ address: z.ZodString;
189
+ signTypedData: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodPromise<z.ZodString>>;
190
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
191
+ address: z.ZodString;
192
+ signTypedData: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodPromise<z.ZodString>>;
193
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
194
+ address: z.ZodString;
195
+ signTypedData: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodPromise<z.ZodString>>;
196
+ }, z.ZodTypeAny, "passthrough">>;
197
+ facilitator: z.ZodObject<{
198
+ url: z.ZodEffects<z.ZodString, string, string>;
199
+ }, "strict", z.ZodTypeAny, {
200
+ url: string;
201
+ }, {
202
+ url: string;
203
+ }>;
204
+ channelRegistry: z.ZodObject<{
205
+ channels: z.ZodEffects<z.ZodArray<z.ZodEnum<[_bosonprotocol_x402_actions.ActionChannel, ..._bosonprotocol_x402_actions.ActionChannel[]]>, "many">, _bosonprotocol_x402_actions.ActionChannel[], _bosonprotocol_x402_actions.ActionChannel[]>;
206
+ endpoints: z.ZodOptional<z.ZodRecord<z.ZodEnum<[_bosonprotocol_x402_facilitator.ActionId, ..._bosonprotocol_x402_facilitator.ActionId[]]>, z.ZodEffects<z.ZodString, string, string>>>;
207
+ xmtp: z.ZodOptional<z.ZodString>;
208
+ mcp: z.ZodOptional<z.ZodString>;
209
+ escrow: z.ZodString;
210
+ }, "strict", z.ZodTypeAny, {
211
+ channels: _bosonprotocol_x402_actions.ActionChannel[];
212
+ escrow: string;
213
+ mcp?: string | undefined;
214
+ xmtp?: string | undefined;
215
+ endpoints?: Partial<Record<_bosonprotocol_x402_facilitator.ActionId, string>> | undefined;
216
+ }, {
217
+ channels: _bosonprotocol_x402_actions.ActionChannel[];
218
+ escrow: string;
219
+ mcp?: string | undefined;
220
+ xmtp?: string | undefined;
221
+ endpoints?: Partial<Record<_bosonprotocol_x402_facilitator.ActionId, string>> | undefined;
222
+ }>;
223
+ exchangeReader: z.ZodOptional<z.ZodObject<{
224
+ read: z.ZodFunction<z.ZodTuple<[z.ZodString], z.ZodUnknown>, z.ZodUnknown>;
225
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
226
+ read: z.ZodFunction<z.ZodTuple<[z.ZodString], z.ZodUnknown>, z.ZodUnknown>;
227
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
228
+ read: z.ZodFunction<z.ZodTuple<[z.ZodString], z.ZodUnknown>, z.ZodUnknown>;
229
+ }, z.ZodTypeAny, "passthrough">>>;
230
+ subgraphUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
231
+ coreSdkRead: z.ZodOptional<z.ZodObject<{
232
+ getFunds: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
233
+ getSellersByAddress: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
234
+ getBuyers: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
235
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
236
+ getFunds: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
237
+ getSellersByAddress: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
238
+ getBuyers: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
239
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
240
+ getFunds: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
241
+ getSellersByAddress: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
242
+ getBuyers: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
243
+ }, z.ZodTypeAny, "passthrough">>>;
244
+ exchangeFulfillmentOptionStore: z.ZodOptional<z.ZodType<Map<unknown, unknown>, z.ZodTypeDef, Map<unknown, unknown>>>;
245
+ fulfillmentRecoveryStore: z.ZodOptional<z.ZodType<Map<unknown, unknown>, z.ZodTypeDef, Map<unknown, unknown>>>;
246
+ fulfillmentChannels: z.ZodOptional<z.ZodEffects<z.ZodArray<z.ZodObject<{
247
+ id: z.ZodString;
248
+ validate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
249
+ onCommit: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
250
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
251
+ id: z.ZodString;
252
+ validate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
253
+ onCommit: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
254
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
255
+ id: z.ZodString;
256
+ validate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
257
+ onCommit: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
258
+ }, z.ZodTypeAny, "passthrough">>, "many">, z.objectOutputType<{
259
+ id: z.ZodString;
260
+ validate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
261
+ onCommit: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
262
+ }, z.ZodTypeAny, "passthrough">[], z.objectInputType<{
263
+ id: z.ZodString;
264
+ validate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
265
+ onCommit: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
266
+ }, z.ZodTypeAny, "passthrough">[]>>;
267
+ }, "strict", z.ZodTypeAny, {
268
+ chainId: number;
269
+ network: string;
270
+ escrow: string;
271
+ channelRegistry: {
272
+ channels: _bosonprotocol_x402_actions.ActionChannel[];
273
+ escrow: string;
274
+ mcp?: string | undefined;
275
+ xmtp?: string | undefined;
276
+ endpoints?: Partial<Record<_bosonprotocol_x402_facilitator.ActionId, string>> | undefined;
277
+ };
278
+ facilitator: {
279
+ url: string;
280
+ };
281
+ signer: {
282
+ address: string;
283
+ signTypedData: (args_0: unknown, ...args: unknown[]) => Promise<string>;
284
+ } & {
285
+ [k: string]: unknown;
286
+ };
287
+ subgraphUrl?: string | undefined;
288
+ exchangeReader?: z.objectOutputType<{
289
+ read: z.ZodFunction<z.ZodTuple<[z.ZodString], z.ZodUnknown>, z.ZodUnknown>;
290
+ }, z.ZodTypeAny, "passthrough"> | undefined;
291
+ coreSdkRead?: z.objectOutputType<{
292
+ getFunds: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
293
+ getSellersByAddress: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
294
+ getBuyers: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
295
+ }, z.ZodTypeAny, "passthrough"> | undefined;
296
+ exchangeFulfillmentOptionStore?: Map<unknown, unknown> | undefined;
297
+ fulfillmentRecoveryStore?: Map<unknown, unknown> | undefined;
298
+ fulfillmentChannels?: z.objectOutputType<{
299
+ id: z.ZodString;
300
+ validate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
301
+ onCommit: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
302
+ }, z.ZodTypeAny, "passthrough">[] | undefined;
303
+ }, {
304
+ chainId: number;
305
+ network: string;
306
+ escrow: string;
307
+ channelRegistry: {
308
+ channels: _bosonprotocol_x402_actions.ActionChannel[];
309
+ escrow: string;
310
+ mcp?: string | undefined;
311
+ xmtp?: string | undefined;
312
+ endpoints?: Partial<Record<_bosonprotocol_x402_facilitator.ActionId, string>> | undefined;
313
+ };
314
+ facilitator: {
315
+ url: string;
316
+ };
317
+ signer: {
318
+ address: string;
319
+ signTypedData: (args_0: unknown, ...args: unknown[]) => Promise<string>;
320
+ } & {
321
+ [k: string]: unknown;
322
+ };
323
+ subgraphUrl?: string | undefined;
324
+ exchangeReader?: z.objectInputType<{
325
+ read: z.ZodFunction<z.ZodTuple<[z.ZodString], z.ZodUnknown>, z.ZodUnknown>;
326
+ }, z.ZodTypeAny, "passthrough"> | undefined;
327
+ coreSdkRead?: z.objectInputType<{
328
+ getFunds: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
329
+ getSellersByAddress: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
330
+ getBuyers: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
331
+ }, z.ZodTypeAny, "passthrough"> | undefined;
332
+ exchangeFulfillmentOptionStore?: Map<unknown, unknown> | undefined;
333
+ fulfillmentRecoveryStore?: Map<unknown, unknown> | undefined;
334
+ fulfillmentChannels?: z.objectInputType<{
335
+ id: z.ZodString;
336
+ validate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
337
+ onCommit: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
338
+ }, z.ZodTypeAny, "passthrough">[] | undefined;
339
+ }>, {
340
+ chainId: number;
341
+ network: string;
342
+ escrow: string;
343
+ channelRegistry: {
344
+ channels: _bosonprotocol_x402_actions.ActionChannel[];
345
+ escrow: string;
346
+ mcp?: string | undefined;
347
+ xmtp?: string | undefined;
348
+ endpoints?: Partial<Record<_bosonprotocol_x402_facilitator.ActionId, string>> | undefined;
349
+ };
350
+ facilitator: {
351
+ url: string;
352
+ };
353
+ signer: {
354
+ address: string;
355
+ signTypedData: (args_0: unknown, ...args: unknown[]) => Promise<string>;
356
+ } & {
357
+ [k: string]: unknown;
358
+ };
359
+ subgraphUrl?: string | undefined;
360
+ exchangeReader?: z.objectOutputType<{
361
+ read: z.ZodFunction<z.ZodTuple<[z.ZodString], z.ZodUnknown>, z.ZodUnknown>;
362
+ }, z.ZodTypeAny, "passthrough"> | undefined;
363
+ coreSdkRead?: z.objectOutputType<{
364
+ getFunds: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
365
+ getSellersByAddress: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
366
+ getBuyers: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
367
+ }, z.ZodTypeAny, "passthrough"> | undefined;
368
+ exchangeFulfillmentOptionStore?: Map<unknown, unknown> | undefined;
369
+ fulfillmentRecoveryStore?: Map<unknown, unknown> | undefined;
370
+ fulfillmentChannels?: z.objectOutputType<{
371
+ id: z.ZodString;
372
+ validate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
373
+ onCommit: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
374
+ }, z.ZodTypeAny, "passthrough">[] | undefined;
375
+ }, {
376
+ chainId: number;
377
+ network: string;
378
+ escrow: string;
379
+ channelRegistry: {
380
+ channels: _bosonprotocol_x402_actions.ActionChannel[];
381
+ escrow: string;
382
+ mcp?: string | undefined;
383
+ xmtp?: string | undefined;
384
+ endpoints?: Partial<Record<_bosonprotocol_x402_facilitator.ActionId, string>> | undefined;
385
+ };
386
+ facilitator: {
387
+ url: string;
388
+ };
389
+ signer: {
390
+ address: string;
391
+ signTypedData: (args_0: unknown, ...args: unknown[]) => Promise<string>;
392
+ } & {
393
+ [k: string]: unknown;
394
+ };
395
+ subgraphUrl?: string | undefined;
396
+ exchangeReader?: z.objectInputType<{
397
+ read: z.ZodFunction<z.ZodTuple<[z.ZodString], z.ZodUnknown>, z.ZodUnknown>;
398
+ }, z.ZodTypeAny, "passthrough"> | undefined;
399
+ coreSdkRead?: z.objectInputType<{
400
+ getFunds: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
401
+ getSellersByAddress: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
402
+ getBuyers: z.ZodFunction<z.ZodTuple<[z.ZodUnknown], z.ZodUnknown>, z.ZodUnknown>;
403
+ }, z.ZodTypeAny, "passthrough"> | undefined;
404
+ exchangeFulfillmentOptionStore?: Map<unknown, unknown> | undefined;
405
+ fulfillmentRecoveryStore?: Map<unknown, unknown> | undefined;
406
+ fulfillmentChannels?: z.objectInputType<{
407
+ id: z.ZodString;
408
+ validate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
409
+ onCommit: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
410
+ }, z.ZodTypeAny, "passthrough">[] | undefined;
411
+ }>;
412
+ /**
413
+ * Cross-field invariant: `config.escrow` and
414
+ * `config.channelRegistry.escrow` must agree — the Diamond address is
415
+ * the same on both sides. Run this after `x402bServerConfigSchema.parse`.
416
+ */
417
+ declare function assertChannelRegistryEscrowMatch(config: X402bServerConfig): void;
418
+
419
+ export { type CoreSdkBuyerEntity as C, type FulfillmentRecoveryEntry as F, type RedeemFulfillmentChannel as R, type SellerSigner as S, type X402bServerConfig as X, type CoreSdkFundsEntity as a, type CoreSdkReadAdapter as b, type CoreSdkSellerEntity as c, asCoreSdkReadAdapter as d, assertChannelRegistryEscrowMatch as e, x402bServerConfigSchema as x };
@@ -0,0 +1,26 @@
1
+ export { C as CreateFacilitatorClientOptions, F as FacilitatorClient, a as FetchLike, c as createFacilitatorClient } from '../client-tnrpqVMW.js';
2
+ import { FacilitatorErrorCode } from '@bosonprotocol/x402-facilitator';
3
+ export { FacilitatorErrorCode, FacilitatorPerformActionInput, FacilitatorPerformActionResult, FacilitatorSettleInput, FacilitatorSettleResult, FacilitatorVerifyInput, FacilitatorVerifyResult } from '@bosonprotocol/x402-facilitator';
4
+
5
+ /**
6
+ * Stable, x402-server-side codes for HTTP-transport failures.
7
+ * Distinct from the facilitator-side `FacilitatorErrorCode` enum so
8
+ * callers can branch on "the facilitator answered with X" vs "we
9
+ * never reached the facilitator".
10
+ */
11
+ type FacilitatorHttpErrorCode = "NETWORK_ERROR" | "BAD_HTTP_STATUS" | "BAD_RESPONSE_BODY" | "TIMEOUT";
12
+ /** Network- or transport-layer failure when calling the facilitator. */
13
+ declare class FacilitatorHttpError extends Error {
14
+ readonly code: FacilitatorHttpErrorCode;
15
+ readonly status?: number;
16
+ /** Original facilitator-side error code, if the body parsed cleanly. */
17
+ readonly facilitatorCode?: FacilitatorErrorCode;
18
+ constructor(message: string, init: {
19
+ code: FacilitatorHttpErrorCode;
20
+ status?: number;
21
+ facilitatorCode?: FacilitatorErrorCode;
22
+ cause?: unknown;
23
+ });
24
+ }
25
+
26
+ export { FacilitatorHttpError, type FacilitatorHttpErrorCode };
@@ -0,0 +1,136 @@
1
+ 'use strict';
2
+
3
+ // src/facilitator/errors.ts
4
+ var FacilitatorHttpError = class extends Error {
5
+ constructor(message, init) {
6
+ super(message, init.cause !== void 0 ? { cause: init.cause } : void 0);
7
+ this.name = "FacilitatorHttpError";
8
+ this.code = init.code;
9
+ if (init.status !== void 0) this.status = init.status;
10
+ if (init.facilitatorCode !== void 0) this.facilitatorCode = init.facilitatorCode;
11
+ }
12
+ };
13
+
14
+ // src/facilitator/client.ts
15
+ function createFacilitatorClient(opts) {
16
+ const fetchImpl = opts.fetch ?? globalThis.fetch;
17
+ if (fetchImpl === void 0) {
18
+ throw new Error(
19
+ "createFacilitatorClient: no `fetch` implementation available. Pass `opts.fetch` explicitly."
20
+ );
21
+ }
22
+ const baseUrl = opts.url.replace(/\/+$/, "");
23
+ const baseHeaders = { "content-type": "application/json", ...opts.headers ?? {} };
24
+ const post = async (path, body, validate) => {
25
+ let res;
26
+ try {
27
+ res = await fetchImpl(`${baseUrl}${path}`, {
28
+ method: "POST",
29
+ headers: baseHeaders,
30
+ body: JSON.stringify(body)
31
+ });
32
+ } catch (cause) {
33
+ throw new FacilitatorHttpError(`facilitator network error (${path})`, {
34
+ code: "NETWORK_ERROR",
35
+ cause
36
+ });
37
+ }
38
+ let text;
39
+ try {
40
+ text = await res.text();
41
+ } catch (cause) {
42
+ throw new FacilitatorHttpError(`facilitator response body could not be read (${path})`, {
43
+ code: "BAD_RESPONSE_BODY",
44
+ status: res.status,
45
+ cause
46
+ });
47
+ }
48
+ let parsed;
49
+ try {
50
+ parsed = text.length === 0 ? null : JSON.parse(text);
51
+ } catch (cause) {
52
+ throw new FacilitatorHttpError(`facilitator returned non-JSON body (${path})`, {
53
+ code: "BAD_RESPONSE_BODY",
54
+ status: res.status,
55
+ cause
56
+ });
57
+ }
58
+ if (!res.ok) {
59
+ if (res.status === 400 && isFailureBranch(parsed) && validate(parsed)) {
60
+ return parsed;
61
+ }
62
+ const facilitatorCode = extractFacilitatorCode(parsed);
63
+ throw new FacilitatorHttpError(
64
+ `facilitator HTTP ${res.status} (${path}): ${reasonString(parsed) ?? text}`,
65
+ {
66
+ code: "BAD_HTTP_STATUS",
67
+ status: res.status,
68
+ ...facilitatorCode !== void 0 ? { facilitatorCode } : {}
69
+ }
70
+ );
71
+ }
72
+ if (!validate(parsed)) {
73
+ throw new FacilitatorHttpError(`facilitator returned unexpected body shape (${path})`, {
74
+ code: "BAD_RESPONSE_BODY",
75
+ status: res.status
76
+ });
77
+ }
78
+ return parsed;
79
+ };
80
+ return {
81
+ verify: (input) => post("/verify", input, isVerifyResult),
82
+ settle: (input) => post("/settle", input, isSettleResult),
83
+ performAction: (input) => post(
84
+ `/perform-action?action=${encodeURIComponent(input.action)}`,
85
+ input,
86
+ isPerformActionResult
87
+ )
88
+ };
89
+ }
90
+ function isObject(v) {
91
+ return v !== null && typeof v === "object";
92
+ }
93
+ function isFailureBranch(v) {
94
+ return isObject(v) && v.ok === false && typeof v.code === "string" && typeof v.reason === "string";
95
+ }
96
+ function isVerifyResult(parsed) {
97
+ if (!isObject(parsed)) return false;
98
+ if (parsed.ok === true) return true;
99
+ return isFailureBranch(parsed);
100
+ }
101
+ function isSettleResult(parsed) {
102
+ if (!isObject(parsed)) return false;
103
+ if (parsed.ok === true) {
104
+ return typeof parsed.exchangeId === "string" && typeof parsed.txHash === "string";
105
+ }
106
+ return isFailureBranch(parsed);
107
+ }
108
+ function isPerformActionResult(parsed) {
109
+ if (!isObject(parsed)) return false;
110
+ if (parsed.ok === true) {
111
+ if (typeof parsed.txHash !== "string") return false;
112
+ if (parsed.newExchangeState !== void 0 && typeof parsed.newExchangeState !== "string") {
113
+ return false;
114
+ }
115
+ if (parsed.newDisputeState !== void 0 && typeof parsed.newDisputeState !== "string") {
116
+ return false;
117
+ }
118
+ return true;
119
+ }
120
+ return isFailureBranch(parsed);
121
+ }
122
+ function extractFacilitatorCode(body) {
123
+ if (body === null || typeof body !== "object") return void 0;
124
+ const code = body.code;
125
+ return typeof code === "string" ? code : void 0;
126
+ }
127
+ function reasonString(body) {
128
+ if (body === null || typeof body !== "object") return void 0;
129
+ const reason = body.reason;
130
+ return typeof reason === "string" ? reason : void 0;
131
+ }
132
+
133
+ exports.FacilitatorHttpError = FacilitatorHttpError;
134
+ exports.createFacilitatorClient = createFacilitatorClient;
135
+ //# sourceMappingURL=index.js.map
136
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/facilitator/errors.ts","../../../src/facilitator/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Typed error class for HTTP-level facilitator failures. Distinct from\n// the `{ ok: false, code }` shape the facilitator itself returns —\n// those are domain errors and surface in the typed result. This class\n// is thrown only when the HTTP transport fails (network error,\n// non-2xx response with an unparseable body, etc.).\n\nimport type { FacilitatorErrorCode } from \"@bosonprotocol/x402-facilitator\";\n\n/**\n * Stable, x402-server-side codes for HTTP-transport failures.\n * Distinct from the facilitator-side `FacilitatorErrorCode` enum so\n * callers can branch on \"the facilitator answered with X\" vs \"we\n * never reached the facilitator\".\n */\nexport type FacilitatorHttpErrorCode =\n | \"NETWORK_ERROR\"\n | \"BAD_HTTP_STATUS\"\n | \"BAD_RESPONSE_BODY\"\n | \"TIMEOUT\";\n\n/** Network- or transport-layer failure when calling the facilitator. */\nexport class FacilitatorHttpError extends Error {\n readonly code: FacilitatorHttpErrorCode;\n readonly status?: number;\n /** Original facilitator-side error code, if the body parsed cleanly. */\n readonly facilitatorCode?: FacilitatorErrorCode;\n\n constructor(\n message: string,\n init: {\n code: FacilitatorHttpErrorCode;\n status?: number;\n facilitatorCode?: FacilitatorErrorCode;\n cause?: unknown;\n },\n ) {\n super(message, init.cause !== undefined ? { cause: init.cause } : undefined);\n this.name = \"FacilitatorHttpError\";\n this.code = init.code;\n if (init.status !== undefined) this.status = init.status;\n if (init.facilitatorCode !== undefined) this.facilitatorCode = init.facilitatorCode;\n }\n}\n","// HTTP client for the Boson facilitator service. The facilitator runs\n// as a remote process implementing the three endpoints in\n// docs/boson-impl-07-facilitator.md (`/verify`, `/settle`,\n// `/perform-action?action=<action>`); this module wraps `fetch` with the typed\n// request/response shapes exported by `@bosonprotocol/x402-facilitator`.\n//\n// Domain results (`{ ok: true, ... }` or `{ ok: false, code, reason }`)\n// are returned verbatim — both shapes are answers the caller branches\n// on. The facilitator-express adapter emits domain failures over HTTP\n// 400 (so curl users still see a 4xx), but the wire body is still a\n// well-formed result; we recognise that shape on HTTP 400 responses and\n// hand it back rather than throwing. HTTP-transport failures (network\n// down, non-JSON body, schema-mismatched body) throw\n// `FacilitatorHttpError` so the composition layer can distinguish\n// \"facilitator said no\" from \"we couldn't reach the facilitator\".\n\nimport type {\n FacilitatorErrorCode,\n FacilitatorPerformActionInput,\n FacilitatorPerformActionResult,\n FacilitatorSettleInput,\n FacilitatorSettleResult,\n FacilitatorVerifyInput,\n FacilitatorVerifyResult,\n} from \"@bosonprotocol/x402-facilitator\";\n\nimport { FacilitatorHttpError } from \"./errors.js\";\n\nexport type FetchLike = (\n input: string,\n init?: { method?: string; headers?: Record<string, string>; body?: string },\n) => Promise<{\n ok: boolean;\n status: number;\n text: () => Promise<string>;\n json?: () => Promise<unknown>;\n}>;\n\nexport interface CreateFacilitatorClientOptions {\n /** Base URL of the facilitator service (no trailing slash needed). */\n url: string;\n /** Optional `fetch` override — defaults to the global `fetch`. Useful for tests + non-`fetch` runtimes. */\n fetch?: FetchLike;\n /** Optional headers attached to every request — e.g. an `Authorization` for hosted facilitators. */\n headers?: Record<string, string>;\n}\n\nexport interface FacilitatorClient {\n verify(input: FacilitatorVerifyInput): Promise<FacilitatorVerifyResult>;\n settle(input: FacilitatorSettleInput): Promise<FacilitatorSettleResult>;\n performAction(input: FacilitatorPerformActionInput): Promise<FacilitatorPerformActionResult>;\n}\n\n/**\n * Construct a typed HTTP client for the facilitator. The returned\n * methods stringify the input as JSON and POST to the matching path\n * on the configured URL. Bodies matching the well-formed result shape\n * (whether HTTP 2xx success or HTTP 400 domain rejection) are returned\n * verbatim; transport failures (network, non-JSON body,\n * schema-mismatched body) raise `FacilitatorHttpError`.\n */\nexport function createFacilitatorClient(opts: CreateFacilitatorClientOptions): FacilitatorClient {\n const fetchImpl = opts.fetch ?? (globalThis.fetch as FetchLike | undefined);\n if (fetchImpl === undefined) {\n throw new Error(\n \"createFacilitatorClient: no `fetch` implementation available. Pass `opts.fetch` explicitly.\",\n );\n }\n const baseUrl = opts.url.replace(/\\/+$/, \"\");\n const baseHeaders = { \"content-type\": \"application/json\", ...(opts.headers ?? {}) };\n\n const post = async <Req, Res>(\n path: string,\n body: Req,\n validate: (parsed: unknown) => parsed is Res,\n ): Promise<Res> => {\n let res: Awaited<ReturnType<FetchLike>>;\n try {\n res = await fetchImpl(`${baseUrl}${path}`, {\n method: \"POST\",\n headers: baseHeaders,\n body: JSON.stringify(body),\n });\n } catch (cause) {\n throw new FacilitatorHttpError(`facilitator network error (${path})`, {\n code: \"NETWORK_ERROR\",\n cause,\n });\n }\n\n let text: string;\n try {\n text = await res.text();\n } catch (cause) {\n throw new FacilitatorHttpError(`facilitator response body could not be read (${path})`, {\n code: \"BAD_RESPONSE_BODY\",\n status: res.status,\n cause,\n });\n }\n\n let parsed: unknown;\n try {\n parsed = text.length === 0 ? null : JSON.parse(text);\n } catch (cause) {\n throw new FacilitatorHttpError(`facilitator returned non-JSON body (${path})`, {\n code: \"BAD_RESPONSE_BODY\",\n status: res.status,\n cause,\n });\n }\n\n if (!res.ok) {\n // The facilitator-express adapter returns domain failures as\n // HTTP 400 with the `{ok:false, code, reason}` body. Detect that\n // shape and surface it as the typed result — every endpoint's\n // `Res` union includes the same `{ok:false, code, reason}`\n // variant, so the cast through `validate` (which also accepts\n // that shape) preserves type-safety. Any other non-2xx status is\n // a genuine transport failure even if its body happens to look\n // like a facilitator result.\n if (res.status === 400 && isFailureBranch(parsed) && validate(parsed)) {\n return parsed;\n }\n const facilitatorCode = extractFacilitatorCode(parsed);\n throw new FacilitatorHttpError(\n `facilitator HTTP ${res.status} (${path}): ${reasonString(parsed) ?? text}`,\n {\n code: \"BAD_HTTP_STATUS\",\n status: res.status,\n ...(facilitatorCode !== undefined ? { facilitatorCode } : {}),\n },\n );\n }\n\n if (!validate(parsed)) {\n throw new FacilitatorHttpError(`facilitator returned unexpected body shape (${path})`, {\n code: \"BAD_RESPONSE_BODY\",\n status: res.status,\n });\n }\n return parsed;\n };\n\n return {\n verify: (input) =>\n post<FacilitatorVerifyInput, FacilitatorVerifyResult>(\"/verify\", input, isVerifyResult),\n settle: (input) =>\n post<FacilitatorSettleInput, FacilitatorSettleResult>(\"/settle\", input, isSettleResult),\n performAction: (input) =>\n post<FacilitatorPerformActionInput, FacilitatorPerformActionResult>(\n `/perform-action?action=${encodeURIComponent(input.action)}`,\n input,\n isPerformActionResult,\n ),\n };\n}\n\n// --- Response-shape type guards ----------------------------------------\n//\n// A buggy or malicious facilitator could return any 2xx JSON; without a\n// runtime check, `parsed as Res` would silently slip an unexpected shape\n// past the type system and the convenience handlers (PR 4) would\n// dereference fields that aren't there. These guards mirror the\n// discriminated unions in `@bosonprotocol/x402-facilitator`'s types and\n// fail any response that doesn't fit — surfaced as\n// `BAD_RESPONSE_BODY` so the caller treats it as a transport-layer\n// failure rather than a domain answer.\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === \"object\";\n}\n\n/**\n * Failure branch is identical across all three endpoints. Accepts\n * `unknown` so the non-2xx path can pre-check the parsed body without\n * narrowing first.\n */\nfunction isFailureBranch(v: unknown): v is { ok: false; code: string; reason: string } {\n return (\n isObject(v) && v.ok === false && typeof v.code === \"string\" && typeof v.reason === \"string\"\n );\n}\n\nfunction isVerifyResult(parsed: unknown): parsed is FacilitatorVerifyResult {\n if (!isObject(parsed)) return false;\n if (parsed.ok === true) return true; // `{ ok: true }` carries no further fields\n return isFailureBranch(parsed);\n}\n\nfunction isSettleResult(parsed: unknown): parsed is FacilitatorSettleResult {\n if (!isObject(parsed)) return false;\n if (parsed.ok === true) {\n return typeof parsed.exchangeId === \"string\" && typeof parsed.txHash === \"string\";\n }\n return isFailureBranch(parsed);\n}\n\nfunction isPerformActionResult(parsed: unknown): parsed is FacilitatorPerformActionResult {\n if (!isObject(parsed)) return false;\n if (parsed.ok === true) {\n if (typeof parsed.txHash !== \"string\") return false;\n // Entity-keyed actions (e.g. `boson-withdrawFunds`) return just\n // `{ ok: true, txHash }` — no exchange state transition. Accept\n // that shape; only require `newExchangeState` when present.\n if (parsed.newExchangeState !== undefined && typeof parsed.newExchangeState !== \"string\") {\n return false;\n }\n if (parsed.newDisputeState !== undefined && typeof parsed.newDisputeState !== \"string\") {\n return false;\n }\n return true;\n }\n return isFailureBranch(parsed);\n}\n\nfunction extractFacilitatorCode(body: unknown): FacilitatorErrorCode | undefined {\n if (body === null || typeof body !== \"object\") return undefined;\n const code = (body as { code?: unknown }).code;\n return typeof code === \"string\" ? (code as FacilitatorErrorCode) : undefined;\n}\n\nfunction reasonString(body: unknown): string | undefined {\n if (body === null || typeof body !== \"object\") return undefined;\n const reason = (body as { reason?: unknown }).reason;\n return typeof reason === \"string\" ? reason : undefined;\n}\n"]}