Files
2026-05-27 23:29:53 +01:00

6.6 KiB

Tasks: enterprise-api-gateway

Change ID: enterprise-api-gateway Package: @suaveplan/api-gateway Target path: api/framework/api-gateway Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/logger, @suaveplan/retry, @suaveplan/rate-limiter, @suaveplan/events, @suaveplan/api


0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create api/framework/api-gateway/ with all subdirs per layout.
  • package.json v0.0.0, "sideEffects": false, subpath exports per feature module.
  • tsconfig.json extending library-node.json.
  • vitest.config.ts via createTestConfig().
  • Scripts: lint, typecheck, test, test:coverage, build, docs:check, bench.

2. Types, schemas, errors

  • src/types/common.ts — all interfaces and type aliases listed in proposal.
  • src/types/common.md (≥200 words, API reference, both examples).
  • src/schemas/config.schema.ts, route.schema.ts, circuit-breaker.schema.ts — Zod schemas; createGateway parses with GatewayConfigSchema.parse(config).
  • src/errors/gateway-errors.ts — 6-code union, metadata, registerGatewayErrorCodes(), GatewayError re-export.
  • gateway-errors.test.ts and gateway-errors.md.

3. Router

  • Router: path-pattern matching (static + :param + wildcard *), method matching, ordered by specificity (static > param > wildcard).
  • match(req) returns first match or null.
  • router.test.ts: static match, param extraction, wildcard, method mismatch returns null, method * matches any.
  • router.md.

4. LoadBalancer

  • Strategies: round-robin (stateful counter), random, weighted (weight-proportional selection), consistent-hash (FNV-1a on a configurable key).
  • Skip targets with health === 'unhealthy'.
  • balancer.test.ts: each strategy, all-unhealthy throws GatewayError(GATEWAY_NO_ROUTE).
  • balancer.md.

5. TransformerPipeline + helpers

  • TransformerPipeline: ordered list of RequestTransformer | ResponseTransformer; run applies request transformers, runResponse applies response transformers; catch on transformer throws GatewayError(GATEWAY_TRANSFORM_FAILED).
  • Implement all six helper factories listed in proposal.
  • transformer.test.ts: each helper, pipeline ordering, error wrapping.
  • transformer.md.

6. GatewayCircuitBreaker

  • States: closed → open (after failureThreshold consecutive failures) → half-open (after resetTimeout ms) → closed (after successThreshold successes in half-open).
  • record(success) transitions state; emits gateway:circuit:state event on @suaveplan/events bus when state changes.
  • Injectable Clock for timeout tracking.
  • circuit-breaker.test.ts: full state machine cycle; half-open success restores, failure re-opens; open throws GatewayError(GATEWAY_CIRCUIT_OPEN).
  • circuit-breaker.md.

7. HealthChecker

  • HealthChecker interface: start(), stop(), getHealth(targetId): TargetHealth.
  • Default interval-based implementation: probe via configurable probe: (target: Target) => Promise<boolean>; update target.health; emit gateway:target:health event on state change.
  • health.test.ts: probe called on interval, healthy → unhealthy transition emits event, stop() clears interval.
  • health.md.

8. Gateway

  • createGateway(config): parse config via Zod, construct router + balancer + circuit-breaker + transformer pipeline.
  • handle(request):
    1. Rate-limit check (if limiter injected) → 429 via GatewayError.
    2. Router.match → null throws GatewayError(GATEWAY_NO_ROUTE).
    3. Circuit-breaker open → throws GatewayError(GATEWAY_CIRCUIT_OPEN).
    4. Load-balance to target.
    5. Apply request transformers.
    6. Call config.fetcher with retry policy (@suaveplan/retry).
    7. Apply response transformers.
    8. Record circuit-breaker outcome.
    9. Return GatewayResult.
  • Wrap entire flow in OTel span gateway.handle.
  • gateway.test.ts (≤500 lines): happy path, no-route, circuit open, upstream timeout, retry succeeds on second attempt, transformer rewrite applied.
  • gateway.md.

9. Hono adapter

  • gatewayMiddleware(gateway: IGateway): MiddlewareHandler — converts Hono Request → GatewayRequest, calls gateway.handle, maps GatewayResult → Hono Response.
  • hono-adapter.test.ts: 200 forwarded, 404 no-route, circuit-open → 503.
  • hono-adapter.md.

10. Property-based tests (tests/property.test.ts)

  • Router invariant: for any route table + request, match returns either a consistent route or null (never throws); matched route's method contains the request method.
  • Balancer fairness: round-robin distributes N requests across K healthy targets with equal ±1 frequency.
  • Circuit-breaker monotonicity: for any sequence of boolean outcomes, the state sequence never transitions open → closed directly (always through half-open).
  • Timeout 30 000 ms on each it().

11. Benchmarks (src/__benchmarks__/gateway.bench.ts)

  • gateway_handle_passthrough: handle() with 1 target, no transforms, in-process no-op fetcher.
  • gateway_handle_5_transforms: same but with 5 transformers.

12. Integration tests (tests/integration.test.ts)

  • Wire a real Hono upstream server; Gateway.handle proxies to it; assert response forwarded correctly.
  • Circuit-breaker trips after threshold; half-open probe restores.

13. Docs and publish

  • All co-located .md files (≥200 words, both examples, full API reference).
  • Root README.md.
  • bunx turbo lint typecheck test --filter=@suaveplan/api-gateway — green.
  • bunx turbo test:coverage --filter=@suaveplan/api-gateway — 100%.
  • bun scripts/publish.ts --package @suaveplan/api-gateway.
  • Archive → openspec/archive/2026-04-24-enterprise-api-gateway/.