Skip to content

feat: add new hono adapter (and some niceties based on that)#6

Open
ultraviolet10 wants to merge 1 commit intoVanshSahay:mainfrom
ultraviolet10:main
Open

feat: add new hono adapter (and some niceties based on that)#6
ultraviolet10 wants to merge 1 commit intoVanshSahay:mainfrom
ultraviolet10:main

Conversation

@ultraviolet10
Copy link
Copy Markdown

Add Hono Framework Adapter with Architectural Refactoring

Summary

This PR introduces first-class Hono framework support alongside Express, while refactoring the adapter architecture for better extensibility, type safety, and memory efficiency.


🚀 New Feature: Hono Adapter

Added complete Hono framework support as a peer dependency, enabling users to integrate x402 payment flows into Hono-based applications.

Usage:

// Facilitator adapter (single node)
import { createHonoAdapter } from "x402-open/hono";
app.route("/facilitator", createHonoAdapter(facilitator));

// Gateway adapter (multi-node routing)
import { createHonoGatewayAdapter } from "x402-open/hono";
app.route("/facilitator", createHonoGatewayAdapter({ httpPeers: [...] }));

New entry point: x402-open/hono — separate from the main entry point to allow tree-shaking when Hono isn't used.


🏗️ Architectural Improvements

Shared Gateway Core (src/gateway/core.ts)

Extracted all gateway logic into a framework-agnostic core module. Both Express and Hono gateway adapters now consume the same underlying implementation:

  • StickyRouter — Maintains payer/header → peer mappings with TTL for sticky routing
  • PeerRegistry — Tracks dynamically registered peers with heartbeat-based TTL
  • postJson<T>() — Generic typed HTTP client with timeout support
  • normalizeUrl() — Consistent URL trailing-slash handling
  • aggregateSupportedKinds() — Aggregates payment kinds from multiple peers

This design allows adding new framework adapters (Fastify, Koa, etc.) by implementing thin wrappers around the core.

Type Definitions (src/gateway/types.ts)

New centralized type definitions with proper x402 type integration:

interface ForwardBody {
  paymentPayload?: PartialPaymentPayload;
  paymentRequirements?: PaymentRequirements;
  paymentHeader?: string; // legacy format support
}

interface StickyEntry { peer: string; expiresAt: number; }
interface RegisteredPeer { url: string; kinds?: SupportedPaymentKind[]; lastSeenMs: number; }
interface PeerResponse<T = unknown> { status: number; body: T; }

Memory Management

Added automatic cleanup for TTL-based caches to prevent memory leaks in long-running processes:

class StickyRouter {
  constructor(autoCleanup = true) // starts 30s cleanup interval
  cleanup(): void    // manual trigger
  destroy(): void    // stop timer, clear all state
  get size()         // monitoring: { payers: number, headers: number }
}

Both StickyRouter and PeerRegistry use .unref() on timers to allow clean process exit.

Shared Error Handler (src/adapters/shared/errorHandler.ts)

Extracted duplicated error formatting into a shared utility:

export function formatError(error: unknown): FormattedError {
  return {
    error: "Internal server error",
    message: error instanceof Error ? error.message : "Unknown error",
  };
}

Used by both expressAdapter.ts and honoAdapter.ts.


📁 New Files

File Purpose
src/hono.ts Hono entry point, exports createHonoAdapter and createHonoGatewayAdapter
src/adapters/honoAdapter.ts Hono adapter for single facilitator node
src/adapters/honoGateway.ts Hono adapter for multi-node gateway routing
src/gateway/core.ts Framework-agnostic gateway logic (sticky routing, peer registry, helpers)
src/gateway/types.ts Typed interfaces for gateway module
src/adapters/shared/errorHandler.ts Shared error formatting utility
test/cleanup.test.ts Unit tests for StickyRouter and PeerRegistry cleanup
test/e2e.hono.test.ts E2E tests for Hono gateway with Express backend nodes

📦 Package Bundling

Dual Entry Points

{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js"
    },
    "./hono": {
      "types": "./dist/hono.d.ts",
      "import": "./dist/hono.js"
    }
  }
}
  • Main entry (x402-open) — Express adapter, HTTP gateway, facilitator, registrar
  • Hono entry (x402-open/hono) — Hono-specific adapters

Peer Dependencies

{
  "peerDependencies": {
    "express": ">=4",
    "hono": ">=4"
  },
  "peerDependenciesMeta": {
    "express": { "optional": true },
    "hono": { "optional": true }
  }
}

Both frameworks are optional — users only install what they need.

ESM Compliance

  • All relative imports use .js extensions for Node.js ESM resolution
  • Verified with direct node --input-type=module imports

Dependency Cleanup

  • Removed duplicate hono from devDependencies (already in peerDependencies)

🧪 Testing

New Test Files

test/cleanup.test.ts — 9 unit tests for memory management:

  • StickyRouter: removes expired entries, retains valid entries, destroy() clears state
  • PeerRegistry: removes stale peers, retains recent peers, heartbeat refresh works

test/e2e.hono.test.ts — 8 E2E tests:

  • Hono gateway routing to Express backend nodes
  • Aggregated /supported endpoint
  • Sticky routing for verify → settle flows
  • Dynamic peer registration

Test Results

 ✓ test/cleanup.test.ts (9 tests)
 ✓ test/e2e.hono.test.ts (8 tests)
 ✓ test/e2e.gateway.test.ts (2 tests)

 Test Files  3 passed (3)
      Tests  19 passed (19)

Verification Commands

# Build
pnpm build

# Type check
pnpm tsc --noEmit

# Tests
pnpm test

# ESM resolution
node --input-type=module -e "
import { Facilitator } from './dist/index.js';
import { createHonoAdapter } from './dist/hono.js';
console.log('OK');
"

🔧 Type Safety Improvements

  • Replaced any types with proper x402 imports (PaymentPayload, PaymentRequirements, SupportedPaymentKind)
  • Changed catch (e: any) to catch (e: unknown) with proper error extraction
  • Generic postJson<T>() and PeerResponse<T> for typed peer responses
  • Typed request bodies in gateway /register endpoints

File Changes Summary

File Change
src/gateway/core.ts New — shared gateway logic
src/gateway/types.ts New — type definitions
src/hono.ts New — Hono entry point
src/adapters/honoAdapter.ts New — Hono facilitator adapter
src/adapters/honoGateway.ts New — Hono gateway adapter
src/adapters/shared/errorHandler.ts New — shared error formatting
src/adapters/expressAdapter.ts Moved from src/, uses shared error handler
src/httpGateway.ts Refactored — now uses gateway/core.ts
src/registrar.ts Improved — proper SupportedPaymentKind[] typing
src/facilitator.ts Cleanup — removed empty lines, improved type casts
src/index.ts Updated — ESM import extensions
package.json Updated — dual exports, cleaned dependencies
test/cleanup.test.ts New — cleanup unit tests
test/e2e.hono.test.ts New — Hono E2E tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant