/** * ServiceProxy v2 * * Auto-generates proxy clients with: * - Pluggable routing strategies (round-robin, hash, least-pending) * - Interceptor chains (deadline, logging, circuit breaker, retry) * - Pending request tracking (for least-pending routing) * - Contract-based method validation */ import http from "node:http"; import type { ForgeContext } from "../core/ForgeContext.js"; import { type RpcConfig } from "../core/RpcConfig.js"; import type { ServiceClassLike, ServiceContract } from "./index.js"; interface RetryConfig { maxAttempts?: number; baseDelayMs?: number; maxDelayMs?: number; } interface LocalServiceInstance { service: Record; ctx: ForgeContext; } interface ConcurrencyLimiterConfig { initialLimit?: number; minLimit?: number; maxLimit?: number; smoothing?: number; tolerance?: number; } interface ProxyOptions { interceptors?: Record; routingStrategy?: string; retry?: RetryConfig | false; circuitBreaker?: import("../core/RpcConfig.js").CircuitBreakerOptions; bulkhead?: import("../core/RpcConfig.js").BulkheadOptions; concurrency?: ConcurrencyLimiterConfig; defaultTimeout?: number; rpcConfig?: RpcConfig; pool?: RpcPoolConfig; } export interface ServiceInstance { ctx?: { serviceName?: string; } | null; constructor: Function; onMessage: (from: string, payload: unknown) => Promise; [key: string]: unknown; } interface ProxyRequestPayload { __forge_method?: string; __forge_args?: unknown[]; __forge_deadline?: number | string; __forge_event?: string; __forge_data?: unknown; [key: string]: unknown; } interface ConcurrencyStats { [name: string]: unknown; } /** * Default pool limits for the shared HTTP agent used by remote RPC. * Configurable via environment variables: * FORGE_RPC_MAX_SOCKETS — max concurrent sockets across all hosts (default 256) * FORGE_RPC_MAX_SOCKETS_PER_HOST — max concurrent sockets per host (default 64) * FORGE_RPC_KEEPALIVE_MS — keepalive timeout in ms (default 30000) * Also configurable via ProxyOptions.pool. */ export interface RpcPoolConfig { maxSockets?: number; maxSocketsPerHost?: number; keepAliveMs?: number; } /** * Get or create the shared HTTP agent for remote RPC connections. * Uses connection pooling with keepalive to avoid per-request TCP setup. */ export declare function _getRpcAgent(config?: RpcPoolConfig): http.Agent; /** * Destroy the shared RPC agent (for test teardown / graceful shutdown). */ export declare function _destroyRpcAgent(): void; /** * Stop the signature cache (for cleanup/testing). */ export declare function _stopSignatureCache(): void; /** Start (or restart) the periodic rate-limit bucket cleanup timer. */ export declare function _startRateLimitCleanup(): void; /** Stop the periodic rate-limit bucket cleanup timer (for test teardown). */ export declare function _stopRateLimitCleanup(): void; /** Sentinel returned by handleProxyRequest when the message isn't a proxy request. */ export declare const NOT_HANDLED: unique symbol; /** Expose for status endpoints */ export declare function getAllConcurrencyStats(): ConcurrencyStats; /** * Build proxy clients for all services this service connects to. */ export declare function buildServiceProxies(ctx: ForgeContext, serviceClasses: Map, localServices?: Map, options?: ProxyOptions): Record; /** * Create a proxy client for a single target service. */ export declare function createServiceProxy(ctx: ForgeContext, targetName: string, contract: ServiceContract | null, localInstance: LocalServiceInstance | null, options: ProxyOptions): Record; /** * Handle an incoming proxy-style RPC request on the receiving service. * * Detects the __forge_method convention, checks deadlines, * validates the method is exposed, and dispatches. */ export declare function handleProxyRequest(service: ServiceInstance, from: string, payload: ProxyRequestPayload | null | undefined): unknown | typeof NOT_HANDLED; /** * Auto-register HTTP routes from @Route decorator / contract metadata. * * For every route declared via `@Route(method, path)` or the plain-JS * `static contract = { routes: [...] }`, this function registers the * corresponding handler on the service's HTTP router. * * **Contract route handler signature:** * * async handler(body, params, query) -> result * * - `body` — parsed request body (`req.body`) * - `params` — URL path parameters (`req.params`), e.g. `{ id: '42' }` for `/users/:id` * - `query` — query-string parameters (`req.query`), e.g. `{ page: '2' }` * - Return value is automatically serialized as JSON via `res.json(result)`. * POST routes respond with 201; all other methods respond with 200. * Errors are caught and returned as `{ error: message }` with 404 (if the * message contains "not found") or 500. * * **How this differs from manual `ctx.router` routes:** * * When you register routes manually in `onStart(ctx)`, the handler receives * the raw `(req, res)` pair and you are responsible for sending the response: * * ctx.router.get('/health', (req, res) => { * res.json({ ok: true }); * }); * * Contract-based handlers are higher-level: the framework supplies the * individual pieces of the request as arguments and handles serialization * and status codes for you. * * @example * // ── Contract / decorator approach ────────────────────── * // Handler receives (body, params, query) and returns a value. * * class UserService extends Service { * \@Expose() * \@Route('GET', '/users/:id') * async getUser(body, params, query) { * return this.db.findUser(params.id); // auto-serialized, 200 OK * } * * \@Expose() * \@Route('POST', '/users') * async createUser(body, params, query) { * return this.db.insert(body); // auto-serialized, 201 Created * } * } * * // ── Manual approach (in onStart) ────────────────────── * // Handler receives (req, res) and must call res.json() itself. * * async onStart(ctx) { * ctx.router.get('/users/:id', async (req, res) => { * const user = await this.db.findUser(req.params.id); * res.json(user); // you choose the status code * }); * } */ export declare function autoRegisterRoutes(service: ServiceInstance, ctx: ForgeContext): void; /** * Auto-wire event subscriptions from @On decorators. */ export declare function autoWireSubscriptions(service: ServiceInstance, _ctx: ForgeContext): void; export {}; //# sourceMappingURL=ServiceProxy.d.ts.map