@parallel-protocol/x402 0.6.0 → 0.7.1

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/dist/express.js CHANGED
@@ -1,10 +1,36 @@
1
- import { raceTimeout, HANDLER_TIMEOUT, createPaymentMiddleware } from './chunk-TLCEPWQI.js';
1
+ import { raceTimeout, HANDLER_TIMEOUT, parseJsonBody, createPaymentMiddleware } from './chunk-AENXD4OC.js';
2
+ import './chunk-AFRDHGDT.js';
2
3
 
3
4
  // src/express/adapter.ts
5
+ function chunkBytes(args) {
6
+ const [chunk, encoding] = args;
7
+ if (typeof chunk === "string") {
8
+ return Buffer.from(
9
+ chunk,
10
+ typeof encoding === "string" ? encoding : "utf8"
11
+ );
12
+ }
13
+ if (chunk instanceof Uint8Array) return Buffer.from(chunk);
14
+ return void 0;
15
+ }
4
16
  function headerValue(value) {
5
17
  if (value === void 0) return void 0;
6
18
  return Array.isArray(value) ? value[0] : String(value);
7
19
  }
20
+ function writeHeadContentType(args) {
21
+ const headers = args.find((arg) => typeof arg === "object" && arg !== null);
22
+ if (!headers) return void 0;
23
+ if (Array.isArray(headers)) {
24
+ const index = headers.findIndex(
25
+ (entry, i) => i % 2 === 0 && typeof entry === "string" && entry.toLowerCase() === "content-type"
26
+ );
27
+ return index === -1 ? void 0 : headerValue(headers[index + 1]);
28
+ }
29
+ for (const [name, value] of Object.entries(headers)) {
30
+ if (name.toLowerCase() === "content-type") return headerValue(value);
31
+ }
32
+ return void 0;
33
+ }
8
34
  var TIMEOUT_BODY = JSON.stringify({
9
35
  error: "HANDLER_TIMEOUT",
10
36
  message: "The resource handler did not respond in time."
@@ -31,6 +57,11 @@ var ExpressAdapter = class {
31
57
  getUrl() {
32
58
  return `${this.req.protocol}://${this.req.headers.host}${this.req.originalUrl}`;
33
59
  }
60
+ // Whatever a body parser mounted before this middleware left on `req.body`
61
+ // (`express.json()` for JSON); `undefined` when none ran.
62
+ async getBody() {
63
+ return this.req.body;
64
+ }
34
65
  async runHandler(options) {
35
66
  const res = this.res;
36
67
  const next = this.next;
@@ -100,6 +131,24 @@ var ExpressAdapter = class {
100
131
  setResponseHeader: (name, value) => res.setHeader(name, value),
101
132
  getResponseHeader: (name) => headerValue(res.getHeader(name)),
102
133
  removeResponseHeader: (name) => res.removeHeader(name),
134
+ // The buffered chunks are the whole response: parse them when the
135
+ // handler declared JSON.
136
+ getResponseBody: async () => {
137
+ const bytes = [];
138
+ let contentType = headerValue(res.getHeader("content-type"));
139
+ for (const [method, args] of buffered) {
140
+ if (method === "writeHead") {
141
+ contentType ??= writeHeadContentType(args);
142
+ continue;
143
+ }
144
+ const chunk = chunkBytes(args);
145
+ if (chunk) bytes.push(chunk);
146
+ }
147
+ return parseJsonBody(
148
+ contentType,
149
+ Buffer.concat(bytes).toString("utf8")
150
+ );
151
+ },
103
152
  sendResponse: () => {
104
153
  settled = true;
105
154
  restore();
package/dist/fastify.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkK6QETMQA_cjs = require('./chunk-K6QETMQA.cjs');
3
+ var chunkPOU7EH3E_cjs = require('./chunk-POU7EH3E.cjs');
4
+ require('./chunk-JPMK5YBU.cjs');
4
5
  var fp = require('fastify-plugin');
5
6
 
6
7
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -26,9 +27,18 @@ var FastifyAdapter = class {
26
27
  getUrl() {
27
28
  return `${this.req.protocol}://${this.req.host || this.req.hostname}${this.req.url}`;
28
29
  }
30
+ // Fastify has parsed the body by the time the preHandler stage runs, which
31
+ // is where routes priced per request are gated.
32
+ async getBody() {
33
+ return this.req.body;
34
+ }
29
35
  };
30
36
 
31
37
  // src/fastify/index.ts
38
+ function payloadJson(contentType, payload) {
39
+ const text = typeof payload === "string" ? payload : payload instanceof Uint8Array ? Buffer.from(payload).toString("utf8") : void 0;
40
+ return chunkPOU7EH3E_cjs.parseJsonBody(contentType, text);
41
+ }
32
42
  function refuse(reply, body) {
33
43
  const json = JSON.stringify(body);
34
44
  reply.status(402);
@@ -42,10 +52,25 @@ function headerValue(value) {
42
52
  }
43
53
  function paymentMiddleware(config) {
44
54
  return fp__default.default(async (fastify) => {
45
- const gate = chunkK6QETMQA_cjs.createPaymentGate(config);
55
+ const gate = chunkPOU7EH3E_cjs.createPaymentGate(config);
46
56
  const verifiedMap = /* @__PURE__ */ new WeakMap();
47
57
  const rawWriters = /* @__PURE__ */ new WeakMap();
58
+ const needsBody = (req) => {
59
+ const route = chunkPOU7EH3E_cjs.matchRoute(
60
+ new FastifyAdapter(req).getPath(),
61
+ config.routes
62
+ );
63
+ return typeof route?.price === "function" || route?.meter !== void 0;
64
+ };
48
65
  fastify.addHook("onRequest", async (req, reply) => {
66
+ if (needsBody(req)) return;
67
+ await runGate(req, reply);
68
+ });
69
+ fastify.addHook("preHandler", async (req, reply) => {
70
+ if (!needsBody(req)) return;
71
+ await runGate(req, reply);
72
+ });
73
+ async function runGate(req, reply) {
49
74
  const gateResult = await gate(new FastifyAdapter(req));
50
75
  if (gateResult.type === "pass") return;
51
76
  if (gateResult.type === "error") {
@@ -70,7 +95,7 @@ function paymentMiddleware(config) {
70
95
  raw.write = refuse2;
71
96
  raw.end = refuse2;
72
97
  raw.writeHead = refuse2;
73
- });
98
+ }
74
99
  fastify.addHook("onSend", async (req, reply, payload) => {
75
100
  const writers = rawWriters.get(req);
76
101
  if (writers) {
@@ -84,21 +109,21 @@ function paymentMiddleware(config) {
84
109
  if (!verified) return payload;
85
110
  let raw;
86
111
  if (verified.scheme === "upto") {
87
- raw = headerValue(reply.getHeader(chunkK6QETMQA_cjs.SETTLE_AMOUNT_HEADER));
88
- reply.removeHeader(chunkK6QETMQA_cjs.SETTLE_AMOUNT_HEADER);
112
+ raw = headerValue(reply.getHeader(chunkPOU7EH3E_cjs.SETTLE_AMOUNT_HEADER));
113
+ reply.removeHeader(chunkPOU7EH3E_cjs.SETTLE_AMOUNT_HEADER);
89
114
  }
90
115
  if (reply.statusCode < 200 || reply.statusCode >= 300) {
91
116
  return payload;
92
117
  }
93
118
  let settleOptions;
94
119
  if (verified.scheme === "upto") {
95
- const resolved = chunkK6QETMQA_cjs.resolveSettleAmount(
96
- raw,
97
- verified.maxAmount,
98
- verified.decimals
99
- );
120
+ const resolved = await chunkPOU7EH3E_cjs.resolveCharge(verified, raw, {
121
+ status: reply.statusCode,
122
+ header: (name) => headerValue(reply.getHeader(name)),
123
+ body: async () => payloadJson(headerValue(reply.getHeader("content-type")), payload)
124
+ });
100
125
  if (resolved.kind === "invalid") {
101
- return refuse(reply, chunkK6QETMQA_cjs.INVALID_SETTLE_AMOUNT_RESPONSE);
126
+ return refuse(reply, chunkPOU7EH3E_cjs.invalidSettleAmountResponse(resolved.reason));
102
127
  }
103
128
  if (resolved.kind === "skip") return payload;
104
129
  settleOptions = { amount: resolved.amount };
@@ -107,7 +132,7 @@ function paymentMiddleware(config) {
107
132
  try {
108
133
  const settleResponse = settleOptions ? await verified.settle(settleOptions) : await verified.settle();
109
134
  if (settleResponse.success) {
110
- reply.header("payment-response", chunkK6QETMQA_cjs.encodeBase64(settleResponse));
135
+ reply.header("payment-response", chunkPOU7EH3E_cjs.encodeBase64(settleResponse));
111
136
  reply.header("access-control-expose-headers", "payment-response");
112
137
  settled = true;
113
138
  }
@@ -1,5 +1,5 @@
1
1
  import { FastifyRequest, FastifyPluginAsync } from 'fastify';
2
- import { H as HTTPAdapter, P as PaymentMiddlewareConfig } from './types-CBzm5CtM.cjs';
2
+ import { H as HTTPAdapter, P as PaymentMiddlewareConfig } from './types-q_gu5wjz.cjs';
3
3
  import 'viem';
4
4
 
5
5
  declare class FastifyAdapter implements HTTPAdapter {
@@ -9,6 +9,7 @@ declare class FastifyAdapter implements HTTPAdapter {
9
9
  getMethod(): string;
10
10
  getPath(): string;
11
11
  getUrl(): string;
12
+ getBody(): Promise<unknown>;
12
13
  }
13
14
 
14
15
  declare function paymentMiddleware(config: PaymentMiddlewareConfig): FastifyPluginAsync;
package/dist/fastify.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { FastifyRequest, FastifyPluginAsync } from 'fastify';
2
- import { H as HTTPAdapter, P as PaymentMiddlewareConfig } from './types-CBzm5CtM.js';
2
+ import { H as HTTPAdapter, P as PaymentMiddlewareConfig } from './types-q_gu5wjz.js';
3
3
  import 'viem';
4
4
 
5
5
  declare class FastifyAdapter implements HTTPAdapter {
@@ -9,6 +9,7 @@ declare class FastifyAdapter implements HTTPAdapter {
9
9
  getMethod(): string;
10
10
  getPath(): string;
11
11
  getUrl(): string;
12
+ getBody(): Promise<unknown>;
12
13
  }
13
14
 
14
15
  declare function paymentMiddleware(config: PaymentMiddlewareConfig): FastifyPluginAsync;
package/dist/fastify.js CHANGED
@@ -1,4 +1,5 @@
1
- import { createPaymentGate, SETTLE_AMOUNT_HEADER, resolveSettleAmount, encodeBase64, INVALID_SETTLE_AMOUNT_RESPONSE } from './chunk-TLCEPWQI.js';
1
+ import { createPaymentGate, SETTLE_AMOUNT_HEADER, resolveCharge, invalidSettleAmountResponse, encodeBase64, matchRoute, parseJsonBody } from './chunk-AENXD4OC.js';
2
+ import './chunk-AFRDHGDT.js';
2
3
  import fp from 'fastify-plugin';
3
4
 
4
5
  // src/fastify/adapter.ts
@@ -20,9 +21,18 @@ var FastifyAdapter = class {
20
21
  getUrl() {
21
22
  return `${this.req.protocol}://${this.req.host || this.req.hostname}${this.req.url}`;
22
23
  }
24
+ // Fastify has parsed the body by the time the preHandler stage runs, which
25
+ // is where routes priced per request are gated.
26
+ async getBody() {
27
+ return this.req.body;
28
+ }
23
29
  };
24
30
 
25
31
  // src/fastify/index.ts
32
+ function payloadJson(contentType, payload) {
33
+ const text = typeof payload === "string" ? payload : payload instanceof Uint8Array ? Buffer.from(payload).toString("utf8") : void 0;
34
+ return parseJsonBody(contentType, text);
35
+ }
26
36
  function refuse(reply, body) {
27
37
  const json = JSON.stringify(body);
28
38
  reply.status(402);
@@ -39,7 +49,22 @@ function paymentMiddleware(config) {
39
49
  const gate = createPaymentGate(config);
40
50
  const verifiedMap = /* @__PURE__ */ new WeakMap();
41
51
  const rawWriters = /* @__PURE__ */ new WeakMap();
52
+ const needsBody = (req) => {
53
+ const route = matchRoute(
54
+ new FastifyAdapter(req).getPath(),
55
+ config.routes
56
+ );
57
+ return typeof route?.price === "function" || route?.meter !== void 0;
58
+ };
42
59
  fastify.addHook("onRequest", async (req, reply) => {
60
+ if (needsBody(req)) return;
61
+ await runGate(req, reply);
62
+ });
63
+ fastify.addHook("preHandler", async (req, reply) => {
64
+ if (!needsBody(req)) return;
65
+ await runGate(req, reply);
66
+ });
67
+ async function runGate(req, reply) {
43
68
  const gateResult = await gate(new FastifyAdapter(req));
44
69
  if (gateResult.type === "pass") return;
45
70
  if (gateResult.type === "error") {
@@ -64,7 +89,7 @@ function paymentMiddleware(config) {
64
89
  raw.write = refuse2;
65
90
  raw.end = refuse2;
66
91
  raw.writeHead = refuse2;
67
- });
92
+ }
68
93
  fastify.addHook("onSend", async (req, reply, payload) => {
69
94
  const writers = rawWriters.get(req);
70
95
  if (writers) {
@@ -86,13 +111,13 @@ function paymentMiddleware(config) {
86
111
  }
87
112
  let settleOptions;
88
113
  if (verified.scheme === "upto") {
89
- const resolved = resolveSettleAmount(
90
- raw,
91
- verified.maxAmount,
92
- verified.decimals
93
- );
114
+ const resolved = await resolveCharge(verified, raw, {
115
+ status: reply.statusCode,
116
+ header: (name) => headerValue(reply.getHeader(name)),
117
+ body: async () => payloadJson(headerValue(reply.getHeader("content-type")), payload)
118
+ });
94
119
  if (resolved.kind === "invalid") {
95
- return refuse(reply, INVALID_SETTLE_AMOUNT_RESPONSE);
120
+ return refuse(reply, invalidSettleAmountResponse(resolved.reason));
96
121
  }
97
122
  if (resolved.kind === "skip") return payload;
98
123
  settleOptions = { amount: resolved.amount };
package/dist/hono.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkK6QETMQA_cjs = require('./chunk-K6QETMQA.cjs');
3
+ var chunkPOU7EH3E_cjs = require('./chunk-POU7EH3E.cjs');
4
+ require('./chunk-JPMK5YBU.cjs');
4
5
 
5
6
  // src/hono/adapter.ts
6
7
  var TIMEOUT_BODY = JSON.stringify({
@@ -26,9 +27,19 @@ var HonoAdapter = class {
26
27
  getUrl() {
27
28
  return this.c.req.url;
28
29
  }
30
+ // Hono caches the parsed body on the request, so the handler can still read
31
+ // it after the price function did.
32
+ async getBody() {
33
+ if (!chunkPOU7EH3E_cjs.isJsonContentType(this.c.req.header("content-type"))) return void 0;
34
+ try {
35
+ return await this.c.req.json();
36
+ } catch {
37
+ return void 0;
38
+ }
39
+ }
29
40
  async runHandler(options) {
30
- const outcome = await chunkK6QETMQA_cjs.raceTimeout(this.next(), options?.timeoutMs);
31
- if (outcome === chunkK6QETMQA_cjs.HANDLER_TIMEOUT) {
41
+ const outcome = await chunkPOU7EH3E_cjs.raceTimeout(this.next(), options?.timeoutMs);
42
+ if (outcome === chunkPOU7EH3E_cjs.HANDLER_TIMEOUT) {
32
43
  return {
33
44
  status: 504,
34
45
  setResponseHeader: () => {
@@ -52,9 +63,15 @@ var HonoAdapter = class {
52
63
  setResponseHeader: (name, value) => res.headers.set(name, value),
53
64
  getResponseHeader: (name) => res.headers.get(name) ?? void 0,
54
65
  removeResponseHeader: (name) => res.headers.delete(name),
66
+ // Read from a clone: the response body streams once, to the client.
67
+ getResponseBody: () => chunkPOU7EH3E_cjs.jsonFromResponse(res),
55
68
  sendResponse: () => {
56
69
  },
70
+ // The response is replaced: release its body (and whatever upstream
71
+ // stream feeds it) instead of leaving it open until collected.
57
72
  discardResponse: () => {
73
+ res.body?.cancel().catch(() => {
74
+ });
58
75
  }
59
76
  };
60
77
  }
@@ -62,7 +79,7 @@ var HonoAdapter = class {
62
79
 
63
80
  // src/hono/index.ts
64
81
  function paymentMiddleware(config) {
65
- const middleware = chunkK6QETMQA_cjs.createPaymentMiddleware(config);
82
+ const middleware = chunkPOU7EH3E_cjs.createPaymentMiddleware(config);
66
83
  return async (c, next) => {
67
84
  const result = await middleware(new HonoAdapter(c, next));
68
85
  if ("pass" in result) {
package/dist/hono.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Context, Next, MiddlewareHandler } from 'hono';
2
- import { H as HTTPAdapter, R as RunHandlerOptions, a as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-CBzm5CtM.cjs';
2
+ import { H as HTTPAdapter, R as RunHandlerOptions, a as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-q_gu5wjz.cjs';
3
3
  import 'viem';
4
4
 
5
5
  declare class HonoAdapter implements HTTPAdapter {
@@ -10,6 +10,7 @@ declare class HonoAdapter implements HTTPAdapter {
10
10
  getMethod(): string;
11
11
  getPath(): string;
12
12
  getUrl(): string;
13
+ getBody(): Promise<unknown>;
13
14
  runHandler(options?: RunHandlerOptions): Promise<RunHandlerResult>;
14
15
  }
15
16
 
package/dist/hono.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Context, Next, MiddlewareHandler } from 'hono';
2
- import { H as HTTPAdapter, R as RunHandlerOptions, a as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-CBzm5CtM.js';
2
+ import { H as HTTPAdapter, R as RunHandlerOptions, a as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-q_gu5wjz.js';
3
3
  import 'viem';
4
4
 
5
5
  declare class HonoAdapter implements HTTPAdapter {
@@ -10,6 +10,7 @@ declare class HonoAdapter implements HTTPAdapter {
10
10
  getMethod(): string;
11
11
  getPath(): string;
12
12
  getUrl(): string;
13
+ getBody(): Promise<unknown>;
13
14
  runHandler(options?: RunHandlerOptions): Promise<RunHandlerResult>;
14
15
  }
15
16
 
package/dist/hono.js CHANGED
@@ -1,4 +1,5 @@
1
- import { raceTimeout, HANDLER_TIMEOUT, createPaymentMiddleware } from './chunk-TLCEPWQI.js';
1
+ import { isJsonContentType, raceTimeout, HANDLER_TIMEOUT, jsonFromResponse, createPaymentMiddleware } from './chunk-AENXD4OC.js';
2
+ import './chunk-AFRDHGDT.js';
2
3
 
3
4
  // src/hono/adapter.ts
4
5
  var TIMEOUT_BODY = JSON.stringify({
@@ -24,6 +25,16 @@ var HonoAdapter = class {
24
25
  getUrl() {
25
26
  return this.c.req.url;
26
27
  }
28
+ // Hono caches the parsed body on the request, so the handler can still read
29
+ // it after the price function did.
30
+ async getBody() {
31
+ if (!isJsonContentType(this.c.req.header("content-type"))) return void 0;
32
+ try {
33
+ return await this.c.req.json();
34
+ } catch {
35
+ return void 0;
36
+ }
37
+ }
27
38
  async runHandler(options) {
28
39
  const outcome = await raceTimeout(this.next(), options?.timeoutMs);
29
40
  if (outcome === HANDLER_TIMEOUT) {
@@ -50,9 +61,15 @@ var HonoAdapter = class {
50
61
  setResponseHeader: (name, value) => res.headers.set(name, value),
51
62
  getResponseHeader: (name) => res.headers.get(name) ?? void 0,
52
63
  removeResponseHeader: (name) => res.headers.delete(name),
64
+ // Read from a clone: the response body streams once, to the client.
65
+ getResponseBody: () => jsonFromResponse(res),
53
66
  sendResponse: () => {
54
67
  },
68
+ // The response is replaced: release its body (and whatever upstream
69
+ // stream feeds it) instead of leaving it open until collected.
55
70
  discardResponse: () => {
71
+ res.body?.cancel().catch(() => {
72
+ });
56
73
  }
57
74
  };
58
75
  }
package/dist/index.cjs CHANGED
@@ -1,62 +1,75 @@
1
1
  'use strict';
2
2
 
3
- var chunkK6QETMQA_cjs = require('./chunk-K6QETMQA.cjs');
3
+ var chunkPOU7EH3E_cjs = require('./chunk-POU7EH3E.cjs');
4
+ var chunkJPMK5YBU_cjs = require('./chunk-JPMK5YBU.cjs');
4
5
 
5
6
 
6
7
 
7
8
  Object.defineProperty(exports, "FacilitatorClient", {
8
9
  enumerable: true,
9
- get: function () { return chunkK6QETMQA_cjs.FacilitatorClient; }
10
+ get: function () { return chunkPOU7EH3E_cjs.FacilitatorClient; }
10
11
  });
11
12
  Object.defineProperty(exports, "PARALLEL_TOKENS", {
12
13
  enumerable: true,
13
- get: function () { return chunkK6QETMQA_cjs.PARALLEL_TOKENS; }
14
+ get: function () { return chunkPOU7EH3E_cjs.PARALLEL_TOKENS; }
14
15
  });
15
16
  Object.defineProperty(exports, "SETTLE_AMOUNT_HEADER", {
16
17
  enumerable: true,
17
- get: function () { return chunkK6QETMQA_cjs.SETTLE_AMOUNT_HEADER; }
18
- });
19
- Object.defineProperty(exports, "X402ConfigError", {
20
- enumerable: true,
21
- get: function () { return chunkK6QETMQA_cjs.X402ConfigError; }
22
- });
23
- Object.defineProperty(exports, "X402RuntimeError", {
24
- enumerable: true,
25
- get: function () { return chunkK6QETMQA_cjs.X402RuntimeError; }
26
- });
27
- Object.defineProperty(exports, "X402_ERROR_CODES", {
28
- enumerable: true,
29
- get: function () { return chunkK6QETMQA_cjs.X402_ERROR_CODES; }
18
+ get: function () { return chunkPOU7EH3E_cjs.SETTLE_AMOUNT_HEADER; }
30
19
  });
31
20
  Object.defineProperty(exports, "createPaymentGate", {
32
21
  enumerable: true,
33
- get: function () { return chunkK6QETMQA_cjs.createPaymentGate; }
22
+ get: function () { return chunkPOU7EH3E_cjs.createPaymentGate; }
34
23
  });
35
24
  Object.defineProperty(exports, "createPaymentMiddleware", {
36
25
  enumerable: true,
37
- get: function () { return chunkK6QETMQA_cjs.createPaymentMiddleware; }
26
+ get: function () { return chunkPOU7EH3E_cjs.createPaymentMiddleware; }
38
27
  });
39
28
  Object.defineProperty(exports, "encodeBase64", {
40
29
  enumerable: true,
41
- get: function () { return chunkK6QETMQA_cjs.encodeBase64; }
30
+ get: function () { return chunkPOU7EH3E_cjs.encodeBase64; }
42
31
  });
43
32
  Object.defineProperty(exports, "getDefaultAcceptedTokens", {
44
33
  enumerable: true,
45
- get: function () { return chunkK6QETMQA_cjs.getDefaultAcceptedTokens; }
34
+ get: function () { return chunkPOU7EH3E_cjs.getDefaultAcceptedTokens; }
46
35
  });
47
36
  Object.defineProperty(exports, "parsePrice", {
48
37
  enumerable: true,
49
- get: function () { return chunkK6QETMQA_cjs.parsePrice; }
38
+ get: function () { return chunkPOU7EH3E_cjs.parsePrice; }
39
+ });
40
+ Object.defineProperty(exports, "resolveCharge", {
41
+ enumerable: true,
42
+ get: function () { return chunkPOU7EH3E_cjs.resolveCharge; }
50
43
  });
51
44
  Object.defineProperty(exports, "resolveSettleAmount", {
52
45
  enumerable: true,
53
- get: function () { return chunkK6QETMQA_cjs.resolveSettleAmount; }
46
+ get: function () { return chunkPOU7EH3E_cjs.resolveSettleAmount; }
54
47
  });
55
48
  Object.defineProperty(exports, "toEip155Network", {
56
49
  enumerable: true,
57
- get: function () { return chunkK6QETMQA_cjs.toEip155Network; }
50
+ get: function () { return chunkPOU7EH3E_cjs.toEip155Network; }
58
51
  });
59
52
  Object.defineProperty(exports, "validateAddress", {
60
53
  enumerable: true,
61
- get: function () { return chunkK6QETMQA_cjs.validateAddress; }
54
+ get: function () { return chunkPOU7EH3E_cjs.validateAddress; }
55
+ });
56
+ Object.defineProperty(exports, "X402ConfigError", {
57
+ enumerable: true,
58
+ get: function () { return chunkJPMK5YBU_cjs.X402ConfigError; }
59
+ });
60
+ Object.defineProperty(exports, "X402PriceRejectedError", {
61
+ enumerable: true,
62
+ get: function () { return chunkJPMK5YBU_cjs.X402PriceRejectedError; }
63
+ });
64
+ Object.defineProperty(exports, "X402RuntimeError", {
65
+ enumerable: true,
66
+ get: function () { return chunkJPMK5YBU_cjs.X402RuntimeError; }
67
+ });
68
+ Object.defineProperty(exports, "X402_ERROR_CODES", {
69
+ enumerable: true,
70
+ get: function () { return chunkJPMK5YBU_cjs.X402_ERROR_CODES; }
71
+ });
72
+ Object.defineProperty(exports, "isPriceRejection", {
73
+ enumerable: true,
74
+ get: function () { return chunkJPMK5YBU_cjs.isPriceRejection; }
62
75
  });
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FacilitatorConfig, b as FacilitatorPaymentRequirements, S as SettleOptions, c as FacilitatorResponse, P as PaymentMiddlewareConfig, H as HTTPAdapter, d as PaymentGateResult, M as MiddlewareResult, e as PassResult } from './types-CBzm5CtM.cjs';
2
- export { f as PaymentConfirmation, g as PaymentFailure, h as PaymentRequired, i as PaymentRequirements, j as ResourceInfo, k as RouteConfig, R as RunHandlerOptions, a as RunHandlerResult, V as VerifiedPayment } from './types-CBzm5CtM.cjs';
1
+ import { F as FacilitatorConfig, b as FacilitatorPaymentRequirements, S as SettleOptions, c as FacilitatorResponse, P as PaymentMiddlewareConfig, H as HTTPAdapter, d as PaymentGateResult, M as MiddlewareResult, e as PassResult, V as VerifiedPayment } from './types-q_gu5wjz.cjs';
2
+ export { f as MeterContext, g as MeterFunction, h as PaymentConfirmation, i as PaymentFailure, j as PaymentRequired, k as PaymentRequirements, l as PriceContext, m as PriceFunction, n as ResourceInfo, o as RouteConfig, R as RunHandlerOptions, a as RunHandlerResult } from './types-q_gu5wjz.cjs';
3
3
  import { Address } from 'viem';
4
4
 
5
5
  declare class FacilitatorClient {
@@ -24,7 +24,25 @@ declare class X402RuntimeError extends Error {
24
24
  readonly code: string;
25
25
  constructor(code: string);
26
26
  }
27
+ /**
28
+ * Thrown by a per-request price function to refuse a request before anything
29
+ * is invoiced or signed: the middleware answers with `status` and a JSON body
30
+ * `{ error: code, message }`. For requests the route will not serve (an
31
+ * unknown model, an unsupported option) rather than merchant bugs.
32
+ */
33
+ declare class X402PriceRejectedError extends Error {
34
+ readonly code: string;
35
+ readonly status: number;
36
+ constructor(code: string, message: string, status?: number);
37
+ }
38
+ /**
39
+ * Recognises a price refusal by shape rather than by class identity, so a
40
+ * refusal thrown from another copy of this package (a nested install, the
41
+ * CommonJS build next to the ESM one) is still answered as a refusal.
42
+ */
43
+ declare function isPriceRejection(error: unknown): error is X402PriceRejectedError;
27
44
 
45
+ type GateAdapter = Pick<HTTPAdapter, "getHeader" | "getPath" | "getUrl" | "getMethod" | "getBody">;
28
46
  /**
29
47
  * Response header a handler on a metered (`upto`) route sets to the amount it
30
48
  * actually charges, in whole token units like `price` ("0.75"). Read and
@@ -39,7 +57,7 @@ type SettleAmountResolution = {
39
57
  kind: "skip";
40
58
  } | {
41
59
  kind: "invalid";
42
- reason: "malformed" | "precision" | "above-cap";
60
+ reason: "malformed" | "precision" | "above-cap" | "meter";
43
61
  };
44
62
  /**
45
63
  * Turns the handler's `payment-settle-amount` header into a settle decision.
@@ -50,7 +68,23 @@ type SettleAmountResolution = {
50
68
  * silently served for free.
51
69
  */
52
70
  declare function resolveSettleAmount(raw: string | undefined, maxAmount: bigint, decimals: number): SettleAmountResolution;
53
- declare function createPaymentGate(config: PaymentMiddlewareConfig): (adapter: Pick<HTTPAdapter, "getHeader" | "getPath" | "getUrl" | "getMethod">) => Promise<PaymentGateResult>;
71
+ /** What the middleware can read from the handler's response. */
72
+ interface ChargeResponse {
73
+ status: number;
74
+ header(name: string): string | undefined;
75
+ body(): Promise<unknown>;
76
+ }
77
+ /**
78
+ * The amount a metered route settles, once the handler answered with a 2xx.
79
+ * A `meter` function on the route decides first, from the response: its
80
+ * amount is bounded by the cap (an overrun is the merchant's loss, not a
81
+ * refused response), zero serves for free, and no amount hands over to the
82
+ * `payment-settle-amount` header, which keeps its rules (absent charges the
83
+ * cap, above the cap is refused). A meter that throws or returns something
84
+ * that is not an amount fails closed like an invalid header.
85
+ */
86
+ declare function resolveCharge(verified: VerifiedPayment, settleHeader: string | undefined, response: ChargeResponse): Promise<SettleAmountResolution>;
87
+ declare function createPaymentGate(config: PaymentMiddlewareConfig): (adapter: GateAdapter) => Promise<PaymentGateResult>;
54
88
  declare function createPaymentMiddleware(config: PaymentMiddlewareConfig): (adapter: HTTPAdapter) => Promise<MiddlewareResult | PassResult>;
55
89
 
56
90
  type ParallelNetwork = "ethereum" | "base" | "avalanche" | "hyperevm";
@@ -74,4 +108,4 @@ declare function validateAddress(address: string): address is Address;
74
108
  declare function toEip155Network(network: string): string;
75
109
  declare function encodeBase64(obj: unknown): string;
76
110
 
77
- export { FacilitatorClient, FacilitatorConfig, FacilitatorPaymentRequirements, FacilitatorResponse, HTTPAdapter, MiddlewareResult, PARALLEL_TOKENS, type ParallelNetwork, PassResult, PaymentGateResult, PaymentMiddlewareConfig, SETTLE_AMOUNT_HEADER, type SettleAmountResolution, SettleOptions, X402ConfigError, type X402ErrorCode, X402RuntimeError, X402_ERROR_CODES, createPaymentGate, createPaymentMiddleware, encodeBase64, getDefaultAcceptedTokens, parsePrice, resolveSettleAmount, toEip155Network, validateAddress };
111
+ export { type ChargeResponse, FacilitatorClient, FacilitatorConfig, FacilitatorPaymentRequirements, FacilitatorResponse, HTTPAdapter, MiddlewareResult, PARALLEL_TOKENS, type ParallelNetwork, PassResult, PaymentGateResult, PaymentMiddlewareConfig, SETTLE_AMOUNT_HEADER, type SettleAmountResolution, SettleOptions, VerifiedPayment, X402ConfigError, type X402ErrorCode, X402PriceRejectedError, X402RuntimeError, X402_ERROR_CODES, createPaymentGate, createPaymentMiddleware, encodeBase64, getDefaultAcceptedTokens, isPriceRejection, parsePrice, resolveCharge, resolveSettleAmount, toEip155Network, validateAddress };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FacilitatorConfig, b as FacilitatorPaymentRequirements, S as SettleOptions, c as FacilitatorResponse, P as PaymentMiddlewareConfig, H as HTTPAdapter, d as PaymentGateResult, M as MiddlewareResult, e as PassResult } from './types-CBzm5CtM.js';
2
- export { f as PaymentConfirmation, g as PaymentFailure, h as PaymentRequired, i as PaymentRequirements, j as ResourceInfo, k as RouteConfig, R as RunHandlerOptions, a as RunHandlerResult, V as VerifiedPayment } from './types-CBzm5CtM.js';
1
+ import { F as FacilitatorConfig, b as FacilitatorPaymentRequirements, S as SettleOptions, c as FacilitatorResponse, P as PaymentMiddlewareConfig, H as HTTPAdapter, d as PaymentGateResult, M as MiddlewareResult, e as PassResult, V as VerifiedPayment } from './types-q_gu5wjz.js';
2
+ export { f as MeterContext, g as MeterFunction, h as PaymentConfirmation, i as PaymentFailure, j as PaymentRequired, k as PaymentRequirements, l as PriceContext, m as PriceFunction, n as ResourceInfo, o as RouteConfig, R as RunHandlerOptions, a as RunHandlerResult } from './types-q_gu5wjz.js';
3
3
  import { Address } from 'viem';
4
4
 
5
5
  declare class FacilitatorClient {
@@ -24,7 +24,25 @@ declare class X402RuntimeError extends Error {
24
24
  readonly code: string;
25
25
  constructor(code: string);
26
26
  }
27
+ /**
28
+ * Thrown by a per-request price function to refuse a request before anything
29
+ * is invoiced or signed: the middleware answers with `status` and a JSON body
30
+ * `{ error: code, message }`. For requests the route will not serve (an
31
+ * unknown model, an unsupported option) rather than merchant bugs.
32
+ */
33
+ declare class X402PriceRejectedError extends Error {
34
+ readonly code: string;
35
+ readonly status: number;
36
+ constructor(code: string, message: string, status?: number);
37
+ }
38
+ /**
39
+ * Recognises a price refusal by shape rather than by class identity, so a
40
+ * refusal thrown from another copy of this package (a nested install, the
41
+ * CommonJS build next to the ESM one) is still answered as a refusal.
42
+ */
43
+ declare function isPriceRejection(error: unknown): error is X402PriceRejectedError;
27
44
 
45
+ type GateAdapter = Pick<HTTPAdapter, "getHeader" | "getPath" | "getUrl" | "getMethod" | "getBody">;
28
46
  /**
29
47
  * Response header a handler on a metered (`upto`) route sets to the amount it
30
48
  * actually charges, in whole token units like `price` ("0.75"). Read and
@@ -39,7 +57,7 @@ type SettleAmountResolution = {
39
57
  kind: "skip";
40
58
  } | {
41
59
  kind: "invalid";
42
- reason: "malformed" | "precision" | "above-cap";
60
+ reason: "malformed" | "precision" | "above-cap" | "meter";
43
61
  };
44
62
  /**
45
63
  * Turns the handler's `payment-settle-amount` header into a settle decision.
@@ -50,7 +68,23 @@ type SettleAmountResolution = {
50
68
  * silently served for free.
51
69
  */
52
70
  declare function resolveSettleAmount(raw: string | undefined, maxAmount: bigint, decimals: number): SettleAmountResolution;
53
- declare function createPaymentGate(config: PaymentMiddlewareConfig): (adapter: Pick<HTTPAdapter, "getHeader" | "getPath" | "getUrl" | "getMethod">) => Promise<PaymentGateResult>;
71
+ /** What the middleware can read from the handler's response. */
72
+ interface ChargeResponse {
73
+ status: number;
74
+ header(name: string): string | undefined;
75
+ body(): Promise<unknown>;
76
+ }
77
+ /**
78
+ * The amount a metered route settles, once the handler answered with a 2xx.
79
+ * A `meter` function on the route decides first, from the response: its
80
+ * amount is bounded by the cap (an overrun is the merchant's loss, not a
81
+ * refused response), zero serves for free, and no amount hands over to the
82
+ * `payment-settle-amount` header, which keeps its rules (absent charges the
83
+ * cap, above the cap is refused). A meter that throws or returns something
84
+ * that is not an amount fails closed like an invalid header.
85
+ */
86
+ declare function resolveCharge(verified: VerifiedPayment, settleHeader: string | undefined, response: ChargeResponse): Promise<SettleAmountResolution>;
87
+ declare function createPaymentGate(config: PaymentMiddlewareConfig): (adapter: GateAdapter) => Promise<PaymentGateResult>;
54
88
  declare function createPaymentMiddleware(config: PaymentMiddlewareConfig): (adapter: HTTPAdapter) => Promise<MiddlewareResult | PassResult>;
55
89
 
56
90
  type ParallelNetwork = "ethereum" | "base" | "avalanche" | "hyperevm";
@@ -74,4 +108,4 @@ declare function validateAddress(address: string): address is Address;
74
108
  declare function toEip155Network(network: string): string;
75
109
  declare function encodeBase64(obj: unknown): string;
76
110
 
77
- export { FacilitatorClient, FacilitatorConfig, FacilitatorPaymentRequirements, FacilitatorResponse, HTTPAdapter, MiddlewareResult, PARALLEL_TOKENS, type ParallelNetwork, PassResult, PaymentGateResult, PaymentMiddlewareConfig, SETTLE_AMOUNT_HEADER, type SettleAmountResolution, SettleOptions, X402ConfigError, type X402ErrorCode, X402RuntimeError, X402_ERROR_CODES, createPaymentGate, createPaymentMiddleware, encodeBase64, getDefaultAcceptedTokens, parsePrice, resolveSettleAmount, toEip155Network, validateAddress };
111
+ export { type ChargeResponse, FacilitatorClient, FacilitatorConfig, FacilitatorPaymentRequirements, FacilitatorResponse, HTTPAdapter, MiddlewareResult, PARALLEL_TOKENS, type ParallelNetwork, PassResult, PaymentGateResult, PaymentMiddlewareConfig, SETTLE_AMOUNT_HEADER, type SettleAmountResolution, SettleOptions, VerifiedPayment, X402ConfigError, type X402ErrorCode, X402PriceRejectedError, X402RuntimeError, X402_ERROR_CODES, createPaymentGate, createPaymentMiddleware, encodeBase64, getDefaultAcceptedTokens, isPriceRejection, parsePrice, resolveCharge, resolveSettleAmount, toEip155Network, validateAddress };