@x402/express 0.0.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1 +1,281 @@
1
1
  # @x402/express
2
+
3
+ Express middleware integration for the x402 Payment Protocol. This package provides a simple middleware function for adding x402 payment requirements to your Express.js applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm install @x402/express
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import express from "express";
15
+ import { paymentMiddleware, x402ResourceServer } from "@x402/express";
16
+ import { ExactEvmScheme } from "@x402/evm/exact/server";
17
+ import { HTTPFacilitatorClient } from "@x402/core/server";
18
+
19
+ const app = express();
20
+
21
+ const facilitatorClient = new HTTPFacilitatorClient({ url: "https://facilitator.x402.org" });
22
+ const resourceServer = new x402ResourceServer(facilitatorClient)
23
+ .register("eip155:84532", new ExactEvmScheme());
24
+
25
+ // Apply the payment middleware with your configuration
26
+ app.use(
27
+ paymentMiddleware(
28
+ {
29
+ "GET /protected-route": {
30
+ accepts: {
31
+ scheme: "exact",
32
+ price: "$0.10",
33
+ network: "eip155:84532",
34
+ payTo: "0xYourAddress",
35
+ },
36
+ description: "Access to premium content",
37
+ },
38
+ },
39
+ resourceServer,
40
+ ),
41
+ );
42
+
43
+ // Implement your protected route
44
+ app.get("/protected-route", (req, res) => {
45
+ res.json({ message: "This content is behind a paywall" });
46
+ });
47
+
48
+ app.listen(3000);
49
+ ```
50
+
51
+ ## Configuration
52
+
53
+ The `paymentMiddleware` function accepts the following parameters:
54
+
55
+ ```typescript
56
+ paymentMiddleware(
57
+ routes: RoutesConfig,
58
+ server: x402ResourceServer,
59
+ paywallConfig?: PaywallConfig,
60
+ paywall?: PaywallProvider,
61
+ syncFacilitatorOnStart?: boolean
62
+ )
63
+ ```
64
+
65
+ ### Parameters
66
+
67
+ 1. **`routes`** (required): Route configurations for protected endpoints
68
+ 2. **`server`** (required): Pre-configured x402ResourceServer instance
69
+ 3. **`paywallConfig`** (optional): Configuration for the built-in paywall UI
70
+ 4. **`paywall`** (optional): Custom paywall provider
71
+ 5. **`syncFacilitatorOnStart`** (optional): Whether to sync with facilitator on startup (defaults to true)
72
+
73
+ See the sections below for detailed configuration options.
74
+
75
+ ## API Reference
76
+
77
+ ### ExpressAdapter
78
+
79
+ The `ExpressAdapter` class implements the `HTTPAdapter` interface from `@x402/core`, providing Express-specific request handling:
80
+
81
+ ```typescript
82
+ class ExpressAdapter implements HTTPAdapter {
83
+ getHeader(name: string): string | undefined;
84
+ getMethod(): string;
85
+ getPath(): string;
86
+ getUrl(): string;
87
+ getAcceptHeader(): string;
88
+ getUserAgent(): string;
89
+ }
90
+ ```
91
+
92
+ ### Middleware Function
93
+
94
+ ```typescript
95
+ function paymentMiddleware(
96
+ routes: RoutesConfig,
97
+ server: x402ResourceServer,
98
+ paywallConfig?: PaywallConfig,
99
+ paywall?: PaywallProvider,
100
+ syncFacilitatorOnStart?: boolean,
101
+ ): (req: Request, res: Response, next: NextFunction) => Promise<void>;
102
+ ```
103
+
104
+ Creates Express middleware that:
105
+
106
+ 1. Uses the provided x402ResourceServer for payment processing
107
+ 2. Checks if the incoming request matches a protected route
108
+ 3. Validates payment headers if required
109
+ 4. Returns payment instructions (402 status) if payment is missing or invalid
110
+ 5. Processes the request if payment is valid
111
+ 6. Handles settlement after successful response
112
+
113
+ ### Route Configuration
114
+
115
+ Routes are passed as the first parameter to `paymentMiddleware`:
116
+
117
+ ```typescript
118
+ const routes: RoutesConfig = {
119
+ "GET /api/protected": {
120
+ accepts: {
121
+ scheme: "exact",
122
+ price: "$0.10",
123
+ network: "eip155:84532",
124
+ payTo: "0xYourAddress",
125
+ maxTimeoutSeconds: 60,
126
+ },
127
+ description: "Premium API access",
128
+ },
129
+ };
130
+
131
+ app.use(paymentMiddleware(routes, resourceServer));
132
+ ```
133
+
134
+ ### Paywall Configuration
135
+
136
+ The middleware automatically displays a paywall UI when browsers request protected endpoints.
137
+
138
+ **Option 1: Full Paywall UI (Recommended)**
139
+
140
+ Install the optional `@x402/paywall` package for a complete wallet connection and payment UI:
141
+
142
+ ```bash
143
+ pnpm add @x402/paywall
144
+ ```
145
+
146
+ Then configure it:
147
+
148
+ ```typescript
149
+ const paywallConfig: PaywallConfig = {
150
+ appName: "Your App Name",
151
+ appLogo: "/path/to/logo.svg",
152
+ testnet: true,
153
+ };
154
+
155
+ app.use(paymentMiddleware(routes, resourceServer, paywallConfig));
156
+ ```
157
+
158
+ The paywall includes:
159
+
160
+ - EVM wallet support (MetaMask, Coinbase Wallet, etc.)
161
+ - Solana wallet support (Phantom, Solflare, etc.)
162
+ - USDC balance checking
163
+ - Chain switching
164
+ - Onramp integration for mainnet
165
+
166
+ **Option 2: Basic Paywall (No Installation)**
167
+
168
+ Without `@x402/paywall` installed, the middleware returns a basic HTML page with payment instructions. This works but doesn't include wallet connections.
169
+
170
+ **Option 3: Custom Paywall Provider**
171
+
172
+ Provide your own paywall provider:
173
+
174
+ ```typescript
175
+ app.use(paymentMiddleware(routes, resourceServer, paywallConfig, customPaywallProvider));
176
+ ```
177
+
178
+ This allows full customization of the paywall UI.
179
+
180
+ **For advanced configuration** (builder pattern, network-specific bundles, custom handlers), see the [@x402/paywall README](../paywall/README.md).
181
+
182
+ ## Advanced Usage
183
+
184
+ ### Multiple Protected Routes
185
+
186
+ ```typescript
187
+ app.use(
188
+ paymentMiddleware(
189
+ {
190
+ "GET /api/premium/*": {
191
+ accepts: {
192
+ scheme: "exact",
193
+ price: "$1.00",
194
+ network: "eip155:8453",
195
+ payTo: "0xYourAddress",
196
+ },
197
+ description: "Premium API access",
198
+ },
199
+ "GET /api/data": {
200
+ accepts: {
201
+ scheme: "exact",
202
+ price: "$0.50",
203
+ network: "eip155:84532",
204
+ payTo: "0xYourAddress",
205
+ maxTimeoutSeconds: 120,
206
+ },
207
+ description: "Data endpoint access",
208
+ },
209
+ },
210
+ resourceServer,
211
+ ),
212
+ );
213
+ ```
214
+
215
+ ### Custom Facilitator Client
216
+
217
+ If you need to use a custom facilitator server, configure it when creating the x402ResourceServer:
218
+
219
+ ```typescript
220
+ import { HTTPFacilitatorClient } from "@x402/core/server";
221
+ import { x402ResourceServer } from "@x402/express";
222
+ import { ExactEvmScheme } from "@x402/evm/exact/server";
223
+
224
+ const customFacilitator = new HTTPFacilitatorClient({
225
+ url: "https://your-facilitator.com",
226
+ createAuthHeaders: async () => ({
227
+ verify: { Authorization: "Bearer your-token" },
228
+ settle: { Authorization: "Bearer your-token" },
229
+ }),
230
+ });
231
+
232
+ const resourceServer = new x402ResourceServer(customFacilitator)
233
+ .register("eip155:84532", new ExactEvmScheme());
234
+
235
+ app.use(paymentMiddleware(routes, resourceServer, paywallConfig));
236
+ ```
237
+
238
+ ## Migration from x402-express
239
+
240
+ If you're migrating from the legacy `x402-express` package:
241
+
242
+ 1. **Update imports**: Change from `x402-express` to `@x402/express`
243
+ 2. **New API**: Create an x402ResourceServer and register payment schemes
244
+ 3. **Parameter order**: Routes first, then resource server, then optional paywall config
245
+
246
+ ### Before (x402-express):
247
+
248
+ ```typescript
249
+ import { paymentMiddleware } from "x402-express";
250
+
251
+ app.use(
252
+ paymentMiddleware(
253
+ payTo, // First param was payTo address
254
+ routes, // Second param was routes
255
+ facilitator, // Third param was facilitator config
256
+ paywall, // Fourth param was paywall config
257
+ ),
258
+ );
259
+ ```
260
+
261
+ ### After (@x402/express):
262
+
263
+ ```typescript
264
+ import { paymentMiddleware, x402ResourceServer } from "@x402/express";
265
+ import { HTTPFacilitatorClient } from "@x402/core/server";
266
+ import { ExactEvmScheme } from "@x402/evm/exact/server";
267
+
268
+ const facilitator = new HTTPFacilitatorClient({ url: facilitatorUrl });
269
+ const resourceServer = new x402ResourceServer(facilitator)
270
+ .register("eip155:84532", new ExactEvmScheme());
271
+
272
+ app.use(
273
+ paymentMiddleware(
274
+ routes, // First param is routes (payTo is part of route config)
275
+ resourceServer, // Second param is resource server (required)
276
+ paywallConfig, // Third param is paywall config (optional)
277
+ ),
278
+ );
279
+ ```
280
+
281
+ Note: The `payTo` address is now specified within each route configuration rather than as a separate parameter.
@@ -0,0 +1,145 @@
1
+ import { HTTPAdapter, RoutesConfig, x402ResourceServer, PaywallConfig, PaywallProvider, FacilitatorClient } from '@x402/core/server';
2
+ export { PaywallConfig, PaywallProvider, RouteConfigurationError, RouteValidationError, x402HTTPResourceServer, x402ResourceServer } from '@x402/core/server';
3
+ import { Network, SchemeNetworkServer } from '@x402/core/types';
4
+ export { Network, PaymentPayload, PaymentRequired, PaymentRequirements, SchemeNetworkServer } from '@x402/core/types';
5
+ import { Request, Response, NextFunction } from 'express';
6
+
7
+ /**
8
+ * Express adapter implementation
9
+ */
10
+ declare class ExpressAdapter implements HTTPAdapter {
11
+ private req;
12
+ /**
13
+ * Creates a new ExpressAdapter instance.
14
+ *
15
+ * @param req - The Express request object
16
+ */
17
+ constructor(req: Request);
18
+ /**
19
+ * Gets a header value from the request.
20
+ *
21
+ * @param name - The header name
22
+ * @returns The header value or undefined
23
+ */
24
+ getHeader(name: string): string | undefined;
25
+ /**
26
+ * Gets the HTTP method of the request.
27
+ *
28
+ * @returns The HTTP method
29
+ */
30
+ getMethod(): string;
31
+ /**
32
+ * Gets the path of the request.
33
+ *
34
+ * @returns The request path
35
+ */
36
+ getPath(): string;
37
+ /**
38
+ * Gets the full URL of the request.
39
+ *
40
+ * @returns The full request URL
41
+ */
42
+ getUrl(): string;
43
+ /**
44
+ * Gets the Accept header from the request.
45
+ *
46
+ * @returns The Accept header value or empty string
47
+ */
48
+ getAcceptHeader(): string;
49
+ /**
50
+ * Gets the User-Agent header from the request.
51
+ *
52
+ * @returns The User-Agent header value or empty string
53
+ */
54
+ getUserAgent(): string;
55
+ /**
56
+ * Gets all query parameters from the request URL.
57
+ *
58
+ * @returns Record of query parameter key-value pairs
59
+ */
60
+ getQueryParams(): Record<string, string | string[]>;
61
+ /**
62
+ * Gets a specific query parameter by name.
63
+ *
64
+ * @param name - The query parameter name
65
+ * @returns The query parameter value(s) or undefined
66
+ */
67
+ getQueryParam(name: string): string | string[] | undefined;
68
+ /**
69
+ * Gets the parsed request body.
70
+ * Requires express.json() or express.urlencoded() middleware.
71
+ *
72
+ * @returns The parsed request body
73
+ */
74
+ getBody(): unknown;
75
+ }
76
+
77
+ /**
78
+ * Configuration for registering a payment scheme with a specific network
79
+ */
80
+ interface SchemeRegistration {
81
+ /**
82
+ * The network identifier (e.g., 'eip155:84532', 'solana:mainnet')
83
+ */
84
+ network: Network;
85
+ /**
86
+ * The scheme server implementation for this network
87
+ */
88
+ server: SchemeNetworkServer;
89
+ }
90
+ /**
91
+ * Express payment middleware for x402 protocol (direct server instance).
92
+ *
93
+ * Use this when you want to pass a pre-configured x402ResourceServer instance.
94
+ * This provides more flexibility for testing, custom configuration, and reusing
95
+ * server instances across multiple middlewares.
96
+ *
97
+ * @param routes - Route configurations for protected endpoints
98
+ * @param server - Pre-configured x402ResourceServer instance
99
+ * @param paywallConfig - Optional configuration for the built-in paywall UI
100
+ * @param paywall - Optional custom paywall provider (overrides default)
101
+ * @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
102
+ * @returns Express middleware handler
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * import { paymentMiddleware } from "@x402/express";
107
+ * import { x402ResourceServer } from "@x402/core/server";
108
+ * import { registerExactEvmScheme } from "@x402/evm/exact/server";
109
+ *
110
+ * const server = new x402ResourceServer(myFacilitatorClient);
111
+ * registerExactEvmScheme(server, { signer: myServerSigner });
112
+ *
113
+ * app.use(paymentMiddleware(routes, server, paywallConfig));
114
+ * ```
115
+ */
116
+ declare function paymentMiddleware(routes: RoutesConfig, server: x402ResourceServer, paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): (req: Request, res: Response, next: NextFunction) => Promise<void>;
117
+ /**
118
+ * Express payment middleware for x402 protocol (configuration-based).
119
+ *
120
+ * Use this when you want to quickly set up middleware with simple configuration.
121
+ * This function creates and configures the x402ResourceServer internally.
122
+ *
123
+ * @param routes - Route configurations for protected endpoints
124
+ * @param facilitatorClients - Optional facilitator client(s) for payment processing
125
+ * @param schemes - Optional array of scheme registrations for server-side payment processing
126
+ * @param paywallConfig - Optional configuration for the built-in paywall UI
127
+ * @param paywall - Optional custom paywall provider (overrides default)
128
+ * @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
129
+ * @returns Express middleware handler
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * import { paymentMiddlewareFromConfig } from "@x402/express";
134
+ *
135
+ * app.use(paymentMiddlewareFromConfig(
136
+ * routes,
137
+ * myFacilitatorClient,
138
+ * [{ network: "eip155:8453", server: evmSchemeServer }],
139
+ * paywallConfig
140
+ * ));
141
+ * ```
142
+ */
143
+ declare function paymentMiddlewareFromConfig(routes: RoutesConfig, facilitatorClients?: FacilitatorClient | FacilitatorClient[], schemes?: SchemeRegistration[], paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): (req: Request, res: Response, next: NextFunction) => Promise<void>;
144
+
145
+ export { ExpressAdapter, type SchemeRegistration, paymentMiddleware, paymentMiddlewareFromConfig };
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ ExpressAdapter: () => ExpressAdapter,
34
+ RouteConfigurationError: () => import_server3.RouteConfigurationError,
35
+ paymentMiddleware: () => paymentMiddleware,
36
+ paymentMiddlewareFromConfig: () => paymentMiddlewareFromConfig,
37
+ x402HTTPResourceServer: () => import_server2.x402HTTPResourceServer,
38
+ x402ResourceServer: () => import_server2.x402ResourceServer
39
+ });
40
+ module.exports = __toCommonJS(src_exports);
41
+ var import_server = require("@x402/core/server");
42
+
43
+ // src/adapter.ts
44
+ var ExpressAdapter = class {
45
+ /**
46
+ * Creates a new ExpressAdapter instance.
47
+ *
48
+ * @param req - The Express request object
49
+ */
50
+ constructor(req) {
51
+ this.req = req;
52
+ }
53
+ /**
54
+ * Gets a header value from the request.
55
+ *
56
+ * @param name - The header name
57
+ * @returns The header value or undefined
58
+ */
59
+ getHeader(name) {
60
+ const value = this.req.header(name);
61
+ return Array.isArray(value) ? value[0] : value;
62
+ }
63
+ /**
64
+ * Gets the HTTP method of the request.
65
+ *
66
+ * @returns The HTTP method
67
+ */
68
+ getMethod() {
69
+ return this.req.method;
70
+ }
71
+ /**
72
+ * Gets the path of the request.
73
+ *
74
+ * @returns The request path
75
+ */
76
+ getPath() {
77
+ return this.req.path;
78
+ }
79
+ /**
80
+ * Gets the full URL of the request.
81
+ *
82
+ * @returns The full request URL
83
+ */
84
+ getUrl() {
85
+ return `${this.req.protocol}://${this.req.headers.host}${this.req.path}`;
86
+ }
87
+ /**
88
+ * Gets the Accept header from the request.
89
+ *
90
+ * @returns The Accept header value or empty string
91
+ */
92
+ getAcceptHeader() {
93
+ return this.req.header("Accept") || "";
94
+ }
95
+ /**
96
+ * Gets the User-Agent header from the request.
97
+ *
98
+ * @returns The User-Agent header value or empty string
99
+ */
100
+ getUserAgent() {
101
+ return this.req.header("User-Agent") || "";
102
+ }
103
+ /**
104
+ * Gets all query parameters from the request URL.
105
+ *
106
+ * @returns Record of query parameter key-value pairs
107
+ */
108
+ getQueryParams() {
109
+ return this.req.query;
110
+ }
111
+ /**
112
+ * Gets a specific query parameter by name.
113
+ *
114
+ * @param name - The query parameter name
115
+ * @returns The query parameter value(s) or undefined
116
+ */
117
+ getQueryParam(name) {
118
+ const value = this.req.query[name];
119
+ return value;
120
+ }
121
+ /**
122
+ * Gets the parsed request body.
123
+ * Requires express.json() or express.urlencoded() middleware.
124
+ *
125
+ * @returns The parsed request body
126
+ */
127
+ getBody() {
128
+ return this.req.body;
129
+ }
130
+ };
131
+
132
+ // src/index.ts
133
+ var import_server2 = require("@x402/core/server");
134
+ var import_server3 = require("@x402/core/server");
135
+ function checkIfBazaarNeeded(routes) {
136
+ if ("accepts" in routes) {
137
+ return !!(routes.extensions && "bazaar" in routes.extensions);
138
+ }
139
+ return Object.values(routes).some((routeConfig) => {
140
+ return !!(routeConfig.extensions && "bazaar" in routeConfig.extensions);
141
+ });
142
+ }
143
+ function paymentMiddleware(routes, server, paywallConfig, paywall, syncFacilitatorOnStart = true) {
144
+ const httpServer = new import_server.x402HTTPResourceServer(server, routes);
145
+ if (paywall) {
146
+ httpServer.registerPaywallProvider(paywall);
147
+ }
148
+ let initPromise = syncFacilitatorOnStart ? httpServer.initialize() : null;
149
+ let bazaarPromise = null;
150
+ if (checkIfBazaarNeeded(routes)) {
151
+ bazaarPromise = import("@x402/extensions/bazaar").then(({ bazaarResourceServerExtension }) => {
152
+ server.registerExtension(bazaarResourceServerExtension);
153
+ }).catch((err) => {
154
+ console.error("Failed to load bazaar extension:", err);
155
+ });
156
+ }
157
+ return async (req, res, next) => {
158
+ const adapter = new ExpressAdapter(req);
159
+ const context = {
160
+ adapter,
161
+ path: req.path,
162
+ method: req.method,
163
+ paymentHeader: adapter.getHeader("payment-signature") || adapter.getHeader("x-payment")
164
+ };
165
+ if (!httpServer.requiresPayment(context)) {
166
+ return next();
167
+ }
168
+ if (initPromise) {
169
+ await initPromise;
170
+ initPromise = null;
171
+ }
172
+ if (bazaarPromise) {
173
+ await bazaarPromise;
174
+ bazaarPromise = null;
175
+ }
176
+ const result = await httpServer.processHTTPRequest(context, paywallConfig);
177
+ switch (result.type) {
178
+ case "no-payment-required":
179
+ return next();
180
+ case "payment-error":
181
+ const { response } = result;
182
+ res.status(response.status);
183
+ Object.entries(response.headers).forEach(([key, value]) => {
184
+ res.setHeader(key, value);
185
+ });
186
+ if (response.isHtml) {
187
+ res.send(response.body);
188
+ } else {
189
+ res.json(response.body || {});
190
+ }
191
+ return;
192
+ case "payment-verified":
193
+ const { paymentPayload, paymentRequirements } = result;
194
+ const originalWriteHead = res.writeHead.bind(res);
195
+ const originalWrite = res.write.bind(res);
196
+ const originalEnd = res.end.bind(res);
197
+ const originalFlushHeaders = res.flushHeaders.bind(res);
198
+ let bufferedCalls = [];
199
+ let settled = false;
200
+ let endCalled;
201
+ const endPromise = new Promise((resolve) => {
202
+ endCalled = resolve;
203
+ });
204
+ res.writeHead = function(...args) {
205
+ if (!settled) {
206
+ bufferedCalls.push(["writeHead", args]);
207
+ return res;
208
+ }
209
+ return originalWriteHead(...args);
210
+ };
211
+ res.write = function(...args) {
212
+ if (!settled) {
213
+ bufferedCalls.push(["write", args]);
214
+ return true;
215
+ }
216
+ return originalWrite(...args);
217
+ };
218
+ res.end = function(...args) {
219
+ if (!settled) {
220
+ bufferedCalls.push(["end", args]);
221
+ endCalled();
222
+ return res;
223
+ }
224
+ return originalEnd(...args);
225
+ };
226
+ res.flushHeaders = function() {
227
+ if (!settled) {
228
+ bufferedCalls.push(["flushHeaders", []]);
229
+ return;
230
+ }
231
+ return originalFlushHeaders();
232
+ };
233
+ next();
234
+ await endPromise;
235
+ if (res.statusCode >= 400) {
236
+ settled = true;
237
+ res.writeHead = originalWriteHead;
238
+ res.write = originalWrite;
239
+ res.end = originalEnd;
240
+ res.flushHeaders = originalFlushHeaders;
241
+ for (const [method, args] of bufferedCalls) {
242
+ if (method === "writeHead")
243
+ originalWriteHead(...args);
244
+ else if (method === "write")
245
+ originalWrite(...args);
246
+ else if (method === "end") originalEnd(...args);
247
+ else if (method === "flushHeaders") originalFlushHeaders();
248
+ }
249
+ bufferedCalls = [];
250
+ return;
251
+ }
252
+ try {
253
+ const settleResult = await httpServer.processSettlement(
254
+ paymentPayload,
255
+ paymentRequirements
256
+ );
257
+ if (!settleResult.success) {
258
+ bufferedCalls = [];
259
+ res.status(402).json({
260
+ error: "Settlement failed",
261
+ details: settleResult.errorReason
262
+ });
263
+ return;
264
+ }
265
+ Object.entries(settleResult.headers).forEach(([key, value]) => {
266
+ res.setHeader(key, value);
267
+ });
268
+ } catch (error) {
269
+ console.error(error);
270
+ bufferedCalls = [];
271
+ res.status(402).json({
272
+ error: "Settlement failed",
273
+ details: error instanceof Error ? error.message : "Unknown error"
274
+ });
275
+ return;
276
+ } finally {
277
+ settled = true;
278
+ res.writeHead = originalWriteHead;
279
+ res.write = originalWrite;
280
+ res.end = originalEnd;
281
+ res.flushHeaders = originalFlushHeaders;
282
+ for (const [method, args] of bufferedCalls) {
283
+ if (method === "writeHead")
284
+ originalWriteHead(...args);
285
+ else if (method === "write")
286
+ originalWrite(...args);
287
+ else if (method === "end") originalEnd(...args);
288
+ else if (method === "flushHeaders") originalFlushHeaders();
289
+ }
290
+ bufferedCalls = [];
291
+ }
292
+ return;
293
+ }
294
+ };
295
+ }
296
+ function paymentMiddlewareFromConfig(routes, facilitatorClients, schemes, paywallConfig, paywall, syncFacilitatorOnStart = true) {
297
+ const ResourceServer = new import_server.x402ResourceServer(facilitatorClients);
298
+ if (schemes) {
299
+ schemes.forEach(({ network, server: schemeServer }) => {
300
+ ResourceServer.register(network, schemeServer);
301
+ });
302
+ }
303
+ return paymentMiddleware(routes, ResourceServer, paywallConfig, paywall, syncFacilitatorOnStart);
304
+ }
305
+ // Annotate the CommonJS export names for ESM import in node:
306
+ 0 && (module.exports = {
307
+ ExpressAdapter,
308
+ RouteConfigurationError,
309
+ paymentMiddleware,
310
+ paymentMiddlewareFromConfig,
311
+ x402HTTPResourceServer,
312
+ x402ResourceServer
313
+ });
314
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/index.ts","../../src/adapter.ts"],"sourcesContent":["import {\n HTTPRequestContext,\n PaywallConfig,\n PaywallProvider,\n x402HTTPResourceServer,\n x402ResourceServer,\n RoutesConfig,\n FacilitatorClient,\n} from \"@x402/core/server\";\nimport { SchemeNetworkServer, Network } from \"@x402/core/types\";\nimport { NextFunction, Request, Response } from \"express\";\nimport { ExpressAdapter } from \"./adapter\";\n\n/**\n * Check if any routes in the configuration declare bazaar extensions\n *\n * @param routes - Route configuration\n * @returns True if any route has extensions.bazaar defined\n */\nfunction checkIfBazaarNeeded(routes: RoutesConfig): boolean {\n // Handle single route config\n if (\"accepts\" in routes) {\n return !!(routes.extensions && \"bazaar\" in routes.extensions);\n }\n\n // Handle multiple routes\n return Object.values(routes).some(routeConfig => {\n return !!(routeConfig.extensions && \"bazaar\" in routeConfig.extensions);\n });\n}\n\n/**\n * Configuration for registering a payment scheme with a specific network\n */\nexport interface SchemeRegistration {\n /**\n * The network identifier (e.g., 'eip155:84532', 'solana:mainnet')\n */\n network: Network;\n\n /**\n * The scheme server implementation for this network\n */\n server: SchemeNetworkServer;\n}\n\n/**\n * Express payment middleware for x402 protocol (direct server instance).\n *\n * Use this when you want to pass a pre-configured x402ResourceServer instance.\n * This provides more flexibility for testing, custom configuration, and reusing\n * server instances across multiple middlewares.\n *\n * @param routes - Route configurations for protected endpoints\n * @param server - Pre-configured x402ResourceServer instance\n * @param paywallConfig - Optional configuration for the built-in paywall UI\n * @param paywall - Optional custom paywall provider (overrides default)\n * @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)\n * @returns Express middleware handler\n *\n * @example\n * ```typescript\n * import { paymentMiddleware } from \"@x402/express\";\n * import { x402ResourceServer } from \"@x402/core/server\";\n * import { registerExactEvmScheme } from \"@x402/evm/exact/server\";\n *\n * const server = new x402ResourceServer(myFacilitatorClient);\n * registerExactEvmScheme(server, { signer: myServerSigner });\n *\n * app.use(paymentMiddleware(routes, server, paywallConfig));\n * ```\n */\nexport function paymentMiddleware(\n routes: RoutesConfig,\n server: x402ResourceServer,\n paywallConfig?: PaywallConfig,\n paywall?: PaywallProvider,\n syncFacilitatorOnStart: boolean = true,\n) {\n // Create the x402 HTTP server instance with the resource server\n const httpServer = new x402HTTPResourceServer(server, routes);\n\n // Register custom paywall provider if provided\n if (paywall) {\n httpServer.registerPaywallProvider(paywall);\n }\n\n // Store initialization promise (not the result)\n // httpServer.initialize() fetches facilitator support and validates routes\n let initPromise: Promise<void> | null = syncFacilitatorOnStart ? httpServer.initialize() : null;\n\n // Dynamically register bazaar extension if routes declare it\n let bazaarPromise: Promise<void> | null = null;\n if (checkIfBazaarNeeded(routes)) {\n bazaarPromise = import(\"@x402/extensions/bazaar\")\n .then(({ bazaarResourceServerExtension }) => {\n server.registerExtension(bazaarResourceServerExtension);\n })\n .catch(err => {\n console.error(\"Failed to load bazaar extension:\", err);\n });\n }\n\n return async (req: Request, res: Response, next: NextFunction) => {\n // Create adapter and context\n const adapter = new ExpressAdapter(req);\n const context: HTTPRequestContext = {\n adapter,\n path: req.path,\n method: req.method,\n paymentHeader: adapter.getHeader(\"payment-signature\") || adapter.getHeader(\"x-payment\"),\n };\n\n // Check if route requires payment before initializing facilitator\n if (!httpServer.requiresPayment(context)) {\n return next();\n }\n\n // Only initialize when processing a protected route\n if (initPromise) {\n await initPromise;\n initPromise = null; // Clear after first await\n }\n\n // Await bazaar extension loading if needed\n if (bazaarPromise) {\n await bazaarPromise;\n bazaarPromise = null;\n }\n\n // Process payment requirement check\n const result = await httpServer.processHTTPRequest(context, paywallConfig);\n\n // Handle the different result types\n switch (result.type) {\n case \"no-payment-required\":\n // No payment needed, proceed directly to the route handler\n return next();\n\n case \"payment-error\":\n // Payment required but not provided or invalid\n const { response } = result;\n res.status(response.status);\n Object.entries(response.headers).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n if (response.isHtml) {\n res.send(response.body);\n } else {\n res.json(response.body || {});\n }\n return;\n\n case \"payment-verified\":\n // Payment is valid, need to wrap response for settlement\n const { paymentPayload, paymentRequirements } = result;\n\n // Intercept and buffer all core methods that can commit response to client\n const originalWriteHead = res.writeHead.bind(res);\n const originalWrite = res.write.bind(res);\n const originalEnd = res.end.bind(res);\n const originalFlushHeaders = res.flushHeaders.bind(res);\n\n type BufferedCall =\n | [\"writeHead\", Parameters<typeof originalWriteHead>]\n | [\"write\", Parameters<typeof originalWrite>]\n | [\"end\", Parameters<typeof originalEnd>]\n | [\"flushHeaders\", []];\n let bufferedCalls: BufferedCall[] = [];\n let settled = false;\n\n // Create a promise that resolves when the handler finishes and calls res.end()\n let endCalled: () => void;\n const endPromise = new Promise<void>(resolve => {\n endCalled = resolve;\n });\n\n res.writeHead = function (...args: Parameters<typeof originalWriteHead>) {\n if (!settled) {\n bufferedCalls.push([\"writeHead\", args]);\n return res;\n }\n return originalWriteHead(...args);\n } as typeof originalWriteHead;\n\n res.write = function (...args: Parameters<typeof originalWrite>) {\n if (!settled) {\n bufferedCalls.push([\"write\", args]);\n return true;\n }\n return originalWrite(...args);\n } as typeof originalWrite;\n\n res.end = function (...args: Parameters<typeof originalEnd>) {\n if (!settled) {\n bufferedCalls.push([\"end\", args]);\n // Signal that the handler has finished\n endCalled();\n return res;\n }\n return originalEnd(...args);\n } as typeof originalEnd;\n\n res.flushHeaders = function () {\n if (!settled) {\n bufferedCalls.push([\"flushHeaders\", []]);\n return;\n }\n return originalFlushHeaders();\n };\n\n // Proceed to the next middleware or route handler\n next();\n\n // Wait for the handler to actually call res.end() before checking status\n await endPromise;\n\n // If the response from the protected route is >= 400, do not settle payment\n if (res.statusCode >= 400) {\n settled = true;\n res.writeHead = originalWriteHead;\n res.write = originalWrite;\n res.end = originalEnd;\n res.flushHeaders = originalFlushHeaders;\n // Replay all buffered calls in order\n for (const [method, args] of bufferedCalls) {\n if (method === \"writeHead\")\n originalWriteHead(...(args as Parameters<typeof originalWriteHead>));\n else if (method === \"write\")\n originalWrite(...(args as Parameters<typeof originalWrite>));\n else if (method === \"end\") originalEnd(...(args as Parameters<typeof originalEnd>));\n else if (method === \"flushHeaders\") originalFlushHeaders();\n }\n bufferedCalls = [];\n return;\n }\n\n try {\n const settleResult = await httpServer.processSettlement(\n paymentPayload,\n paymentRequirements,\n );\n\n // If settlement fails, return an error and do not send the buffered response\n if (!settleResult.success) {\n bufferedCalls = [];\n res.status(402).json({\n error: \"Settlement failed\",\n details: settleResult.errorReason,\n });\n return;\n }\n\n // Settlement succeeded - add headers to response\n Object.entries(settleResult.headers).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n } catch (error) {\n console.error(error);\n // If settlement fails, don't send the buffered response\n bufferedCalls = [];\n res.status(402).json({\n error: \"Settlement failed\",\n details: error instanceof Error ? error.message : \"Unknown error\",\n });\n return;\n } finally {\n settled = true;\n res.writeHead = originalWriteHead;\n res.write = originalWrite;\n res.end = originalEnd;\n res.flushHeaders = originalFlushHeaders;\n\n // Replay all buffered calls in order\n for (const [method, args] of bufferedCalls) {\n if (method === \"writeHead\")\n originalWriteHead(...(args as Parameters<typeof originalWriteHead>));\n else if (method === \"write\")\n originalWrite(...(args as Parameters<typeof originalWrite>));\n else if (method === \"end\") originalEnd(...(args as Parameters<typeof originalEnd>));\n else if (method === \"flushHeaders\") originalFlushHeaders();\n }\n bufferedCalls = [];\n }\n return;\n }\n };\n}\n\n/**\n * Express payment middleware for x402 protocol (configuration-based).\n *\n * Use this when you want to quickly set up middleware with simple configuration.\n * This function creates and configures the x402ResourceServer internally.\n *\n * @param routes - Route configurations for protected endpoints\n * @param facilitatorClients - Optional facilitator client(s) for payment processing\n * @param schemes - Optional array of scheme registrations for server-side payment processing\n * @param paywallConfig - Optional configuration for the built-in paywall UI\n * @param paywall - Optional custom paywall provider (overrides default)\n * @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)\n * @returns Express middleware handler\n *\n * @example\n * ```typescript\n * import { paymentMiddlewareFromConfig } from \"@x402/express\";\n *\n * app.use(paymentMiddlewareFromConfig(\n * routes,\n * myFacilitatorClient,\n * [{ network: \"eip155:8453\", server: evmSchemeServer }],\n * paywallConfig\n * ));\n * ```\n */\nexport function paymentMiddlewareFromConfig(\n routes: RoutesConfig,\n facilitatorClients?: FacilitatorClient | FacilitatorClient[],\n schemes?: SchemeRegistration[],\n paywallConfig?: PaywallConfig,\n paywall?: PaywallProvider,\n syncFacilitatorOnStart: boolean = true,\n) {\n const ResourceServer = new x402ResourceServer(facilitatorClients);\n\n if (schemes) {\n schemes.forEach(({ network, server: schemeServer }) => {\n ResourceServer.register(network, schemeServer);\n });\n }\n\n // Use the direct paymentMiddleware with the configured server\n // Note: paymentMiddleware handles dynamic bazaar registration\n return paymentMiddleware(routes, ResourceServer, paywallConfig, paywall, syncFacilitatorOnStart);\n}\n\nexport { x402ResourceServer, x402HTTPResourceServer } from \"@x402/core/server\";\n\nexport type {\n PaymentRequired,\n PaymentRequirements,\n PaymentPayload,\n Network,\n SchemeNetworkServer,\n} from \"@x402/core/types\";\n\nexport type { PaywallProvider, PaywallConfig } from \"@x402/core/server\";\n\nexport { RouteConfigurationError } from \"@x402/core/server\";\n\nexport type { RouteValidationError } from \"@x402/core/server\";\n\nexport { ExpressAdapter } from \"./adapter\";\n","import { HTTPAdapter } from \"@x402/core/server\";\nimport { Request } from \"express\";\n\n/**\n * Express adapter implementation\n */\nexport class ExpressAdapter implements HTTPAdapter {\n /**\n * Creates a new ExpressAdapter instance.\n *\n * @param req - The Express request object\n */\n constructor(private req: Request) {}\n\n /**\n * Gets a header value from the request.\n *\n * @param name - The header name\n * @returns The header value or undefined\n */\n getHeader(name: string): string | undefined {\n const value = this.req.header(name);\n return Array.isArray(value) ? value[0] : value;\n }\n\n /**\n * Gets the HTTP method of the request.\n *\n * @returns The HTTP method\n */\n getMethod(): string {\n return this.req.method;\n }\n\n /**\n * Gets the path of the request.\n *\n * @returns The request path\n */\n getPath(): string {\n return this.req.path;\n }\n\n /**\n * Gets the full URL of the request.\n *\n * @returns The full request URL\n */\n getUrl(): string {\n return `${this.req.protocol}://${this.req.headers.host}${this.req.path}`;\n }\n\n /**\n * Gets the Accept header from the request.\n *\n * @returns The Accept header value or empty string\n */\n getAcceptHeader(): string {\n return this.req.header(\"Accept\") || \"\";\n }\n\n /**\n * Gets the User-Agent header from the request.\n *\n * @returns The User-Agent header value or empty string\n */\n getUserAgent(): string {\n return this.req.header(\"User-Agent\") || \"\";\n }\n\n /**\n * Gets all query parameters from the request URL.\n *\n * @returns Record of query parameter key-value pairs\n */\n getQueryParams(): Record<string, string | string[]> {\n return this.req.query as Record<string, string | string[]>;\n }\n\n /**\n * Gets a specific query parameter by name.\n *\n * @param name - The query parameter name\n * @returns The query parameter value(s) or undefined\n */\n getQueryParam(name: string): string | string[] | undefined {\n const value = this.req.query[name];\n return value as string | string[] | undefined;\n }\n\n /**\n * Gets the parsed request body.\n * Requires express.json() or express.urlencoded() middleware.\n *\n * @returns The parsed request body\n */\n getBody(): unknown {\n return this.req.body;\n }\n}\n"],"mappings":"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","names":["import_server"]}
@@ -0,0 +1,145 @@
1
+ import { HTTPAdapter, RoutesConfig, x402ResourceServer, PaywallConfig, PaywallProvider, FacilitatorClient } from '@x402/core/server';
2
+ export { PaywallConfig, PaywallProvider, RouteConfigurationError, RouteValidationError, x402HTTPResourceServer, x402ResourceServer } from '@x402/core/server';
3
+ import { Network, SchemeNetworkServer } from '@x402/core/types';
4
+ export { Network, PaymentPayload, PaymentRequired, PaymentRequirements, SchemeNetworkServer } from '@x402/core/types';
5
+ import { Request, Response, NextFunction } from 'express';
6
+
7
+ /**
8
+ * Express adapter implementation
9
+ */
10
+ declare class ExpressAdapter implements HTTPAdapter {
11
+ private req;
12
+ /**
13
+ * Creates a new ExpressAdapter instance.
14
+ *
15
+ * @param req - The Express request object
16
+ */
17
+ constructor(req: Request);
18
+ /**
19
+ * Gets a header value from the request.
20
+ *
21
+ * @param name - The header name
22
+ * @returns The header value or undefined
23
+ */
24
+ getHeader(name: string): string | undefined;
25
+ /**
26
+ * Gets the HTTP method of the request.
27
+ *
28
+ * @returns The HTTP method
29
+ */
30
+ getMethod(): string;
31
+ /**
32
+ * Gets the path of the request.
33
+ *
34
+ * @returns The request path
35
+ */
36
+ getPath(): string;
37
+ /**
38
+ * Gets the full URL of the request.
39
+ *
40
+ * @returns The full request URL
41
+ */
42
+ getUrl(): string;
43
+ /**
44
+ * Gets the Accept header from the request.
45
+ *
46
+ * @returns The Accept header value or empty string
47
+ */
48
+ getAcceptHeader(): string;
49
+ /**
50
+ * Gets the User-Agent header from the request.
51
+ *
52
+ * @returns The User-Agent header value or empty string
53
+ */
54
+ getUserAgent(): string;
55
+ /**
56
+ * Gets all query parameters from the request URL.
57
+ *
58
+ * @returns Record of query parameter key-value pairs
59
+ */
60
+ getQueryParams(): Record<string, string | string[]>;
61
+ /**
62
+ * Gets a specific query parameter by name.
63
+ *
64
+ * @param name - The query parameter name
65
+ * @returns The query parameter value(s) or undefined
66
+ */
67
+ getQueryParam(name: string): string | string[] | undefined;
68
+ /**
69
+ * Gets the parsed request body.
70
+ * Requires express.json() or express.urlencoded() middleware.
71
+ *
72
+ * @returns The parsed request body
73
+ */
74
+ getBody(): unknown;
75
+ }
76
+
77
+ /**
78
+ * Configuration for registering a payment scheme with a specific network
79
+ */
80
+ interface SchemeRegistration {
81
+ /**
82
+ * The network identifier (e.g., 'eip155:84532', 'solana:mainnet')
83
+ */
84
+ network: Network;
85
+ /**
86
+ * The scheme server implementation for this network
87
+ */
88
+ server: SchemeNetworkServer;
89
+ }
90
+ /**
91
+ * Express payment middleware for x402 protocol (direct server instance).
92
+ *
93
+ * Use this when you want to pass a pre-configured x402ResourceServer instance.
94
+ * This provides more flexibility for testing, custom configuration, and reusing
95
+ * server instances across multiple middlewares.
96
+ *
97
+ * @param routes - Route configurations for protected endpoints
98
+ * @param server - Pre-configured x402ResourceServer instance
99
+ * @param paywallConfig - Optional configuration for the built-in paywall UI
100
+ * @param paywall - Optional custom paywall provider (overrides default)
101
+ * @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
102
+ * @returns Express middleware handler
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * import { paymentMiddleware } from "@x402/express";
107
+ * import { x402ResourceServer } from "@x402/core/server";
108
+ * import { registerExactEvmScheme } from "@x402/evm/exact/server";
109
+ *
110
+ * const server = new x402ResourceServer(myFacilitatorClient);
111
+ * registerExactEvmScheme(server, { signer: myServerSigner });
112
+ *
113
+ * app.use(paymentMiddleware(routes, server, paywallConfig));
114
+ * ```
115
+ */
116
+ declare function paymentMiddleware(routes: RoutesConfig, server: x402ResourceServer, paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): (req: Request, res: Response, next: NextFunction) => Promise<void>;
117
+ /**
118
+ * Express payment middleware for x402 protocol (configuration-based).
119
+ *
120
+ * Use this when you want to quickly set up middleware with simple configuration.
121
+ * This function creates and configures the x402ResourceServer internally.
122
+ *
123
+ * @param routes - Route configurations for protected endpoints
124
+ * @param facilitatorClients - Optional facilitator client(s) for payment processing
125
+ * @param schemes - Optional array of scheme registrations for server-side payment processing
126
+ * @param paywallConfig - Optional configuration for the built-in paywall UI
127
+ * @param paywall - Optional custom paywall provider (overrides default)
128
+ * @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
129
+ * @returns Express middleware handler
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * import { paymentMiddlewareFromConfig } from "@x402/express";
134
+ *
135
+ * app.use(paymentMiddlewareFromConfig(
136
+ * routes,
137
+ * myFacilitatorClient,
138
+ * [{ network: "eip155:8453", server: evmSchemeServer }],
139
+ * paywallConfig
140
+ * ));
141
+ * ```
142
+ */
143
+ declare function paymentMiddlewareFromConfig(routes: RoutesConfig, facilitatorClients?: FacilitatorClient | FacilitatorClient[], schemes?: SchemeRegistration[], paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): (req: Request, res: Response, next: NextFunction) => Promise<void>;
144
+
145
+ export { ExpressAdapter, type SchemeRegistration, paymentMiddleware, paymentMiddlewareFromConfig };
@@ -0,0 +1,277 @@
1
+ // src/index.ts
2
+ import {
3
+ x402HTTPResourceServer,
4
+ x402ResourceServer
5
+ } from "@x402/core/server";
6
+
7
+ // src/adapter.ts
8
+ var ExpressAdapter = class {
9
+ /**
10
+ * Creates a new ExpressAdapter instance.
11
+ *
12
+ * @param req - The Express request object
13
+ */
14
+ constructor(req) {
15
+ this.req = req;
16
+ }
17
+ /**
18
+ * Gets a header value from the request.
19
+ *
20
+ * @param name - The header name
21
+ * @returns The header value or undefined
22
+ */
23
+ getHeader(name) {
24
+ const value = this.req.header(name);
25
+ return Array.isArray(value) ? value[0] : value;
26
+ }
27
+ /**
28
+ * Gets the HTTP method of the request.
29
+ *
30
+ * @returns The HTTP method
31
+ */
32
+ getMethod() {
33
+ return this.req.method;
34
+ }
35
+ /**
36
+ * Gets the path of the request.
37
+ *
38
+ * @returns The request path
39
+ */
40
+ getPath() {
41
+ return this.req.path;
42
+ }
43
+ /**
44
+ * Gets the full URL of the request.
45
+ *
46
+ * @returns The full request URL
47
+ */
48
+ getUrl() {
49
+ return `${this.req.protocol}://${this.req.headers.host}${this.req.path}`;
50
+ }
51
+ /**
52
+ * Gets the Accept header from the request.
53
+ *
54
+ * @returns The Accept header value or empty string
55
+ */
56
+ getAcceptHeader() {
57
+ return this.req.header("Accept") || "";
58
+ }
59
+ /**
60
+ * Gets the User-Agent header from the request.
61
+ *
62
+ * @returns The User-Agent header value or empty string
63
+ */
64
+ getUserAgent() {
65
+ return this.req.header("User-Agent") || "";
66
+ }
67
+ /**
68
+ * Gets all query parameters from the request URL.
69
+ *
70
+ * @returns Record of query parameter key-value pairs
71
+ */
72
+ getQueryParams() {
73
+ return this.req.query;
74
+ }
75
+ /**
76
+ * Gets a specific query parameter by name.
77
+ *
78
+ * @param name - The query parameter name
79
+ * @returns The query parameter value(s) or undefined
80
+ */
81
+ getQueryParam(name) {
82
+ const value = this.req.query[name];
83
+ return value;
84
+ }
85
+ /**
86
+ * Gets the parsed request body.
87
+ * Requires express.json() or express.urlencoded() middleware.
88
+ *
89
+ * @returns The parsed request body
90
+ */
91
+ getBody() {
92
+ return this.req.body;
93
+ }
94
+ };
95
+
96
+ // src/index.ts
97
+ import { x402ResourceServer as x402ResourceServer2, x402HTTPResourceServer as x402HTTPResourceServer2 } from "@x402/core/server";
98
+ import { RouteConfigurationError } from "@x402/core/server";
99
+ function checkIfBazaarNeeded(routes) {
100
+ if ("accepts" in routes) {
101
+ return !!(routes.extensions && "bazaar" in routes.extensions);
102
+ }
103
+ return Object.values(routes).some((routeConfig) => {
104
+ return !!(routeConfig.extensions && "bazaar" in routeConfig.extensions);
105
+ });
106
+ }
107
+ function paymentMiddleware(routes, server, paywallConfig, paywall, syncFacilitatorOnStart = true) {
108
+ const httpServer = new x402HTTPResourceServer(server, routes);
109
+ if (paywall) {
110
+ httpServer.registerPaywallProvider(paywall);
111
+ }
112
+ let initPromise = syncFacilitatorOnStart ? httpServer.initialize() : null;
113
+ let bazaarPromise = null;
114
+ if (checkIfBazaarNeeded(routes)) {
115
+ bazaarPromise = import("@x402/extensions/bazaar").then(({ bazaarResourceServerExtension }) => {
116
+ server.registerExtension(bazaarResourceServerExtension);
117
+ }).catch((err) => {
118
+ console.error("Failed to load bazaar extension:", err);
119
+ });
120
+ }
121
+ return async (req, res, next) => {
122
+ const adapter = new ExpressAdapter(req);
123
+ const context = {
124
+ adapter,
125
+ path: req.path,
126
+ method: req.method,
127
+ paymentHeader: adapter.getHeader("payment-signature") || adapter.getHeader("x-payment")
128
+ };
129
+ if (!httpServer.requiresPayment(context)) {
130
+ return next();
131
+ }
132
+ if (initPromise) {
133
+ await initPromise;
134
+ initPromise = null;
135
+ }
136
+ if (bazaarPromise) {
137
+ await bazaarPromise;
138
+ bazaarPromise = null;
139
+ }
140
+ const result = await httpServer.processHTTPRequest(context, paywallConfig);
141
+ switch (result.type) {
142
+ case "no-payment-required":
143
+ return next();
144
+ case "payment-error":
145
+ const { response } = result;
146
+ res.status(response.status);
147
+ Object.entries(response.headers).forEach(([key, value]) => {
148
+ res.setHeader(key, value);
149
+ });
150
+ if (response.isHtml) {
151
+ res.send(response.body);
152
+ } else {
153
+ res.json(response.body || {});
154
+ }
155
+ return;
156
+ case "payment-verified":
157
+ const { paymentPayload, paymentRequirements } = result;
158
+ const originalWriteHead = res.writeHead.bind(res);
159
+ const originalWrite = res.write.bind(res);
160
+ const originalEnd = res.end.bind(res);
161
+ const originalFlushHeaders = res.flushHeaders.bind(res);
162
+ let bufferedCalls = [];
163
+ let settled = false;
164
+ let endCalled;
165
+ const endPromise = new Promise((resolve) => {
166
+ endCalled = resolve;
167
+ });
168
+ res.writeHead = function(...args) {
169
+ if (!settled) {
170
+ bufferedCalls.push(["writeHead", args]);
171
+ return res;
172
+ }
173
+ return originalWriteHead(...args);
174
+ };
175
+ res.write = function(...args) {
176
+ if (!settled) {
177
+ bufferedCalls.push(["write", args]);
178
+ return true;
179
+ }
180
+ return originalWrite(...args);
181
+ };
182
+ res.end = function(...args) {
183
+ if (!settled) {
184
+ bufferedCalls.push(["end", args]);
185
+ endCalled();
186
+ return res;
187
+ }
188
+ return originalEnd(...args);
189
+ };
190
+ res.flushHeaders = function() {
191
+ if (!settled) {
192
+ bufferedCalls.push(["flushHeaders", []]);
193
+ return;
194
+ }
195
+ return originalFlushHeaders();
196
+ };
197
+ next();
198
+ await endPromise;
199
+ if (res.statusCode >= 400) {
200
+ settled = true;
201
+ res.writeHead = originalWriteHead;
202
+ res.write = originalWrite;
203
+ res.end = originalEnd;
204
+ res.flushHeaders = originalFlushHeaders;
205
+ for (const [method, args] of bufferedCalls) {
206
+ if (method === "writeHead")
207
+ originalWriteHead(...args);
208
+ else if (method === "write")
209
+ originalWrite(...args);
210
+ else if (method === "end") originalEnd(...args);
211
+ else if (method === "flushHeaders") originalFlushHeaders();
212
+ }
213
+ bufferedCalls = [];
214
+ return;
215
+ }
216
+ try {
217
+ const settleResult = await httpServer.processSettlement(
218
+ paymentPayload,
219
+ paymentRequirements
220
+ );
221
+ if (!settleResult.success) {
222
+ bufferedCalls = [];
223
+ res.status(402).json({
224
+ error: "Settlement failed",
225
+ details: settleResult.errorReason
226
+ });
227
+ return;
228
+ }
229
+ Object.entries(settleResult.headers).forEach(([key, value]) => {
230
+ res.setHeader(key, value);
231
+ });
232
+ } catch (error) {
233
+ console.error(error);
234
+ bufferedCalls = [];
235
+ res.status(402).json({
236
+ error: "Settlement failed",
237
+ details: error instanceof Error ? error.message : "Unknown error"
238
+ });
239
+ return;
240
+ } finally {
241
+ settled = true;
242
+ res.writeHead = originalWriteHead;
243
+ res.write = originalWrite;
244
+ res.end = originalEnd;
245
+ res.flushHeaders = originalFlushHeaders;
246
+ for (const [method, args] of bufferedCalls) {
247
+ if (method === "writeHead")
248
+ originalWriteHead(...args);
249
+ else if (method === "write")
250
+ originalWrite(...args);
251
+ else if (method === "end") originalEnd(...args);
252
+ else if (method === "flushHeaders") originalFlushHeaders();
253
+ }
254
+ bufferedCalls = [];
255
+ }
256
+ return;
257
+ }
258
+ };
259
+ }
260
+ function paymentMiddlewareFromConfig(routes, facilitatorClients, schemes, paywallConfig, paywall, syncFacilitatorOnStart = true) {
261
+ const ResourceServer = new x402ResourceServer(facilitatorClients);
262
+ if (schemes) {
263
+ schemes.forEach(({ network, server: schemeServer }) => {
264
+ ResourceServer.register(network, schemeServer);
265
+ });
266
+ }
267
+ return paymentMiddleware(routes, ResourceServer, paywallConfig, paywall, syncFacilitatorOnStart);
268
+ }
269
+ export {
270
+ ExpressAdapter,
271
+ RouteConfigurationError,
272
+ paymentMiddleware,
273
+ paymentMiddlewareFromConfig,
274
+ x402HTTPResourceServer2 as x402HTTPResourceServer,
275
+ x402ResourceServer2 as x402ResourceServer
276
+ };
277
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/index.ts","../../src/adapter.ts"],"sourcesContent":["import {\n HTTPRequestContext,\n PaywallConfig,\n PaywallProvider,\n x402HTTPResourceServer,\n x402ResourceServer,\n RoutesConfig,\n FacilitatorClient,\n} from \"@x402/core/server\";\nimport { SchemeNetworkServer, Network } from \"@x402/core/types\";\nimport { NextFunction, Request, Response } from \"express\";\nimport { ExpressAdapter } from \"./adapter\";\n\n/**\n * Check if any routes in the configuration declare bazaar extensions\n *\n * @param routes - Route configuration\n * @returns True if any route has extensions.bazaar defined\n */\nfunction checkIfBazaarNeeded(routes: RoutesConfig): boolean {\n // Handle single route config\n if (\"accepts\" in routes) {\n return !!(routes.extensions && \"bazaar\" in routes.extensions);\n }\n\n // Handle multiple routes\n return Object.values(routes).some(routeConfig => {\n return !!(routeConfig.extensions && \"bazaar\" in routeConfig.extensions);\n });\n}\n\n/**\n * Configuration for registering a payment scheme with a specific network\n */\nexport interface SchemeRegistration {\n /**\n * The network identifier (e.g., 'eip155:84532', 'solana:mainnet')\n */\n network: Network;\n\n /**\n * The scheme server implementation for this network\n */\n server: SchemeNetworkServer;\n}\n\n/**\n * Express payment middleware for x402 protocol (direct server instance).\n *\n * Use this when you want to pass a pre-configured x402ResourceServer instance.\n * This provides more flexibility for testing, custom configuration, and reusing\n * server instances across multiple middlewares.\n *\n * @param routes - Route configurations for protected endpoints\n * @param server - Pre-configured x402ResourceServer instance\n * @param paywallConfig - Optional configuration for the built-in paywall UI\n * @param paywall - Optional custom paywall provider (overrides default)\n * @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)\n * @returns Express middleware handler\n *\n * @example\n * ```typescript\n * import { paymentMiddleware } from \"@x402/express\";\n * import { x402ResourceServer } from \"@x402/core/server\";\n * import { registerExactEvmScheme } from \"@x402/evm/exact/server\";\n *\n * const server = new x402ResourceServer(myFacilitatorClient);\n * registerExactEvmScheme(server, { signer: myServerSigner });\n *\n * app.use(paymentMiddleware(routes, server, paywallConfig));\n * ```\n */\nexport function paymentMiddleware(\n routes: RoutesConfig,\n server: x402ResourceServer,\n paywallConfig?: PaywallConfig,\n paywall?: PaywallProvider,\n syncFacilitatorOnStart: boolean = true,\n) {\n // Create the x402 HTTP server instance with the resource server\n const httpServer = new x402HTTPResourceServer(server, routes);\n\n // Register custom paywall provider if provided\n if (paywall) {\n httpServer.registerPaywallProvider(paywall);\n }\n\n // Store initialization promise (not the result)\n // httpServer.initialize() fetches facilitator support and validates routes\n let initPromise: Promise<void> | null = syncFacilitatorOnStart ? httpServer.initialize() : null;\n\n // Dynamically register bazaar extension if routes declare it\n let bazaarPromise: Promise<void> | null = null;\n if (checkIfBazaarNeeded(routes)) {\n bazaarPromise = import(\"@x402/extensions/bazaar\")\n .then(({ bazaarResourceServerExtension }) => {\n server.registerExtension(bazaarResourceServerExtension);\n })\n .catch(err => {\n console.error(\"Failed to load bazaar extension:\", err);\n });\n }\n\n return async (req: Request, res: Response, next: NextFunction) => {\n // Create adapter and context\n const adapter = new ExpressAdapter(req);\n const context: HTTPRequestContext = {\n adapter,\n path: req.path,\n method: req.method,\n paymentHeader: adapter.getHeader(\"payment-signature\") || adapter.getHeader(\"x-payment\"),\n };\n\n // Check if route requires payment before initializing facilitator\n if (!httpServer.requiresPayment(context)) {\n return next();\n }\n\n // Only initialize when processing a protected route\n if (initPromise) {\n await initPromise;\n initPromise = null; // Clear after first await\n }\n\n // Await bazaar extension loading if needed\n if (bazaarPromise) {\n await bazaarPromise;\n bazaarPromise = null;\n }\n\n // Process payment requirement check\n const result = await httpServer.processHTTPRequest(context, paywallConfig);\n\n // Handle the different result types\n switch (result.type) {\n case \"no-payment-required\":\n // No payment needed, proceed directly to the route handler\n return next();\n\n case \"payment-error\":\n // Payment required but not provided or invalid\n const { response } = result;\n res.status(response.status);\n Object.entries(response.headers).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n if (response.isHtml) {\n res.send(response.body);\n } else {\n res.json(response.body || {});\n }\n return;\n\n case \"payment-verified\":\n // Payment is valid, need to wrap response for settlement\n const { paymentPayload, paymentRequirements } = result;\n\n // Intercept and buffer all core methods that can commit response to client\n const originalWriteHead = res.writeHead.bind(res);\n const originalWrite = res.write.bind(res);\n const originalEnd = res.end.bind(res);\n const originalFlushHeaders = res.flushHeaders.bind(res);\n\n type BufferedCall =\n | [\"writeHead\", Parameters<typeof originalWriteHead>]\n | [\"write\", Parameters<typeof originalWrite>]\n | [\"end\", Parameters<typeof originalEnd>]\n | [\"flushHeaders\", []];\n let bufferedCalls: BufferedCall[] = [];\n let settled = false;\n\n // Create a promise that resolves when the handler finishes and calls res.end()\n let endCalled: () => void;\n const endPromise = new Promise<void>(resolve => {\n endCalled = resolve;\n });\n\n res.writeHead = function (...args: Parameters<typeof originalWriteHead>) {\n if (!settled) {\n bufferedCalls.push([\"writeHead\", args]);\n return res;\n }\n return originalWriteHead(...args);\n } as typeof originalWriteHead;\n\n res.write = function (...args: Parameters<typeof originalWrite>) {\n if (!settled) {\n bufferedCalls.push([\"write\", args]);\n return true;\n }\n return originalWrite(...args);\n } as typeof originalWrite;\n\n res.end = function (...args: Parameters<typeof originalEnd>) {\n if (!settled) {\n bufferedCalls.push([\"end\", args]);\n // Signal that the handler has finished\n endCalled();\n return res;\n }\n return originalEnd(...args);\n } as typeof originalEnd;\n\n res.flushHeaders = function () {\n if (!settled) {\n bufferedCalls.push([\"flushHeaders\", []]);\n return;\n }\n return originalFlushHeaders();\n };\n\n // Proceed to the next middleware or route handler\n next();\n\n // Wait for the handler to actually call res.end() before checking status\n await endPromise;\n\n // If the response from the protected route is >= 400, do not settle payment\n if (res.statusCode >= 400) {\n settled = true;\n res.writeHead = originalWriteHead;\n res.write = originalWrite;\n res.end = originalEnd;\n res.flushHeaders = originalFlushHeaders;\n // Replay all buffered calls in order\n for (const [method, args] of bufferedCalls) {\n if (method === \"writeHead\")\n originalWriteHead(...(args as Parameters<typeof originalWriteHead>));\n else if (method === \"write\")\n originalWrite(...(args as Parameters<typeof originalWrite>));\n else if (method === \"end\") originalEnd(...(args as Parameters<typeof originalEnd>));\n else if (method === \"flushHeaders\") originalFlushHeaders();\n }\n bufferedCalls = [];\n return;\n }\n\n try {\n const settleResult = await httpServer.processSettlement(\n paymentPayload,\n paymentRequirements,\n );\n\n // If settlement fails, return an error and do not send the buffered response\n if (!settleResult.success) {\n bufferedCalls = [];\n res.status(402).json({\n error: \"Settlement failed\",\n details: settleResult.errorReason,\n });\n return;\n }\n\n // Settlement succeeded - add headers to response\n Object.entries(settleResult.headers).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n } catch (error) {\n console.error(error);\n // If settlement fails, don't send the buffered response\n bufferedCalls = [];\n res.status(402).json({\n error: \"Settlement failed\",\n details: error instanceof Error ? error.message : \"Unknown error\",\n });\n return;\n } finally {\n settled = true;\n res.writeHead = originalWriteHead;\n res.write = originalWrite;\n res.end = originalEnd;\n res.flushHeaders = originalFlushHeaders;\n\n // Replay all buffered calls in order\n for (const [method, args] of bufferedCalls) {\n if (method === \"writeHead\")\n originalWriteHead(...(args as Parameters<typeof originalWriteHead>));\n else if (method === \"write\")\n originalWrite(...(args as Parameters<typeof originalWrite>));\n else if (method === \"end\") originalEnd(...(args as Parameters<typeof originalEnd>));\n else if (method === \"flushHeaders\") originalFlushHeaders();\n }\n bufferedCalls = [];\n }\n return;\n }\n };\n}\n\n/**\n * Express payment middleware for x402 protocol (configuration-based).\n *\n * Use this when you want to quickly set up middleware with simple configuration.\n * This function creates and configures the x402ResourceServer internally.\n *\n * @param routes - Route configurations for protected endpoints\n * @param facilitatorClients - Optional facilitator client(s) for payment processing\n * @param schemes - Optional array of scheme registrations for server-side payment processing\n * @param paywallConfig - Optional configuration for the built-in paywall UI\n * @param paywall - Optional custom paywall provider (overrides default)\n * @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)\n * @returns Express middleware handler\n *\n * @example\n * ```typescript\n * import { paymentMiddlewareFromConfig } from \"@x402/express\";\n *\n * app.use(paymentMiddlewareFromConfig(\n * routes,\n * myFacilitatorClient,\n * [{ network: \"eip155:8453\", server: evmSchemeServer }],\n * paywallConfig\n * ));\n * ```\n */\nexport function paymentMiddlewareFromConfig(\n routes: RoutesConfig,\n facilitatorClients?: FacilitatorClient | FacilitatorClient[],\n schemes?: SchemeRegistration[],\n paywallConfig?: PaywallConfig,\n paywall?: PaywallProvider,\n syncFacilitatorOnStart: boolean = true,\n) {\n const ResourceServer = new x402ResourceServer(facilitatorClients);\n\n if (schemes) {\n schemes.forEach(({ network, server: schemeServer }) => {\n ResourceServer.register(network, schemeServer);\n });\n }\n\n // Use the direct paymentMiddleware with the configured server\n // Note: paymentMiddleware handles dynamic bazaar registration\n return paymentMiddleware(routes, ResourceServer, paywallConfig, paywall, syncFacilitatorOnStart);\n}\n\nexport { x402ResourceServer, x402HTTPResourceServer } from \"@x402/core/server\";\n\nexport type {\n PaymentRequired,\n PaymentRequirements,\n PaymentPayload,\n Network,\n SchemeNetworkServer,\n} from \"@x402/core/types\";\n\nexport type { PaywallProvider, PaywallConfig } from \"@x402/core/server\";\n\nexport { RouteConfigurationError } from \"@x402/core/server\";\n\nexport type { RouteValidationError } from \"@x402/core/server\";\n\nexport { ExpressAdapter } from \"./adapter\";\n","import { HTTPAdapter } from \"@x402/core/server\";\nimport { Request } from \"express\";\n\n/**\n * Express adapter implementation\n */\nexport class ExpressAdapter implements HTTPAdapter {\n /**\n * Creates a new ExpressAdapter instance.\n *\n * @param req - The Express request object\n */\n constructor(private req: Request) {}\n\n /**\n * Gets a header value from the request.\n *\n * @param name - The header name\n * @returns The header value or undefined\n */\n getHeader(name: string): string | undefined {\n const value = this.req.header(name);\n return Array.isArray(value) ? value[0] : value;\n }\n\n /**\n * Gets the HTTP method of the request.\n *\n * @returns The HTTP method\n */\n getMethod(): string {\n return this.req.method;\n }\n\n /**\n * Gets the path of the request.\n *\n * @returns The request path\n */\n getPath(): string {\n return this.req.path;\n }\n\n /**\n * Gets the full URL of the request.\n *\n * @returns The full request URL\n */\n getUrl(): string {\n return `${this.req.protocol}://${this.req.headers.host}${this.req.path}`;\n }\n\n /**\n * Gets the Accept header from the request.\n *\n * @returns The Accept header value or empty string\n */\n getAcceptHeader(): string {\n return this.req.header(\"Accept\") || \"\";\n }\n\n /**\n * Gets the User-Agent header from the request.\n *\n * @returns The User-Agent header value or empty string\n */\n getUserAgent(): string {\n return this.req.header(\"User-Agent\") || \"\";\n }\n\n /**\n * Gets all query parameters from the request URL.\n *\n * @returns Record of query parameter key-value pairs\n */\n getQueryParams(): Record<string, string | string[]> {\n return this.req.query as Record<string, string | string[]>;\n }\n\n /**\n * Gets a specific query parameter by name.\n *\n * @param name - The query parameter name\n * @returns The query parameter value(s) or undefined\n */\n getQueryParam(name: string): string | string[] | undefined {\n const value = this.req.query[name];\n return value as string | string[] | undefined;\n }\n\n /**\n * Gets the parsed request body.\n * Requires express.json() or express.urlencoded() middleware.\n *\n * @returns The parsed request body\n */\n getBody(): unknown {\n return this.req.body;\n }\n}\n"],"mappings":"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","names":["x402ResourceServer","x402HTTPResourceServer"]}
package/package.json CHANGED
@@ -1,12 +1,74 @@
1
1
  {
2
2
  "name": "@x402/express",
3
- "version": "0.0.1",
4
- "description": "The Express middleware package for the x402 payment protocol",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "author": "Coinbase Inc.",
3
+ "version": "2.0.0",
4
+ "main": "./dist/cjs/index.js",
5
+ "module": "./dist/esm/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "keywords": [],
10
8
  "license": "Apache-2.0",
11
- "repository": "https://github.com/coinbase/x402"
9
+ "author": "Coinbase Inc.",
10
+ "repository": "https://github.com/coinbase/x402",
11
+ "description": "x402 Payment Protocol",
12
+ "devDependencies": {
13
+ "@eslint/js": "^9.24.0",
14
+ "@types/express": "^5.0.1",
15
+ "@types/node": "^22.13.4",
16
+ "@typescript-eslint/eslint-plugin": "^8.29.1",
17
+ "@typescript-eslint/parser": "^8.29.1",
18
+ "eslint": "^9.24.0",
19
+ "eslint-plugin-import": "^2.31.0",
20
+ "eslint-plugin-jsdoc": "^50.6.9",
21
+ "eslint-plugin-prettier": "^5.2.6",
22
+ "express": "^4.18.2",
23
+ "prettier": "3.5.2",
24
+ "tsup": "^8.4.0",
25
+ "tsx": "^4.19.2",
26
+ "typescript": "^5.7.3",
27
+ "vite": "^6.2.6",
28
+ "vite-tsconfig-paths": "^5.1.4",
29
+ "vitest": "^3.0.5"
30
+ },
31
+ "dependencies": {
32
+ "@coinbase/cdp-sdk": "^1.22.0",
33
+ "@solana/kit": "^2.1.1",
34
+ "viem": "^2.39.3",
35
+ "zod": "^3.24.2",
36
+ "@x402/core": "^2.0.0",
37
+ "@x402/extensions": "^2.0.0"
38
+ },
39
+ "peerDependencies": {
40
+ "express": "^4.0.0 || ^5.0.0",
41
+ "@x402/paywall": "2.0.0"
42
+ },
43
+ "peerDependenciesMeta": {
44
+ "@x402/paywall": {
45
+ "optional": true
46
+ }
47
+ },
48
+ "exports": {
49
+ ".": {
50
+ "import": {
51
+ "types": "./dist/esm/index.d.mts",
52
+ "default": "./dist/esm/index.mjs"
53
+ },
54
+ "require": {
55
+ "types": "./dist/cjs/index.d.ts",
56
+ "default": "./dist/cjs/index.js"
57
+ }
58
+ }
59
+ },
60
+ "files": [
61
+ "dist"
62
+ ],
63
+ "scripts": {
64
+ "start": "tsx --env-file=.env index.ts",
65
+ "test": "vitest run",
66
+ "test:watch": "vitest",
67
+ "build": "tsup",
68
+ "watch": "tsc --watch",
69
+ "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"",
70
+ "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"",
71
+ "lint": "eslint . --ext .ts --fix",
72
+ "lint:check": "eslint . --ext .ts"
73
+ }
12
74
  }
package/index.js DELETED
@@ -1,3 +0,0 @@
1
- // Placeholder for @x402/express
2
- module.exports = {};
3
-