@armory-sh/middleware-hono 0.3.29 → 0.3.30

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
@@ -14,24 +14,47 @@ bun add @armory-sh/middleware-hono
14
14
 
15
15
  Armory enables HTTP API payments via EIP-3009 `transferWithAuthorization`. Accept payments from any x402-compatible client—Coinbase SDK, Armory SDK, or your own implementation.
16
16
 
17
- ## Key Exports
17
+ ## API Reference
18
+
19
+ ### Middleware
18
20
 
19
21
  ```typescript
20
22
  import {
21
- // Middleware
22
23
  paymentMiddleware,
23
24
  routeAwarePaymentMiddleware,
24
25
 
25
- // Requirements
26
- createPaymentRequirements,
27
-
28
26
  // Types
29
27
  type PaymentConfig,
30
28
  type RouteAwarePaymentConfig,
31
- type HonoPaymentContext,
29
+ type AugmentedContext,
32
30
  } from '@armory-sh/middleware-hono';
33
31
  ```
34
32
 
33
+ ### Payment Requirements
34
+
35
+ ```typescript
36
+ import {
37
+ createPaymentRequirements,
38
+
39
+ // Types
40
+ type ResolvedRequirementsConfig,
41
+ } from '@armory-sh/middleware-hono';
42
+ ```
43
+
44
+ ### Extensions
45
+
46
+ ```typescript
47
+ import {
48
+ buildExtensions,
49
+ extractExtension,
50
+
51
+ // Types
52
+ type ExtensionConfig,
53
+ } from '@armory-sh/middleware-hono';
54
+ ```
55
+
56
+ ---
57
+
35
58
  ## Quick Start
36
59
 
37
60
  ### Basic Middleware
@@ -168,6 +191,7 @@ interface RouteAwarePaymentConfig extends PaymentConfig {
168
191
  | Base Sepolia | 84532 |
169
192
  | SKALE Base | 1187947933 |
170
193
  | SKALE Base Sepolia | 324705682 |
194
+ | Ethereum Sepolia | 11155111 |
171
195
 
172
196
  ## License
173
197
 
package/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@ interface PaymentConfig {
23
23
  facilitatorUrl?: string;
24
24
  facilitatorUrlByChain?: Record<string, string>;
25
25
  facilitatorUrlByToken?: Record<string, Record<string, string>>;
26
+ extensions?: Record<string, unknown>;
26
27
  }
27
28
  interface ResolvedRequirementsConfig {
28
29
  requirements: PaymentRequirementsV2[];
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { buildExtensions, extractExtension } from './chunk-XYM6YXAQ.js';
2
- import { matchRoute, V2_HEADERS, safeBase64Encode, decodePayloadHeader, findRequirementByAccepted, verifyPayment, createPaymentRequiredHeaders, settlePayment, createSettlementHeaders, resolveNetwork, isValidationError, resolveToken, createPaymentRequirements as createPaymentRequirements$1, PAYMENT_SIGNATURE_HEADER, PAYMENT_REQUIRED_HEADER, validateRouteConfig, TOKENS, registerToken } from '@armory-sh/base';
2
+ import { matchRoute, V2_HEADERS, safeBase64Encode, decodePayloadHeader, findRequirementByAccepted, verifyPayment, createPaymentRequiredHeaders, settlePayment, createSettlementHeaders, resolveNetwork, isValidationError, resolveToken, createPaymentRequirements as createPaymentRequirements$1, PAYMENT_SIGNATURE_HEADER, PAYMENT_REQUIRED_HEADER, validateRouteConfig, TOKENS, registerToken, getSupported } from '@armory-sh/base';
3
3
 
4
4
  var resolveRouteConfig = (config) => {
5
5
  const validationError = validateRouteConfig(config);
@@ -155,6 +155,8 @@ var routeAwarePaymentMiddleware = (config) => {
155
155
  };
156
156
 
157
157
  // src/index.ts
158
+ var extensionCapabilityCache = /* @__PURE__ */ new Map();
159
+ var EXTENSION_CAPABILITY_TTL_MS = 5 * 60 * 1e3;
158
160
  function ensureTokensRegistered() {
159
161
  for (const token of Object.values(TOKENS)) {
160
162
  try {
@@ -171,17 +173,13 @@ function resolveFacilitatorUrlFromRequirement(config, requirement) {
171
173
  config.facilitatorUrlByToken
172
174
  )) {
173
175
  const resolvedChain = resolveNetwork(chainKey);
174
- if (!isValidationError(resolvedChain) && resolvedChain.config.chainId === chainId) {
175
- for (const [, url] of Object.entries(tokenMap)) {
176
- const network = resolveNetwork(chainKey);
177
- if (!isValidationError(network)) {
178
- for (const tokenKey of Object.keys(tokenMap)) {
179
- const resolvedToken = resolveToken(tokenKey, network);
180
- if (!isValidationError(resolvedToken) && resolvedToken.config.contractAddress.toLowerCase() === assetAddress) {
181
- return url;
182
- }
183
- }
184
- }
176
+ if (isValidationError(resolvedChain) || resolvedChain.config.chainId !== chainId) {
177
+ continue;
178
+ }
179
+ for (const [tokenKey, url] of Object.entries(tokenMap)) {
180
+ const resolvedToken = resolveToken(tokenKey, resolvedChain);
181
+ if (!isValidationError(resolvedToken) && resolvedToken.config.contractAddress.toLowerCase() === assetAddress) {
182
+ return url;
185
183
  }
186
184
  }
187
185
  }
@@ -216,8 +214,56 @@ function createPaymentRequirements(config) {
216
214
  ensureTokensRegistered();
217
215
  return createPaymentRequirements$1(config);
218
216
  }
217
+ async function resolvePaymentRequiredExtensions(config, requirements) {
218
+ if (!config.extensions) {
219
+ return {};
220
+ }
221
+ let filtered = { ...config.extensions };
222
+ for (const requirement of requirements) {
223
+ const facilitatorUrl = resolveFacilitatorUrlFromRequirement(config, requirement);
224
+ if (!facilitatorUrl) {
225
+ continue;
226
+ }
227
+ const cacheKey = `${facilitatorUrl}|${requirement.network.toLowerCase()}`;
228
+ const now = Date.now();
229
+ let keys = extensionCapabilityCache.get(cacheKey);
230
+ if (!keys || keys.expiresAt <= now) {
231
+ try {
232
+ const supported = await getSupported({ url: facilitatorUrl });
233
+ const nextKeys = /* @__PURE__ */ new Set();
234
+ for (const kind of supported.kinds) {
235
+ if (kind.network.toLowerCase() !== requirement.network.toLowerCase()) {
236
+ continue;
237
+ }
238
+ if (kind.extra && typeof kind.extra === "object") {
239
+ for (const key of Object.keys(kind.extra)) {
240
+ nextKeys.add(key);
241
+ }
242
+ }
243
+ }
244
+ keys = { expiresAt: now + EXTENSION_CAPABILITY_TTL_MS, keys: nextKeys };
245
+ } catch {
246
+ keys = { expiresAt: now + EXTENSION_CAPABILITY_TTL_MS, keys: /* @__PURE__ */ new Set() };
247
+ }
248
+ extensionCapabilityCache.set(cacheKey, keys);
249
+ }
250
+ filtered = Object.fromEntries(
251
+ Object.entries(filtered).filter(([key]) => keys.keys.has(key))
252
+ );
253
+ if (Object.keys(filtered).length === 0) {
254
+ return {};
255
+ }
256
+ }
257
+ return filtered;
258
+ }
219
259
  function paymentMiddleware(config) {
220
260
  const { requirements, error } = createPaymentRequirements(config);
261
+ const resolvePaymentRequiredHeader = async () => createPaymentRequiredHeaders(requirements, {
262
+ extensions: await resolvePaymentRequiredExtensions(
263
+ config,
264
+ requirements
265
+ )
266
+ })[PAYMENT_REQUIRED_HEADER];
221
267
  return async (c, next) => {
222
268
  if (error) {
223
269
  c.status(500);
@@ -250,7 +296,7 @@ function paymentMiddleware(config) {
250
296
  c.status(402);
251
297
  c.header(
252
298
  PAYMENT_REQUIRED_HEADER,
253
- safeBase64Encode(JSON.stringify(paymentRequired))
299
+ await resolvePaymentRequiredHeader() ?? safeBase64Encode(JSON.stringify(paymentRequired))
254
300
  );
255
301
  return c.json({
256
302
  error: "Payment required",
@@ -297,7 +343,7 @@ function paymentMiddleware(config) {
297
343
  c.status(402);
298
344
  c.header(
299
345
  PAYMENT_REQUIRED_HEADER,
300
- createPaymentRequiredHeaders(requirements)[PAYMENT_REQUIRED_HEADER]
346
+ await resolvePaymentRequiredHeader()
301
347
  );
302
348
  return c.json({
303
349
  error: "Payment verification failed",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@armory-sh/middleware-hono",
3
- "version": "0.3.29",
3
+ "version": "0.3.30",
4
4
  "license": "MIT",
5
5
  "author": "Sawyer Cutler <sawyer@dirtroad.dev>",
6
6
  "keywords": [
@@ -52,8 +52,8 @@
52
52
  "hono": "^4"
53
53
  },
54
54
  "dependencies": {
55
- "@armory-sh/base": "0.2.29",
56
- "@armory-sh/extensions": "0.1.10"
55
+ "@armory-sh/base": "0.2.30",
56
+ "@armory-sh/extensions": "0.1.11"
57
57
  },
58
58
  "devDependencies": {
59
59
  "bun-types": "latest",