@tanakayuto/intmax402-hono 0.1.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 ADDED
@@ -0,0 +1,95 @@
1
+ # @tanakayuto/intmax402-hono
2
+
3
+ [Hono](https://hono.dev/) middleware for the INTMAX402 protocol — HTTP 402 Payment Required / identity verification via INTMAX zkRollup.
4
+
5
+ ## Features
6
+
7
+ - 🦄 **Hono-native** — typed middleware with `MiddlewareHandler<Env>`
8
+ - ⚡ **Edge-ready** — works on Cloudflare Workers, Deno Deploy, Bun, and Node.js
9
+ - 🔐 **Identity & Payment modes** — same config as Express adapter
10
+ - 🧠 **AI-agent friendly** — designed for autonomous agent-to-agent payments
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @tanakayuto/intmax402-hono hono
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```typescript
21
+ import { Hono } from "hono"
22
+ import { intmax402, Intmax402Env } from "@tanakayuto/intmax402-hono"
23
+
24
+ const app = new Hono<Intmax402Env>()
25
+
26
+ // Free route
27
+ app.get("/free", (c) => c.json({ message: "free access" }))
28
+
29
+ // Identity-gated route
30
+ app.get(
31
+ "/premium",
32
+ intmax402({ mode: "identity", secret: process.env.INTMAX402_SECRET! }),
33
+ (c) => c.json({
34
+ message: "verified",
35
+ address: c.get("intmax402").address,
36
+ })
37
+ )
38
+
39
+ // Payment-gated route
40
+ app.get(
41
+ "/paid",
42
+ intmax402({
43
+ mode: "payment",
44
+ secret: process.env.INTMAX402_SECRET!,
45
+ serverAddress: process.env.SERVER_ADDRESS!,
46
+ amount: "1.0",
47
+ chainId: 137,
48
+ }),
49
+ (c) => c.json({ message: "paid content" })
50
+ )
51
+
52
+ export default app
53
+ ```
54
+
55
+ ## Hono vs Express
56
+
57
+ | Feature | Express | Hono |
58
+ |---|---|---|
59
+ | Runtime | Node.js only | Node / Workers / Deno / Bun |
60
+ | Typing | `req.intmax402` (augmented) | `c.get("intmax402")` (typed via `Env`) |
61
+ | Edge support | ❌ | ✅ Cloudflare Workers ready |
62
+ | Bundle size | Heavy | Ultralight |
63
+
64
+ ### Typed Context
65
+
66
+ Hono uses a generic `Env` type for context variables. Import `Intmax402Env` and pass it to your `Hono` instance:
67
+
68
+ ```typescript
69
+ import { Intmax402Env } from "@tanakayuto/intmax402-hono"
70
+
71
+ const app = new Hono<Intmax402Env>()
72
+ ```
73
+
74
+ After a successful auth middleware call, `c.get("intmax402")` returns:
75
+
76
+ ```typescript
77
+ {
78
+ address: string // verified Ethereum address
79
+ verified: boolean // always true if middleware passed
80
+ txHash?: string // present in payment mode
81
+ }
82
+ ```
83
+
84
+ ## Cloudflare Workers
85
+
86
+ No changes needed for Workers — Hono's fetch handler works natively:
87
+
88
+ ```typescript
89
+ // worker.ts
90
+ export default app
91
+ ```
92
+
93
+ ## License
94
+
95
+ MIT
@@ -0,0 +1,2 @@
1
+ export { intmax402 } from "./middleware";
2
+ export type { Intmax402Env } from "./middleware";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.intmax402 = void 0;
4
+ var middleware_1 = require("./middleware");
5
+ Object.defineProperty(exports, "intmax402", { enumerable: true, get: function () { return middleware_1.intmax402; } });
@@ -0,0 +1,12 @@
1
+ import { MiddlewareHandler } from "hono";
2
+ import { INTMAX402Config } from "@tanakayuto/intmax402-core";
3
+ export type Intmax402Env = {
4
+ Variables: {
5
+ intmax402: {
6
+ address: string;
7
+ verified: boolean;
8
+ txHash?: string;
9
+ };
10
+ };
11
+ };
12
+ export declare function intmax402(config: INTMAX402Config): MiddlewareHandler<Intmax402Env>;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.intmax402 = intmax402;
4
+ const intmax402_core_1 = require("@tanakayuto/intmax402-core");
5
+ const crypto_1 = require("@tanakayuto/intmax402-express/dist/crypto");
6
+ const verify_payment_1 = require("@tanakayuto/intmax402-express/dist/verify-payment");
7
+ function intmax402(config) {
8
+ return async (c, next) => {
9
+ const url = new URL(c.req.url);
10
+ const authHeader = c.req.header("authorization");
11
+ const ip = c.req.header("x-forwarded-for")?.split(",")[0]?.trim()
12
+ ?? c.req.header("x-real-ip")
13
+ ?? "unknown";
14
+ if (!authHeader) {
15
+ const nonce = (0, intmax402_core_1.generateNonce)(config.secret, ip, url.pathname, config.bindIp ?? false);
16
+ const statusCode = config.mode === "payment" ? 402 : 401;
17
+ return c.json({
18
+ error: config.mode === "payment" ? "Payment Required" : "Unauthorized",
19
+ protocol: "INTMAX402",
20
+ mode: config.mode,
21
+ }, statusCode, {
22
+ "WWW-Authenticate": buildWWWAuthenticate(nonce, config),
23
+ });
24
+ }
25
+ const credential = (0, intmax402_core_1.parseAuthorization)(authHeader);
26
+ if (!credential)
27
+ return c.json({ error: "Invalid authorization header" }, 401);
28
+ if (!(0, intmax402_core_1.verifyNonce)(credential.nonce, config.secret, ip, url.pathname, config.bindIp ?? false)) {
29
+ return c.json({ error: "Invalid or expired nonce" }, 401);
30
+ }
31
+ if (config.allowList?.length) {
32
+ if (!config.allowList.includes(credential.address.toLowerCase())) {
33
+ return c.json({ error: "Address not in allow list" }, 403);
34
+ }
35
+ }
36
+ const isValidSig = (0, crypto_1.verifySignature)(credential.signature, credential.nonce, credential.address);
37
+ if (!isValidSig)
38
+ return c.json({ error: "Invalid signature" }, 401);
39
+ if (config.mode === "payment") {
40
+ if (!credential.txHash)
41
+ return c.json({ error: "Payment transaction hash required" }, 402);
42
+ if (!config.serverAddress || !config.amount)
43
+ return c.json({ error: "Server misconfigured" }, 500);
44
+ const result = await (0, verify_payment_1.verifyPayment)(credential.txHash, config.amount, config.serverAddress);
45
+ if (!result.valid)
46
+ return c.json({ error: result.error ?? "Payment verification failed" }, 402);
47
+ }
48
+ c.set("intmax402", { address: credential.address, verified: true, txHash: credential.txHash });
49
+ await next();
50
+ };
51
+ }
52
+ function buildWWWAuthenticate(nonce, config) {
53
+ let header = `INTMAX402 realm="intmax402", nonce="${nonce}", mode="${config.mode}"`;
54
+ if (config.serverAddress)
55
+ header += `, serverAddress="${config.serverAddress}"`;
56
+ if (config.amount)
57
+ header += `, amount="${config.amount}"`;
58
+ if (config.tokenAddress)
59
+ header += `, tokenAddress="${config.tokenAddress}"`;
60
+ if (config.chainId)
61
+ header += `, chainId="${config.chainId}"`;
62
+ return header;
63
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@tanakayuto/intmax402-hono",
3
+ "version": "0.1.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "files": [
7
+ "dist",
8
+ "README.md"
9
+ ],
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/zaq2989/intmax402"
16
+ },
17
+ "keywords": [
18
+ "intmax",
19
+ "http-402",
20
+ "payment",
21
+ "hono",
22
+ "cloudflare-workers",
23
+ "ai-agent"
24
+ ],
25
+ "license": "MIT",
26
+ "dependencies": {
27
+ "@tanakayuto/intmax402-core": "0.2.2",
28
+ "@tanakayuto/intmax402-express": "0.2.2",
29
+ "ethers": "^6.0.0"
30
+ },
31
+ "peerDependencies": {
32
+ "hono": "^4.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "typescript": "^5.4.0",
36
+ "@types/node": "^20.0.0",
37
+ "hono": "^4.0.0"
38
+ },
39
+ "scripts": {
40
+ "build": "tsc",
41
+ "clean": "rm -rf dist"
42
+ }
43
+ }