@armory-sh/middleware-elysia 0.3.26 → 0.3.28

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.
@@ -1,5 +1,5 @@
1
- import { Elysia } from 'elysia';
2
1
  import { matchRoute, PAYMENT_SIGNATURE_HEADER, createPaymentRequiredHeaders, decodePayloadHeader, verifyPayment, settlePayment, createSettlementHeaders, validateRouteConfig } from '@armory-sh/base';
2
+ import { Elysia } from 'elysia';
3
3
 
4
4
  // src/routes.ts
5
5
  var resolveRouteConfig = (config) => {
@@ -37,18 +37,25 @@ var routeAwarePaymentMiddleware = (perRouteConfig) => {
37
37
  try {
38
38
  if (configError) {
39
39
  return errorResponse(
40
- { error: "Payment middleware configuration error", details: configError.message },
40
+ {
41
+ error: "Payment middleware configuration error",
42
+ details: configError.message
43
+ },
41
44
  500
42
45
  );
43
46
  }
44
47
  const path = new URL(context.request.url).pathname;
45
- const matchedRoute = resolvedRoutes.find((r) => matchRoute(r.pattern, path));
48
+ const matchedRoute = resolvedRoutes.find(
49
+ (r) => matchRoute(r.pattern, path)
50
+ );
46
51
  if (!matchedRoute) {
47
52
  return;
48
53
  }
49
54
  const routeConfig = matchedRoute.config;
50
55
  const { requirements, facilitatorUrl } = routeConfig;
51
- const paymentHeader = context.request.headers.get(PAYMENT_SIGNATURE_HEADER);
56
+ const paymentHeader = context.request.headers.get(
57
+ PAYMENT_SIGNATURE_HEADER
58
+ );
52
59
  if (!paymentHeader) {
53
60
  return errorResponse(
54
61
  { error: "Payment required", accepts: [requirements] },
@@ -62,16 +69,29 @@ var routeAwarePaymentMiddleware = (perRouteConfig) => {
62
69
  accepted: requirements
63
70
  });
64
71
  } catch (error) {
65
- return errorResponse({
66
- error: "Invalid payment payload",
67
- message: error instanceof Error ? error.message : "Unknown error"
68
- }, 400);
72
+ return errorResponse(
73
+ {
74
+ error: "Invalid payment payload",
75
+ message: error instanceof Error ? error.message : "Unknown error"
76
+ },
77
+ 400
78
+ );
69
79
  }
70
80
  if (!facilitatorUrl) {
71
- return errorResponse({ error: "Payment middleware configuration error", message: "Facilitator URL is required for verification" }, 500);
81
+ return errorResponse(
82
+ {
83
+ error: "Payment middleware configuration error",
84
+ message: "Facilitator URL is required for verification"
85
+ },
86
+ 500
87
+ );
72
88
  }
73
89
  const verifyConfig = { url: facilitatorUrl };
74
- const verifyResult = await verifyPayment(payload, requirements, verifyConfig);
90
+ const verifyResult = await verifyPayment(
91
+ payload,
92
+ requirements,
93
+ verifyConfig
94
+ );
75
95
  if (!verifyResult.isValid) {
76
96
  return errorResponse(
77
97
  { error: verifyResult.invalidReason },
@@ -80,12 +100,20 @@ var routeAwarePaymentMiddleware = (perRouteConfig) => {
80
100
  );
81
101
  }
82
102
  const payerAddress = verifyResult.payer ?? payload.payload.authorization.from;
83
- context.payment = { payload, payerAddress, verified: true, route: matchedRoute.pattern };
103
+ context.payment = {
104
+ payload,
105
+ payerAddress,
106
+ verified: true,
107
+ route: matchedRoute.pattern
108
+ };
84
109
  } catch (error) {
85
- return errorResponse({
86
- error: "Payment middleware error",
87
- message: error instanceof Error ? error.message : "Unknown error"
88
- }, 500);
110
+ return errorResponse(
111
+ {
112
+ error: "Payment middleware error",
113
+ message: error instanceof Error ? error.message : "Unknown error"
114
+ },
115
+ 500
116
+ );
89
117
  }
90
118
  }).onAfterHandle(async (context) => {
91
119
  const payment = context.payment;
@@ -102,12 +130,25 @@ var routeAwarePaymentMiddleware = (perRouteConfig) => {
102
130
  return;
103
131
  }
104
132
  if (!routeConfig.facilitatorUrl) {
105
- return errorResponse({ error: "Payment middleware configuration error", message: "Facilitator URL is required for settlement" }, 500);
133
+ return errorResponse(
134
+ {
135
+ error: "Payment middleware configuration error",
136
+ message: "Facilitator URL is required for settlement"
137
+ },
138
+ 500
139
+ );
106
140
  }
107
141
  const settleConfig = { url: routeConfig.facilitatorUrl };
108
- const settlementResult = await settlePayment(payment.payload, routeConfig.requirements, settleConfig);
142
+ const settlementResult = await settlePayment(
143
+ payment.payload,
144
+ routeConfig.requirements,
145
+ settleConfig
146
+ );
109
147
  if (!settlementResult.success) {
110
- return errorResponse({ error: "Settlement failed", details: settlementResult.errorReason }, 502);
148
+ return errorResponse(
149
+ { error: "Settlement failed", details: settlementResult.errorReason },
150
+ 502
151
+ );
111
152
  }
112
153
  return {
113
154
  headers: createSettlementHeaders(settlementResult)
package/dist/index.d.ts CHANGED
@@ -1,20 +1,33 @@
1
+ import { PaymentRequirementsV2, X402PaymentPayload, X402PaymentRequirements } from '@armory-sh/base';
1
2
  import { Elysia } from 'elysia';
2
- import { X402PaymentPayload, X402PaymentRequirements } from '@armory-sh/base';
3
3
  export { PaymentMiddlewareConfigEntry, RouteAwarePaymentInfo, RouteAwarePaymentMiddlewareConfig, routeAwarePaymentMiddleware } from './routes.js';
4
4
 
5
- interface PaymentMiddlewareConfig {
6
- requirements: X402PaymentRequirements;
7
- facilitatorUrl: string;
8
- }
9
- interface PaymentInfo {
10
- payload: X402PaymentPayload;
11
- payerAddress: string;
12
- verified: boolean;
5
+ type NetworkId = string | number;
6
+ type TokenId = string;
7
+ interface PaymentConfig {
8
+ payTo?: string;
9
+ requirements?: PaymentRequirementsV2 | PaymentRequirementsV2[];
10
+ chains?: NetworkId[];
11
+ chain?: NetworkId;
12
+ tokens?: TokenId[];
13
+ token?: TokenId;
14
+ amount?: string;
15
+ maxTimeoutSeconds?: number;
16
+ facilitatorUrl?: string;
17
+ facilitatorUrlByChain?: Record<string, string>;
18
+ facilitatorUrlByToken?: Record<string, Record<string, string>>;
19
+ extensions?: Record<string, unknown>;
13
20
  }
14
- interface PaymentContext {
15
- payment: PaymentInfo;
21
+ interface ResolvedRequirementsConfig {
22
+ requirements: PaymentRequirementsV2[];
23
+ error?: {
24
+ code: string;
25
+ message: string;
26
+ };
16
27
  }
17
- declare const paymentMiddleware: (config: PaymentMiddlewareConfig) => Elysia<"", {
28
+ declare function resolveFacilitatorUrlFromRequirement(config: PaymentConfig, requirement: PaymentRequirementsV2): string | undefined;
29
+ declare function createPaymentRequirements(config: PaymentConfig): ResolvedRequirementsConfig;
30
+ declare const paymentMiddleware: (config: PaymentConfig) => Elysia<"", {
18
31
  decorator: {};
19
32
  store: {};
20
33
  derive: {};
@@ -37,7 +50,12 @@ declare const paymentMiddleware: (config: PaymentMiddlewareConfig) => Elysia<"",
37
50
  response: {};
38
51
  }, {
39
52
  derive: {
40
- readonly payment: PaymentInfo | undefined;
53
+ readonly payment: {
54
+ payload: X402PaymentPayload;
55
+ payerAddress: string;
56
+ verified: boolean;
57
+ selectedRequirement: PaymentRequirementsV2;
58
+ } | undefined;
41
59
  };
42
60
  resolve: {};
43
61
  schema: {};
@@ -49,4 +67,17 @@ declare const paymentMiddleware: (config: PaymentMiddlewareConfig) => Elysia<"",
49
67
  };
50
68
  }>;
51
69
 
52
- export { type PaymentContext, type PaymentInfo, type PaymentMiddlewareConfig, paymentMiddleware };
70
+ interface PaymentMiddlewareConfig {
71
+ requirements: X402PaymentRequirements;
72
+ facilitatorUrl: string;
73
+ }
74
+ interface PaymentInfo {
75
+ payload: X402PaymentPayload;
76
+ payerAddress: string;
77
+ verified: boolean;
78
+ }
79
+ interface PaymentContext {
80
+ payment: PaymentInfo;
81
+ }
82
+
83
+ export { type PaymentConfig, type PaymentContext, type PaymentInfo, type PaymentMiddlewareConfig, type ResolvedRequirementsConfig, createPaymentRequirements, paymentMiddleware, resolveFacilitatorUrlFromRequirement };
package/dist/index.js CHANGED
@@ -1,56 +1,216 @@
1
- export { routeAwarePaymentMiddleware } from './chunk-XZ4WVA3M.js';
1
+ export { routeAwarePaymentMiddleware } from './chunk-L45RQOFN.js';
2
+ import { resolveNetwork, isValidationError, resolveToken, createPaymentRequirements as createPaymentRequirements$1, PAYMENT_SIGNATURE_HEADER, decodePayloadHeader, findRequirementByAccepted, verifyPayment, settlePayment, createSettlementHeaders, createPaymentRequiredHeaders, TOKENS, registerToken, getSupported } from '@armory-sh/base';
2
3
  import { Elysia } from 'elysia';
3
- import { PAYMENT_SIGNATURE_HEADER, createPaymentRequiredHeaders, decodePayloadHeader, verifyPayment, settlePayment, createSettlementHeaders } from '@armory-sh/base';
4
4
 
5
+ var extensionCapabilityCache = /* @__PURE__ */ new Map();
6
+ var EXTENSION_CAPABILITY_TTL_MS = 5 * 60 * 1e3;
7
+ function ensureTokensRegistered() {
8
+ for (const token of Object.values(TOKENS)) {
9
+ try {
10
+ registerToken(token);
11
+ } catch {
12
+ }
13
+ }
14
+ }
15
+ function resolveFacilitatorUrlFromRequirement(config, requirement) {
16
+ const chainId = parseInt(requirement.network.split(":")[1] || "0", 10);
17
+ const assetAddress = requirement.asset.toLowerCase();
18
+ if (config.facilitatorUrlByToken) {
19
+ for (const [chainKey, tokenMap] of Object.entries(
20
+ config.facilitatorUrlByToken
21
+ )) {
22
+ const resolvedChain = resolveNetwork(chainKey);
23
+ if (isValidationError(resolvedChain) || resolvedChain.config.chainId !== chainId) {
24
+ continue;
25
+ }
26
+ for (const [tokenKey, url] of Object.entries(tokenMap)) {
27
+ const resolvedToken = resolveToken(tokenKey, resolvedChain);
28
+ if (!isValidationError(resolvedToken) && resolvedToken.config.contractAddress.toLowerCase() === assetAddress) {
29
+ return url;
30
+ }
31
+ }
32
+ }
33
+ }
34
+ if (config.facilitatorUrlByChain) {
35
+ for (const [chainKey, url] of Object.entries(
36
+ config.facilitatorUrlByChain
37
+ )) {
38
+ const resolvedChain = resolveNetwork(chainKey);
39
+ if (!isValidationError(resolvedChain) && resolvedChain.config.chainId === chainId) {
40
+ return url;
41
+ }
42
+ }
43
+ }
44
+ return config.facilitatorUrl;
45
+ }
46
+ function createPaymentRequirements(config) {
47
+ if (config.requirements) {
48
+ return {
49
+ requirements: Array.isArray(config.requirements) ? config.requirements : [config.requirements]
50
+ };
51
+ }
52
+ if (!config.payTo) {
53
+ return {
54
+ requirements: [],
55
+ error: {
56
+ code: "VALIDATION_FAILED",
57
+ message: "Missing payment configuration: provide payTo or explicit requirements"
58
+ }
59
+ };
60
+ }
61
+ ensureTokensRegistered();
62
+ return createPaymentRequirements$1(config);
63
+ }
64
+ async function resolvePaymentRequiredExtensions(config, requirements) {
65
+ if (!config.extensions) {
66
+ return {};
67
+ }
68
+ let filtered = { ...config.extensions };
69
+ for (const requirement of requirements) {
70
+ const facilitatorUrl = resolveFacilitatorUrlFromRequirement(config, requirement);
71
+ if (!facilitatorUrl) {
72
+ continue;
73
+ }
74
+ const cacheKey = `${facilitatorUrl}|${requirement.network.toLowerCase()}`;
75
+ const now = Date.now();
76
+ let keys = extensionCapabilityCache.get(cacheKey);
77
+ if (!keys || keys.expiresAt <= now) {
78
+ try {
79
+ const supported = await getSupported({ url: facilitatorUrl });
80
+ const nextKeys = /* @__PURE__ */ new Set();
81
+ for (const kind of supported.kinds) {
82
+ if (kind.network.toLowerCase() !== requirement.network.toLowerCase()) {
83
+ continue;
84
+ }
85
+ if (kind.extra && typeof kind.extra === "object") {
86
+ for (const key of Object.keys(kind.extra)) {
87
+ nextKeys.add(key);
88
+ }
89
+ }
90
+ }
91
+ keys = { expiresAt: now + EXTENSION_CAPABILITY_TTL_MS, keys: nextKeys };
92
+ } catch {
93
+ keys = { expiresAt: now + EXTENSION_CAPABILITY_TTL_MS, keys: /* @__PURE__ */ new Set() };
94
+ }
95
+ extensionCapabilityCache.set(cacheKey, keys);
96
+ }
97
+ filtered = Object.fromEntries(
98
+ Object.entries(filtered).filter(([key]) => keys.keys.has(key))
99
+ );
100
+ if (Object.keys(filtered).length === 0) {
101
+ return {};
102
+ }
103
+ }
104
+ return filtered;
105
+ }
5
106
  var errorResponse = (error, status, headers) => new Response(JSON.stringify(error), {
6
107
  status,
7
108
  headers: { "Content-Type": "application/json", ...headers }
8
109
  });
9
110
  var paymentMiddleware = (config) => {
10
- const { requirements, facilitatorUrl } = config;
111
+ const { requirements, error } = createPaymentRequirements(config);
112
+ const resolvePaymentRequiredHeaders = async () => createPaymentRequiredHeaders(requirements, {
113
+ extensions: await resolvePaymentRequiredExtensions(
114
+ config,
115
+ requirements
116
+ )
117
+ });
11
118
  return new Elysia({ name: "armory-payment" }).derive(() => ({
12
119
  payment: void 0
13
120
  })).onBeforeHandle(async (context) => {
121
+ if (error) {
122
+ return errorResponse(
123
+ {
124
+ error: "Payment middleware configuration error",
125
+ details: error.message
126
+ },
127
+ 500
128
+ );
129
+ }
130
+ const primaryRequirement = requirements[0];
131
+ if (!primaryRequirement) {
132
+ return errorResponse(
133
+ {
134
+ error: "Payment middleware configuration error",
135
+ message: "No payment requirements configured"
136
+ },
137
+ 500
138
+ );
139
+ }
140
+ const paymentHeader = context.request.headers.get(
141
+ PAYMENT_SIGNATURE_HEADER
142
+ );
143
+ if (!paymentHeader) {
144
+ const requiredHeaders = await resolvePaymentRequiredHeaders();
145
+ return errorResponse(
146
+ { error: "Payment required", accepts: requirements },
147
+ 402,
148
+ requiredHeaders
149
+ );
150
+ }
151
+ let paymentPayload;
14
152
  try {
15
- const paymentHeader = context.request.headers.get(PAYMENT_SIGNATURE_HEADER);
16
- if (!paymentHeader) {
17
- return errorResponse(
18
- { error: "Payment required", accepts: [requirements] },
19
- 402,
20
- createPaymentRequiredHeaders(requirements)
21
- );
22
- }
23
- let paymentPayload;
24
- try {
25
- paymentPayload = decodePayloadHeader(paymentHeader, {
26
- accepted: requirements
27
- });
28
- } catch (error) {
29
- return errorResponse({
153
+ paymentPayload = decodePayloadHeader(paymentHeader, {
154
+ accepted: primaryRequirement
155
+ });
156
+ } catch (err) {
157
+ return errorResponse(
158
+ {
30
159
  error: "Invalid payment payload",
31
- message: error instanceof Error ? error.message : "Unknown error"
32
- }, 400);
33
- }
34
- if (!facilitatorUrl) {
35
- return errorResponse({ error: "Payment middleware configuration error", message: "Facilitator URL is required for verification" }, 500);
36
- }
37
- const verifyConfig = { url: facilitatorUrl };
38
- const verifyResult = await verifyPayment(paymentPayload, requirements, verifyConfig);
39
- if (!verifyResult.isValid) {
40
- return errorResponse(
41
- { error: verifyResult.invalidReason },
42
- 402,
43
- createPaymentRequiredHeaders(requirements)
44
- );
45
- }
46
- const payerAddress = verifyResult.payer ?? paymentPayload.payload.authorization.from;
47
- context.payment = { payload: paymentPayload, payerAddress, verified: true };
48
- } catch (error) {
49
- return errorResponse({
50
- error: "Payment middleware error",
51
- message: error instanceof Error ? error.message : "Unknown error"
52
- }, 500);
160
+ message: err instanceof Error ? err.message : "Unknown error"
161
+ },
162
+ 400
163
+ );
164
+ }
165
+ const selectedRequirement = findRequirementByAccepted(
166
+ requirements,
167
+ paymentPayload.accepted
168
+ );
169
+ if (!selectedRequirement) {
170
+ return errorResponse(
171
+ {
172
+ error: "Invalid payment payload",
173
+ message: "Accepted requirement is not configured for this endpoint"
174
+ },
175
+ 400
176
+ );
53
177
  }
178
+ const facilitatorUrl = resolveFacilitatorUrlFromRequirement(
179
+ config,
180
+ selectedRequirement
181
+ );
182
+ if (!facilitatorUrl) {
183
+ return errorResponse(
184
+ {
185
+ error: "Payment middleware configuration error",
186
+ message: "Facilitator URL is required for verification"
187
+ },
188
+ 500
189
+ );
190
+ }
191
+ const verifyResult = await verifyPayment(
192
+ paymentPayload,
193
+ selectedRequirement,
194
+ { url: facilitatorUrl }
195
+ );
196
+ if (!verifyResult.isValid) {
197
+ const requiredHeaders = await resolvePaymentRequiredHeaders();
198
+ return errorResponse(
199
+ {
200
+ error: "Payment verification failed",
201
+ message: verifyResult.invalidReason
202
+ },
203
+ 402,
204
+ requiredHeaders
205
+ );
206
+ }
207
+ const payerAddress = verifyResult.payer ?? paymentPayload.payload.authorization.from;
208
+ context.payment = {
209
+ payload: paymentPayload,
210
+ payerAddress,
211
+ verified: true,
212
+ selectedRequirement
213
+ };
54
214
  }).onAfterHandle(async (context) => {
55
215
  const payment = context.payment;
56
216
  if (!payment) {
@@ -60,18 +220,34 @@ var paymentMiddleware = (config) => {
60
220
  if (statusCode >= 400) {
61
221
  return;
62
222
  }
223
+ const facilitatorUrl = resolveFacilitatorUrlFromRequirement(
224
+ config,
225
+ payment.selectedRequirement
226
+ );
63
227
  if (!facilitatorUrl) {
64
- return errorResponse({ error: "Payment middleware configuration error", message: "Facilitator URL is required for settlement" }, 500);
228
+ return errorResponse(
229
+ {
230
+ error: "Payment middleware configuration error",
231
+ message: "Facilitator URL is required for settlement"
232
+ },
233
+ 500
234
+ );
65
235
  }
66
- const settleConfig = { url: facilitatorUrl };
67
- const settlementResult = await settlePayment(payment.payload, requirements, settleConfig);
68
- if (!settlementResult.success) {
69
- return errorResponse({ error: "Settlement failed", details: settlementResult.errorReason }, 502);
236
+ const settleResult = await settlePayment(
237
+ payment.payload,
238
+ payment.selectedRequirement,
239
+ { url: facilitatorUrl }
240
+ );
241
+ if (!settleResult.success) {
242
+ return errorResponse(
243
+ { error: "Settlement failed", details: settleResult.errorReason },
244
+ 502
245
+ );
70
246
  }
71
247
  return {
72
- headers: createSettlementHeaders(settlementResult)
248
+ headers: createSettlementHeaders(settleResult)
73
249
  };
74
250
  });
75
251
  };
76
252
 
77
- export { paymentMiddleware };
253
+ export { createPaymentRequirements, paymentMiddleware, resolveFacilitatorUrlFromRequirement };
package/dist/routes.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Elysia } from 'elysia';
2
1
  import { X402PaymentRequirements, X402PaymentPayload } from '@armory-sh/base';
2
+ import { Elysia } from 'elysia';
3
3
 
4
4
  interface RouteAwarePaymentMiddlewareConfig {
5
5
  route?: string;
package/dist/routes.js CHANGED
@@ -1 +1 @@
1
- export { routeAwarePaymentMiddleware } from './chunk-XZ4WVA3M.js';
1
+ export { routeAwarePaymentMiddleware } from './chunk-L45RQOFN.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@armory-sh/middleware-elysia",
3
- "version": "0.3.26",
3
+ "version": "0.3.28",
4
4
  "license": "MIT",
5
5
  "author": "Sawyer Cutler <sawyer@dirtroad.dev>",
6
6
  "keywords": [
@@ -47,7 +47,7 @@
47
47
  "elysia": "^1"
48
48
  },
49
49
  "dependencies": {
50
- "@armory-sh/base": "0.2.28"
50
+ "@armory-sh/base": "0.2.30"
51
51
  },
52
52
  "devDependencies": {
53
53
  "bun-types": "latest",
@@ -57,6 +57,8 @@
57
57
  },
58
58
  "scripts": {
59
59
  "build": "rm -rf dist && tsup",
60
+ "lint": "bun run build",
61
+ "format": "bun run lint",
60
62
  "test": "bun test"
61
63
  }
62
64
  }