@armory-sh/middleware-bun 0.3.2

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.
@@ -0,0 +1,24 @@
1
+ import { Address, CAIP2ChainId, CAIPAssetId } from '@armory-sh/base';
2
+
3
+ interface FacilitatorConfig {
4
+ url: string;
5
+ createHeaders?: () => Record<string, string>;
6
+ }
7
+ type SettlementMode = "verify" | "settle" | "async";
8
+ type PayToAddress = Address | CAIP2ChainId | CAIPAssetId;
9
+ interface MiddlewareConfig {
10
+ payTo: PayToAddress;
11
+ network: string | number;
12
+ amount: string;
13
+ facilitator?: FacilitatorConfig;
14
+ settlementMode?: SettlementMode;
15
+ }
16
+
17
+ type BunMiddleware = (request: Request) => Promise<Response | null>;
18
+ interface BunMiddlewareConfig extends MiddlewareConfig {
19
+ defaultVersion?: 1 | 2;
20
+ waitForSettlement?: boolean;
21
+ }
22
+ declare const createBunMiddleware: (config: BunMiddlewareConfig) => BunMiddleware;
23
+
24
+ export { type BunMiddleware, type BunMiddlewareConfig, createBunMiddleware };
package/dist/index.js ADDED
@@ -0,0 +1,249 @@
1
+ // src/index.ts
2
+ import { decodePayment, isExactEvmPayload } from "@armory-sh/base";
3
+
4
+ // src/core.ts
5
+ import {
6
+ getNetworkConfig,
7
+ getNetworkByChainId,
8
+ encodePaymentPayload
9
+ } from "@armory-sh/base";
10
+ var getNetworkName = (network) => {
11
+ if (typeof network === "string") return network;
12
+ const net = getNetworkByChainId(network);
13
+ if (!net) throw new Error(`No network found for chainId: ${network}`);
14
+ return net.name.toLowerCase().replace(" mainnet", "").replace(" sepolia", "-sepolia");
15
+ };
16
+ var createV1Requirements = (config, expiry) => {
17
+ const networkName = getNetworkName(config.network);
18
+ const network = getNetworkConfig(networkName);
19
+ if (!network) throw new Error(`Unsupported network: ${networkName}`);
20
+ return {
21
+ amount: config.amount,
22
+ network: networkName,
23
+ contractAddress: network.usdcAddress,
24
+ payTo: config.payTo,
25
+ expiry
26
+ };
27
+ };
28
+ var createV2Requirements = (config, expiry) => {
29
+ const networkName = getNetworkName(config.network);
30
+ const network = getNetworkConfig(networkName);
31
+ if (!network) throw new Error(`Unsupported network: ${networkName}`);
32
+ return {
33
+ amount: config.amount,
34
+ to: config.payTo,
35
+ chainId: network.caip2Id,
36
+ assetId: network.caipAssetId,
37
+ nonce: `${Date.now()}-${crypto.randomUUID()}`,
38
+ expiry
39
+ };
40
+ };
41
+ var createPaymentRequirements = (config, version = 1) => {
42
+ const networkName = getNetworkName(config.network);
43
+ const network = getNetworkConfig(networkName);
44
+ if (!network) throw new Error(`Unsupported network: ${networkName}`);
45
+ const expiry = Math.floor(Date.now() / 1e3) + 3600;
46
+ return version === 1 ? createV1Requirements(config, expiry) : createV2Requirements(config, expiry);
47
+ };
48
+ var findHeaderValue = (headers, name) => {
49
+ const value = headers[name];
50
+ if (typeof value === "string") return value;
51
+ if (Array.isArray(value) && value.length > 0) return value[0];
52
+ const lowerName = name.toLowerCase();
53
+ for (const [key, val] of Object.entries(headers)) {
54
+ if (key.toLowerCase() === lowerName) {
55
+ if (typeof val === "string") return val;
56
+ if (Array.isArray(val) && val.length > 0) return val[0];
57
+ }
58
+ }
59
+ return void 0;
60
+ };
61
+ var parseHeader = (header) => {
62
+ try {
63
+ if (header.startsWith("{")) return JSON.parse(header);
64
+ return JSON.parse(atob(header));
65
+ } catch {
66
+ return null;
67
+ }
68
+ };
69
+ var extractPaymentPayload = (request) => {
70
+ const v1Header = findHeaderValue(request.headers, "X-PAYMENT");
71
+ if (v1Header) return parseHeader(v1Header);
72
+ const v2Header = findHeaderValue(request.headers, "PAYMENT-SIGNATURE");
73
+ if (v2Header) return parseHeader(v2Header);
74
+ return null;
75
+ };
76
+ var postFacilitator = async (url, headers, body) => {
77
+ const response = await fetch(url, {
78
+ method: "POST",
79
+ headers: { "Content-Type": "application/json", ...headers },
80
+ body: JSON.stringify(body)
81
+ });
82
+ if (!response.ok) {
83
+ const error = await response.text();
84
+ throw new Error(error || `Request failed: ${response.status}`);
85
+ }
86
+ return response.json();
87
+ };
88
+ var verifyWithFacilitator = async (request, facilitator) => {
89
+ const payload = extractPaymentPayload(request);
90
+ if (!payload) {
91
+ return { success: false, error: "No payment payload found in request headers" };
92
+ }
93
+ try {
94
+ const url = new URL("/verify", facilitator.url);
95
+ const headers = facilitator.createHeaders?.() ?? {};
96
+ const data = await postFacilitator(url.toString(), headers, { payload, headers: request.headers });
97
+ return {
98
+ success: true,
99
+ payerAddress: data.payerAddress,
100
+ balance: data.balance,
101
+ requiredAmount: data.requiredAmount
102
+ };
103
+ } catch (error) {
104
+ return {
105
+ success: false,
106
+ error: error instanceof Error ? error.message : "Unknown verification error"
107
+ };
108
+ }
109
+ };
110
+ var settleWithFacilitator = async (request, facilitator) => {
111
+ const payload = extractPaymentPayload(request);
112
+ if (!payload) {
113
+ return { success: false, error: "No payment payload found in request headers" };
114
+ }
115
+ try {
116
+ const url = new URL("/settle", facilitator.url);
117
+ const headers = facilitator.createHeaders?.() ?? {};
118
+ const data = await postFacilitator(url.toString(), headers, { payload, headers: request.headers });
119
+ return {
120
+ success: true,
121
+ txHash: data.txHash
122
+ };
123
+ } catch (error) {
124
+ return {
125
+ success: false,
126
+ error: error instanceof Error ? error.message : "Unknown settlement error"
127
+ };
128
+ }
129
+ };
130
+ var createPaymentRequiredHeaders = (requirements, version) => {
131
+ if (version === 1) {
132
+ return { "X-PAYMENT-REQUIRED": encodePaymentPayload(requirements) };
133
+ }
134
+ return { "PAYMENT-REQUIRED": Buffer.from(JSON.stringify(requirements)).toString("base64") };
135
+ };
136
+ var createSettlementHeaders = (response, version) => {
137
+ if (version === 1) {
138
+ const isSuccess2 = "success" in response ? response.success : response.status === "success";
139
+ const txHash2 = "transaction" in response ? response.transaction : response.txHash || "";
140
+ const settlementJson2 = JSON.stringify({
141
+ status: isSuccess2 ? "success" : "failed",
142
+ txHash: txHash2 ?? ""
143
+ });
144
+ return { "X-PAYMENT-RESPONSE": Buffer.from(settlementJson2).toString("base64") };
145
+ }
146
+ const txHash = "transaction" in response ? response.transaction : response.txHash || "";
147
+ const isSuccess = "success" in response ? response.success : response.status === "success";
148
+ const settlementJson = JSON.stringify({
149
+ status: isSuccess ? "success" : "failed",
150
+ txHash: txHash || ""
151
+ });
152
+ return { "PAYMENT-RESPONSE": Buffer.from(settlementJson).toString("base64") };
153
+ };
154
+
155
+ // src/simple.ts
156
+ import {
157
+ resolveNetwork,
158
+ resolveToken,
159
+ validateAcceptConfig,
160
+ isValidationError
161
+ } from "@armory-sh/base";
162
+
163
+ // src/index.ts
164
+ var parsePaymentHeader = async (request) => {
165
+ const x402Sig = request.headers.get("X402-PAYMENT");
166
+ if (x402Sig) {
167
+ try {
168
+ const payload = decodePayment(x402Sig);
169
+ if (isExactEvmPayload(payload)) {
170
+ return { payload, version: 2, payerAddress: payload.payload.authorization.from };
171
+ }
172
+ } catch {
173
+ }
174
+ }
175
+ return null;
176
+ };
177
+ var toHttpRequest = (request) => {
178
+ const headers = {};
179
+ request.headers.forEach((v, k) => {
180
+ headers[k] = v;
181
+ });
182
+ return { headers, method: request.method, url: request.url };
183
+ };
184
+ var errorResponse = (error, status, headers) => new Response(JSON.stringify({ error }), {
185
+ status,
186
+ headers: { "Content-Type": "application/json", ...headers }
187
+ });
188
+ var createSettlementResponse = (success, txHash) => ({
189
+ success,
190
+ transaction: txHash ?? "",
191
+ errorReason: success ? void 0 : "Settlement failed",
192
+ network: "base"
193
+ });
194
+ var successResponse = (payerAddress, version, settlement) => {
195
+ const isSuccess = settlement?.success;
196
+ const txHash = settlement?.transaction;
197
+ return new Response(
198
+ JSON.stringify({
199
+ verified: true,
200
+ payerAddress,
201
+ version,
202
+ settlement: settlement ? { success: isSuccess, txHash } : void 0
203
+ }),
204
+ {
205
+ status: 200,
206
+ headers: {
207
+ "Content-Type": "application/json",
208
+ "X-Payment-Verified": "true",
209
+ "X-Payer-Address": payerAddress,
210
+ ...settlement ? createSettlementHeaders(settlement, version) : {}
211
+ }
212
+ }
213
+ );
214
+ };
215
+ var createBunMiddleware = (config) => {
216
+ const { facilitator, settlementMode = "verify", defaultVersion = 2, waitForSettlement = false } = config;
217
+ const requirementsV1 = createPaymentRequirements(config, 1);
218
+ const requirementsV2 = createPaymentRequirements(config, 2);
219
+ return async (request) => {
220
+ const paymentResult = await parsePaymentHeader(request);
221
+ if (!paymentResult) {
222
+ const requirements = defaultVersion === 1 ? requirementsV1 : requirementsV2;
223
+ return errorResponse("Payment required", 402, createPaymentRequiredHeaders(requirements, defaultVersion));
224
+ }
225
+ const { version, payerAddress } = paymentResult;
226
+ if (facilitator) {
227
+ const verifyResult = await verifyWithFacilitator(toHttpRequest(request), facilitator);
228
+ if (!verifyResult.success) {
229
+ const requirements = version === 1 ? requirementsV1 : requirementsV2;
230
+ return errorResponse(`Payment verification failed: ${verifyResult.error}`, 402, createPaymentRequiredHeaders(requirements, version));
231
+ }
232
+ }
233
+ if (settlementMode === "settle" && facilitator) {
234
+ const settle = async () => {
235
+ const result = await settleWithFacilitator(toHttpRequest(request), facilitator);
236
+ return result.success ? successResponse(payerAddress, version, createSettlementResponse(true, result.txHash)) : errorResponse(result.error ?? "Settlement failed", 400);
237
+ };
238
+ if (waitForSettlement) {
239
+ return await settle();
240
+ }
241
+ settle().catch(console.error);
242
+ return successResponse(payerAddress, version);
243
+ }
244
+ return successResponse(payerAddress, version);
245
+ };
246
+ };
247
+ export {
248
+ createBunMiddleware
249
+ };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@armory-sh/middleware-bun",
3
+ "version": "0.3.2",
4
+ "license": "MIT",
5
+ "author": "Sawyer Cutler <sawyer@dirtroad.dev>",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/thegreataxios/armory.git",
24
+ "directory": "packages/middleware-bun"
25
+ },
26
+ "dependencies": {
27
+ "@armory-sh/base": "0.2.4",
28
+ "@armory-sh/facilitator": "0.2.4"
29
+ },
30
+ "devDependencies": {
31
+ "bun-types": "latest",
32
+ "typescript": "5.9.3"
33
+ },
34
+ "scripts": {
35
+ "build": "tsup",
36
+ "test": "bun test"
37
+ }
38
+ }