@theokit/http 0.4.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 +172 -0
- package/dist/app.d.ts +67 -0
- package/dist/app.js +11 -0
- package/dist/app.js.map +1 -0
- package/dist/chunk-34KOKJ5M.js +71 -0
- package/dist/chunk-34KOKJ5M.js.map +1 -0
- package/dist/chunk-3PGQVQWG.js +276 -0
- package/dist/chunk-3PGQVQWG.js.map +1 -0
- package/dist/chunk-7QVYU63E.js +7 -0
- package/dist/chunk-7QVYU63E.js.map +1 -0
- package/dist/chunk-HLW7YKZE.js +99 -0
- package/dist/chunk-HLW7YKZE.js.map +1 -0
- package/dist/chunk-LKNI6QEP.js +20 -0
- package/dist/chunk-LKNI6QEP.js.map +1 -0
- package/dist/chunk-LWCNTZN6.js +87 -0
- package/dist/chunk-LWCNTZN6.js.map +1 -0
- package/dist/chunk-SMWUPP2C.js +125 -0
- package/dist/chunk-SMWUPP2C.js.map +1 -0
- package/dist/chunk-TBMGRXH5.js +477 -0
- package/dist/chunk-TBMGRXH5.js.map +1 -0
- package/dist/chunk-U46H4CGF.js +34 -0
- package/dist/chunk-U46H4CGF.js.map +1 -0
- package/dist/exception-filter-chain-BCSQ3MZ2.js +10 -0
- package/dist/exception-filter-chain-BCSQ3MZ2.js.map +1 -0
- package/dist/index.d.ts +1047 -0
- package/dist/index.js +761 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptor-chain-6S3PUV7J.js +9 -0
- package/dist/interceptor-chain-6S3PUV7J.js.map +1 -0
- package/dist/middleware-consumer-ljxK1fU_.d.ts +58 -0
- package/dist/runtime-node.d.ts +21 -0
- package/dist/runtime-node.js +12 -0
- package/dist/runtime-node.js.map +1 -0
- package/dist/theokit-plugin.d.ts +65 -0
- package/dist/theokit-plugin.js +432 -0
- package/dist/theokit-plugin.js.map +1 -0
- package/dist/types-CGthbcon.d.ts +19 -0
- package/package.json +58 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/decorators/controller.ts","../src/decorators/methods.ts","../src/decorators/params.ts","../src/decorators/response.ts","../src/decorators/middleware.ts","../src/decorators/set-metadata.ts","../src/decorators/throttle.ts","../src/bridge/register-controllers.ts","../src/bridge/create-server.ts","../src/typed-client.ts","../src/contract.ts"],"sourcesContent":["import { setMeta, CONTROLLER_PREFIX } from '../metadata/index.js'\n\nexport interface ControllerOptions {\n host?: string\n}\n\nexport interface ControllerMeta {\n prefix: string\n host?: string\n}\n\n/**\n * Class decorator that declares a route-prefix scope.\n * Equivalent to NestJS's @Controller('prefix').\n */\nexport function Controller(prefix = '', opts: ControllerOptions = {}): ClassDecorator {\n return (target) => {\n setMeta<ControllerMeta>(CONTROLLER_PREFIX, target, {\n prefix,\n host: opts.host,\n })\n }\n}\n","import { setMeta, getMeta, ROUTE_METHODS } from '../metadata/index.js'\n\nexport type HttpVerb = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'ALL'\n\nexport interface RouteMethodEntry {\n verb: HttpVerb\n path: string\n propertyKey: string | symbol\n}\n\nfunction makeVerbDecorator(verb: HttpVerb) {\n return function (path = ''): MethodDecorator {\n return (target, propertyKey) => {\n const existing = getMeta<RouteMethodEntry[]>(ROUTE_METHODS, target.constructor) ?? []\n existing.push({ verb, path, propertyKey })\n setMeta(ROUTE_METHODS, target.constructor, existing)\n }\n }\n}\n\nexport const Get = makeVerbDecorator('GET')\nexport const Post = makeVerbDecorator('POST')\nexport const Put = makeVerbDecorator('PUT')\nexport const Patch = makeVerbDecorator('PATCH')\nexport const Delete = makeVerbDecorator('DELETE')\nexport const Options = makeVerbDecorator('OPTIONS')\nexport const Head = makeVerbDecorator('HEAD')\nexport const All = makeVerbDecorator('ALL')\n","import type { ZodTypeAny } from 'zod'\n\nimport { setMeta, getMeta, ROUTE_PARAMS } from '../metadata/index.js'\n\nexport type ParamSource =\n | 'req'\n | 'res'\n | 'body'\n | 'param'\n | 'query'\n | 'headers'\n | 'session'\n | 'ip'\n | 'host'\n\nexport interface ParamEntry {\n source: ParamSource\n key?: string\n index: number\n passthrough?: boolean\n /** Explicit Zod schema — when set, bypasses design:paramtypes + DTO resolution.\n * Preferred path: `@Body(zMySchema)` works without emitDecoratorMetadata. */\n schema?: ZodTypeAny\n}\n\n/** Detect whether a value is a Zod schema (has `.safeParse()` method). */\nfunction isZodSchema(v: unknown): v is ZodTypeAny {\n return (\n v !== null &&\n v !== undefined &&\n typeof v === 'object' &&\n typeof (v as Record<string, unknown>).safeParse === 'function'\n )\n}\n\nfunction makeParamDecorator(source: ParamSource) {\n /**\n * Overloaded parameter decorator:\n * - `@Body()` / `@Param()` / `@Query()` — whole-object extraction\n * - `@Body('key')` / `@Param('key')` — named-field extraction\n * - `@Body(zodSchema)` — whole-object with explicit Zod validation\n * (works WITHOUT emitDecoratorMetadata — TheoKit \"Zod is SSoT\" alignment)\n */\n return function (keyOrSchema?: string | ZodTypeAny): ParameterDecorator {\n return (target, propertyKey, parameterIndex) => {\n if (propertyKey === undefined) return\n const map =\n getMeta<Map<string | symbol, ParamEntry[]>>(ROUTE_PARAMS, target.constructor) ?? new Map()\n const entries = map.get(propertyKey) ?? []\n\n const entry: ParamEntry = { source, index: parameterIndex }\n if (typeof keyOrSchema === 'string') {\n entry.key = keyOrSchema\n } else if (isZodSchema(keyOrSchema)) {\n entry.schema = keyOrSchema\n }\n\n entries.push(entry)\n map.set(propertyKey, entries)\n setMeta(ROUTE_PARAMS, target.constructor, map)\n }\n }\n}\n\nexport const Req = makeParamDecorator('req')\nexport const Body = makeParamDecorator('body')\nexport const Param = makeParamDecorator('param')\nexport const Query = makeParamDecorator('query')\nexport const Headers = makeParamDecorator('headers')\nexport const Session = makeParamDecorator('session')\nexport const Ip = makeParamDecorator('ip')\nexport const HostParam = makeParamDecorator('host')\n\nexport function Res(opts?: { passthrough?: boolean }): ParameterDecorator {\n return (target, propertyKey, parameterIndex) => {\n if (propertyKey === undefined) return\n const map =\n getMeta<Map<string | symbol, ParamEntry[]>>(ROUTE_PARAMS, target.constructor) ?? new Map()\n const entries = map.get(propertyKey) ?? []\n entries.push({\n source: 'res',\n index: parameterIndex,\n passthrough: opts?.passthrough,\n })\n map.set(propertyKey, entries)\n setMeta(ROUTE_PARAMS, target.constructor, map)\n }\n}\n","import { setMeta, getMeta, ROUTE_STATUS, ROUTE_HEADERS, ROUTE_REDIRECT } from '../metadata/index.js'\n\nexport function HttpCode(status: number): MethodDecorator {\n return (target, propertyKey) => {\n setMeta(ROUTE_STATUS, target.constructor, status, propertyKey as string)\n }\n}\n\nexport function Header(name: string, value: string): MethodDecorator {\n return (target, propertyKey) => {\n const existing =\n getMeta<[string, string][]>(ROUTE_HEADERS, target.constructor, propertyKey) ?? []\n existing.push([name, value])\n setMeta(ROUTE_HEADERS, target.constructor, existing, propertyKey as string)\n }\n}\n\nexport interface RedirectMeta {\n url: string\n status: number\n}\n\nexport function Redirect(url: string, status = 302): MethodDecorator {\n return (target, propertyKey) => {\n setMeta<RedirectMeta>(ROUTE_REDIRECT, target.constructor, { url, status }, propertyKey)\n }\n}\n","import {\n setMeta,\n getMeta,\n USE_GUARDS,\n USE_INTERCEPTORS,\n USE_FILTERS,\n CATCH_EXCEPTIONS,\n} from '../metadata/index.js'\n\nexport function UseGuards(...guards: Function[]): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey ? target.constructor : target\n const existing = getMeta<Function[]>(USE_GUARDS, actualTarget, propertyKey) ?? []\n setMeta(USE_GUARDS, actualTarget, [...existing, ...guards], propertyKey)\n }\n}\n\nexport function UseInterceptors(...interceptors: Function[]): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey ? target.constructor : target\n const existing = getMeta<Function[]>(USE_INTERCEPTORS, actualTarget, propertyKey) ?? []\n setMeta(USE_INTERCEPTORS, actualTarget, [...existing, ...interceptors], propertyKey)\n }\n}\n\nexport function UseFilters(...filters: Function[]): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey ? target.constructor : target\n const existing = getMeta<Function[]>(USE_FILTERS, actualTarget, propertyKey) ?? []\n setMeta(USE_FILTERS, actualTarget, [...existing, ...filters], propertyKey)\n }\n}\n\n/** @Catch(ExceptionType, ...) — marks which exception types an ExceptionFilter handles.\n * Empty args = catch-all filter. */\nexport function Catch(...exceptions: Function[]): ClassDecorator {\n return (target: object) => {\n setMeta(CATCH_EXCEPTIONS, target, exceptions)\n }\n}\n","/**\n * @SetMetadata + Reflector — NestJS-style custom metadata for guards.\n *\n * Enables role-based auth pattern:\n * const Roles = createDecorator<string[]>()\n * @Roles(['admin']) → guard reads via reflector.get(Roles, handler)\n */\nimport 'reflect-metadata'\n\n/** Unique key type for type-safe metadata decorators. */\nexport type MetadataKey<T> = symbol & { __type?: T }\n\n/**\n * Create a typed decorator that attaches metadata to a handler or class.\n * NestJS equivalent: `Reflector.createDecorator<T>()`.\n *\n * @example\n * ```ts\n * const Roles = createDecorator<string[]>()\n *\n * @Controller('cats')\n * class CatsController {\n * @Post()\n * @Roles(['admin'])\n * create() { ... }\n * }\n * ```\n */\n/** Monotonic counter for unique decorator keys — deterministic, no randomness. */\nlet decoratorKeyCounter = 0\n\nexport function createDecorator<T>(): (value: T) => MethodDecorator & ClassDecorator {\n const key = Symbol.for(`theokit:custom:${++decoratorKeyCounter}`) as MetadataKey<T>\n\n const decorator = (value: T): MethodDecorator & ClassDecorator => {\n return (target: object, propertyKey?: string | symbol) => {\n const metaTarget = propertyKey !== undefined ? target.constructor : target\n Reflect.defineMetadata(key, value, metaTarget, propertyKey as string)\n }\n }\n\n // Attach the key so Reflector can read it\n ;(decorator as unknown as { key: MetadataKey<T> }).key = key\n return decorator\n}\n\n/**\n * Low-level @SetMetadata decorator — attaches arbitrary metadata.\n * NestJS equivalent: `@SetMetadata(key, value)`.\n *\n * Prefer `createDecorator<T>()` for type-safe metadata.\n */\nexport function SetMetadata<T>(\n metaKey: string | symbol,\n value: T,\n): MethodDecorator & ClassDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const metaTarget = propertyKey !== undefined ? target.constructor : target\n Reflect.defineMetadata(metaKey, value, metaTarget, propertyKey as string)\n }\n}\n\n/**\n * Reflector — reads metadata set by createDecorator or @SetMetadata.\n * NestJS-compatible Reflector with getAllAndOverride/getAllAndMerge.\n * HTTP-only per ADR D1.\n */\nexport class Reflector {\n /**\n * Read metadata set by a typed decorator created via createDecorator<T>().\n *\n * @example\n * ```ts\n * const Roles = createDecorator<string[]>()\n * const reflector = new Reflector()\n * const roles = reflector.get(Roles, handlerFn) // string[] | undefined\n * ```\n */\n get<T>(\n decorator: (value: T) => MethodDecorator & ClassDecorator,\n target: Function,\n propertyKey?: string | symbol,\n ): T | undefined {\n const key = (decorator as { key?: MetadataKey<T> }).key\n if (!key) return undefined\n if (propertyKey !== undefined) {\n return Reflect.getMetadata(key, target, propertyKey) as T | undefined\n }\n return Reflect.getMetadata(key, target) as T | undefined\n }\n\n /**\n * Read metadata set by @SetMetadata(key, value).\n */\n getByKey<T>(\n key: string | symbol,\n target: Function,\n propertyKey?: string | symbol,\n ): T | undefined {\n if (propertyKey !== undefined) {\n return Reflect.getMetadata(key, target, propertyKey) as T | undefined\n }\n return Reflect.getMetadata(key, target) as T | undefined\n }\n\n /**\n * Read metadata checking method-level first, then class-level.\n * Returns the first non-undefined value found.\n *\n * NestJS equivalent: `reflector.getAllAndOverride(ROLES_KEY, [context.getHandler(), context.getClass()])`\n *\n * @example\n * ```ts\n * const Roles = createDecorator<string[]>()\n * // In a guard:\n * const roles = reflector.getAllAndOverride(Roles, context.getClass(), context.getMethodName())\n * // Checks method-level @Roles first, falls back to class-level @Roles\n * ```\n */\n getAllAndOverride<T>(\n decorator: (value: T) => MethodDecorator & ClassDecorator,\n target: Function,\n propertyKey?: string | symbol,\n ): T | undefined {\n if (propertyKey !== undefined) {\n const methodLevel = this.get(decorator, target, propertyKey)\n if (methodLevel !== undefined) return methodLevel\n }\n return this.get(decorator, target)\n }\n\n /**\n * Read metadata checking method-level first, then class-level, by raw key.\n * Returns the first non-undefined value found.\n */\n getAllAndOverrideByKey<T>(\n key: string | symbol,\n target: Function,\n propertyKey?: string | symbol,\n ): T | undefined {\n if (propertyKey !== undefined) {\n const methodLevel = this.getByKey<T>(key, target, propertyKey)\n if (methodLevel !== undefined) return methodLevel\n }\n return this.getByKey<T>(key, target)\n }\n\n /**\n * Read metadata from both method-level and class-level, merging arrays.\n * Returns all found values as a flat array.\n *\n * NestJS equivalent: `reflector.getAllAndMerge(ROLES_KEY, [context.getHandler(), context.getClass()])`\n *\n * @example\n * ```ts\n * const Tags = createDecorator<string[]>()\n *\n * @Tags(['api'])\n * @Controller('cats')\n * class CatsCtrl {\n * @Tags(['read'])\n * @Get()\n * findAll() {}\n * }\n *\n * reflector.getAllAndMerge(Tags, CatsCtrl, 'findAll')\n * // → ['read', 'api'] (method + class merged)\n * ```\n */\n getAllAndMerge<T>(\n decorator: (value: T) => MethodDecorator & ClassDecorator,\n target: Function,\n propertyKey?: string | symbol,\n ): T extends (infer U)[] ? U[] : T[] {\n const result: unknown[] = []\n if (propertyKey !== undefined) {\n const methodLevel = this.get(decorator, target, propertyKey)\n if (methodLevel !== undefined) {\n if (Array.isArray(methodLevel)) result.push(...methodLevel)\n else result.push(methodLevel)\n }\n }\n const classLevel = this.get(decorator, target)\n if (classLevel !== undefined) {\n if (Array.isArray(classLevel)) result.push(...classLevel)\n else result.push(classLevel)\n }\n return result as T extends (infer U)[] ? U[] : T[]\n }\n}\n","/**\n * @Throttle() + @SkipThrottle() — NestJS-style rate limiting decorators.\n *\n * Thin bridge over @theokit/plugin-rate-limit. These decorators store\n * metadata that the rate-limit plugin reads at request time to override\n * or skip the global throttle config per route/controller.\n *\n * Usage:\n * ```ts\n * @Controller('api/tasks')\n * @Throttle({ limit: 100, ttl: 60_000 }) // 100 req/min for all routes\n * class TasksController {\n * @Get()\n * @SkipThrottle() // no rate limit on this route\n * health() { return { ok: true } }\n *\n * @Post()\n * @Throttle({ limit: 5, ttl: 60_000 }) // stricter: 5 req/min\n * create(@Body(schema) body) { ... }\n * }\n * ```\n */\nimport { setMeta, getMeta } from '../metadata/index.js'\n\nconst THROTTLE_KEY = Symbol.for('theokit:http-decorators:throttle')\nconst SKIP_THROTTLE_KEY = Symbol.for('theokit:http-decorators:skip-throttle')\n\nexport interface ThrottleOptions {\n /** Maximum requests within the TTL window. */\n limit: number\n /** Time-to-live in milliseconds. */\n ttl: number\n /** Optional throttle set name (for multiple throttler definitions). */\n name?: string\n}\n\n/**\n * Override the global rate limit for a controller or specific route.\n * NestJS equivalent: `@Throttle({ default: { limit, ttl } })`.\n */\nexport function Throttle(options: ThrottleOptions): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey !== undefined ? target.constructor : target\n setMeta(THROTTLE_KEY, actualTarget, options, propertyKey)\n }\n}\n\n/**\n * Skip rate limiting for a controller or specific route.\n * NestJS equivalent: `@SkipThrottle()`.\n *\n * @param skip — defaults to `true`. Pass `false` to re-enable on a\n * specific route inside a skipped controller.\n */\nexport function SkipThrottle(skip = true): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey !== undefined ? target.constructor : target\n setMeta(SKIP_THROTTLE_KEY, actualTarget, skip, propertyKey)\n }\n}\n\n/**\n * Read throttle metadata for a given class or method.\n * Used by the rate-limit plugin to resolve per-route overrides.\n */\nexport function getThrottleOptions(\n target: Function,\n propertyKey?: string | symbol,\n): ThrottleOptions | undefined {\n return getMeta<ThrottleOptions>(THROTTLE_KEY, target, propertyKey)\n}\n\n/**\n * Check if throttling is skipped for a given class or method.\n */\nexport function isThrottleSkipped(target: Function, propertyKey?: string | symbol): boolean {\n return getMeta<boolean>(SKIP_THROTTLE_KEY, target, propertyKey) ?? false\n}\n","import type { HttpVerb } from '../decorators/methods.js'\n\nimport { walkControllerMetadata, type WalkResult } from './walk-metadata.js'\n\nexport interface RouteRegistration {\n verb: HttpVerb\n fullPath: string\n walkResult: WalkResult\n}\n\n/**\n * Low-level API: walks decorator metadata per controller class and returns\n * structured route descriptors. Used internally by the Vite plugin (ADR D7)\n * and available to advanced consumers who don't use Vite.\n *\n * EC-5: deduplicates by class reference; warns on duplicates.\n */\nexport function registerControllers(controllers: Function[]): RouteRegistration[] {\n const seen = new Set<Function>()\n const unique: Function[] = []\n for (const Ctor of controllers) {\n if (seen.has(Ctor)) {\n console.warn(\n `[@theokit/http] Controller ${Ctor.name} registered multiple times — dropping duplicate`,\n )\n continue\n }\n seen.add(Ctor)\n unique.push(Ctor)\n }\n return unique.flatMap((Ctor) => {\n const walks = walkControllerMetadata(Ctor)\n return walks.map((w) => ({\n verb: w.verb,\n fullPath: w.fullPath,\n walkResult: w,\n }))\n })\n}\n","/* eslint-disable security/detect-non-literal-regexp --\n * Route patterns like /cats/:id are converted to regex at startup —\n * NOT from user HTTP input. The patterns come from decorator metadata\n * authored by the developer. No injection vector.\n */\nimport 'reflect-metadata'\n\nimport type { ParamEntry } from '../decorators/params.js'\nimport { ForbiddenException } from '../exceptions/http-exception.js'\n\nimport { resolveOrNew, type DiContainer } from './di-resolve.js'\nimport { runExceptionFilters } from './exception-filter-chain.js'\nimport { createExecutionContext, type CanActivate, type ExecutionContext } from './execution-context.js'\nimport { runInterceptors } from './interceptor-chain.js'\nimport {\n MiddlewareConsumerImpl,\n runMiddleware,\n type ResolvedMiddleware,\n} from './middleware-consumer.js'\nimport { createNodeAdapter } from './runtime/node.js'\nimport { walkControllerMetadata, type WalkResult } from './walk-metadata.js'\n\n/**\n * Creates a real HTTP server from decorated controller classes.\n * Uses Web Standard Request/Response internally; Node adapter at the boundary.\n */\nexport interface CreateDecoratorServerOptions {\n controllers: Function[]\n container?: DiContainer\n configure?: (consumer: MiddlewareConsumerImpl) => void\n}\n\nexport function createDecoratorServer(\n controllersOrOpts: Function[] | CreateDecoratorServerOptions,\n) {\n const { controllers, container, configure } = Array.isArray(controllersOrOpts)\n ? { controllers: controllersOrOpts, container: undefined, configure: undefined }\n : controllersOrOpts\n\n // Collect middleware\n const middlewareConsumer = new MiddlewareConsumerImpl(container)\n if (configure) configure(middlewareConsumer)\n const middlewareEntries = middlewareConsumer.getEntries()\n\n // Dedupe controllers (EC-5)\n const seen = new Set<Function>()\n const unique: Function[] = []\n for (const Ctor of controllers) {\n if (seen.has(Ctor)) continue\n seen.add(Ctor)\n unique.push(Ctor)\n }\n\n // Walk metadata\n const routes: { walk: WalkResult; instance: object }[] = []\n for (const Ctor of unique) {\n const instance = resolveOrNew(Ctor, container)\n const walks = walkControllerMetadata(Ctor)\n for (const w of walks) {\n routes.push({ walk: w, instance })\n }\n }\n\n // Sort: static routes first\n routes.sort((a, b) => {\n const aP = a.walk.fullPath.includes(':')\n const bP = b.walk.fullPath.includes(':')\n if (aP !== bP) return aP ? 1 : -1\n return 0\n })\n\n // Create Web Standard handler + Node adapter\n const adapter = createNodeAdapter()\n const handler = (request: Request) => handleRequest(routes, request, container, middlewareEntries)\n return adapter.createServer(handler)\n}\n\n// ─── Web Standard request handler ────────────────────────────\n\nasync function handleRequest(\n routes: { walk: WalkResult; instance: object }[],\n request: Request,\n container?: DiContainer,\n middlewareEntries: ResolvedMiddleware[] = [],\n): Promise<Response> {\n const url = new URL(request.url)\n const method = request.method.toUpperCase()\n const pathname = url.pathname\n\n const match = findRoute(routes, method, pathname)\n if (!match) {\n return jsonResponse(404, { error: { code: 'NOT_FOUND', message: `No route for ${method} ${pathname}` } })\n }\n\n const { walk, instance, params } = match\n\n try {\n // Middleware\n const mwResponse = await runMiddleware(middlewareEntries, request, pathname)\n if (mwResponse) return mwResponse\n\n // Guards\n const ctx = createExecutionContext(request, instance.constructor, walk.propertyKey)\n const guardResponse = await runGuards(walk.guards, ctx, container)\n if (guardResponse) return guardResponse\n\n // Body\n const body = await resolveBody(method, request, walk)\n if (body instanceof Response) return body // validation error response\n\n // Build args\n const args = buildArgs(walk.paramEntries, { request, body, params, query: Object.fromEntries(url.searchParams) })\n\n // Redirect\n if (walk.redirect) {\n return new Response(null, { status: walk.redirect.status, headers: { location: walk.redirect.url } })\n }\n\n const handlerFn = (instance as Record<string | symbol, Function>)[walk.propertyKey]\n\n // Interceptors wrap handler\n const result = await runInterceptors(\n walk.interceptors,\n () => handlerFn.apply(instance, args) as Promise<unknown>,\n request,\n container,\n )\n\n return buildResponse(result, walk, method)\n } catch (err) {\n return runExceptionFilters(err, walk.filters, request, container)\n }\n}\n\n// ─── Guards (return Response on rejection, null on pass) ─────\n\nasync function runGuards(\n guards: Function[],\n context: ExecutionContext,\n container?: DiContainer,\n): Promise<Response | null> {\n for (const GuardCtor of guards) {\n const guard = resolveOrNew(GuardCtor, container) as CanActivate\n const allowed = await guard.canActivate(context)\n if (!allowed) {\n const ex = new ForbiddenException('Forbidden resource')\n return jsonResponse(ex.statusCode, ex.toJSON())\n }\n }\n return null\n}\n\n// ─── Body resolution ─────────────────────────────────────────\n\nasync function resolveBody(method: string, request: Request, walk: WalkResult): Promise<unknown> {\n if (!['POST', 'PUT', 'PATCH'].includes(method)) return undefined\n\n let body: unknown\n try {\n const text = await request.text()\n body = text ? JSON.parse(text) : undefined\n } catch {\n body = undefined\n }\n\n if (walk.bodySchema && body !== undefined) {\n const result = walk.bodySchema.safeParse(body)\n if (!result.success) {\n return jsonResponse(422, { error: { code: 'VALIDATION_ERROR', issues: result.error.issues } })\n }\n body = result.data\n }\n return body\n}\n\n// ─── Response builder ────────────────────────────────────────\n\nfunction buildResponse(result: unknown, walk: WalkResult, method: string): Response {\n const status = walk.status ?? (method === 'POST' ? 201 : 200)\n const headers: Record<string, string> = { 'content-type': 'application/json' }\n for (const [name, value] of walk.headers) {\n headers[name.toLowerCase()] = value\n }\n\n if (result === undefined || result === null) {\n return new Response(null, { status: status === 200 ? 204 : status, headers })\n }\n\n if (typeof result === 'string') {\n headers['content-type'] = 'text/plain'\n return new Response(result, { status, headers })\n }\n\n return new Response(JSON.stringify(result), { status, headers })\n}\n\nfunction jsonResponse(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n\n// ─── Route matching ──────────────────────────────────────────\n\ninterface RouteMatch {\n walk: WalkResult\n instance: object\n params: Record<string, string>\n}\n\nfunction findRoute(\n routes: { walk: WalkResult; instance: object }[],\n method: string,\n pathname: string,\n): RouteMatch | null {\n for (const { walk, instance } of routes) {\n if (walk.verb !== 'ALL' && walk.verb !== method) continue\n const params = matchPath(walk.fullPath, pathname)\n if (params !== null) return { walk, instance, params }\n }\n return null\n}\n\nfunction matchPath(pattern: string, pathname: string): Record<string, string> | null {\n const paramNames: string[] = []\n const regexStr = pattern.replace(/:(\\w+)/g, (_m, name: string) => {\n paramNames.push(name)\n return '([^/]+)'\n })\n const match = new RegExp(`^${regexStr}$`).exec(pathname)\n if (!match) return null\n const params: Record<string, string> = {}\n paramNames.forEach((name, i) => { params[name] = match[i + 1] })\n return params\n}\n\n// ─── Argument builder ────────────────────────────────────────\n\ninterface ArgContext {\n request: Request\n body: unknown\n params: Record<string, string>\n query: Record<string, string>\n}\n\nfunction buildArgs(paramEntries: ParamEntry[], ctx: ArgContext): unknown[] {\n if (paramEntries.length === 0) return []\n const maxIndex = Math.max(...paramEntries.map((p) => p.index))\n const args: unknown[] = Array.from({ length: maxIndex + 1 }, () => undefined)\n for (const p of paramEntries) {\n switch (p.source) {\n case 'req':\n args[p.index] = ctx.request\n break\n case 'body':\n args[p.index] = p.key ? (ctx.body as Record<string, unknown>)[p.key] : ctx.body\n break\n case 'param':\n args[p.index] = p.key ? ctx.params[p.key] : ctx.params\n break\n case 'query':\n args[p.index] = p.key ? ctx.query[p.key] : ctx.query\n break\n case 'headers':\n args[p.index] = p.key ? ctx.request.headers.get(p.key.toLowerCase()) : Object.fromEntries(ctx.request.headers.entries())\n break\n case 'ip':\n args[p.index] = ctx.request.headers.get('x-forwarded-for') ?? '127.0.0.1'\n break\n case 'session':\n args[p.index] = undefined\n break\n default:\n args[p.index] = undefined\n }\n }\n return args\n}\n","/**\n * Typed Client — end-to-end type inference from route contracts.\n *\n * Zero codegen, zero runtime overhead on types. The developer defines\n * a route map using `contract()`, and `createTypedClient<T>()` infers\n * request body + response types automatically.\n */\nimport type { z } from 'zod'\n\n// ── Route Map types ──\n\nexport interface RouteDefinition {\n body?: z.ZodType\n params?: Record<string, 'string' | 'number'>\n query?: Record<string, 'string' | 'number' | 'boolean'>\n response: unknown\n}\n\nexport type RouteMap = Record<string, RouteDefinition>\n\n// ── Type extraction ──\n\ntype InferBody<D> = D extends { body: z.ZodType } ? z.infer<D['body']> : never\ntype InferResponse<D> = D extends { response: infer R } ? R : unknown\n\n// ── Client interface (simplified — works with strict DTS) ──\n\nexport interface TypedClient<M extends RouteMap> {\n get<P extends string & keyof M>(\n path: P,\n opts?: { query?: Record<string, string>; headers?: Record<string, string> },\n ): Promise<InferResponse<M[`GET ${P}`] extends never ? M[P] : M[`GET ${P}`]>>\n\n post<P extends string>(\n path: P,\n body?: InferBody<M[`POST ${P}`]>,\n opts?: { headers?: Record<string, string> },\n ): Promise<InferResponse<M[`POST ${P}`]>>\n\n put<P extends string>(\n path: P,\n body?: InferBody<M[`PUT ${P}`]>,\n opts?: { headers?: Record<string, string> },\n ): Promise<InferResponse<M[`PUT ${P}`]>>\n\n delete<P extends string>(\n path: P,\n opts?: { headers?: Record<string, string> },\n ): Promise<InferResponse<M[`DELETE ${P}`]>>\n}\n\n// ── Client factory ──\n\nexport function createTypedClient<M extends RouteMap>(\n baseUrl: string,\n defaultHeaders?: Record<string, string>,\n): TypedClient<M> {\n async function request(method: string, path: string, body?: unknown, opts?: { headers?: Record<string, string>; query?: Record<string, string> }) {\n const url = new URL(path, baseUrl)\n if (opts?.query) {\n for (const [k, v] of Object.entries(opts.query)) url.searchParams.set(k, v)\n }\n const headers: Record<string, string> = { ...defaultHeaders, ...opts?.headers }\n if (body !== undefined) headers['content-type'] = 'application/json'\n\n const res = await fetch(url.toString(), {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n\n if (!res.ok) {\n const error = await res.json().catch(() => ({ message: res.statusText }))\n throw new TypedClientError(res.status, error as Record<string, unknown>)\n }\n\n if (res.status === 204) return undefined\n return res.json()\n }\n\n return {\n get: (path: string, opts?: Record<string, unknown>) => request('GET', path, undefined, opts as { headers?: Record<string, string>; query?: Record<string, string> }),\n post: (path: string, body?: unknown, opts?: Record<string, unknown>) => request('POST', path, body, opts as { headers?: Record<string, string> }),\n put: (path: string, body?: unknown, opts?: Record<string, unknown>) => request('PUT', path, body, opts as { headers?: Record<string, string> }),\n delete: (path: string, opts?: Record<string, unknown>) => request('DELETE', path, undefined, opts as { headers?: Record<string, string> }),\n } as TypedClient<M>\n}\n\nexport class TypedClientError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: Record<string, unknown>,\n ) {\n super(`HTTP ${status}: ${JSON.stringify(body)}`)\n this.name = 'TypedClientError'\n }\n}\n","/**\n * Route Contract — type-level bridge between @Controller and TypedClient.\n *\n * The developer defines a contract object mapping routes to their types.\n * This object is the single source of truth for both server validation\n * and client type inference.\n *\n * @example\n * ```ts\n * // server/contracts.ts — shared between server and client\n * import { z } from 'zod'\n * import { contract } from '@theokit/http'\n *\n * export const zCreateTask = z.object({\n * title: z.string().min(3),\n * priority: z.enum(['low', 'medium', 'high']).default('medium'),\n * })\n *\n * export interface Task { id: number; title: string; priority: string; done: boolean }\n *\n * export const routes = contract({\n * 'GET /api/tasks': { response: [] as Task[] },\n * 'GET /api/tasks/:id': { response: {} as Task },\n * 'POST /api/tasks': { body: zCreateTask, response: {} as Task },\n * 'PUT /api/tasks/:id': { body: z.object({ done: z.boolean() }), response: {} as Task },\n * 'DELETE /api/tasks/:id': { response: undefined as void },\n * })\n * export type AppRoutes = typeof routes\n * ```\n *\n * The `contract()` function is identity at runtime (zero overhead) but\n * enforces the RouteMap type at the type level, enabling full inference.\n */\nimport type { RouteMap } from './typed-client.js'\n\n/**\n * Identity function that enforces RouteMap type constraint.\n * Zero runtime overhead — exists only for type inference.\n */\nexport function contract<T extends RouteMap>(routes: T): T {\n return routes\n}\n"],"mappings":"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","names":["Controller","prefix","opts","target","setMeta","CONTROLLER_PREFIX","host","makeVerbDecorator","verb","path","target","propertyKey","existing","getMeta","ROUTE_METHODS","push","setMeta","Get","Post","Put","Patch","Delete","Options","Head","All","isZodSchema","v","undefined","safeParse","makeParamDecorator","source","keyOrSchema","target","propertyKey","parameterIndex","map","getMeta","ROUTE_PARAMS","Map","entries","get","entry","index","key","schema","push","set","setMeta","Req","Body","Param","Query","Headers","Session","Ip","HostParam","Res","opts","passthrough","HttpCode","status","target","propertyKey","setMeta","ROUTE_STATUS","Header","name","value","existing","getMeta","ROUTE_HEADERS","push","Redirect","url","ROUTE_REDIRECT","UseGuards","guards","target","propertyKey","actualTarget","existing","getMeta","USE_GUARDS","setMeta","UseInterceptors","interceptors","USE_INTERCEPTORS","UseFilters","filters","USE_FILTERS","Catch","exceptions","CATCH_EXCEPTIONS","decoratorKeyCounter","createDecorator","key","Symbol","for","decorator","value","target","propertyKey","metaTarget","undefined","Reflect","defineMetadata","SetMetadata","metaKey","Reflector","get","getMetadata","getByKey","getAllAndOverride","methodLevel","getAllAndOverrideByKey","getAllAndMerge","result","Array","isArray","push","classLevel","THROTTLE_KEY","Symbol","for","SKIP_THROTTLE_KEY","Throttle","options","target","propertyKey","actualTarget","undefined","setMeta","SkipThrottle","skip","getThrottleOptions","getMeta","isThrottleSkipped","registerControllers","controllers","seen","Set","unique","Ctor","has","console","warn","name","add","push","flatMap","walks","walkControllerMetadata","map","w","verb","fullPath","walkResult","createDecoratorServer","controllersOrOpts","controllers","container","configure","Array","isArray","undefined","middlewareConsumer","MiddlewareConsumerImpl","middlewareEntries","getEntries","seen","Set","unique","Ctor","has","add","push","routes","instance","resolveOrNew","walks","walkControllerMetadata","w","walk","sort","a","b","aP","fullPath","includes","bP","adapter","createNodeAdapter","handler","request","handleRequest","createServer","url","URL","method","toUpperCase","pathname","match","findRoute","jsonResponse","error","code","message","params","mwResponse","runMiddleware","ctx","createExecutionContext","propertyKey","guardResponse","runGuards","guards","body","resolveBody","Response","args","buildArgs","paramEntries","query","Object","fromEntries","searchParams","redirect","status","headers","location","handlerFn","result","runInterceptors","interceptors","apply","buildResponse","err","runExceptionFilters","filters","context","GuardCtor","guard","allowed","canActivate","ex","ForbiddenException","statusCode","toJSON","text","JSON","parse","bodySchema","safeParse","success","issues","data","name","value","toLowerCase","stringify","verb","matchPath","pattern","paramNames","regexStr","replace","_m","RegExp","exec","forEach","i","length","maxIndex","Math","max","map","p","index","from","source","key","get","entries","createTypedClient","baseUrl","defaultHeaders","request","method","path","body","opts","url","URL","query","k","v","Object","entries","searchParams","set","headers","undefined","res","fetch","toString","JSON","stringify","ok","error","json","catch","message","statusText","TypedClientError","status","get","post","put","delete","Error","name","contract","routes"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared DI resolution helper for guards, interceptors, and middleware.
|
|
3
|
+
* Extracted to avoid DRY violation (EC-3) — was duplicated in create-server.ts
|
|
4
|
+
* and theokit-plugin.ts.
|
|
5
|
+
*/
|
|
6
|
+
/** DI container interface — structural match for @theokit/di Container. */
|
|
7
|
+
interface DiContainer {
|
|
8
|
+
resolve<T>(token: Function): T;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolve a class via DI container if provided; otherwise fall back to bare `new`.
|
|
12
|
+
* Guards, interceptors, and middleware classes all use this pattern.
|
|
13
|
+
*/
|
|
14
|
+
declare function resolveOrNew(Ctor: Function, container?: DiContainer): object;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* NestJS-style Middleware support — Web Standard Request/Response.
|
|
18
|
+
*
|
|
19
|
+
* Middleware runs BEFORE guards: middleware → guards → interceptors → handler
|
|
20
|
+
*
|
|
21
|
+
* Supports class middleware (NestMiddleware) and functional middleware.
|
|
22
|
+
* Returns a Response to short-circuit, or null to continue.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** NestJS-compatible middleware interface — Web Standard. */
|
|
26
|
+
interface NestMiddleware {
|
|
27
|
+
use(request: Request, next: () => Promise<Response | null>): Response | null | Promise<Response | null>;
|
|
28
|
+
}
|
|
29
|
+
/** Functional middleware. */
|
|
30
|
+
type MiddlewareFn = (request: Request, next: () => Promise<Response | null>) => Response | null | Promise<Response | null>;
|
|
31
|
+
interface ResolvedMiddleware {
|
|
32
|
+
handler: MiddlewareFn;
|
|
33
|
+
routePatterns: string[];
|
|
34
|
+
excludePatterns: string[];
|
|
35
|
+
}
|
|
36
|
+
type RouteInfo = string | {
|
|
37
|
+
path: string;
|
|
38
|
+
method?: string;
|
|
39
|
+
};
|
|
40
|
+
interface MiddlewareConfigProxy {
|
|
41
|
+
forRoutes(...routes: RouteInfo[]): MiddlewareConsumerImpl;
|
|
42
|
+
exclude(...routes: RouteInfo[]): MiddlewareConfigProxy;
|
|
43
|
+
}
|
|
44
|
+
declare class MiddlewareConsumerImpl {
|
|
45
|
+
private entries;
|
|
46
|
+
private container?;
|
|
47
|
+
constructor(container?: DiContainer);
|
|
48
|
+
apply(...middleware: (Function | MiddlewareFn)[]): MiddlewareConfigProxy;
|
|
49
|
+
getEntries(): ResolvedMiddleware[];
|
|
50
|
+
private resolveMiddleware;
|
|
51
|
+
}
|
|
52
|
+
declare function middlewareMatchesPath(entry: ResolvedMiddleware, requestPath: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Run all matching middleware. Returns a Response if any short-circuited, null otherwise.
|
|
55
|
+
*/
|
|
56
|
+
declare function runMiddleware(entries: ResolvedMiddleware[], request: Request, requestPath: string): Promise<Response | null>;
|
|
57
|
+
|
|
58
|
+
export { type DiContainer as D, MiddlewareConsumerImpl as M, type NestMiddleware as N, type ResolvedMiddleware as R, type MiddlewareConfigProxy as a, type MiddlewareFn as b, runMiddleware as c, middlewareMatchesPath as m, resolveOrNew as r };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { R as RuntimeAdapter } from './types-CGthbcon.js';
|
|
3
|
+
export { S as ServerHandle } from './types-CGthbcon.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Node.js runtime adapter — converts IncomingMessage/ServerResponse ↔ Request/Response.
|
|
7
|
+
*
|
|
8
|
+
* Per ADR D460: node:http types live ONLY in this adapter file.
|
|
9
|
+
* The rest of the pipeline operates on Web Standard Request/Response.
|
|
10
|
+
*
|
|
11
|
+
* EC-1: Body converted via Readable.toWeb() for streaming (no full buffering).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Convert Node.js IncomingMessage to Web Standard Request. */
|
|
15
|
+
declare function nodeIncomingToRequest(nodeReq: IncomingMessage): Request;
|
|
16
|
+
/** Write Web Standard Response to Node.js ServerResponse. */
|
|
17
|
+
declare function writeResponseToNode(response: Response, nodeRes: ServerResponse): Promise<void>;
|
|
18
|
+
/** Create a Node.js HTTP server that delegates to a Web Standard handler. */
|
|
19
|
+
declare function createNodeAdapter(): RuntimeAdapter;
|
|
20
|
+
|
|
21
|
+
export { RuntimeAdapter, createNodeAdapter, nodeIncomingToRequest, writeResponseToNode };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createNodeAdapter,
|
|
3
|
+
nodeIncomingToRequest,
|
|
4
|
+
writeResponseToNode
|
|
5
|
+
} from "./chunk-HLW7YKZE.js";
|
|
6
|
+
import "./chunk-7QVYU63E.js";
|
|
7
|
+
export {
|
|
8
|
+
createNodeAdapter,
|
|
9
|
+
nodeIncomingToRequest,
|
|
10
|
+
writeResponseToNode
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=runtime-node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { D as DiContainer, M as MiddlewareConsumerImpl } from './middleware-consumer-ljxK1fU_.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TheoKit integration plugin for @theokit/http.
|
|
5
|
+
*
|
|
6
|
+
* Two modes:
|
|
7
|
+
*
|
|
8
|
+
* **Mode 1 — controllersGlob (RECOMMENDED):**
|
|
9
|
+
* Controllers are discovered by glob pattern and loaded via @swc/core at
|
|
10
|
+
* first request. This bypasses the esbuild parameter-decorator limitation
|
|
11
|
+
* because controller files are never imported through tsx/esbuild.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* // theo.config.ts — NO controller import needed
|
|
15
|
+
* import { defineConfig } from 'theokit/server'
|
|
16
|
+
* import { httpDecoratorsPlugin } from '@theokit/http/theokit-plugin'
|
|
17
|
+
*
|
|
18
|
+
* export default defineConfig({
|
|
19
|
+
* plugins: [
|
|
20
|
+
* httpDecoratorsPlugin({
|
|
21
|
+
* controllersGlob: 'server/controllers/**\/*.controller.ts'
|
|
22
|
+
* })
|
|
23
|
+
* ]
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* **Mode 2 — direct controllers array (tests, pre-compiled):**
|
|
28
|
+
* Pass controller classes directly. Only works when the transpiler
|
|
29
|
+
* supports parameter decorators (e.g., SWC in Vitest, or manual
|
|
30
|
+
* Reflect.defineMetadata in tests).
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* httpDecoratorsPlugin({ controllers: [CatsController] })
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
interface HttpDecoratorsPluginOptions {
|
|
38
|
+
/** Direct controller class references (Mode 2 — tests, pre-compiled). */
|
|
39
|
+
controllers?: Function[];
|
|
40
|
+
/** Glob pattern relative to project root (Mode 1 — RECOMMENDED).
|
|
41
|
+
* Example: 'server/controllers/**\/*.controller.ts'
|
|
42
|
+
* Requires @swc/core as devDependency. */
|
|
43
|
+
controllersGlob?: string;
|
|
44
|
+
/** Optional DI container (e.g., @theokit/di Container). */
|
|
45
|
+
container?: DiContainer;
|
|
46
|
+
/** NestJS-style middleware configuration callback. */
|
|
47
|
+
configure?: (consumer: MiddlewareConsumerImpl) => void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* TheoKit plugin that mounts decorated controllers into the `theokit dev`
|
|
51
|
+
* / `theokit start` request pipeline via the `onRequest` hook.
|
|
52
|
+
*
|
|
53
|
+
* Returns a plain `{ name, register }` object compatible with
|
|
54
|
+
* `TheoPlugin` from `theokit/server` (Pattern D6 — we don't import the
|
|
55
|
+
* type to avoid adding theokit as a compile-time dep; the structural
|
|
56
|
+
* shape is sufficient).
|
|
57
|
+
*/
|
|
58
|
+
declare function httpDecoratorsPlugin(opts: HttpDecoratorsPluginOptions): {
|
|
59
|
+
name: string;
|
|
60
|
+
register(app: {
|
|
61
|
+
addHook: (name: string, fn: (ctx: Record<string, unknown>) => Promise<void>) => void;
|
|
62
|
+
}): void;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export { type HttpDecoratorsPluginOptions, httpDecoratorsPlugin };
|