import type { ForgeContext } from "../core/ForgeContext.js"; /** * Service Base Class v2 * * Developers extend this to create ThreadForge services. * * NEW in v2: * - Auto-generated proxy clients for other services * (this.users.getUser(id) instead of this.request('users', {...})) * - @Expose, @Route, @Emit, @On decorators for contracts * - Plain JS fallback via static `contract` property * - Automatic HTTP route registration from @Route * - Automatic event subscription wiring from @On * * @example * ```js * import { Service, Expose, Route, Emit } from 'threadforge'; * * export default class UserService extends Service { * * @Expose() * async getUser(userId) { * return this.db.findUser(userId); * } * * @Expose() * @Route('POST', '/users') * @Emit('user.created') * async createUser(data) { * const user = await this.db.insert(data); * await this.notifications.userCreated(user); // auto-generated proxy * return user; * } * } * ``` * * Plain JS equivalent (no decorators): * ```js * export default class UserService extends Service { * static contract = { * expose: ['getUser', 'createUser'], * routes: [ * { method: 'POST', path: '/users', handler: 'createUser' }, * ], * emits: { createUser: 'user.created' }, * }; * * async getUser(userId) { ... } * async createUser(data) { ... } * } * ``` * * **Route handler signatures — two approaches:** * * 1. Contract routes (`@Route` / `static contract.routes`): * Handler receives `(body, params, query)` and returns a value that is * auto-serialized as JSON (201 for POST, 200 otherwise). * * **IMPORTANT: GET handler signature** * GET handlers receive the same `(body, params, query)` signature as all * other methods. Since GET requests have no request body, the first * argument is always an empty object `{}`. Use destructuring to skip it: * * ```js * // Recommended — destructure to ignore the empty body: * @Route('GET', '/users/:id') * async getUser(_body, params, query) { * // _body is always {} for GET requests * return this.db.findUser(params.id); * } * * // Also works — use positional args: * @Route('GET', '/search') * async search(_, _params, query) { * return this.db.search(query.q); * } * ``` * * The same applies to plain JS contracts: * ```js * static contract = { * routes: [{ method: 'GET', path: '/users/:id', handler: 'getUser' }], * }; * async getUser(_body, params) { * return this.db.findUser(params.id); * } * ``` * * 2. Manual routes (registered in `onStart` via `ctx.router`): * Handler receives the raw `(req, res)` and must call `res.json()` or * `res.end()` itself. These follow Node.js HTTP conventions and do not * have the empty body argument. * * See `autoRegisterRoutes` in `ServiceProxy.js` for full details. */ interface ProxyPayload { __forge_method?: string; __forge_args?: unknown[]; [key: string]: unknown; } export declare class Service { ctx: ForgeContext | null; /** * Auto-generated proxy clients for connected services. * Populated by the framework during initialization. * * Usage: this.users.getUser('123') * this.notifications.sendAlert({ ... }) */ [key: string]: unknown; constructor(); /** * Called when the service starts. Initialize resources here. * Routes from @Route decorators are auto-registered BEFORE this is called, * so you can add additional routes in onStart if needed. */ onStart(_ctx: ForgeContext | null): Promise; /** * Called when a message arrives from another service. * For proxy-style calls, this is handled automatically. * Override for custom fire-and-forget message handling. */ onMessage(_from: string, _payload: unknown): Promise; /** * Called when a request arrives from another service. * * For proxy-style calls (@Expose methods), this is dispatched * automatically — you don't need to implement a switch/case. * * Override only for custom non-proxy request handling. */ onRequest(from: string, payload: ProxyPayload): Promise; /** * Called when the service is shutting down. */ onStop(): Promise; /** * Send a fire-and-forget message to another service. * Prefer using proxy clients (this.serviceName.method()) for * request/response patterns. */ send(target: string, payload: unknown): Promise; /** * Send a request to another service. * Prefer using proxy clients instead of this low-level API. */ request(target: string, payload: unknown, timeoutMs?: number): Promise; /** Broadcast to all workers of a target service. */ broadcast(target: string, payload: unknown): Promise; /** @internal */ _init(ctx: ForgeContext): Promise; /** * @internal * Set proxy clients on the service instance. * Called by the worker bootstrap after all services are loaded. */ _setProxies(proxies: Record): void; /** @internal */ _start(): Promise; /** @internal */ _stop(): Promise; } export {}; //# sourceMappingURL=Service.d.ts.map