@lunora/x402 1.0.0-alpha.2 → 1.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @lunora/x402
2
2
 
3
+ > **Experimental** — this package is outside the Lunora 1.0 stability promise: its API may change in any release, without a major version bump.
4
+
3
5
  > Agentic payments over the [x402](https://x402.org) protocol for [Lunora](https://lunora.sh).
4
6
 
5
7
  x402 turns HTTP `402 Payment Required` into a machine-payable rail: no accounts,
@@ -1,5 +1,5 @@
1
- import { F as FacilitatorConfig, c as X402ChargeConfig, f as X402Price } from "../packem_shared/config.d-CddwCiBm.mjs";
2
- export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, type a as EvmAddress, type P as PaymentEventRow, type d as X402Network, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2, x as toPaymentEventRow, y as toReceipt } from "../packem_shared/config.d-CddwCiBm.mjs";
1
+ import { F as FacilitatorConfig, c as X402ChargeConfig, f as X402Price } from "../packem_shared/config.d-5Nqi5iox.mjs";
2
+ export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, type a as EvmAddress, type P as PaymentEventRow, type d as X402Network, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2, x as toPaymentEventRow, y as toReceipt } from "../packem_shared/config.d-5Nqi5iox.mjs";
3
3
  import { HTTPFacilitatorClient } from '@x402/core/server';
4
4
  import { RouteConfig } from '@x402/core/http';
5
5
  import '@x402/evm';
@@ -14,21 +14,44 @@ import '@x402/core/types';
14
14
  * `supported`), so the same header map is handed to each. With no config the
15
15
  * client points at the public {@link resolveFacilitatorUrl default} and sends no
16
16
  * auth headers.
17
+ * @experimental
17
18
  */
18
19
  declare const createFacilitatorClient: (config?: FacilitatorConfig) => HTTPFacilitatorClient;
19
- /** A handler shaped like a Lunora HTTP action: `(context, request) => Response`. */
20
+ /**
21
+ * A handler shaped like a Lunora HTTP action: `(context, request) => Response`.
22
+ * @experimental
23
+ */
20
24
  type HttpActionHandler<Context> = (context: Context, request: Request) => Promise<Response> | Response;
21
25
  /**
22
26
  * Gate `handler` behind an x402 paywall described by `config`. Returns a handler
23
27
  * of the same shape, ready to pass to `httpAction`.
28
+ * @experimental
24
29
  */
25
30
  declare const withX402: <Context>(config: X402ChargeConfig, handler: HttpActionHandler<Context>) => HttpActionHandler<Context>;
26
- /** Runs the protected resource handler, producing the Response to gate. */
31
+ /**
32
+ * Runs the protected resource handler, producing the Response to gate.
33
+ * @experimental
34
+ */
27
35
  type ChargeHandler = () => Promise<Response> | Response;
28
- /** A prepared, initialised paywall. Build once (it fetches facilitator support), reuse per request. */
36
+ /**
37
+ * Per-request platform seams `handle` can use, beyond the request/handler pair.
38
+ * @experimental
39
+ */
40
+ interface ChargeHandlerDeps {
41
+ /**
42
+ * Keep background work (the receipt sink) alive past the response — the
43
+ * request's `ctx.waitUntil`. Absent on paths with no platform execution
44
+ * context reaching the middleware (e.g. today's HTTP-action rail).
45
+ */
46
+ readonly waitUntil?: (promise: Promise<unknown>) => void;
47
+ }
48
+ /**
49
+ * A prepared, initialised paywall. Build once (it fetches facilitator support), reuse per request.
50
+ * @experimental
51
+ */
29
52
  interface ChargeMiddleware {
30
53
  /** Gate `request`: challenge / verify / settle around `runHandler`. */
31
- handle: (request: Request, runHandler: ChargeHandler) => Promise<Response>;
54
+ handle: (request: Request, runHandler: ChargeHandler, deps?: ChargeHandlerDeps) => Promise<Response>;
32
55
  }
33
56
  /**
34
57
  * Route metadata a caller can layer onto the generated catch-all route. The
@@ -36,22 +59,54 @@ interface ChargeMiddleware {
36
59
  * names the paid function (x402 core falls back to the request URL otherwise —
37
60
  * every RPC POSTs to the same `/_lunora/rpc`, so the URL can't tell two paid
38
61
  * procedures apart).
62
+ * @experimental
39
63
  */
40
64
  type ChargeRouteOverrides = Pick<RouteConfig, "description" | "resource">;
41
65
  /**
66
+ * Behaviour knobs for {@link createChargeMiddleware} beyond route metadata.
67
+ * @experimental
68
+ */
69
+ interface ChargeMiddlewareOptions {
70
+ /**
71
+ * Settle the verified payment **before** dispatching `runHandler`, instead
72
+ * of after. Use this for a mutation/procedure gate: a settlement failure
73
+ * then means the handler never runs at all, so a paid mutation's writes can
74
+ * never be committed without payment (the free-execution gap X402-04
75
+ * closes). Once settlement succeeds the payment is final (on-chain) — a
76
+ * handler failure after that point is a normal application error, not a
77
+ * payment to unwind: there is nothing left to cancel, so it is not caught
78
+ * here and simply propagates.
79
+ *
80
+ * Default `false` (settle-after, the historical behaviour): the handler's
81
+ * response is passed to settlement as transport context (`responseHeaders`
82
+ * — read by some schemes for settlement overrides), and a handler throw
83
+ * still releases the verified-but-unsettled payment via
84
+ * `cancellationDispatcher.cancel`. Because settlement can still fail after
85
+ * the handler already ran on this path, `.x402()` handlers gated this way
86
+ * MUST be idempotent or compensatable — see the `@lunora/x402` charge docs.
87
+ */
88
+ readonly settleBeforeHandler?: boolean;
89
+ }
90
+ /**
42
91
  * Build and initialise a {@link ChargeMiddleware} for `config`. Fetches
43
92
  * facilitator support once (via `initialize()`), so call this once per config
44
93
  * and reuse the result across requests. `routeOverrides` layers extra route
45
- * metadata (e.g. `resource`) onto the generated catch-all route.
94
+ * metadata (e.g. `resource`) onto the generated catch-all route; `options`
95
+ * controls settlement ordering (see {@link ChargeMiddlewareOptions}).
96
+ * @experimental
46
97
  */
47
- declare const createChargeMiddleware: (config: X402ChargeConfig, routeOverrides?: ChargeRouteOverrides) => Promise<ChargeMiddleware>;
98
+ declare const createChargeMiddleware: (config: X402ChargeConfig, routeOverrides?: ChargeRouteOverrides, options?: ChargeMiddlewareOptions) => Promise<ChargeMiddleware>;
48
99
  /**
49
100
  * Charge config for the procedure gate: the worker-level settlement vocabulary
50
101
  * (network, recipient, facilitator) minus `price` — price is per-procedure and
51
102
  * arrives with each {@link X402ProcedureSpec}.
103
+ * @experimental
52
104
  */
53
105
  type X402ProcedureChargeConfig = Omit<X402ChargeConfig, "price">;
54
- /** The per-RPC charge spec the runtime passes the gate for each paid dispatch. */
106
+ /**
107
+ * The per-RPC charge spec the runtime passes the gate for each paid dispatch.
108
+ * @experimental
109
+ */
55
110
  interface X402ProcedureSpec {
56
111
  /** The `file:function` id of the paid procedure; becomes the x402 challenge `resource`. */
57
112
  readonly functionPath: string;
@@ -62,15 +117,23 @@ interface X402ProcedureSpec {
62
117
  * Gate one paid RPC. Returns a real `402` + `PAYMENT-REQUIRED` challenge when the
63
118
  * request is unpaid, or the dispatched response (with `X-PAYMENT-RESPONSE`
64
119
  * attached) once the client's `X-PAYMENT` is verified and settled. `dispatch`
65
- * runs the actual shard forward — it is only invoked after payment is verified.
120
+ * runs the actual shard forward — settlement happens **before** `dispatch` is
121
+ * invoked (settle-first), so a settlement failure means the shard forward
122
+ * (the mutation's commit) never runs at all — no committed-but-unpaid write is
123
+ * possible. `deps.waitUntil`, when supplied (the request's `ctx.waitUntil`),
124
+ * keeps the opt-in receipt sink alive past the response.
125
+ * @experimental
66
126
  */
67
- type X402ProcedureChargeGate = (request: Request, spec: X402ProcedureSpec, dispatch: () => Promise<Response>) => Promise<Response>;
127
+ type X402ProcedureChargeGate = (request: Request, spec: X402ProcedureSpec, dispatch: () => Promise<Response>, deps?: ChargeHandlerDeps) => Promise<Response>;
68
128
  /**
69
129
  * Build the injectable procedure charge gate for `config`. One initialised
70
130
  * {@link ChargeMiddleware} is memoised per `functionPath` (each bakes that
71
131
  * function's price + `resource`), since `createChargeMiddleware` fetches
72
132
  * facilitator support on first use. A failed init is not cached, so a transient
73
- * facilitator outage retries on the next request.
133
+ * facilitator outage retries on the next request. Settlement runs before
134
+ * `dispatch` (`settleBeforeHandler: true`) since `dispatch` commits the
135
+ * procedure's real mutation — see `createChargeMiddleware`'s `ChargeMiddlewareOptions`.
136
+ * @experimental
74
137
  */
75
138
  declare const createProcedureChargeGate: (config: X402ProcedureChargeConfig) => X402ProcedureChargeGate;
76
- export { type ChargeHandler, type ChargeMiddleware, type ChargeRouteOverrides, type FacilitatorConfig, type HttpActionHandler, type X402ChargeConfig, type X402Price, type X402ProcedureChargeConfig, type X402ProcedureChargeGate, type X402ProcedureSpec, createChargeMiddleware, createFacilitatorClient, createProcedureChargeGate, withX402 };
139
+ export { type ChargeHandler, type ChargeHandlerDeps, type ChargeMiddleware, type ChargeMiddlewareOptions, type ChargeRouteOverrides, type FacilitatorConfig, type HttpActionHandler, type X402ChargeConfig, type X402Price, type X402ProcedureChargeConfig, type X402ProcedureChargeGate, type X402ProcedureSpec, createChargeMiddleware, createFacilitatorClient, createProcedureChargeGate, withX402 };
@@ -1,5 +1,5 @@
1
- import { F as FacilitatorConfig, c as X402ChargeConfig, f as X402Price } from "../packem_shared/config.d-CddwCiBm.js";
2
- export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, type a as EvmAddress, type P as PaymentEventRow, type d as X402Network, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2, x as toPaymentEventRow, y as toReceipt } from "../packem_shared/config.d-CddwCiBm.js";
1
+ import { F as FacilitatorConfig, c as X402ChargeConfig, f as X402Price } from "../packem_shared/config.d-5Nqi5iox.js";
2
+ export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, type a as EvmAddress, type P as PaymentEventRow, type d as X402Network, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2, x as toPaymentEventRow, y as toReceipt } from "../packem_shared/config.d-5Nqi5iox.js";
3
3
  import { HTTPFacilitatorClient } from '@x402/core/server';
4
4
  import { RouteConfig } from '@x402/core/http';
5
5
  import '@x402/evm';
@@ -14,21 +14,44 @@ import '@x402/core/types';
14
14
  * `supported`), so the same header map is handed to each. With no config the
15
15
  * client points at the public {@link resolveFacilitatorUrl default} and sends no
16
16
  * auth headers.
17
+ * @experimental
17
18
  */
18
19
  declare const createFacilitatorClient: (config?: FacilitatorConfig) => HTTPFacilitatorClient;
19
- /** A handler shaped like a Lunora HTTP action: `(context, request) => Response`. */
20
+ /**
21
+ * A handler shaped like a Lunora HTTP action: `(context, request) => Response`.
22
+ * @experimental
23
+ */
20
24
  type HttpActionHandler<Context> = (context: Context, request: Request) => Promise<Response> | Response;
21
25
  /**
22
26
  * Gate `handler` behind an x402 paywall described by `config`. Returns a handler
23
27
  * of the same shape, ready to pass to `httpAction`.
28
+ * @experimental
24
29
  */
25
30
  declare const withX402: <Context>(config: X402ChargeConfig, handler: HttpActionHandler<Context>) => HttpActionHandler<Context>;
26
- /** Runs the protected resource handler, producing the Response to gate. */
31
+ /**
32
+ * Runs the protected resource handler, producing the Response to gate.
33
+ * @experimental
34
+ */
27
35
  type ChargeHandler = () => Promise<Response> | Response;
28
- /** A prepared, initialised paywall. Build once (it fetches facilitator support), reuse per request. */
36
+ /**
37
+ * Per-request platform seams `handle` can use, beyond the request/handler pair.
38
+ * @experimental
39
+ */
40
+ interface ChargeHandlerDeps {
41
+ /**
42
+ * Keep background work (the receipt sink) alive past the response — the
43
+ * request's `ctx.waitUntil`. Absent on paths with no platform execution
44
+ * context reaching the middleware (e.g. today's HTTP-action rail).
45
+ */
46
+ readonly waitUntil?: (promise: Promise<unknown>) => void;
47
+ }
48
+ /**
49
+ * A prepared, initialised paywall. Build once (it fetches facilitator support), reuse per request.
50
+ * @experimental
51
+ */
29
52
  interface ChargeMiddleware {
30
53
  /** Gate `request`: challenge / verify / settle around `runHandler`. */
31
- handle: (request: Request, runHandler: ChargeHandler) => Promise<Response>;
54
+ handle: (request: Request, runHandler: ChargeHandler, deps?: ChargeHandlerDeps) => Promise<Response>;
32
55
  }
33
56
  /**
34
57
  * Route metadata a caller can layer onto the generated catch-all route. The
@@ -36,22 +59,54 @@ interface ChargeMiddleware {
36
59
  * names the paid function (x402 core falls back to the request URL otherwise —
37
60
  * every RPC POSTs to the same `/_lunora/rpc`, so the URL can't tell two paid
38
61
  * procedures apart).
62
+ * @experimental
39
63
  */
40
64
  type ChargeRouteOverrides = Pick<RouteConfig, "description" | "resource">;
41
65
  /**
66
+ * Behaviour knobs for {@link createChargeMiddleware} beyond route metadata.
67
+ * @experimental
68
+ */
69
+ interface ChargeMiddlewareOptions {
70
+ /**
71
+ * Settle the verified payment **before** dispatching `runHandler`, instead
72
+ * of after. Use this for a mutation/procedure gate: a settlement failure
73
+ * then means the handler never runs at all, so a paid mutation's writes can
74
+ * never be committed without payment (the free-execution gap X402-04
75
+ * closes). Once settlement succeeds the payment is final (on-chain) — a
76
+ * handler failure after that point is a normal application error, not a
77
+ * payment to unwind: there is nothing left to cancel, so it is not caught
78
+ * here and simply propagates.
79
+ *
80
+ * Default `false` (settle-after, the historical behaviour): the handler's
81
+ * response is passed to settlement as transport context (`responseHeaders`
82
+ * — read by some schemes for settlement overrides), and a handler throw
83
+ * still releases the verified-but-unsettled payment via
84
+ * `cancellationDispatcher.cancel`. Because settlement can still fail after
85
+ * the handler already ran on this path, `.x402()` handlers gated this way
86
+ * MUST be idempotent or compensatable — see the `@lunora/x402` charge docs.
87
+ */
88
+ readonly settleBeforeHandler?: boolean;
89
+ }
90
+ /**
42
91
  * Build and initialise a {@link ChargeMiddleware} for `config`. Fetches
43
92
  * facilitator support once (via `initialize()`), so call this once per config
44
93
  * and reuse the result across requests. `routeOverrides` layers extra route
45
- * metadata (e.g. `resource`) onto the generated catch-all route.
94
+ * metadata (e.g. `resource`) onto the generated catch-all route; `options`
95
+ * controls settlement ordering (see {@link ChargeMiddlewareOptions}).
96
+ * @experimental
46
97
  */
47
- declare const createChargeMiddleware: (config: X402ChargeConfig, routeOverrides?: ChargeRouteOverrides) => Promise<ChargeMiddleware>;
98
+ declare const createChargeMiddleware: (config: X402ChargeConfig, routeOverrides?: ChargeRouteOverrides, options?: ChargeMiddlewareOptions) => Promise<ChargeMiddleware>;
48
99
  /**
49
100
  * Charge config for the procedure gate: the worker-level settlement vocabulary
50
101
  * (network, recipient, facilitator) minus `price` — price is per-procedure and
51
102
  * arrives with each {@link X402ProcedureSpec}.
103
+ * @experimental
52
104
  */
53
105
  type X402ProcedureChargeConfig = Omit<X402ChargeConfig, "price">;
54
- /** The per-RPC charge spec the runtime passes the gate for each paid dispatch. */
106
+ /**
107
+ * The per-RPC charge spec the runtime passes the gate for each paid dispatch.
108
+ * @experimental
109
+ */
55
110
  interface X402ProcedureSpec {
56
111
  /** The `file:function` id of the paid procedure; becomes the x402 challenge `resource`. */
57
112
  readonly functionPath: string;
@@ -62,15 +117,23 @@ interface X402ProcedureSpec {
62
117
  * Gate one paid RPC. Returns a real `402` + `PAYMENT-REQUIRED` challenge when the
63
118
  * request is unpaid, or the dispatched response (with `X-PAYMENT-RESPONSE`
64
119
  * attached) once the client's `X-PAYMENT` is verified and settled. `dispatch`
65
- * runs the actual shard forward — it is only invoked after payment is verified.
120
+ * runs the actual shard forward — settlement happens **before** `dispatch` is
121
+ * invoked (settle-first), so a settlement failure means the shard forward
122
+ * (the mutation's commit) never runs at all — no committed-but-unpaid write is
123
+ * possible. `deps.waitUntil`, when supplied (the request's `ctx.waitUntil`),
124
+ * keeps the opt-in receipt sink alive past the response.
125
+ * @experimental
66
126
  */
67
- type X402ProcedureChargeGate = (request: Request, spec: X402ProcedureSpec, dispatch: () => Promise<Response>) => Promise<Response>;
127
+ type X402ProcedureChargeGate = (request: Request, spec: X402ProcedureSpec, dispatch: () => Promise<Response>, deps?: ChargeHandlerDeps) => Promise<Response>;
68
128
  /**
69
129
  * Build the injectable procedure charge gate for `config`. One initialised
70
130
  * {@link ChargeMiddleware} is memoised per `functionPath` (each bakes that
71
131
  * function's price + `resource`), since `createChargeMiddleware` fetches
72
132
  * facilitator support on first use. A failed init is not cached, so a transient
73
- * facilitator outage retries on the next request.
133
+ * facilitator outage retries on the next request. Settlement runs before
134
+ * `dispatch` (`settleBeforeHandler: true`) since `dispatch` commits the
135
+ * procedure's real mutation — see `createChargeMiddleware`'s `ChargeMiddlewareOptions`.
136
+ * @experimental
74
137
  */
75
138
  declare const createProcedureChargeGate: (config: X402ProcedureChargeConfig) => X402ProcedureChargeGate;
76
- export { type ChargeHandler, type ChargeMiddleware, type ChargeRouteOverrides, type FacilitatorConfig, type HttpActionHandler, type X402ChargeConfig, type X402Price, type X402ProcedureChargeConfig, type X402ProcedureChargeGate, type X402ProcedureSpec, createChargeMiddleware, createFacilitatorClient, createProcedureChargeGate, withX402 };
139
+ export { type ChargeHandler, type ChargeHandlerDeps, type ChargeMiddleware, type ChargeMiddlewareOptions, type ChargeRouteOverrides, type FacilitatorConfig, type HttpActionHandler, type X402ChargeConfig, type X402Price, type X402ProcedureChargeConfig, type X402ProcedureChargeGate, type X402ProcedureSpec, createChargeMiddleware, createFacilitatorClient, createProcedureChargeGate, withX402 };
@@ -1,7 +1,7 @@
1
1
  export { DEFAULT_FACILITATOR_URL, resolveFacilitatorUrl } from '../packem_shared/DEFAULT_FACILITATOR_URL-Cbz6kIqa.mjs';
2
2
  export { createFacilitatorClient } from '../packem_shared/createFacilitatorClient-rXHBnCZm.mjs';
3
3
  export { isEvmNetwork, isSvmNetwork, toCaip2 } from '../packem_shared/EVM_NETWORKS-BhnYWUQ4.mjs';
4
- export { withX402 } from '../packem_shared/withX402-rUq0voT8.mjs';
5
- export { createChargeMiddleware } from '../packem_shared/createChargeMiddleware-BJkYJeFf.mjs';
6
- export { createProcedureChargeGate } from '../packem_shared/createProcedureChargeGate-CV8ITlQP.mjs';
4
+ export { withX402 } from '../packem_shared/withX402-DILL2DvD.mjs';
5
+ export { createChargeMiddleware } from '../packem_shared/createChargeMiddleware-D3yhOpFs.mjs';
6
+ export { createProcedureChargeGate } from '../packem_shared/createProcedureChargeGate-eh9yv36U.mjs';
7
7
  export { toPaymentEventRow, toReceipt } from '../packem_shared/toPaymentEventRow-DW4O9N7Y.mjs';
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, E as EVM_NETWORKS, type a as EvmAddress, type F as FacilitatorConfig, type b as FriendlyNetwork, N as NETWORK_TO_CAIP2, S as SVM_NETWORKS, type X as X402CdpSignerConfig, type c as X402ChargeConfig, type d as X402Network, type e as X402PayConfig, type f as X402Price, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, type j as X402SignerConfig, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2 } from "./packem_shared/config.d-CddwCiBm.mjs";
1
+ export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, E as EVM_NETWORKS, type a as EvmAddress, type F as FacilitatorConfig, type b as FriendlyNetwork, N as NETWORK_TO_CAIP2, S as SVM_NETWORKS, type X as X402CdpSignerConfig, type c as X402ChargeConfig, type d as X402Network, type e as X402PayConfig, type f as X402Price, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, type j as X402SignerConfig, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2 } from "./packem_shared/config.d-5Nqi5iox.mjs";
2
2
  import '@x402/evm';
3
3
  import '@x402/svm';
4
4
  import '@x402/core/http';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, E as EVM_NETWORKS, type a as EvmAddress, type F as FacilitatorConfig, type b as FriendlyNetwork, N as NETWORK_TO_CAIP2, S as SVM_NETWORKS, type X as X402CdpSignerConfig, type c as X402ChargeConfig, type d as X402Network, type e as X402PayConfig, type f as X402Price, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, type j as X402SignerConfig, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2 } from "./packem_shared/config.d-CddwCiBm.js";
1
+ export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, E as EVM_NETWORKS, type a as EvmAddress, type F as FacilitatorConfig, type b as FriendlyNetwork, N as NETWORK_TO_CAIP2, S as SVM_NETWORKS, type X as X402CdpSignerConfig, type c as X402ChargeConfig, type d as X402Network, type e as X402PayConfig, type f as X402Price, type g as X402Receipt, type h as X402ReceiptSink, type i as X402Recipient, type j as X402SignerConfig, k as isEvmNetwork, l as isSvmNetwork, r as resolveFacilitatorUrl, t as toCaip2 } from "./packem_shared/config.d-5Nqi5iox.js";
2
2
  import '@x402/evm';
3
3
  import '@x402/svm';
4
4
  import '@x402/core/http';
@@ -10,6 +10,9 @@ const createSpendState = () => {
10
10
  add: (amount) => {
11
11
  spent += amount;
12
12
  },
13
+ release: (amount) => {
14
+ spent = spent > amount ? spent - amount : 0n;
15
+ },
13
16
  get spentAtomic() {
14
17
  return spent;
15
18
  }
@@ -59,27 +62,29 @@ const buildPaymentGuard = (policy, state) => {
59
62
  reason: `x402 policy: this payment (${requirement.amount}) would exceed the per-run cap (already spent ${state.spentAtomic.toString()}, cap ${maxPerRun.toString()}, in atomic base units).`
60
63
  };
61
64
  }
65
+ state.add(amount);
62
66
  if (policy.onPaymentRequired !== void 0) {
63
67
  const approved = await policy.onPaymentRequired(requirement);
64
68
  if (!approved) {
69
+ state.release(amount);
65
70
  return { abort: true, reason: "x402 policy: payment was declined by onPaymentRequired." };
66
71
  }
67
72
  }
68
73
  return void 0;
69
74
  };
70
75
  };
71
- const recordSpend = (state) => (context) => {
72
- state.add(BigInt(context.selectedRequirements.amount));
76
+ const releaseSpendOnFailure = (state) => (context) => {
77
+ state.release(BigInt(context.selectedRequirements.amount));
73
78
  return Promise.resolve();
74
79
  };
75
80
  const assertBoundedPolicy = (policy) => {
76
- const bounded = policy.maxPerCall !== void 0 || policy.maxPerRun !== void 0 || (policy.allowedRecipients?.length ?? 0) > 0 || (policy.allowedNetworks?.length ?? 0) > 0 || policy.onPaymentRequired !== void 0;
81
+ const bounded = policy.maxPerCall !== void 0 || policy.maxPerRun !== void 0 || policy.onPaymentRequired !== void 0;
77
82
  if (!bounded) {
78
83
  throw new LunoraError(
79
84
  "FORBIDDEN",
80
- "x402 pay: refusing to build a wallet with an unbounded spend policy. Set at least one of maxPerCall, maxPerRun, allowedRecipients, allowedNetworks, or onPaymentRequired."
85
+ "x402 pay: refusing to build a wallet with an unbounded spend policy. Set at least one of maxPerCall, maxPerRun, or onPaymentRequired (allowedNetworks/allowedRecipients narrow but do not bound spend)."
81
86
  );
82
87
  }
83
88
  };
84
89
 
85
- export { DEFAULT_STABLECOIN_DECIMALS, assertBoundedPolicy, buildPaymentGuard, buildSpendPolicy, createSpendState, recordSpend, usdToAtomic };
90
+ export { DEFAULT_STABLECOIN_DECIMALS, assertBoundedPolicy, buildPaymentGuard, buildSpendPolicy, createSpendState, releaseSpendOnFailure, usdToAtomic };
@@ -1,13 +1,14 @@
1
1
  import { ClientEvmSigner } from '@x402/evm';
2
2
  import { ClientSvmSigner } from '@x402/svm';
3
3
  import { ProcessSettleSuccessResponse } from '@x402/core/http';
4
- import { BeforePaymentCreationHook, PaymentPolicy, AfterPaymentCreationHook } from '@x402/core/client';
4
+ import { BeforePaymentCreationHook, PaymentPolicy, OnPaymentCreationFailureHook } from '@x402/core/client';
5
5
  import { PaymentRequirements } from '@x402/core/types';
6
6
  /**
7
7
  * A normalised record of one settled x402 payment. The settled `amount` is kept
8
8
  * as its exact on-chain atomic-unit string (USDC has 6 decimals) — never coerced
9
9
  * to a fractional-dollar number — so no precision is lost crossing the reporting
10
10
  * seam.
11
+ * @experimental
11
12
  */
12
13
  interface X402Receipt {
13
14
  /** Settled amount in the asset's atomic base units (USDC: 6 decimals), as an exact string. */
@@ -33,6 +34,7 @@ interface X402Receipt {
33
34
  * settlement, does not block the paid response on it, and swallows any error it
34
35
  * throws — so a sink must never rely on being awaited or on its failures
35
36
  * surfacing.
37
+ * @experimental
36
38
  */
37
39
  type X402ReceiptSink = (receipt: X402Receipt) => Promise<void> | void;
38
40
  /**
@@ -41,6 +43,7 @@ type X402ReceiptSink = (receipt: X402Receipt) => Promise<void> | void;
41
43
  * the settlement result carries neither. Prefers the actual settled `amount`
42
44
  * (present for `upto`-scheme partial settlements) and falls back to the route's
43
45
  * required amount for `exact`.
46
+ * @experimental
44
47
  */
45
48
  declare const toReceipt: (settlement: ProcessSettleSuccessResponse, context: {
46
49
  readonly resource: string;
@@ -50,6 +53,7 @@ declare const toReceipt: (settlement: ProcessSettleSuccessResponse, context: {
50
53
  * A row for `@lunora/payment`'s durable `events` table. Deliberately a plain
51
54
  * structural type — building one imports nothing from `@lunora/payment`, so the
52
55
  * rails stay decoupled.
56
+ * @experimental
53
57
  */
54
58
  interface PaymentEventRow {
55
59
  /** Epoch milliseconds the settlement was recorded. */
@@ -77,6 +81,7 @@ interface PaymentEventRow {
77
81
  * table (`packages/payment/src/schema.ts`). Amount / from / to / resource are
78
82
  * intentionally not on this row — that card renders none of them; read them off
79
83
  * the {@link X402Receipt} (e.g. into your own revenue table) if you need them.
84
+ * @experimental
80
85
  */
81
86
  declare const toPaymentEventRow: (receipt: X402Receipt) => PaymentEventRow;
82
87
  /**
@@ -96,19 +101,27 @@ declare const toPaymentEventRow: (receipt: X402Receipt) => PaymentEventRow;
96
101
  * friendly aliases; a caller who needs them can still pass a raw CAIP-2 id with an
97
102
  * explicit asset.
98
103
  */
99
- /** A CAIP-2 chain identifier, e.g. `"eip155:8453"` (Base) or `"solana:5eyk…"`. */
104
+ /**
105
+ * A CAIP-2 chain identifier, e.g. `"eip155:8453"` (Base) or `"solana:5eyk…"`.
106
+ * @experimental
107
+ */
100
108
  type Caip2 = `${string}:${string}`;
101
- /** Friendly network names Lunora maps to CAIP-2 for `@x402/core`. */
109
+ /**
110
+ * Friendly network names Lunora maps to CAIP-2 for `@x402/core`.
111
+ * @experimental
112
+ */
102
113
  type FriendlyNetwork = "arbitrum" | "arbitrum-sepolia" | "base" | "base-sepolia" | "ethereum" | "polygon" | "solana" | "solana-devnet";
103
114
  /**
104
115
  * A network Lunora can settle on: a {@link FriendlyNetwork} alias (mapped to
105
116
  * CAIP-2 internally) or a raw {@link Caip2} id for chains without a friendly name.
117
+ * @experimental
106
118
  */
107
119
  type X402Network = Caip2 | FriendlyNetwork;
108
120
  /**
109
121
  * Friendly name → CAIP-2 id. Values verified against `@x402/evm` and `@x402/svm`
110
122
  * `DEFAULT_STABLECOINS` at 2.17.0. `base` / `base-sepolia` are the primary
111
123
  * prod / test pair.
124
+ * @experimental
112
125
  */
113
126
  declare const NETWORK_TO_CAIP2: {
114
127
  readonly arbitrum: "eip155:42161";
@@ -120,23 +133,37 @@ declare const NETWORK_TO_CAIP2: {
120
133
  readonly solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
121
134
  readonly "solana-devnet": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
122
135
  };
123
- /** EVM friendly networks (signed via `@x402/evm` + viem). */
136
+ /**
137
+ * EVM friendly networks (signed via `@x402/evm` + viem).
138
+ * @experimental
139
+ */
124
140
  declare const EVM_NETWORKS: readonly ["arbitrum", "arbitrum-sepolia", "base", "base-sepolia", "ethereum", "polygon"];
125
- /** Solana friendly networks (signed via `@x402/svm`). */
141
+ /**
142
+ * Solana friendly networks (signed via `@x402/svm`).
143
+ * @experimental
144
+ */
126
145
  declare const SVM_NETWORKS: readonly ["solana", "solana-devnet"];
127
146
  /**
128
147
  * Resolve a network to its CAIP-2 id. Friendly aliases are looked up; a value
129
148
  * that already looks like CAIP-2 (`namespace:reference`) passes through.
149
+ * @experimental
130
150
  */
131
151
  declare const toCaip2: (network: X402Network) => Caip2;
132
- /** True when `network` settles on an EVM chain (viem signer path). */
152
+ /**
153
+ * True when `network` settles on an EVM chain (viem signer path).
154
+ * @experimental
155
+ */
133
156
  declare const isEvmNetwork: (network: X402Network) => boolean;
134
- /** True when `network` settles on Solana (`@x402/svm` signer path). */
157
+ /**
158
+ * True when `network` settles on Solana (`@x402/svm` signer path).
159
+ * @experimental
160
+ */
135
161
  declare const isSvmNetwork: (network: X402Network) => boolean;
136
162
  /**
137
163
  * USDC — and every asset in `@x402/evm` / `@x402/svm`'s `DEFAULT_STABLECOINS` —
138
164
  * uses 6 decimals, so a USD price converts to atomic base units at `10 ** 6`.
139
165
  * Override per {@link SpendPolicy.decimals} only for a custom, non-6-decimal asset.
166
+ * @experimental
140
167
  */
141
168
  declare const DEFAULT_STABLECOIN_DECIMALS = 6;
142
169
  /**
@@ -145,6 +172,7 @@ declare const DEFAULT_STABLECOIN_DECIMALS = 6;
145
172
  *
146
173
  * Caps are denominated in USD (the stablecoin's dollar value); addresses and
147
174
  * networks are matched against the requirement the server offers.
175
+ * @experimental
148
176
  */
149
177
  interface SpendPolicy {
150
178
  /** Network allowlist. When set, only these networks may be paid on. */
@@ -164,51 +192,88 @@ interface SpendPolicy {
164
192
  */
165
193
  readonly onPaymentRequired?: (requirement: PaymentRequirements) => Promise<boolean> | boolean;
166
194
  }
167
- /** A running spend ledger the per-run cap is measured against; the guard reads it, the recorder adds to it. */
195
+ /**
196
+ * A running spend ledger the per-run cap is measured (and reserved) against.
197
+ * @experimental
198
+ */
168
199
  interface SpendState {
169
- /** Add a just-committed payment (atomic base units) to the total. */
200
+ /** Reserve a payment (atomic base units) against the running total, before it is signed. */
170
201
  readonly add: (amount: bigint) => void;
202
+ /** Release a previously reserved amount (atomic base units) — e.g. a declined or failed payment. Clamps at 0. */
203
+ readonly release: (amount: bigint) => void;
171
204
  /** Cumulative spend so far, in atomic base units. */
172
205
  readonly spentAtomic: bigint;
173
206
  }
174
- /** A fresh spend ledger. One per wallet instance; the guard + recorder share it. */
207
+ /**
208
+ * A fresh spend ledger. One per wallet instance; the guard reserves into it and
209
+ * releases from it.
210
+ * @experimental
211
+ */
175
212
  declare const createSpendState: () => SpendState;
176
213
  /**
177
214
  * Convert a USD amount (`0.01`, `"0.01"`, or the `"$0.01"` shorthand) to atomic
178
215
  * stablecoin base units, exactly — parsed digit-by-digit so no binary-float drift
179
216
  * can round a cap the wrong way. Throws on a malformed amount (including
180
217
  * exponential notation like `"1e-7"`, which a decimal string never needs).
218
+ * @experimental
181
219
  */
182
220
  declare const usdToAtomic: (usd: X402Price, decimals?: number) => bigint;
183
221
  /**
184
222
  * A `PaymentPolicy` that narrows the server's offered requirements to those a
185
223
  * bounded wallet may pay: within the per-call cap, to an allowed recipient, on an
186
224
  * allowed network. An empty result means the client cannot pay — fail-closed.
225
+ * @experimental
187
226
  */
188
227
  declare const buildSpendPolicy: (policy: SpendPolicy) => PaymentPolicy;
189
228
  /**
190
229
  * A `BeforePaymentCreationHook` enforcing the stateful bounds the stateless
191
230
  * {@link buildSpendPolicy} filter can't: the cumulative per-run cap and the async
192
231
  * confirmation gate. Aborts (no signature) when either would be violated.
232
+ *
233
+ * The per-run cap is *reserved* into `state` as soon as the check passes — before
234
+ * the `await policy.onPaymentRequired` below, and before `@x402/core` ever attempts
235
+ * to sign — not recorded afterwards. This closes a check-then-act race: without an
236
+ * atomic reserve, N concurrent payments could each read the same `spentAtomic`,
237
+ * all pass the cap check, and all record, overspending the cap by up to
238
+ * (N−1)×maxPerCall. A declined confirmation releases the reservation before this
239
+ * hook returns; {@link releaseSpendOnFailure} releases it if the signature itself
240
+ * later fails. The reservation is intentionally *not* released on success — a
241
+ * committed payment stays counted.
242
+ * @experimental
193
243
  */
194
244
  declare const buildPaymentGuard: (policy: SpendPolicy, state: SpendState) => BeforePaymentCreationHook;
195
245
  /**
196
- * An `AfterPaymentCreationHook` that adds the just-created payment to `state`, so
197
- * the next {@link buildPaymentGuard} call measures the per-run cap against it.
246
+ * An `OnPaymentCreationFailureHook` that releases a reservation
247
+ * {@link buildPaymentGuard} made when the scheme's signature creation itself
248
+ * throws (network error, wallet error, …) after the guard already approved and
249
+ * reserved the amount. Without this, a failed signature would permanently
250
+ * over-count against the per-run cap for the rest of the run — fail-closed, but
251
+ * needlessly so when the client (`wrapFetchWithPayment`) may retry.
252
+ * @experimental
198
253
  */
199
- declare const recordSpend: (state: SpendState) => AfterPaymentCreationHook;
254
+ declare const releaseSpendOnFailure: (state: SpendState) => OnPaymentCreationFailureHook;
200
255
  /**
201
256
  * Guard at wallet-build time: refuse a policy with no bound whatsoever. Signing
202
257
  * money on an agent's behalf with unlimited spend authority is never the intent,
203
258
  * so this fails loudly rather than defaulting to unbounded.
259
+ *
260
+ * `allowedNetworks` / `allowedRecipients` narrow *where* a payment can go, but
261
+ * neither caps *how much* — a policy with only an allowlist still authorises
262
+ * unlimited spend to any recipient it permits. Only `maxPerCall`, `maxPerRun`, or
263
+ * a dynamic `onPaymentRequired` gate actually bound spend, so only those count here.
264
+ * @experimental
204
265
  */
205
266
  declare const assertBoundedPolicy: (policy: SpendPolicy) => void;
206
267
  /**
207
268
  * The public, Coinbase-operated facilitator (verify + settle). It needs no API
208
269
  * key. Override with a self-hosted or CDP facilitator via {@link FacilitatorConfig}.
270
+ * @experimental
209
271
  */
210
272
  declare const DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator";
211
- /** How to reach a facilitator's `/verify` + `/settle` endpoints. */
273
+ /**
274
+ * How to reach a facilitator's `/verify` + `/settle` endpoints.
275
+ * @experimental
276
+ */
212
277
  interface FacilitatorConfig {
213
278
  /** Extra headers for a private facilitator (e.g. a CDP bearer token). */
214
279
  readonly headers?: Record<string, string>;
@@ -221,11 +286,18 @@ interface FacilitatorConfig {
221
286
  * to the network's stablecoin base units (USDC has 6 decimals) at challenge
222
287
  * time. (Kept `number | string` rather than a `` `$${string}` `` template
223
288
  * member — the template is subsumed by `string`, so it only adds noise.)
289
+ * @experimental
224
290
  */
225
291
  type X402Price = number | string;
226
- /** An EVM recipient address (the merchant wallet that receives settlement). */
292
+ /**
293
+ * An EVM recipient address (the merchant wallet that receives settlement).
294
+ * @experimental
295
+ */
227
296
  type EvmAddress = `0x${string}`;
228
- /** Recipient wallet the facilitator settles payments to, per network family. */
297
+ /**
298
+ * Recipient wallet the facilitator settles payments to, per network family.
299
+ * @experimental
300
+ */
229
301
  interface X402Recipient {
230
302
  /** EVM payout address (required for EVM networks). */
231
303
  readonly evm?: EvmAddress;
@@ -235,6 +307,7 @@ interface X402Recipient {
235
307
  /**
236
308
  * Server-side (charge rail) config. The server needs only a **recipient
237
309
  * address** — no private key — because the facilitator performs settlement.
310
+ * @experimental
238
311
  */
239
312
  interface X402ChargeConfig {
240
313
  readonly facilitator?: FacilitatorConfig;
@@ -256,6 +329,7 @@ interface X402ChargeConfig {
256
329
  * Client-side (pay rail) config. The signer holds spending authority, so the
257
330
  * pay rail is ActionCtx-only and MUST be paired with a spend `policy` — the pay
258
331
  * rail refuses to build if the policy is unbounded.
332
+ * @experimental
259
333
  */
260
334
  interface X402PayConfig {
261
335
  /** Network to transact on. Determines the signer family (EVM vs SVM). */
@@ -275,6 +349,7 @@ interface X402PayConfig {
275
349
  * custody is `@coinbase/cdp-sdk`.) EVM only today; for CDP on Solana, build a
276
350
  * `@solana/kit` signer around your CDP account and pass it via the `"signer"`
277
351
  * escape hatch.
352
+ * @experimental
278
353
  */
279
354
  interface X402CdpSignerConfig {
280
355
  /** CDP account name to get-or-create and sign with. */
@@ -306,6 +381,7 @@ interface X402CdpSignerConfig {
306
381
  * Wired today: raw-key (EVM + SVM), the user-supplied signer (both families),
307
382
  * and CDP-managed EVM custody. CDP on Solana is not yet wired — use the escape
308
383
  * hatch.
384
+ * @experimental
309
385
  */
310
386
  type X402SignerConfig = X402CdpSignerConfig | {
311
387
  /** Name of the `ctx.secrets` entry holding the private key. */
@@ -320,6 +396,9 @@ type X402SignerConfig = X402CdpSignerConfig | {
320
396
  readonly signer: ClientEvmSigner | ClientSvmSigner;
321
397
  readonly type: "signer";
322
398
  };
323
- /** Resolve a facilitator's base URL, applying the public default. */
399
+ /**
400
+ * Resolve a facilitator's base URL, applying the public default.
401
+ * @experimental
402
+ */
324
403
  declare const resolveFacilitatorUrl: (facilitator?: FacilitatorConfig) => string;
325
- export { Caip2 as C, DEFAULT_FACILITATOR_URL as D, EVM_NETWORKS as E, FacilitatorConfig as F, NETWORK_TO_CAIP2 as N, PaymentEventRow as P, SVM_NETWORKS as S, X402CdpSignerConfig as X, EvmAddress as a, FriendlyNetwork as b, X402ChargeConfig as c, X402Network as d, X402PayConfig as e, X402Price as f, X402Receipt as g, X402ReceiptSink as h, X402Recipient as i, X402SignerConfig as j, isEvmNetwork as k, isSvmNetwork as l, DEFAULT_STABLECOIN_DECIMALS as m, SpendPolicy as n, SpendState as o, assertBoundedPolicy as p, buildPaymentGuard as q, resolveFacilitatorUrl as r, buildSpendPolicy as s, toCaip2 as t, createSpendState as u, recordSpend as v, usdToAtomic as w, toPaymentEventRow as x, toReceipt as y };
404
+ export { Caip2 as C, DEFAULT_FACILITATOR_URL as D, EVM_NETWORKS as E, FacilitatorConfig as F, NETWORK_TO_CAIP2 as N, PaymentEventRow as P, SVM_NETWORKS as S, X402CdpSignerConfig as X, EvmAddress as a, FriendlyNetwork as b, X402ChargeConfig as c, X402Network as d, X402PayConfig as e, X402Price as f, X402Receipt as g, X402ReceiptSink as h, X402Recipient as i, X402SignerConfig as j, isEvmNetwork as k, isSvmNetwork as l, DEFAULT_STABLECOIN_DECIMALS as m, SpendPolicy as n, SpendState as o, assertBoundedPolicy as p, buildPaymentGuard as q, resolveFacilitatorUrl as r, buildSpendPolicy as s, toCaip2 as t, createSpendState as u, releaseSpendOnFailure as v, usdToAtomic as w, toPaymentEventRow as x, toReceipt as y };
@@ -1,13 +1,14 @@
1
1
  import { ClientEvmSigner } from '@x402/evm';
2
2
  import { ClientSvmSigner } from '@x402/svm';
3
3
  import { ProcessSettleSuccessResponse } from '@x402/core/http';
4
- import { BeforePaymentCreationHook, PaymentPolicy, AfterPaymentCreationHook } from '@x402/core/client';
4
+ import { BeforePaymentCreationHook, PaymentPolicy, OnPaymentCreationFailureHook } from '@x402/core/client';
5
5
  import { PaymentRequirements } from '@x402/core/types';
6
6
  /**
7
7
  * A normalised record of one settled x402 payment. The settled `amount` is kept
8
8
  * as its exact on-chain atomic-unit string (USDC has 6 decimals) — never coerced
9
9
  * to a fractional-dollar number — so no precision is lost crossing the reporting
10
10
  * seam.
11
+ * @experimental
11
12
  */
12
13
  interface X402Receipt {
13
14
  /** Settled amount in the asset's atomic base units (USDC: 6 decimals), as an exact string. */
@@ -33,6 +34,7 @@ interface X402Receipt {
33
34
  * settlement, does not block the paid response on it, and swallows any error it
34
35
  * throws — so a sink must never rely on being awaited or on its failures
35
36
  * surfacing.
37
+ * @experimental
36
38
  */
37
39
  type X402ReceiptSink = (receipt: X402Receipt) => Promise<void> | void;
38
40
  /**
@@ -41,6 +43,7 @@ type X402ReceiptSink = (receipt: X402Receipt) => Promise<void> | void;
41
43
  * the settlement result carries neither. Prefers the actual settled `amount`
42
44
  * (present for `upto`-scheme partial settlements) and falls back to the route's
43
45
  * required amount for `exact`.
46
+ * @experimental
44
47
  */
45
48
  declare const toReceipt: (settlement: ProcessSettleSuccessResponse, context: {
46
49
  readonly resource: string;
@@ -50,6 +53,7 @@ declare const toReceipt: (settlement: ProcessSettleSuccessResponse, context: {
50
53
  * A row for `@lunora/payment`'s durable `events` table. Deliberately a plain
51
54
  * structural type — building one imports nothing from `@lunora/payment`, so the
52
55
  * rails stay decoupled.
56
+ * @experimental
53
57
  */
54
58
  interface PaymentEventRow {
55
59
  /** Epoch milliseconds the settlement was recorded. */
@@ -77,6 +81,7 @@ interface PaymentEventRow {
77
81
  * table (`packages/payment/src/schema.ts`). Amount / from / to / resource are
78
82
  * intentionally not on this row — that card renders none of them; read them off
79
83
  * the {@link X402Receipt} (e.g. into your own revenue table) if you need them.
84
+ * @experimental
80
85
  */
81
86
  declare const toPaymentEventRow: (receipt: X402Receipt) => PaymentEventRow;
82
87
  /**
@@ -96,19 +101,27 @@ declare const toPaymentEventRow: (receipt: X402Receipt) => PaymentEventRow;
96
101
  * friendly aliases; a caller who needs them can still pass a raw CAIP-2 id with an
97
102
  * explicit asset.
98
103
  */
99
- /** A CAIP-2 chain identifier, e.g. `"eip155:8453"` (Base) or `"solana:5eyk…"`. */
104
+ /**
105
+ * A CAIP-2 chain identifier, e.g. `"eip155:8453"` (Base) or `"solana:5eyk…"`.
106
+ * @experimental
107
+ */
100
108
  type Caip2 = `${string}:${string}`;
101
- /** Friendly network names Lunora maps to CAIP-2 for `@x402/core`. */
109
+ /**
110
+ * Friendly network names Lunora maps to CAIP-2 for `@x402/core`.
111
+ * @experimental
112
+ */
102
113
  type FriendlyNetwork = "arbitrum" | "arbitrum-sepolia" | "base" | "base-sepolia" | "ethereum" | "polygon" | "solana" | "solana-devnet";
103
114
  /**
104
115
  * A network Lunora can settle on: a {@link FriendlyNetwork} alias (mapped to
105
116
  * CAIP-2 internally) or a raw {@link Caip2} id for chains without a friendly name.
117
+ * @experimental
106
118
  */
107
119
  type X402Network = Caip2 | FriendlyNetwork;
108
120
  /**
109
121
  * Friendly name → CAIP-2 id. Values verified against `@x402/evm` and `@x402/svm`
110
122
  * `DEFAULT_STABLECOINS` at 2.17.0. `base` / `base-sepolia` are the primary
111
123
  * prod / test pair.
124
+ * @experimental
112
125
  */
113
126
  declare const NETWORK_TO_CAIP2: {
114
127
  readonly arbitrum: "eip155:42161";
@@ -120,23 +133,37 @@ declare const NETWORK_TO_CAIP2: {
120
133
  readonly solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
121
134
  readonly "solana-devnet": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
122
135
  };
123
- /** EVM friendly networks (signed via `@x402/evm` + viem). */
136
+ /**
137
+ * EVM friendly networks (signed via `@x402/evm` + viem).
138
+ * @experimental
139
+ */
124
140
  declare const EVM_NETWORKS: readonly ["arbitrum", "arbitrum-sepolia", "base", "base-sepolia", "ethereum", "polygon"];
125
- /** Solana friendly networks (signed via `@x402/svm`). */
141
+ /**
142
+ * Solana friendly networks (signed via `@x402/svm`).
143
+ * @experimental
144
+ */
126
145
  declare const SVM_NETWORKS: readonly ["solana", "solana-devnet"];
127
146
  /**
128
147
  * Resolve a network to its CAIP-2 id. Friendly aliases are looked up; a value
129
148
  * that already looks like CAIP-2 (`namespace:reference`) passes through.
149
+ * @experimental
130
150
  */
131
151
  declare const toCaip2: (network: X402Network) => Caip2;
132
- /** True when `network` settles on an EVM chain (viem signer path). */
152
+ /**
153
+ * True when `network` settles on an EVM chain (viem signer path).
154
+ * @experimental
155
+ */
133
156
  declare const isEvmNetwork: (network: X402Network) => boolean;
134
- /** True when `network` settles on Solana (`@x402/svm` signer path). */
157
+ /**
158
+ * True when `network` settles on Solana (`@x402/svm` signer path).
159
+ * @experimental
160
+ */
135
161
  declare const isSvmNetwork: (network: X402Network) => boolean;
136
162
  /**
137
163
  * USDC — and every asset in `@x402/evm` / `@x402/svm`'s `DEFAULT_STABLECOINS` —
138
164
  * uses 6 decimals, so a USD price converts to atomic base units at `10 ** 6`.
139
165
  * Override per {@link SpendPolicy.decimals} only for a custom, non-6-decimal asset.
166
+ * @experimental
140
167
  */
141
168
  declare const DEFAULT_STABLECOIN_DECIMALS = 6;
142
169
  /**
@@ -145,6 +172,7 @@ declare const DEFAULT_STABLECOIN_DECIMALS = 6;
145
172
  *
146
173
  * Caps are denominated in USD (the stablecoin's dollar value); addresses and
147
174
  * networks are matched against the requirement the server offers.
175
+ * @experimental
148
176
  */
149
177
  interface SpendPolicy {
150
178
  /** Network allowlist. When set, only these networks may be paid on. */
@@ -164,51 +192,88 @@ interface SpendPolicy {
164
192
  */
165
193
  readonly onPaymentRequired?: (requirement: PaymentRequirements) => Promise<boolean> | boolean;
166
194
  }
167
- /** A running spend ledger the per-run cap is measured against; the guard reads it, the recorder adds to it. */
195
+ /**
196
+ * A running spend ledger the per-run cap is measured (and reserved) against.
197
+ * @experimental
198
+ */
168
199
  interface SpendState {
169
- /** Add a just-committed payment (atomic base units) to the total. */
200
+ /** Reserve a payment (atomic base units) against the running total, before it is signed. */
170
201
  readonly add: (amount: bigint) => void;
202
+ /** Release a previously reserved amount (atomic base units) — e.g. a declined or failed payment. Clamps at 0. */
203
+ readonly release: (amount: bigint) => void;
171
204
  /** Cumulative spend so far, in atomic base units. */
172
205
  readonly spentAtomic: bigint;
173
206
  }
174
- /** A fresh spend ledger. One per wallet instance; the guard + recorder share it. */
207
+ /**
208
+ * A fresh spend ledger. One per wallet instance; the guard reserves into it and
209
+ * releases from it.
210
+ * @experimental
211
+ */
175
212
  declare const createSpendState: () => SpendState;
176
213
  /**
177
214
  * Convert a USD amount (`0.01`, `"0.01"`, or the `"$0.01"` shorthand) to atomic
178
215
  * stablecoin base units, exactly — parsed digit-by-digit so no binary-float drift
179
216
  * can round a cap the wrong way. Throws on a malformed amount (including
180
217
  * exponential notation like `"1e-7"`, which a decimal string never needs).
218
+ * @experimental
181
219
  */
182
220
  declare const usdToAtomic: (usd: X402Price, decimals?: number) => bigint;
183
221
  /**
184
222
  * A `PaymentPolicy` that narrows the server's offered requirements to those a
185
223
  * bounded wallet may pay: within the per-call cap, to an allowed recipient, on an
186
224
  * allowed network. An empty result means the client cannot pay — fail-closed.
225
+ * @experimental
187
226
  */
188
227
  declare const buildSpendPolicy: (policy: SpendPolicy) => PaymentPolicy;
189
228
  /**
190
229
  * A `BeforePaymentCreationHook` enforcing the stateful bounds the stateless
191
230
  * {@link buildSpendPolicy} filter can't: the cumulative per-run cap and the async
192
231
  * confirmation gate. Aborts (no signature) when either would be violated.
232
+ *
233
+ * The per-run cap is *reserved* into `state` as soon as the check passes — before
234
+ * the `await policy.onPaymentRequired` below, and before `@x402/core` ever attempts
235
+ * to sign — not recorded afterwards. This closes a check-then-act race: without an
236
+ * atomic reserve, N concurrent payments could each read the same `spentAtomic`,
237
+ * all pass the cap check, and all record, overspending the cap by up to
238
+ * (N−1)×maxPerCall. A declined confirmation releases the reservation before this
239
+ * hook returns; {@link releaseSpendOnFailure} releases it if the signature itself
240
+ * later fails. The reservation is intentionally *not* released on success — a
241
+ * committed payment stays counted.
242
+ * @experimental
193
243
  */
194
244
  declare const buildPaymentGuard: (policy: SpendPolicy, state: SpendState) => BeforePaymentCreationHook;
195
245
  /**
196
- * An `AfterPaymentCreationHook` that adds the just-created payment to `state`, so
197
- * the next {@link buildPaymentGuard} call measures the per-run cap against it.
246
+ * An `OnPaymentCreationFailureHook` that releases a reservation
247
+ * {@link buildPaymentGuard} made when the scheme's signature creation itself
248
+ * throws (network error, wallet error, …) after the guard already approved and
249
+ * reserved the amount. Without this, a failed signature would permanently
250
+ * over-count against the per-run cap for the rest of the run — fail-closed, but
251
+ * needlessly so when the client (`wrapFetchWithPayment`) may retry.
252
+ * @experimental
198
253
  */
199
- declare const recordSpend: (state: SpendState) => AfterPaymentCreationHook;
254
+ declare const releaseSpendOnFailure: (state: SpendState) => OnPaymentCreationFailureHook;
200
255
  /**
201
256
  * Guard at wallet-build time: refuse a policy with no bound whatsoever. Signing
202
257
  * money on an agent's behalf with unlimited spend authority is never the intent,
203
258
  * so this fails loudly rather than defaulting to unbounded.
259
+ *
260
+ * `allowedNetworks` / `allowedRecipients` narrow *where* a payment can go, but
261
+ * neither caps *how much* — a policy with only an allowlist still authorises
262
+ * unlimited spend to any recipient it permits. Only `maxPerCall`, `maxPerRun`, or
263
+ * a dynamic `onPaymentRequired` gate actually bound spend, so only those count here.
264
+ * @experimental
204
265
  */
205
266
  declare const assertBoundedPolicy: (policy: SpendPolicy) => void;
206
267
  /**
207
268
  * The public, Coinbase-operated facilitator (verify + settle). It needs no API
208
269
  * key. Override with a self-hosted or CDP facilitator via {@link FacilitatorConfig}.
270
+ * @experimental
209
271
  */
210
272
  declare const DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator";
211
- /** How to reach a facilitator's `/verify` + `/settle` endpoints. */
273
+ /**
274
+ * How to reach a facilitator's `/verify` + `/settle` endpoints.
275
+ * @experimental
276
+ */
212
277
  interface FacilitatorConfig {
213
278
  /** Extra headers for a private facilitator (e.g. a CDP bearer token). */
214
279
  readonly headers?: Record<string, string>;
@@ -221,11 +286,18 @@ interface FacilitatorConfig {
221
286
  * to the network's stablecoin base units (USDC has 6 decimals) at challenge
222
287
  * time. (Kept `number | string` rather than a `` `$${string}` `` template
223
288
  * member — the template is subsumed by `string`, so it only adds noise.)
289
+ * @experimental
224
290
  */
225
291
  type X402Price = number | string;
226
- /** An EVM recipient address (the merchant wallet that receives settlement). */
292
+ /**
293
+ * An EVM recipient address (the merchant wallet that receives settlement).
294
+ * @experimental
295
+ */
227
296
  type EvmAddress = `0x${string}`;
228
- /** Recipient wallet the facilitator settles payments to, per network family. */
297
+ /**
298
+ * Recipient wallet the facilitator settles payments to, per network family.
299
+ * @experimental
300
+ */
229
301
  interface X402Recipient {
230
302
  /** EVM payout address (required for EVM networks). */
231
303
  readonly evm?: EvmAddress;
@@ -235,6 +307,7 @@ interface X402Recipient {
235
307
  /**
236
308
  * Server-side (charge rail) config. The server needs only a **recipient
237
309
  * address** — no private key — because the facilitator performs settlement.
310
+ * @experimental
238
311
  */
239
312
  interface X402ChargeConfig {
240
313
  readonly facilitator?: FacilitatorConfig;
@@ -256,6 +329,7 @@ interface X402ChargeConfig {
256
329
  * Client-side (pay rail) config. The signer holds spending authority, so the
257
330
  * pay rail is ActionCtx-only and MUST be paired with a spend `policy` — the pay
258
331
  * rail refuses to build if the policy is unbounded.
332
+ * @experimental
259
333
  */
260
334
  interface X402PayConfig {
261
335
  /** Network to transact on. Determines the signer family (EVM vs SVM). */
@@ -275,6 +349,7 @@ interface X402PayConfig {
275
349
  * custody is `@coinbase/cdp-sdk`.) EVM only today; for CDP on Solana, build a
276
350
  * `@solana/kit` signer around your CDP account and pass it via the `"signer"`
277
351
  * escape hatch.
352
+ * @experimental
278
353
  */
279
354
  interface X402CdpSignerConfig {
280
355
  /** CDP account name to get-or-create and sign with. */
@@ -306,6 +381,7 @@ interface X402CdpSignerConfig {
306
381
  * Wired today: raw-key (EVM + SVM), the user-supplied signer (both families),
307
382
  * and CDP-managed EVM custody. CDP on Solana is not yet wired — use the escape
308
383
  * hatch.
384
+ * @experimental
309
385
  */
310
386
  type X402SignerConfig = X402CdpSignerConfig | {
311
387
  /** Name of the `ctx.secrets` entry holding the private key. */
@@ -320,6 +396,9 @@ type X402SignerConfig = X402CdpSignerConfig | {
320
396
  readonly signer: ClientEvmSigner | ClientSvmSigner;
321
397
  readonly type: "signer";
322
398
  };
323
- /** Resolve a facilitator's base URL, applying the public default. */
399
+ /**
400
+ * Resolve a facilitator's base URL, applying the public default.
401
+ * @experimental
402
+ */
324
403
  declare const resolveFacilitatorUrl: (facilitator?: FacilitatorConfig) => string;
325
- export { Caip2 as C, DEFAULT_FACILITATOR_URL as D, EVM_NETWORKS as E, FacilitatorConfig as F, NETWORK_TO_CAIP2 as N, PaymentEventRow as P, SVM_NETWORKS as S, X402CdpSignerConfig as X, EvmAddress as a, FriendlyNetwork as b, X402ChargeConfig as c, X402Network as d, X402PayConfig as e, X402Price as f, X402Receipt as g, X402ReceiptSink as h, X402Recipient as i, X402SignerConfig as j, isEvmNetwork as k, isSvmNetwork as l, DEFAULT_STABLECOIN_DECIMALS as m, SpendPolicy as n, SpendState as o, assertBoundedPolicy as p, buildPaymentGuard as q, resolveFacilitatorUrl as r, buildSpendPolicy as s, toCaip2 as t, createSpendState as u, recordSpend as v, usdToAtomic as w, toPaymentEventRow as x, toReceipt as y };
404
+ export { Caip2 as C, DEFAULT_FACILITATOR_URL as D, EVM_NETWORKS as E, FacilitatorConfig as F, NETWORK_TO_CAIP2 as N, PaymentEventRow as P, SVM_NETWORKS as S, X402CdpSignerConfig as X, EvmAddress as a, FriendlyNetwork as b, X402ChargeConfig as c, X402Network as d, X402PayConfig as e, X402Price as f, X402Receipt as g, X402ReceiptSink as h, X402Recipient as i, X402SignerConfig as j, isEvmNetwork as k, isSvmNetwork as l, DEFAULT_STABLECOIN_DECIMALS as m, SpendPolicy as n, SpendState as o, assertBoundedPolicy as p, buildPaymentGuard as q, resolveFacilitatorUrl as r, buildSpendPolicy as s, toCaip2 as t, createSpendState as u, releaseSpendOnFailure as v, usdToAtomic as w, toPaymentEventRow as x, toReceipt as y };
@@ -19,19 +19,20 @@ const buildResourceServer = async (config) => {
19
19
 
20
20
  const PAYMENT_HEADER = "X-PAYMENT";
21
21
  const headerRecord = (headers) => {
22
- const record = {};
22
+ const record = /* @__PURE__ */ Object.create(null);
23
23
  for (const [key, value] of headers) {
24
24
  record[key] = value;
25
25
  }
26
26
  return record;
27
27
  };
28
- const reportReceipt = (sink, settlement, resource) => {
28
+ const reportReceipt = (sink, settlement, resource, waitUntil) => {
29
29
  if (sink === void 0) {
30
30
  return;
31
31
  }
32
32
  try {
33
- Promise.resolve(sink(toReceipt(settlement, { resource, ts: Date.now() }))).catch(() => {
33
+ const sent = Promise.resolve(sink(toReceipt(settlement, { resource, ts: Date.now() }))).catch(() => {
34
34
  });
35
+ waitUntil?.(sent);
35
36
  } catch {
36
37
  }
37
38
  };
@@ -66,7 +67,7 @@ const createRequestAdapter = (request, url) => {
66
67
  return values.length === 1 ? values[0] : values;
67
68
  },
68
69
  getQueryParams: () => {
69
- const params = {};
70
+ const params = /* @__PURE__ */ Object.create(null);
70
71
  for (const key of new Set(url.searchParams.keys())) {
71
72
  const values = url.searchParams.getAll(key);
72
73
  const [first, ...rest] = values;
@@ -102,11 +103,12 @@ const withHeaders = (response, extra) => {
102
103
  }
103
104
  return new Response(response.body, { headers, status: response.status, statusText: response.statusText });
104
105
  };
105
- const createChargeMiddleware = async (config, routeOverrides) => {
106
+ const createChargeMiddleware = async (config, routeOverrides, options) => {
106
107
  const server = await buildResourceServer(config);
107
108
  const http = new x402HTTPResourceServer(server, { ...buildRoute(config), ...routeOverrides });
108
109
  await http.initialize();
109
- const handle = async (request, runHandler) => {
110
+ const settleBeforeHandler = options?.settleBeforeHandler ?? false;
111
+ const handle = async (request, runHandler, deps) => {
110
112
  const url = new URL(request.url);
111
113
  const context = {
112
114
  adapter: createRequestAdapter(request, url),
@@ -121,6 +123,18 @@ const createChargeMiddleware = async (config, routeOverrides) => {
121
123
  if (result.type === "payment-error") {
122
124
  return toResponse(result.response);
123
125
  }
126
+ const resource = routeOverrides?.resource ?? request.url;
127
+ if (settleBeforeHandler) {
128
+ const settlement2 = await http.processSettlement(result.paymentPayload, result.paymentRequirements, result.declaredExtensions, {
129
+ request: context
130
+ });
131
+ if (!settlement2.success) {
132
+ return toResponse(settlement2.response);
133
+ }
134
+ reportReceipt(config.onReceipt, settlement2, resource, deps?.waitUntil);
135
+ const response2 = await runHandler();
136
+ return withHeaders(response2, settlement2.headers);
137
+ }
124
138
  let response;
125
139
  try {
126
140
  response = await runHandler();
@@ -137,7 +151,7 @@ const createChargeMiddleware = async (config, routeOverrides) => {
137
151
  responseHeaders: headerRecord(response.headers)
138
152
  });
139
153
  if (settlement.success) {
140
- reportReceipt(config.onReceipt, settlement, routeOverrides?.resource ?? request.url);
154
+ reportReceipt(config.onReceipt, settlement, resource, deps?.waitUntil);
141
155
  return withHeaders(response, settlement.headers);
142
156
  }
143
157
  return toResponse(settlement.response);
@@ -1,6 +1,6 @@
1
1
  import { x402Client } from '@x402/core/client';
2
2
  import { wrapFetchWithPayment } from '@x402/fetch';
3
- import { assertBoundedPolicy, buildSpendPolicy, buildPaymentGuard, recordSpend, createSpendState } from './DEFAULT_STABLECOIN_DECIMALS-CSu5b5lD.mjs';
3
+ import { assertBoundedPolicy, buildSpendPolicy, buildPaymentGuard, releaseSpendOnFailure, createSpendState } from './DEFAULT_STABLECOIN_DECIMALS-CpW619nu.mjs';
4
4
  import { registerWallet } from './registerWallet-I4pVwq65.mjs';
5
5
 
6
6
  const createPayFetch = async (config, deps) => {
@@ -10,7 +10,7 @@ const createPayFetch = async (config, deps) => {
10
10
  const state = createSpendState();
11
11
  client.registerPolicy(buildSpendPolicy(config.policy));
12
12
  client.onBeforePaymentCreation(buildPaymentGuard(config.policy, state));
13
- client.onAfterPaymentCreation(recordSpend(state));
13
+ client.onPaymentCreationFailure(releaseSpendOnFailure(state));
14
14
  return wrapFetchWithPayment(deps.fetch ?? globalThis.fetch, client);
15
15
  };
16
16
 
@@ -1,18 +1,20 @@
1
- import { createChargeMiddleware } from './createChargeMiddleware-BJkYJeFf.mjs';
1
+ import { createChargeMiddleware } from './createChargeMiddleware-D3yhOpFs.mjs';
2
2
 
3
3
  const createProcedureChargeGate = (config) => {
4
4
  const middlewareByFunction = /* @__PURE__ */ new Map();
5
- return async (request, spec, dispatch) => {
5
+ return async (request, spec, dispatch, deps) => {
6
6
  let pending = middlewareByFunction.get(spec.functionPath);
7
7
  if (pending === void 0) {
8
- pending = createChargeMiddleware({ ...config, price: spec.price }, { resource: spec.functionPath }).catch((error) => {
9
- middlewareByFunction.delete(spec.functionPath);
10
- throw error;
11
- });
8
+ pending = createChargeMiddleware({ ...config, price: spec.price }, { resource: spec.functionPath }, { settleBeforeHandler: true }).catch(
9
+ (error) => {
10
+ middlewareByFunction.delete(spec.functionPath);
11
+ throw error;
12
+ }
13
+ );
12
14
  middlewareByFunction.set(spec.functionPath, pending);
13
15
  }
14
16
  const middleware = await pending;
15
- return middleware.handle(request, dispatch);
17
+ return middleware.handle(request, dispatch, deps);
16
18
  };
17
19
  };
18
20
 
@@ -1,4 +1,4 @@
1
- import { createChargeMiddleware } from './createChargeMiddleware-BJkYJeFf.mjs';
1
+ import { createChargeMiddleware } from './createChargeMiddleware-D3yhOpFs.mjs';
2
2
 
3
3
  const withX402 = (config, handler) => {
4
4
  let pending;
@@ -1,5 +1,5 @@
1
- import { e as X402PayConfig } from "../packem_shared/config.d-CddwCiBm.mjs";
2
- export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, m as DEFAULT_STABLECOIN_DECIMALS, type n as SpendPolicy, type o as SpendState, type X as X402CdpSignerConfig, type d as X402Network, type f as X402Price, type j as X402SignerConfig, p as assertBoundedPolicy, q as buildPaymentGuard, s as buildSpendPolicy, u as createSpendState, k as isEvmNetwork, l as isSvmNetwork, v as recordSpend, r as resolveFacilitatorUrl, t as toCaip2, w as usdToAtomic } from "../packem_shared/config.d-CddwCiBm.mjs";
1
+ import { e as X402PayConfig } from "../packem_shared/config.d-5Nqi5iox.mjs";
2
+ export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, m as DEFAULT_STABLECOIN_DECIMALS, type n as SpendPolicy, type o as SpendState, type X as X402CdpSignerConfig, type d as X402Network, type f as X402Price, type j as X402SignerConfig, p as assertBoundedPolicy, q as buildPaymentGuard, s as buildSpendPolicy, u as createSpendState, k as isEvmNetwork, l as isSvmNetwork, v as releaseSpendOnFailure, r as resolveFacilitatorUrl, t as toCaip2, w as usdToAtomic } from "../packem_shared/config.d-5Nqi5iox.mjs";
3
3
  import { x402Client } from '@x402/core/client';
4
4
  import { ClientSvmSigner } from '@x402/svm';
5
5
  import { PrivateKeyAccount } from 'viem/accounts';
@@ -8,7 +8,10 @@ import '@x402/core/http';
8
8
  import '@x402/core/types';
9
9
  /** Reads a secret by name; resolves `undefined` when unset. */
10
10
  type GetSecret = (name: string) => Promise<string | undefined> | string | undefined;
11
- /** How the wallet reads its key material — wired to `ctx.secrets.get` in an action. */
11
+ /**
12
+ * How the wallet reads its key material — wired to `ctx.secrets.get` in an action.
13
+ * @experimental
14
+ */
12
15
  interface WalletDeps {
13
16
  /** Read a secret (e.g. a private key) by name; `undefined` when unset. */
14
17
  readonly getSecret: GetSecret;
@@ -17,6 +20,7 @@ interface WalletDeps {
17
20
  * Resolve a viem `LocalAccount` from a raw private key. The key may be given with
18
21
  * or without the `0x` prefix. The account is a structural `ClientEvmSigner`
19
22
  * (`address` + `signTypedData`), so `@x402/evm` accepts it directly.
23
+ * @experimental
20
24
  */
21
25
  declare const resolveEvmAccount: (privateKey: string) => Promise<PrivateKeyAccount>;
22
26
  /**
@@ -25,6 +29,7 @@ declare const resolveEvmAccount: (privateKey: string) => Promise<PrivateKeyAccou
25
29
  * format) or as a base58 string. A 64-byte value is a full secret key (seed ‖
26
30
  * public key); a 32-byte value is the seed alone. The returned signer is a
27
31
  * structural `ClientSvmSigner` (`TransactionSigner`), so `@x402/svm` accepts it.
32
+ * @experimental
28
33
  */
29
34
  declare const resolveSvmSigner: (secret: string) => Promise<ClientSvmSigner>;
30
35
  /**
@@ -34,11 +39,18 @@ declare const resolveSvmSigner: (secret: string) => Promise<ClientSvmSigner>;
34
39
  * read), `"raw-key"` (a `ctx.secrets` private key → viem account on EVM or a
35
40
  * `@solana/kit` keypair on SVM), or `"cdp"` (a Coinbase-managed wallet via
36
41
  * `@coinbase/cdp-sdk`).
42
+ * @experimental
37
43
  */
38
44
  declare const registerWallet: (client: x402Client, config: X402PayConfig, deps: WalletDeps) => Promise<void>;
39
- /** A payment-enabled `fetch`: same signature as the platform `fetch`. */
45
+ /**
46
+ * A payment-enabled `fetch`: same signature as the platform `fetch`.
47
+ * @experimental
48
+ */
40
49
  type PayFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
41
- /** Dependencies for building a pay-rail fetch: secret access, plus an optional base `fetch` to wrap. */
50
+ /**
51
+ * Dependencies for building a pay-rail fetch: secret access, plus an optional base `fetch` to wrap.
52
+ * @experimental
53
+ */
42
54
  interface X402PayDeps extends WalletDeps {
43
55
  /** The `fetch` to wrap (defaults to `globalThis.fetch`). Inject to test or to chain transports. */
44
56
  readonly fetch?: typeof globalThis.fetch;
@@ -47,9 +59,13 @@ interface X402PayDeps extends WalletDeps {
47
59
  * Build a payment-enabled `fetch` for `config`. Throws (before resolving a
48
60
  * signer) when `config.policy` is unbounded — an agent wallet is never built
49
61
  * with unlimited spend authority.
62
+ * @experimental
50
63
  */
51
64
  declare const createPayFetch: (config: X402PayConfig, deps: X402PayDeps) => Promise<PayFetch>;
52
- /** A configured pay rail: a payment-enabled `fetch` bounded by the spend policy. */
65
+ /**
66
+ * A configured pay rail: a payment-enabled `fetch` bounded by the spend policy.
67
+ * @experimental
68
+ */
53
69
  interface X402Pay {
54
70
  /** A `fetch` that transparently pays for `402`-gated resources under the policy. */
55
71
  readonly fetch: PayFetch;
@@ -58,6 +74,7 @@ interface X402Pay {
58
74
  * Build a pay rail for `config`. The returned `fetch` answers `402` challenges by
59
75
  * signing and retrying, within `config.policy`. Throws (before touching the
60
76
  * signer) when the policy is unbounded.
77
+ * @experimental
61
78
  */
62
79
  declare const createX402Pay: (config: X402PayConfig, deps: X402PayDeps) => Promise<X402Pay>;
63
80
  /**
@@ -72,6 +89,7 @@ declare const createX402Pay: (config: X402PayConfig, deps: X402PayDeps) => Promi
72
89
  * per-run cap scopes to the ctx, not to each request. A failed build (e.g. an
73
90
  * unbounded policy) is memoised too, keeping the rail deterministically
74
91
  * fail-closed.
92
+ * @experimental
75
93
  */
76
94
  declare const lazyX402Pay: (config: X402PayConfig, deps: X402PayDeps) => X402Pay;
77
95
  export { type PayFetch, type WalletDeps, X402Pay, type X402PayConfig, type X402PayDeps, createPayFetch, createX402Pay, lazyX402Pay, registerWallet, resolveEvmAccount, resolveSvmSigner };
@@ -1,5 +1,5 @@
1
- import { e as X402PayConfig } from "../packem_shared/config.d-CddwCiBm.js";
2
- export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, m as DEFAULT_STABLECOIN_DECIMALS, type n as SpendPolicy, type o as SpendState, type X as X402CdpSignerConfig, type d as X402Network, type f as X402Price, type j as X402SignerConfig, p as assertBoundedPolicy, q as buildPaymentGuard, s as buildSpendPolicy, u as createSpendState, k as isEvmNetwork, l as isSvmNetwork, v as recordSpend, r as resolveFacilitatorUrl, t as toCaip2, w as usdToAtomic } from "../packem_shared/config.d-CddwCiBm.js";
1
+ import { e as X402PayConfig } from "../packem_shared/config.d-5Nqi5iox.js";
2
+ export { type C as Caip2, D as DEFAULT_FACILITATOR_URL, m as DEFAULT_STABLECOIN_DECIMALS, type n as SpendPolicy, type o as SpendState, type X as X402CdpSignerConfig, type d as X402Network, type f as X402Price, type j as X402SignerConfig, p as assertBoundedPolicy, q as buildPaymentGuard, s as buildSpendPolicy, u as createSpendState, k as isEvmNetwork, l as isSvmNetwork, v as releaseSpendOnFailure, r as resolveFacilitatorUrl, t as toCaip2, w as usdToAtomic } from "../packem_shared/config.d-5Nqi5iox.js";
3
3
  import { x402Client } from '@x402/core/client';
4
4
  import { ClientSvmSigner } from '@x402/svm';
5
5
  import { PrivateKeyAccount } from 'viem/accounts';
@@ -8,7 +8,10 @@ import '@x402/core/http';
8
8
  import '@x402/core/types';
9
9
  /** Reads a secret by name; resolves `undefined` when unset. */
10
10
  type GetSecret = (name: string) => Promise<string | undefined> | string | undefined;
11
- /** How the wallet reads its key material — wired to `ctx.secrets.get` in an action. */
11
+ /**
12
+ * How the wallet reads its key material — wired to `ctx.secrets.get` in an action.
13
+ * @experimental
14
+ */
12
15
  interface WalletDeps {
13
16
  /** Read a secret (e.g. a private key) by name; `undefined` when unset. */
14
17
  readonly getSecret: GetSecret;
@@ -17,6 +20,7 @@ interface WalletDeps {
17
20
  * Resolve a viem `LocalAccount` from a raw private key. The key may be given with
18
21
  * or without the `0x` prefix. The account is a structural `ClientEvmSigner`
19
22
  * (`address` + `signTypedData`), so `@x402/evm` accepts it directly.
23
+ * @experimental
20
24
  */
21
25
  declare const resolveEvmAccount: (privateKey: string) => Promise<PrivateKeyAccount>;
22
26
  /**
@@ -25,6 +29,7 @@ declare const resolveEvmAccount: (privateKey: string) => Promise<PrivateKeyAccou
25
29
  * format) or as a base58 string. A 64-byte value is a full secret key (seed ‖
26
30
  * public key); a 32-byte value is the seed alone. The returned signer is a
27
31
  * structural `ClientSvmSigner` (`TransactionSigner`), so `@x402/svm` accepts it.
32
+ * @experimental
28
33
  */
29
34
  declare const resolveSvmSigner: (secret: string) => Promise<ClientSvmSigner>;
30
35
  /**
@@ -34,11 +39,18 @@ declare const resolveSvmSigner: (secret: string) => Promise<ClientSvmSigner>;
34
39
  * read), `"raw-key"` (a `ctx.secrets` private key → viem account on EVM or a
35
40
  * `@solana/kit` keypair on SVM), or `"cdp"` (a Coinbase-managed wallet via
36
41
  * `@coinbase/cdp-sdk`).
42
+ * @experimental
37
43
  */
38
44
  declare const registerWallet: (client: x402Client, config: X402PayConfig, deps: WalletDeps) => Promise<void>;
39
- /** A payment-enabled `fetch`: same signature as the platform `fetch`. */
45
+ /**
46
+ * A payment-enabled `fetch`: same signature as the platform `fetch`.
47
+ * @experimental
48
+ */
40
49
  type PayFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
41
- /** Dependencies for building a pay-rail fetch: secret access, plus an optional base `fetch` to wrap. */
50
+ /**
51
+ * Dependencies for building a pay-rail fetch: secret access, plus an optional base `fetch` to wrap.
52
+ * @experimental
53
+ */
42
54
  interface X402PayDeps extends WalletDeps {
43
55
  /** The `fetch` to wrap (defaults to `globalThis.fetch`). Inject to test or to chain transports. */
44
56
  readonly fetch?: typeof globalThis.fetch;
@@ -47,9 +59,13 @@ interface X402PayDeps extends WalletDeps {
47
59
  * Build a payment-enabled `fetch` for `config`. Throws (before resolving a
48
60
  * signer) when `config.policy` is unbounded — an agent wallet is never built
49
61
  * with unlimited spend authority.
62
+ * @experimental
50
63
  */
51
64
  declare const createPayFetch: (config: X402PayConfig, deps: X402PayDeps) => Promise<PayFetch>;
52
- /** A configured pay rail: a payment-enabled `fetch` bounded by the spend policy. */
65
+ /**
66
+ * A configured pay rail: a payment-enabled `fetch` bounded by the spend policy.
67
+ * @experimental
68
+ */
53
69
  interface X402Pay {
54
70
  /** A `fetch` that transparently pays for `402`-gated resources under the policy. */
55
71
  readonly fetch: PayFetch;
@@ -58,6 +74,7 @@ interface X402Pay {
58
74
  * Build a pay rail for `config`. The returned `fetch` answers `402` challenges by
59
75
  * signing and retrying, within `config.policy`. Throws (before touching the
60
76
  * signer) when the policy is unbounded.
77
+ * @experimental
61
78
  */
62
79
  declare const createX402Pay: (config: X402PayConfig, deps: X402PayDeps) => Promise<X402Pay>;
63
80
  /**
@@ -72,6 +89,7 @@ declare const createX402Pay: (config: X402PayConfig, deps: X402PayDeps) => Promi
72
89
  * per-run cap scopes to the ctx, not to each request. A failed build (e.g. an
73
90
  * unbounded policy) is memoised too, keeping the rail deterministically
74
91
  * fail-closed.
92
+ * @experimental
75
93
  */
76
94
  declare const lazyX402Pay: (config: X402PayConfig, deps: X402PayDeps) => X402Pay;
77
95
  export { type PayFetch, type WalletDeps, X402Pay, type X402PayConfig, type X402PayDeps, createPayFetch, createX402Pay, lazyX402Pay, registerWallet, resolveEvmAccount, resolveSvmSigner };
@@ -1,7 +1,7 @@
1
- import { createPayFetch } from '../packem_shared/createPayFetch-O2vkvM1v.mjs';
1
+ import { createPayFetch } from '../packem_shared/createPayFetch-BeT05njL.mjs';
2
2
  export { DEFAULT_FACILITATOR_URL, resolveFacilitatorUrl } from '../packem_shared/DEFAULT_FACILITATOR_URL-Cbz6kIqa.mjs';
3
3
  export { isEvmNetwork, isSvmNetwork, toCaip2 } from '../packem_shared/EVM_NETWORKS-BhnYWUQ4.mjs';
4
- export { DEFAULT_STABLECOIN_DECIMALS, assertBoundedPolicy, buildPaymentGuard, buildSpendPolicy, createSpendState, recordSpend, usdToAtomic } from '../packem_shared/DEFAULT_STABLECOIN_DECIMALS-CSu5b5lD.mjs';
4
+ export { DEFAULT_STABLECOIN_DECIMALS, assertBoundedPolicy, buildPaymentGuard, buildSpendPolicy, createSpendState, releaseSpendOnFailure, usdToAtomic } from '../packem_shared/DEFAULT_STABLECOIN_DECIMALS-CpW619nu.mjs';
5
5
  export { registerWallet, resolveEvmAccount, resolveSvmSigner } from '../packem_shared/registerWallet-I4pVwq65.mjs';
6
6
 
7
7
  const createX402Pay = async (config, deps) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/x402",
3
- "version": "1.0.0-alpha.2",
3
+ "version": "1.0.0-alpha.4",
4
4
  "description": "Agentic payments (x402) for Lunora: charge agents per request (charge rail) and let your agents pay x402-gated resources (pay rail)",
5
5
  "keywords": [
6
6
  "agents",
@@ -52,7 +52,7 @@
52
52
  "access": "public"
53
53
  },
54
54
  "dependencies": {
55
- "@lunora/errors": "1.0.0-alpha.4",
55
+ "@lunora/errors": "1.0.0-alpha.5",
56
56
  "@solana/kit": "5.5.1",
57
57
  "@x402/core": "2.17.0",
58
58
  "@x402/evm": "2.17.0",