@parallel-protocol/x402 0.10.0 → 0.10.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/fastify.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createPaymentGate, SETTLE_AMOUNT_HEADER, resolveCharge, invalidSettleAmountResponse, settleVerified, settlementFailureResponse, encodeBase64, matchRoute, parseJsonBody } from './chunk-VSNDEQGS.js';
2
- import './chunk-QAECRSWF.js';
1
+ import { createPaymentGate, routeTable, answerAsPreflight, dropConditionalHeaders, SETTLE_AMOUNT_HEADER, resolveCharge, invalidSettleAmountResponse, settleVerified, settlementFailureResponse, encodeBase64, findRoute, parseJsonBody } from './chunk-CNASNROQ.js';
2
+ import './chunk-MZCHBBUN.js';
3
3
  import fp from 'fastify-plugin';
4
4
 
5
5
  // src/fastify/adapter.ts
@@ -33,7 +33,37 @@ function payloadJson(contentType, payload) {
33
33
  const text = typeof payload === "string" ? payload : payload instanceof Uint8Array ? Buffer.from(payload).toString("utf8") : void 0;
34
34
  return parseJsonBody(contentType, text);
35
35
  }
36
- function refuse(reply, result) {
36
+ function keepCookiesBefore(reply, cookies) {
37
+ const raw = reply.raw;
38
+ const writeHead = raw.writeHead;
39
+ raw.writeHead = ((...args) => {
40
+ for (const arg of args)
41
+ if (typeof arg === "object" && arg !== null && !Array.isArray(arg))
42
+ delete arg["set-cookie"];
43
+ raw.removeHeader("set-cookie");
44
+ if (cookies !== void 0) raw.setHeader("set-cookie", cookies);
45
+ return writeHead.apply(raw, args);
46
+ });
47
+ }
48
+ function release(payload) {
49
+ if (payload instanceof ReadableStream) payload.cancel().catch(() => {
50
+ });
51
+ else if (typeof payload?.destroy === "function")
52
+ payload.destroy();
53
+ }
54
+ function refuse(reply, result, before, payload) {
55
+ release(payload);
56
+ if (before) {
57
+ for (const name of Object.keys(reply.getHeaders()))
58
+ reply.removeHeader(name);
59
+ for (const [name, value] of Object.entries(before.headers))
60
+ if (value !== void 0) reply.header(name, value);
61
+ for (const name of before.trailers) reply.removeTrailer(name);
62
+ for (const name of ["writeHead", "write", "end"])
63
+ Reflect.deleteProperty(reply.raw, name);
64
+ keepCookiesBefore(reply, before.headers["set-cookie"]);
65
+ }
66
+ reply.raw.statusMessage = "";
37
67
  const json = JSON.stringify(result.body);
38
68
  reply.status(result.status);
39
69
  for (const [name, value] of Object.entries(result.headers)) {
@@ -51,32 +81,48 @@ function paymentMiddleware(config) {
51
81
  const gate = createPaymentGate(config);
52
82
  const verifiedMap = /* @__PURE__ */ new WeakMap();
53
83
  const rawWriters = /* @__PURE__ */ new WeakMap();
84
+ const replyBefore = /* @__PURE__ */ new WeakMap();
85
+ const routes = routeTable(config.routes);
86
+ const needsBodyByRequest = /* @__PURE__ */ new WeakMap();
54
87
  const needsBody = (req) => {
55
- const route = matchRoute(
56
- new FastifyAdapter(req).getPath(),
57
- config.routes
58
- );
59
- return typeof route?.price === "function" || route?.meter !== void 0;
88
+ let needs = needsBodyByRequest.get(req);
89
+ if (needs === void 0) {
90
+ const { route } = findRoute(new FastifyAdapter(req).getPath(), routes);
91
+ needs = typeof route?.price === "function" || route?.meter !== void 0;
92
+ needsBodyByRequest.set(req, needs);
93
+ }
94
+ return needs;
60
95
  };
61
96
  fastify.addHook("onRequest", async (req, reply) => {
62
97
  if (needsBody(req)) return;
63
- await runGate(req, reply);
98
+ return runGate(req, reply);
64
99
  });
65
100
  fastify.addHook("preHandler", async (req, reply) => {
66
101
  if (!needsBody(req)) return;
67
- await runGate(req, reply);
102
+ return runGate(req, reply);
68
103
  });
69
104
  async function runGate(req, reply) {
70
105
  const gateResult = await gate(new FastifyAdapter(req));
71
- if (gateResult.type === "pass") return;
106
+ if (gateResult.type === "pass") {
107
+ if (gateResult.preflight) answerAsPreflight(reply.raw);
108
+ return;
109
+ }
72
110
  if (gateResult.type === "error") {
73
111
  for (const [key, value] of Object.entries(gateResult.result.headers)) {
74
112
  reply.header(key, value);
75
113
  }
76
- reply.status(gateResult.result.status).send(gateResult.result.body);
77
- return;
114
+ return reply.status(gateResult.result.status).send(gateResult.result.body);
78
115
  }
79
116
  verifiedMap.set(req, gateResult);
117
+ dropConditionalHeaders(req.headers);
118
+ const trailers = /* @__PURE__ */ new Set();
119
+ const headers = structuredClone(reply.getHeaders());
120
+ replyBefore.set(req, { headers, trailers });
121
+ const trailer = reply.trailer.bind(reply);
122
+ reply.trailer = ((name, fn) => {
123
+ trailers.add(name.toLowerCase());
124
+ return trailer(name, fn);
125
+ });
80
126
  const raw = reply.raw;
81
127
  rawWriters.set(req, {
82
128
  write: raw.write,
@@ -119,14 +165,24 @@ function paymentMiddleware(config) {
119
165
  body: async () => payloadJson(headerValue(reply.getHeader("content-type")), payload)
120
166
  });
121
167
  if (resolved.kind === "invalid") {
122
- return refuse(reply, invalidSettleAmountResponse(resolved.reason));
168
+ return refuse(
169
+ reply,
170
+ invalidSettleAmountResponse(resolved.reason),
171
+ replyBefore.get(req),
172
+ payload
173
+ );
123
174
  }
124
175
  if (resolved.kind === "skip") return payload;
125
176
  settleOptions = { amount: resolved.amount };
126
177
  }
127
178
  const outcome = await settleVerified(verified, settleOptions);
128
179
  if (!outcome.settled)
129
- return refuse(reply, settlementFailureResponse(outcome));
180
+ return refuse(
181
+ reply,
182
+ settlementFailureResponse(outcome),
183
+ replyBefore.get(req),
184
+ payload
185
+ );
130
186
  reply.header("payment-response", encodeBase64(outcome.confirmation));
131
187
  reply.header("access-control-expose-headers", "payment-response");
132
188
  return payload;
package/dist/hono.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkMYCUOJ3H_cjs = require('./chunk-MYCUOJ3H.cjs');
4
- require('./chunk-KZZY5LS6.cjs');
3
+ var chunkAYMDIX7A_cjs = require('./chunk-AYMDIX7A.cjs');
4
+ require('./chunk-4CEMNRCZ.cjs');
5
5
 
6
6
  // src/hono/adapter.ts
7
7
  var TIMEOUT_BODY = JSON.stringify({
@@ -30,7 +30,7 @@ var HonoAdapter = class {
30
30
  // Hono caches the parsed body on the request, so the handler can still read
31
31
  // it after the price function did.
32
32
  async getBody() {
33
- if (!chunkMYCUOJ3H_cjs.isJsonContentType(this.c.req.header("content-type"))) return void 0;
33
+ if (!chunkAYMDIX7A_cjs.isJsonContentType(this.c.req.header("content-type"))) return void 0;
34
34
  try {
35
35
  return await this.c.req.json();
36
36
  } catch {
@@ -38,8 +38,15 @@ var HonoAdapter = class {
38
38
  }
39
39
  }
40
40
  async runHandler(options) {
41
- const outcome = await chunkMYCUOJ3H_cjs.raceTimeout(this.next(), options?.timeoutMs);
42
- if (outcome === chunkMYCUOJ3H_cjs.HANDLER_TIMEOUT) {
41
+ const headersBefore = new Headers(this.c.res.headers);
42
+ const replace = (body, status) => {
43
+ const headers = new Headers(headersBefore);
44
+ if (body !== null) headers.set("Content-Type", "application/json");
45
+ this.c.res = void 0;
46
+ this.c.res = new Response(body, { status, headers });
47
+ };
48
+ const outcome = await chunkAYMDIX7A_cjs.raceTimeout(this.next(), options?.timeoutMs);
49
+ if (outcome === chunkAYMDIX7A_cjs.HANDLER_TIMEOUT) {
43
50
  return {
44
51
  status: 504,
45
52
  setResponseHeader: () => {
@@ -47,12 +54,7 @@ var HonoAdapter = class {
47
54
  getResponseHeader: () => void 0,
48
55
  removeResponseHeader: () => {
49
56
  },
50
- sendResponse: () => {
51
- this.c.res = new Response(TIMEOUT_BODY, {
52
- status: 504,
53
- headers: { "Content-Type": "application/json" }
54
- });
55
- },
57
+ sendResponse: () => replace(TIMEOUT_BODY, 504),
56
58
  discardResponse: () => {
57
59
  }
58
60
  };
@@ -64,7 +66,7 @@ var HonoAdapter = class {
64
66
  getResponseHeader: (name) => res.headers.get(name) ?? void 0,
65
67
  removeResponseHeader: (name) => res.headers.delete(name),
66
68
  // Read from a clone: the response body streams once, to the client.
67
- getResponseBody: () => chunkMYCUOJ3H_cjs.jsonFromResponse(res),
69
+ getResponseBody: () => chunkAYMDIX7A_cjs.jsonFromResponse(res),
68
70
  sendResponse: () => {
69
71
  },
70
72
  // The response is replaced: release its body (and whatever upstream
@@ -72,6 +74,7 @@ var HonoAdapter = class {
72
74
  discardResponse: () => {
73
75
  res.body?.cancel().catch(() => {
74
76
  });
77
+ replace(null);
75
78
  }
76
79
  };
77
80
  }
@@ -79,15 +82,20 @@ var HonoAdapter = class {
79
82
 
80
83
  // src/hono/index.ts
81
84
  function paymentMiddleware(config) {
82
- const middleware = chunkMYCUOJ3H_cjs.createPaymentMiddleware(config);
85
+ const middleware = chunkAYMDIX7A_cjs.createPaymentMiddleware(config);
83
86
  return async (c, next) => {
84
87
  const result = await middleware(new HonoAdapter(c, next));
85
88
  if ("pass" in result) {
86
89
  if (result.handlerRan) return;
87
90
  await next();
91
+ if (result.preflight) {
92
+ const init = chunkAYMDIX7A_cjs.preflightInit(c.res);
93
+ c.res = void 0;
94
+ c.res = new Response(null, init);
95
+ }
88
96
  return;
89
97
  }
90
- return new Response(JSON.stringify(result.body), {
98
+ c.res = new Response(JSON.stringify(result.body), {
91
99
  status: result.status,
92
100
  headers: { ...result.headers, "Content-Type": "application/json" }
93
101
  });
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-BWL9h1eY.cjs';
2
+ import { H as HTTPAdapter, R as RunHandlerOptions, a as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-CRBMTQaq.cjs';
3
3
  import 'viem';
4
4
 
5
5
  declare class HonoAdapter implements HTTPAdapter {
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-BWL9h1eY.js';
2
+ import { H as HTTPAdapter, R as RunHandlerOptions, a as RunHandlerResult, P as PaymentMiddlewareConfig } from './types-CRBMTQaq.js';
3
3
  import 'viem';
4
4
 
5
5
  declare class HonoAdapter implements HTTPAdapter {
package/dist/hono.js CHANGED
@@ -1,5 +1,5 @@
1
- import { isJsonContentType, raceTimeout, HANDLER_TIMEOUT, jsonFromResponse, createPaymentMiddleware } from './chunk-VSNDEQGS.js';
2
- import './chunk-QAECRSWF.js';
1
+ import { isJsonContentType, raceTimeout, HANDLER_TIMEOUT, jsonFromResponse, createPaymentMiddleware, preflightInit } from './chunk-CNASNROQ.js';
2
+ import './chunk-MZCHBBUN.js';
3
3
 
4
4
  // src/hono/adapter.ts
5
5
  var TIMEOUT_BODY = JSON.stringify({
@@ -36,6 +36,13 @@ var HonoAdapter = class {
36
36
  }
37
37
  }
38
38
  async runHandler(options) {
39
+ const headersBefore = new Headers(this.c.res.headers);
40
+ const replace = (body, status) => {
41
+ const headers = new Headers(headersBefore);
42
+ if (body !== null) headers.set("Content-Type", "application/json");
43
+ this.c.res = void 0;
44
+ this.c.res = new Response(body, { status, headers });
45
+ };
39
46
  const outcome = await raceTimeout(this.next(), options?.timeoutMs);
40
47
  if (outcome === HANDLER_TIMEOUT) {
41
48
  return {
@@ -45,12 +52,7 @@ var HonoAdapter = class {
45
52
  getResponseHeader: () => void 0,
46
53
  removeResponseHeader: () => {
47
54
  },
48
- sendResponse: () => {
49
- this.c.res = new Response(TIMEOUT_BODY, {
50
- status: 504,
51
- headers: { "Content-Type": "application/json" }
52
- });
53
- },
55
+ sendResponse: () => replace(TIMEOUT_BODY, 504),
54
56
  discardResponse: () => {
55
57
  }
56
58
  };
@@ -70,6 +72,7 @@ var HonoAdapter = class {
70
72
  discardResponse: () => {
71
73
  res.body?.cancel().catch(() => {
72
74
  });
75
+ replace(null);
73
76
  }
74
77
  };
75
78
  }
@@ -83,9 +86,14 @@ function paymentMiddleware(config) {
83
86
  if ("pass" in result) {
84
87
  if (result.handlerRan) return;
85
88
  await next();
89
+ if (result.preflight) {
90
+ const init = preflightInit(c.res);
91
+ c.res = void 0;
92
+ c.res = new Response(null, init);
93
+ }
86
94
  return;
87
95
  }
88
- return new Response(JSON.stringify(result.body), {
96
+ c.res = new Response(JSON.stringify(result.body), {
89
97
  status: result.status,
90
98
  headers: { ...result.headers, "Content-Type": "application/json" }
91
99
  });
package/dist/index.cjs CHANGED
@@ -1,83 +1,87 @@
1
1
  'use strict';
2
2
 
3
- var chunkMYCUOJ3H_cjs = require('./chunk-MYCUOJ3H.cjs');
4
- var chunkKZZY5LS6_cjs = require('./chunk-KZZY5LS6.cjs');
3
+ var chunkAYMDIX7A_cjs = require('./chunk-AYMDIX7A.cjs');
4
+ var chunk4CEMNRCZ_cjs = require('./chunk-4CEMNRCZ.cjs');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "FacilitatorClient", {
9
9
  enumerable: true,
10
- get: function () { return chunkMYCUOJ3H_cjs.FacilitatorClient; }
10
+ get: function () { return chunkAYMDIX7A_cjs.FacilitatorClient; }
11
11
  });
12
12
  Object.defineProperty(exports, "PARALLEL_TOKENS", {
13
13
  enumerable: true,
14
- get: function () { return chunkMYCUOJ3H_cjs.PARALLEL_TOKENS; }
14
+ get: function () { return chunkAYMDIX7A_cjs.PARALLEL_TOKENS; }
15
15
  });
16
16
  Object.defineProperty(exports, "SETTLE_AMOUNT_HEADER", {
17
17
  enumerable: true,
18
- get: function () { return chunkMYCUOJ3H_cjs.SETTLE_AMOUNT_HEADER; }
18
+ get: function () { return chunkAYMDIX7A_cjs.SETTLE_AMOUNT_HEADER; }
19
19
  });
20
20
  Object.defineProperty(exports, "createPaymentGate", {
21
21
  enumerable: true,
22
- get: function () { return chunkMYCUOJ3H_cjs.createPaymentGate; }
22
+ get: function () { return chunkAYMDIX7A_cjs.createPaymentGate; }
23
23
  });
24
24
  Object.defineProperty(exports, "createPaymentMiddleware", {
25
25
  enumerable: true,
26
- get: function () { return chunkMYCUOJ3H_cjs.createPaymentMiddleware; }
26
+ get: function () { return chunkAYMDIX7A_cjs.createPaymentMiddleware; }
27
27
  });
28
28
  Object.defineProperty(exports, "encodeBase64", {
29
29
  enumerable: true,
30
- get: function () { return chunkMYCUOJ3H_cjs.encodeBase64; }
30
+ get: function () { return chunkAYMDIX7A_cjs.encodeBase64; }
31
31
  });
32
32
  Object.defineProperty(exports, "getDefaultAcceptedTokens", {
33
33
  enumerable: true,
34
- get: function () { return chunkMYCUOJ3H_cjs.getDefaultAcceptedTokens; }
34
+ get: function () { return chunkAYMDIX7A_cjs.getDefaultAcceptedTokens; }
35
+ });
36
+ Object.defineProperty(exports, "normalizeRoutePath", {
37
+ enumerable: true,
38
+ get: function () { return chunkAYMDIX7A_cjs.normalizeRoutePath; }
35
39
  });
36
40
  Object.defineProperty(exports, "parsePrice", {
37
41
  enumerable: true,
38
- get: function () { return chunkMYCUOJ3H_cjs.parsePrice; }
42
+ get: function () { return chunkAYMDIX7A_cjs.parsePrice; }
39
43
  });
40
44
  Object.defineProperty(exports, "resolveCharge", {
41
45
  enumerable: true,
42
- get: function () { return chunkMYCUOJ3H_cjs.resolveCharge; }
46
+ get: function () { return chunkAYMDIX7A_cjs.resolveCharge; }
43
47
  });
44
48
  Object.defineProperty(exports, "resolveSettleAmount", {
45
49
  enumerable: true,
46
- get: function () { return chunkMYCUOJ3H_cjs.resolveSettleAmount; }
50
+ get: function () { return chunkAYMDIX7A_cjs.resolveSettleAmount; }
47
51
  });
48
52
  Object.defineProperty(exports, "settleVerified", {
49
53
  enumerable: true,
50
- get: function () { return chunkMYCUOJ3H_cjs.settleVerified; }
54
+ get: function () { return chunkAYMDIX7A_cjs.settleVerified; }
51
55
  });
52
56
  Object.defineProperty(exports, "settlementFailureResponse", {
53
57
  enumerable: true,
54
- get: function () { return chunkMYCUOJ3H_cjs.settlementFailureResponse; }
58
+ get: function () { return chunkAYMDIX7A_cjs.settlementFailureResponse; }
55
59
  });
56
60
  Object.defineProperty(exports, "toEip155Network", {
57
61
  enumerable: true,
58
- get: function () { return chunkMYCUOJ3H_cjs.toEip155Network; }
62
+ get: function () { return chunkAYMDIX7A_cjs.toEip155Network; }
59
63
  });
60
64
  Object.defineProperty(exports, "validateAddress", {
61
65
  enumerable: true,
62
- get: function () { return chunkMYCUOJ3H_cjs.validateAddress; }
66
+ get: function () { return chunkAYMDIX7A_cjs.validateAddress; }
63
67
  });
64
68
  Object.defineProperty(exports, "X402ConfigError", {
65
69
  enumerable: true,
66
- get: function () { return chunkKZZY5LS6_cjs.X402ConfigError; }
70
+ get: function () { return chunk4CEMNRCZ_cjs.X402ConfigError; }
67
71
  });
68
72
  Object.defineProperty(exports, "X402PriceRejectedError", {
69
73
  enumerable: true,
70
- get: function () { return chunkKZZY5LS6_cjs.X402PriceRejectedError; }
74
+ get: function () { return chunk4CEMNRCZ_cjs.X402PriceRejectedError; }
71
75
  });
72
76
  Object.defineProperty(exports, "X402RuntimeError", {
73
77
  enumerable: true,
74
- get: function () { return chunkKZZY5LS6_cjs.X402RuntimeError; }
78
+ get: function () { return chunk4CEMNRCZ_cjs.X402RuntimeError; }
75
79
  });
76
80
  Object.defineProperty(exports, "X402_ERROR_CODES", {
77
81
  enumerable: true,
78
- get: function () { return chunkKZZY5LS6_cjs.X402_ERROR_CODES; }
82
+ get: function () { return chunk4CEMNRCZ_cjs.X402_ERROR_CODES; }
79
83
  });
80
84
  Object.defineProperty(exports, "isPriceRejection", {
81
85
  enumerable: true,
82
- get: function () { return chunkKZZY5LS6_cjs.isPriceRejection; }
86
+ get: function () { return chunk4CEMNRCZ_cjs.isPriceRejection; }
83
87
  });
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FacilitatorConfig, b as FacilitatorPaymentRequirements, V as VerifyResult, c as FacilitatorSettleOptions, d as FacilitatorResponse, e as PaymentConfirmation, P as PaymentMiddlewareConfig, H as HTTPAdapter, f as PaymentGateResult, M as MiddlewareResult, g as PassResult, h as VerifiedPayment, S as SettleOptions } from './types-BWL9h1eY.cjs';
2
- export { i as MeterContext, j as MeterFunction, k as PaymentFailure, l as PaymentRequired, m as PaymentRequirements, n as PriceContext, o as PriceFunction, p as ResourceInfo, q as RouteConfig, R as RunHandlerOptions, a as RunHandlerResult } from './types-BWL9h1eY.cjs';
1
+ import { F as FacilitatorConfig, b as FacilitatorPaymentRequirements, V as VerifyResult, c as FacilitatorSettleOptions, d as FacilitatorResponse, e as PaymentConfirmation, P as PaymentMiddlewareConfig, H as HTTPAdapter, f as PaymentGateResult, M as MiddlewareResult, g as PassResult, h as VerifiedPayment, S as SettleOptions } from './types-CRBMTQaq.cjs';
2
+ export { i as MeterContext, j as MeterFunction, k as PaymentFailure, l as PaymentRequired, m as PaymentRequirements, n as PriceContext, o as PriceFunction, p as ResourceInfo, q as RouteConfig, R as RunHandlerOptions, a as RunHandlerResult } from './types-CRBMTQaq.cjs';
3
3
  import { Address } from 'viem';
4
4
 
5
5
  declare class FacilitatorClient {
@@ -20,6 +20,8 @@ declare const X402_ERROR_CODES: {
20
20
  readonly FACILITATOR_UNAVAILABLE: "FACILITATOR_UNAVAILABLE";
21
21
  readonly FACILITATOR_INVALID_RESPONSE: "FACILITATOR_INVALID_RESPONSE";
22
22
  readonly INVALID_PAYMENT: "INVALID_PAYMENT";
23
+ /** A path with `.` or `..` segments on a paid route, refused with 400. */
24
+ readonly INVALID_PATH: "INVALID_PATH";
23
25
  /** The facilitator did not answer a settle within the client's timeout: the payment may still settle. */
24
26
  readonly SETTLE_TIMEOUT: "SETTLE_TIMEOUT";
25
27
  };
@@ -59,7 +61,7 @@ declare class X402PriceRejectedError extends Error {
59
61
  */
60
62
  declare function isPriceRejection(error: unknown): error is X402PriceRejectedError;
61
63
 
62
- type GateAdapter = Pick<HTTPAdapter, "getHeader" | "getPath" | "getUrl" | "getMethod" | "getBody">;
64
+ type GateAdapter = Pick<HTTPAdapter, "getHeader" | "getPath" | "getFullPath" | "getUrl" | "getMethod" | "getBody">;
63
65
  /**
64
66
  * Response header a handler on a metered (`upto`) route sets to the amount it
65
67
  * actually charges, in whole token units like `price` ("0.75"). Read and
@@ -156,5 +158,7 @@ declare function parsePrice(price: string | bigint, decimals?: number): bigint;
156
158
  declare function validateAddress(address: string): address is Address;
157
159
  declare function toEip155Network(network: string): string;
158
160
  declare function encodeBase64(obj: unknown): string;
161
+ /** The form routes are matched on: `"/API//Data/"` becomes `"/api/data"`. */
162
+ declare function normalizeRoutePath(path: string): string;
159
163
 
160
- export { type ChargeResponse, FacilitatorClient, FacilitatorConfig, FacilitatorPaymentRequirements, FacilitatorResponse, FacilitatorSettleOptions, HTTPAdapter, MiddlewareResult, PARALLEL_TOKENS, type ParallelNetwork, PassResult, PaymentConfirmation, PaymentGateResult, PaymentMiddlewareConfig, SETTLE_AMOUNT_HEADER, type SettleAmountResolution, SettleOptions, type SettlementFailure, type SettlementFailureBody, type SettlementOutcome, VerifiedPayment, VerifyResult, X402ConfigError, type X402ErrorCode, X402PriceRejectedError, X402RuntimeError, X402_ERROR_CODES, createPaymentGate, createPaymentMiddleware, encodeBase64, getDefaultAcceptedTokens, isPriceRejection, parsePrice, resolveCharge, resolveSettleAmount, settleVerified, settlementFailureResponse, toEip155Network, validateAddress };
164
+ export { type ChargeResponse, FacilitatorClient, FacilitatorConfig, FacilitatorPaymentRequirements, FacilitatorResponse, FacilitatorSettleOptions, HTTPAdapter, MiddlewareResult, PARALLEL_TOKENS, type ParallelNetwork, PassResult, PaymentConfirmation, PaymentGateResult, PaymentMiddlewareConfig, SETTLE_AMOUNT_HEADER, type SettleAmountResolution, SettleOptions, type SettlementFailure, type SettlementFailureBody, type SettlementOutcome, VerifiedPayment, VerifyResult, X402ConfigError, type X402ErrorCode, X402PriceRejectedError, X402RuntimeError, X402_ERROR_CODES, createPaymentGate, createPaymentMiddleware, encodeBase64, getDefaultAcceptedTokens, isPriceRejection, normalizeRoutePath, parsePrice, resolveCharge, resolveSettleAmount, settleVerified, settlementFailureResponse, toEip155Network, validateAddress };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FacilitatorConfig, b as FacilitatorPaymentRequirements, V as VerifyResult, c as FacilitatorSettleOptions, d as FacilitatorResponse, e as PaymentConfirmation, P as PaymentMiddlewareConfig, H as HTTPAdapter, f as PaymentGateResult, M as MiddlewareResult, g as PassResult, h as VerifiedPayment, S as SettleOptions } from './types-BWL9h1eY.js';
2
- export { i as MeterContext, j as MeterFunction, k as PaymentFailure, l as PaymentRequired, m as PaymentRequirements, n as PriceContext, o as PriceFunction, p as ResourceInfo, q as RouteConfig, R as RunHandlerOptions, a as RunHandlerResult } from './types-BWL9h1eY.js';
1
+ import { F as FacilitatorConfig, b as FacilitatorPaymentRequirements, V as VerifyResult, c as FacilitatorSettleOptions, d as FacilitatorResponse, e as PaymentConfirmation, P as PaymentMiddlewareConfig, H as HTTPAdapter, f as PaymentGateResult, M as MiddlewareResult, g as PassResult, h as VerifiedPayment, S as SettleOptions } from './types-CRBMTQaq.js';
2
+ export { i as MeterContext, j as MeterFunction, k as PaymentFailure, l as PaymentRequired, m as PaymentRequirements, n as PriceContext, o as PriceFunction, p as ResourceInfo, q as RouteConfig, R as RunHandlerOptions, a as RunHandlerResult } from './types-CRBMTQaq.js';
3
3
  import { Address } from 'viem';
4
4
 
5
5
  declare class FacilitatorClient {
@@ -20,6 +20,8 @@ declare const X402_ERROR_CODES: {
20
20
  readonly FACILITATOR_UNAVAILABLE: "FACILITATOR_UNAVAILABLE";
21
21
  readonly FACILITATOR_INVALID_RESPONSE: "FACILITATOR_INVALID_RESPONSE";
22
22
  readonly INVALID_PAYMENT: "INVALID_PAYMENT";
23
+ /** A path with `.` or `..` segments on a paid route, refused with 400. */
24
+ readonly INVALID_PATH: "INVALID_PATH";
23
25
  /** The facilitator did not answer a settle within the client's timeout: the payment may still settle. */
24
26
  readonly SETTLE_TIMEOUT: "SETTLE_TIMEOUT";
25
27
  };
@@ -59,7 +61,7 @@ declare class X402PriceRejectedError extends Error {
59
61
  */
60
62
  declare function isPriceRejection(error: unknown): error is X402PriceRejectedError;
61
63
 
62
- type GateAdapter = Pick<HTTPAdapter, "getHeader" | "getPath" | "getUrl" | "getMethod" | "getBody">;
64
+ type GateAdapter = Pick<HTTPAdapter, "getHeader" | "getPath" | "getFullPath" | "getUrl" | "getMethod" | "getBody">;
63
65
  /**
64
66
  * Response header a handler on a metered (`upto`) route sets to the amount it
65
67
  * actually charges, in whole token units like `price` ("0.75"). Read and
@@ -156,5 +158,7 @@ declare function parsePrice(price: string | bigint, decimals?: number): bigint;
156
158
  declare function validateAddress(address: string): address is Address;
157
159
  declare function toEip155Network(network: string): string;
158
160
  declare function encodeBase64(obj: unknown): string;
161
+ /** The form routes are matched on: `"/API//Data/"` becomes `"/api/data"`. */
162
+ declare function normalizeRoutePath(path: string): string;
159
163
 
160
- export { type ChargeResponse, FacilitatorClient, FacilitatorConfig, FacilitatorPaymentRequirements, FacilitatorResponse, FacilitatorSettleOptions, HTTPAdapter, MiddlewareResult, PARALLEL_TOKENS, type ParallelNetwork, PassResult, PaymentConfirmation, PaymentGateResult, PaymentMiddlewareConfig, SETTLE_AMOUNT_HEADER, type SettleAmountResolution, SettleOptions, type SettlementFailure, type SettlementFailureBody, type SettlementOutcome, VerifiedPayment, VerifyResult, X402ConfigError, type X402ErrorCode, X402PriceRejectedError, X402RuntimeError, X402_ERROR_CODES, createPaymentGate, createPaymentMiddleware, encodeBase64, getDefaultAcceptedTokens, isPriceRejection, parsePrice, resolveCharge, resolveSettleAmount, settleVerified, settlementFailureResponse, toEip155Network, validateAddress };
164
+ export { type ChargeResponse, FacilitatorClient, FacilitatorConfig, FacilitatorPaymentRequirements, FacilitatorResponse, FacilitatorSettleOptions, HTTPAdapter, MiddlewareResult, PARALLEL_TOKENS, type ParallelNetwork, PassResult, PaymentConfirmation, PaymentGateResult, PaymentMiddlewareConfig, SETTLE_AMOUNT_HEADER, type SettleAmountResolution, SettleOptions, type SettlementFailure, type SettlementFailureBody, type SettlementOutcome, VerifiedPayment, VerifyResult, X402ConfigError, type X402ErrorCode, X402PriceRejectedError, X402RuntimeError, X402_ERROR_CODES, createPaymentGate, createPaymentMiddleware, encodeBase64, getDefaultAcceptedTokens, isPriceRejection, normalizeRoutePath, parsePrice, resolveCharge, resolveSettleAmount, settleVerified, settlementFailureResponse, toEip155Network, validateAddress };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { FacilitatorClient, PARALLEL_TOKENS, SETTLE_AMOUNT_HEADER, createPaymentGate, createPaymentMiddleware, encodeBase64, getDefaultAcceptedTokens, parsePrice, resolveCharge, resolveSettleAmount, settleVerified, settlementFailureResponse, toEip155Network, validateAddress } from './chunk-VSNDEQGS.js';
2
- export { X402ConfigError, X402PriceRejectedError, X402RuntimeError, X402_ERROR_CODES, isPriceRejection } from './chunk-QAECRSWF.js';
1
+ export { FacilitatorClient, PARALLEL_TOKENS, SETTLE_AMOUNT_HEADER, createPaymentGate, createPaymentMiddleware, encodeBase64, getDefaultAcceptedTokens, normalizeRoutePath, parsePrice, resolveCharge, resolveSettleAmount, settleVerified, settlementFailureResponse, toEip155Network, validateAddress } from './chunk-CNASNROQ.js';
2
+ export { X402ConfigError, X402PriceRejectedError, X402RuntimeError, X402_ERROR_CODES, isPriceRejection } from './chunk-MZCHBBUN.js';
package/dist/llm.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkKZZY5LS6_cjs = require('./chunk-KZZY5LS6.cjs');
3
+ var chunk4CEMNRCZ_cjs = require('./chunk-4CEMNRCZ.cjs');
4
4
 
5
5
  // src/llm/estimate.ts
6
6
  function estimateTokens(text) {
@@ -71,7 +71,7 @@ function lookup(table, model) {
71
71
  function staticPrices(table) {
72
72
  for (const [model, price] of Object.entries(table)) {
73
73
  if (!isValidPrice(price)) {
74
- throw new chunkKZZY5LS6_cjs.X402ConfigError(
74
+ throw new chunk4CEMNRCZ_cjs.X402ConfigError(
75
75
  `Invalid price for model "${model}": expected { input, output } in USD per million tokens`
76
76
  );
77
77
  }
@@ -106,13 +106,13 @@ function shortName(value) {
106
106
  function parseDuration(value) {
107
107
  if (typeof value === "number") {
108
108
  if (!Number.isFinite(value) || value < 0) {
109
- throw new chunkKZZY5LS6_cjs.X402ConfigError(`Invalid duration: ${value}`);
109
+ throw new chunk4CEMNRCZ_cjs.X402ConfigError(`Invalid duration: ${value}`);
110
110
  }
111
111
  return value;
112
112
  }
113
113
  const match = value.trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)$/i);
114
114
  if (!match) {
115
- throw new chunkKZZY5LS6_cjs.X402ConfigError(
115
+ throw new chunk4CEMNRCZ_cjs.X402ConfigError(
116
116
  `Invalid duration "${value}": expected a number of milliseconds or a value like "24h", "30m", "10s"`
117
117
  );
118
118
  }
@@ -180,7 +180,7 @@ function openRouterPrices(options = {}) {
180
180
  if (canRetry()) await load().catch(() => {
181
181
  });
182
182
  if (!table) {
183
- throw new chunkKZZY5LS6_cjs.X402PriceRejectedError(
183
+ throw new chunk4CEMNRCZ_cjs.X402PriceRejectedError(
184
184
  "PRICING_UNAVAILABLE",
185
185
  "Model prices are not available right now, retry shortly",
186
186
  503
@@ -197,7 +197,7 @@ function openRouterPrices(options = {}) {
197
197
  });
198
198
  }
199
199
  if (inflight) {
200
- throw new chunkKZZY5LS6_cjs.X402PriceRejectedError(
200
+ throw new chunk4CEMNRCZ_cjs.X402PriceRejectedError(
201
201
  "UNKNOWN_MODEL",
202
202
  `No price known for model "${shortName(model)}" yet. The price list is being refreshed, retry in a few seconds.`
203
203
  );
@@ -210,7 +210,7 @@ function openRouterPrices(options = {}) {
210
210
  // src/llm/cap.ts
211
211
  var DEFAULT_MARGIN = 0;
212
212
  function reject(code, message) {
213
- throw new chunkKZZY5LS6_cjs.X402PriceRejectedError(code, message);
213
+ throw new chunk4CEMNRCZ_cjs.X402PriceRejectedError(code, message);
214
214
  }
215
215
  function isTextOnly(modalities) {
216
216
  return Array.isArray(modalities) && modalities.every((modality) => modality === "text");
@@ -222,17 +222,17 @@ function chatCompletionCap(options) {
222
222
  const allowed = options.models ? new Set(options.models) : void 0;
223
223
  const estimate = options.estimateInputTokens ?? estimateInputTokens;
224
224
  if (positiveInteger(maxOutputTokens) === void 0) {
225
- throw new chunkKZZY5LS6_cjs.X402ConfigError(
225
+ throw new chunk4CEMNRCZ_cjs.X402ConfigError(
226
226
  `chatCompletionCap: maxOutputTokens must be a positive integer, got ${maxOutputTokens}`
227
227
  );
228
228
  }
229
229
  if (!Number.isFinite(margin) || margin < 0) {
230
- throw new chunkKZZY5LS6_cjs.X402ConfigError(
230
+ throw new chunk4CEMNRCZ_cjs.X402ConfigError(
231
231
  `chatCompletionCap: margin must be a non-negative number, got ${margin}`
232
232
  );
233
233
  }
234
234
  if (allowed?.size === 0) {
235
- throw new chunkKZZY5LS6_cjs.X402ConfigError("chatCompletionCap: models must not be empty");
235
+ throw new chunk4CEMNRCZ_cjs.X402ConfigError("chatCompletionCap: models must not be empty");
236
236
  }
237
237
  return async (ctx) => {
238
238
  const raw = ctx.body;
@@ -295,7 +295,7 @@ function chatCompletionCap(options) {
295
295
  reject("UNKNOWN_MODEL", `No price known for model "${shortName(model)}"`);
296
296
  }
297
297
  if (!isValidPrice(price)) {
298
- throw new chunkKZZY5LS6_cjs.X402ConfigError(
298
+ throw new chunk4CEMNRCZ_cjs.X402ConfigError(
299
299
  `chatCompletionCap: the price source returned an invalid price for model "${shortName(model)}"`
300
300
  );
301
301
  }
@@ -343,13 +343,13 @@ var failure = (status, error, message) => ({
343
343
  });
344
344
  async function forwardChatCompletion(body, options) {
345
345
  if (typeof options.baseUrl !== "string" || !URL.canParse(options.baseUrl)) {
346
- throw new chunkKZZY5LS6_cjs.X402ConfigError(
346
+ throw new chunk4CEMNRCZ_cjs.X402ConfigError(
347
347
  `forwardChatCompletion: baseUrl must be a URL, got ${JSON.stringify(options.baseUrl)}`
348
348
  );
349
349
  }
350
350
  const maxOutputTokens = options.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS;
351
351
  if (positiveInteger(maxOutputTokens) === void 0) {
352
- throw new chunkKZZY5LS6_cjs.X402ConfigError(
352
+ throw new chunk4CEMNRCZ_cjs.X402ConfigError(
353
353
  `forwardChatCompletion: maxOutputTokens must be a positive integer, got ${maxOutputTokens}`
354
354
  );
355
355
  }
package/dist/llm.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { o as PriceFunction, j as MeterFunction } from './types-BWL9h1eY.cjs';
1
+ import { o as PriceFunction, j as MeterFunction } from './types-CRBMTQaq.cjs';
2
2
  import 'viem';
3
3
 
4
4
  /**
package/dist/llm.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { o as PriceFunction, j as MeterFunction } from './types-BWL9h1eY.js';
1
+ import { o as PriceFunction, j as MeterFunction } from './types-CRBMTQaq.js';
2
2
  import 'viem';
3
3
 
4
4
  /**
package/dist/llm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { X402ConfigError, X402PriceRejectedError } from './chunk-QAECRSWF.js';
1
+ import { X402ConfigError, X402PriceRejectedError } from './chunk-MZCHBBUN.js';
2
2
 
3
3
  // src/llm/estimate.ts
4
4
  function estimateTokens(text) {