express-file-cluster 0.2.11 → 0.3.1
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/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/chunk-MQNVNOH5.cjs.map +1 -1
- package/dist/chunk-QOKLEPTX.cjs.map +1 -1
- package/dist/chunk-XH7O25BL.cjs.map +1 -1
- package/dist/cli/index.cjs +26 -13
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +26 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +36 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +36 -20
- package/dist/index.js.map +1 -1
- package/dist/tasks/index.cjs.map +1 -1
- package/dist/tasks/index.d.cts +1 -1
- package/dist/tasks/index.d.ts +1 -1
- package/dist/{types-BZehD43d.d.cts → types-CFU2IhRW.d.cts} +7 -2
- package/dist/{types-BZehD43d.d.ts → types-CFU2IhRW.d.ts} +7 -2
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/router/mount.ts","../src/cluster/index.ts","../src/db/mongo.ts","../src/db/model.ts","../src/db/index.ts","../src/tasks/scanner.ts","../src/tasks/thread-runner.ts","../src/tasks/bullmq-backend.ts","../src/dashboard.ts","../src/errors.ts","../src/compose.ts"],"sourcesContent":["import http from 'node:http';\nimport fs from 'node:fs';\nimport express from 'express';\nimport cors from 'cors';\nimport cookieParser from 'cookie-parser';\nimport cluster from 'node:cluster';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { EFCConfig, MountedRoute } from './types.js';\nimport { scanDir } from './router/scan.js';\nimport { mountRoutes } from './router/mount.js';\nimport { runMaster, shutdownMaster } from './cluster/index.js';\nimport { configureAuth } from './auth/index.js';\nimport { HttpError } from './errors.js';\nimport { connectMongo } from './db/mongo.js';\nimport { setDbClient } from './db/index.js';\nimport { scanTasks } from './tasks/scanner.js';\nimport { initBullMQ } from './tasks/bullmq-backend.js';\nimport { generateDashboard } from './dashboard.js';\n\nfunction detectDatabase(url?: string): 'mongodb' | 'postgresql' | undefined {\n if (!url) return undefined;\n if (url.startsWith('mongodb')) return 'mongodb';\n if (url.startsWith('postgres')) return 'postgresql';\n return undefined;\n}\n\nexport async function ignite(config: EFCConfig): Promise<http.Server | undefined> {\n const {\n port: _port,\n workers,\n globalMiddlewares = [],\n onWorkerReady,\n onWorkerCrash,\n onError,\n } = config;\n\n // Resolve api/tasks dirs from convention. argv[1]-based resolution is unreliable in tsx watch\n // + cluster workers (argv[1] can point to the tsx binary instead of the user's script), so we\n // probe multiple candidates and use the first that exists on disk.\n function resolveConventionDir(name: string): string {\n const candidates: string[] = [\n ...(process.argv[1] ? [path.join(path.dirname(process.argv[1]), name)] : []),\n path.join(process.cwd(), 'src', name),\n path.join(process.cwd(), name),\n path.join(process.cwd(), 'dist', name),\n ];\n return candidates.find((c) => fs.existsSync(c)) ?? candidates[0]!;\n }\n const apiDir = resolveConventionDir('api');\n const tasksDir = resolveConventionDir('tasks');\n const basePath = config.basePath ?? '/v1/api';\n\n function readProjectMeta(): { name: string; version: string } {\n try {\n const raw = fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8');\n const pkg = JSON.parse(raw) as { name?: string; version?: string };\n return { name: pkg.name ?? 'API', version: pkg.version ?? '' };\n } catch {\n return { name: 'API', version: '' };\n }\n }\n\n const envPort = process.env['PORT'] != null ? Number(process.env['PORT']) : NaN;\n const port =\n _port != null && !Number.isNaN(_port) ? _port : !Number.isNaN(envPort) ? envPort : 3000;\n const databaseUrl = config.databaseUrl ?? process.env['DATABASE_URL'];\n const database = config.database ?? detectDatabase(databaseUrl);\n const jwtSecret = config.jwtSecret ?? process.env['JWT_SECRET'];\n const authStrategy = config.authStrategy ?? 'http-only';\n const clusterEnabled = config.cluster ?? process.env['NODE_ENV'] === 'production';\n\n if (clusterEnabled && cluster.isPrimary) {\n runMaster({\n workers: workers ?? os.cpus().length,\n ...(onWorkerReady !== undefined && { onWorkerReady }),\n ...(onWorkerCrash !== undefined && { onWorkerCrash }),\n });\n return;\n }\n\n const app = express();\n\n const corsOption = config.cors ?? true;\n if (corsOption !== false) {\n const envOrigins = process.env['CORS_ORIGINS'];\n let origin: string | string[] | boolean;\n if (envOrigins) {\n const list = envOrigins\n .split(',')\n .map((o) => o.trim())\n .filter(Boolean);\n origin = list; // always array so cors validates against the request Origin\n } else if (typeof corsOption === 'object' && corsOption.origin !== undefined) {\n origin = corsOption.origin;\n } else {\n origin = true;\n }\n const corsOpts = typeof corsOption === 'object' ? { ...corsOption, origin } : { origin };\n app.use(cors(corsOpts));\n }\n\n app.use(express.json());\n app.use(express.urlencoded({ extended: true }));\n app.use(cookieParser());\n\n for (const mw of globalMiddlewares) {\n app.use(mw);\n }\n\n // Pre-Flight step 1: Connect database\n if (database === 'mongodb' && databaseUrl) {\n const conn = await connectMongo(databaseUrl);\n setDbClient(conn as unknown as Record<string, unknown>);\n }\n\n // Pre-Flight step 2: Configure auth\n if (jwtSecret) {\n const cookieDomain = process.env['COOKIE_DOMAIN'];\n configureAuth({\n secret: jwtSecret,\n strategy: authStrategy,\n expiresIn: process.env['JWT_EXPIRES_IN'] ?? '7d',\n ...(cookieDomain !== undefined && { cookieDomain }),\n });\n }\n\n // Pre-Flight step 3: Scan and register tasks\n if (tasksDir) {\n await scanTasks(tasksDir);\n }\n\n // Pre-Flight step 4: Start task queue backend\n if (config.tasks) {\n if (config.tasks.backend === 'bullmq') {\n await initBullMQ({\n redisUrl: config.tasks.redisUrl ?? 'redis://localhost:6379',\n concurrency: config.tasks.concurrency ?? 5,\n });\n }\n }\n\n // Pre-Flight step 5: Scan routes and mount\n const routes = scanDir(apiDir);\n const apiRouter = express.Router();\n const mounted: MountedRoute[] = await mountRoutes(apiRouter, routes);\n app.use(basePath, apiRouter);\n\n if (config.dashboard !== false && process.env['NODE_ENV'] === 'development') {\n app.get('/', (_req, res) => {\n const { name, version } = readProjectMeta();\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.send(generateDashboard(mounted, basePath, port, name, version));\n });\n }\n\n if (onError) {\n app.use(onError);\n } else {\n app.use(\n (err: unknown, _req: express.Request, res: express.Response, _next: express.NextFunction) => {\n const asHttp = err instanceof Error && 'statusCode' in err ? (err as HttpError) : null;\n if (asHttp) {\n res\n .status(asHttp.statusCode)\n .json({ error: asHttp.message, statusCode: asHttp.statusCode });\n } else {\n console.error('[EFC] Unhandled error:', err);\n res.status(500).json({ error: 'Internal Server Error', statusCode: 500 });\n }\n },\n );\n }\n\n return new Promise<http.Server>((resolve, reject) => {\n const server = app.listen(port);\n\n server.once('listening', () => {\n const wid = (cluster.worker as { id: number } | undefined)?.id ?? 'primary';\n const addr = server.address() as { port: number } | null;\n console.log(`[EFC] Worker ${wid} listening on :${addr?.port ?? port}`);\n resolve(server);\n });\n\n server.once('error', reject);\n });\n}\n\nexport function gracefulShutdown(server: http.Server | undefined, timeoutMs = 10_000): void {\n const shutdown = (signal: string) => {\n console.log(`[EFC] ${signal} received — closing server gracefully…`);\n if (server) {\n server.closeIdleConnections();\n server.close(() => {\n console.log('[EFC] Server closed');\n process.exit(0);\n });\n setTimeout(() => {\n console.error('[EFC] Forced exit after timeout');\n process.exit(1);\n }, timeoutMs).unref();\n } else {\n shutdownMaster();\n }\n };\n process.once('SIGTERM', () => shutdown('SIGTERM'));\n process.once('SIGINT', () => shutdown('SIGINT'));\n}\n\nexport { HttpError } from './errors.js';\nexport { compose } from './compose.js';\nexport { db, setDbClient, getDbClient, defineModel } from './db/index.js';\nexport { scanDir } from './router/scan.js';\nexport type {\n EFCConfig,\n CorsConfig,\n RouteEntry,\n RouteMeta,\n TaskOptions,\n TaskDefinition,\n DatabaseEngine,\n AuthStrategy,\n ModelSchema,\n ModelCRUD,\n} from './types.js';\n","import type { IRouter, RequestHandler } from 'express';\nimport type { MountedRoute, RouteMeta, RouteEntry } from '../types.js';\n\nconst HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'] as const;\ntype HttpMethod = (typeof HTTP_METHODS)[number];\n\nfunction asyncWrap(handler: RequestHandler): RequestHandler {\n return (req, res, next) => {\n Promise.resolve(handler(req, res, next)).catch(next);\n };\n}\n\nexport async function mountRoutes(\n app: IRouter,\n routes: RouteEntry[],\n): Promise<MountedRoute[]> {\n const mounted: MountedRoute[] = [];\n\n for (const route of routes) {\n const mod = (await import(route.filePath)) as Record<string, unknown>;\n const routeMiddlewares: RequestHandler[] = Array.isArray(mod['middlewares'])\n ? (mod['middlewares'] as RequestHandler[])\n : [];\n\n const raw = mod['meta'];\n const meta: RouteMeta | undefined =\n raw != null && typeof raw === 'object' && !Array.isArray(raw)\n ? (raw as RouteMeta)\n : undefined;\n\n const implemented: HttpMethod[] = [];\n const unimplemented: HttpMethod[] = [];\n\n for (const method of HTTP_METHODS) {\n if (typeof mod[method] === 'function') {\n implemented.push(method);\n app[method.toLowerCase() as Lowercase<HttpMethod>](\n route.urlPath,\n ...routeMiddlewares,\n asyncWrap(mod[method] as RequestHandler),\n );\n } else {\n unimplemented.push(method);\n }\n }\n\n if (implemented.length > 0 && unimplemented.length > 0) {\n const allowHeader = implemented.join(', ');\n app.all(route.urlPath, (req, res, next) => {\n if ((unimplemented as string[]).includes(req.method)) {\n res.set('Allow', allowHeader).status(405).json({ error: 'Method Not Allowed' });\n } else {\n next();\n }\n });\n }\n\n mounted.push({ ...route, methods: implemented, ...(meta !== undefined && { meta }) });\n }\n\n return mounted;\n}\n\nexport { asyncWrap };\n","import cluster from 'node:cluster';\nimport os from 'node:os';\n\ninterface ClusterOptions {\n workers?: number;\n onWorkerReady?: ((id: number) => void) | undefined;\n onWorkerCrash?: ((id: number, code: number) => void) | undefined;\n}\n\nlet isShuttingDown = false;\n\nexport function shutdownMaster(): void {\n isShuttingDown = true;\n console.log('[EFC] Cluster master shutting down, waiting for workers…');\n}\n\nexport function runMaster(options: ClusterOptions = {}): void {\n const count = options.workers ?? os.cpus().length;\n\n console.log(`[EFC] Primary ${process.pid} starting ${count} workers`);\n\n for (let i = 0; i < count; i++) {\n cluster.fork();\n }\n\n cluster.on('online', (worker) => {\n options.onWorkerReady?.(worker.id);\n });\n\n cluster.on('exit', (worker, code, signal) => {\n const exitCode = code ?? (signal ? -1 : 0);\n if (isShuttingDown) {\n console.log(`[EFC] Worker ${worker.id} gracefully exited`);\n if (Object.keys(cluster.workers ?? {}).length === 0) {\n console.log('[EFC] All workers exited. Primary exiting.');\n process.exit(0);\n }\n return;\n }\n\n console.warn(`[EFC] Worker ${worker.id} exited (code=${exitCode}), respawning…`);\n options.onWorkerCrash?.(worker.id, exitCode);\n cluster.fork();\n });\n}\n\nexport { cluster };\n","// Wraps mongoose connection for Pre-Flight. Throws clearly if mongoose isn't installed.\n\nlet mongoose: typeof import('mongoose');\n\nasync function loadMongoose(): Promise<typeof import('mongoose')> {\n if (mongoose) return mongoose;\n try {\n mongoose = await import('mongoose');\n return mongoose;\n } catch {\n throw new Error('[EFC] mongoose is not installed. Run: npm install mongoose');\n }\n}\n\nexport async function connectMongo(url: string): Promise<import('mongoose').Connection> {\n const mg = await loadMongoose();\n await mg.connect(url);\n console.log('[EFC] MongoDB connected');\n return mg.connection;\n}\n","import type { ModelSchema, ModelCRUD } from '../types.js';\n\ntype AnyRecord = Record<string, unknown> & { id: string };\n\nfunction normalise(doc: Record<string, unknown>): AnyRecord {\n const id = doc['_id'] ? String(doc['_id']) : '';\n return { ...doc, id } as AnyRecord;\n}\n\nasync function getModel(\n name: string,\n schemaObj: Record<string, unknown>,\n): Promise<import('mongoose').Model<Record<string, unknown>>> {\n let mg: typeof import('mongoose');\n try {\n const mod = await import('mongoose');\n mg = (mod.default || mod) as typeof import('mongoose');\n } catch {\n throw new Error('[EFC] mongoose is not installed. Run: npm install mongoose');\n }\n\n if (mg.models[name]) return mg.models[name] as import('mongoose').Model<Record<string, unknown>>;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const schema = new mg.Schema(schemaObj as any, { timestamps: true });\n return mg.model<Record<string, unknown>>(name, schema);\n}\n\nfunction buildMongooseSchema(schema: ModelSchema): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, def] of Object.entries(schema)) {\n const entry: Record<string, unknown> = {};\n switch (def.type) {\n case 'string':\n entry['type'] = String;\n break;\n case 'number':\n entry['type'] = Number;\n break;\n case 'boolean':\n entry['type'] = Boolean;\n break;\n case 'date':\n entry['type'] = Date;\n break;\n case 'object':\n entry['type'] = Object;\n break;\n case 'array':\n entry['type'] = Array;\n break;\n }\n if (def.required !== undefined) entry['required'] = def.required;\n if (def.unique !== undefined) entry['unique'] = def.unique;\n if (def.default !== undefined) entry['default'] = def.default;\n out[key] = entry;\n }\n return out;\n}\n\nexport function defineModel<T extends Record<string, any>>(\n name: string,\n schema: ModelSchema,\n): ModelCRUD<T> {\n const mongooseSchema = buildMongooseSchema(schema);\n\n return {\n async find(filter = {}) {\n const M = await getModel(name, mongooseSchema);\n const docs = await M.find(filter as Record<string, unknown>).lean();\n return (docs as Record<string, unknown>[]).map(normalise) as (T & { id: string })[];\n },\n\n async findById(id) {\n const M = await getModel(name, mongooseSchema);\n const doc = await M.findById(id).lean();\n if (!doc) return null;\n return normalise(doc as Record<string, unknown>) as T & { id: string };\n },\n\n async findOne(filter) {\n const M = await getModel(name, mongooseSchema);\n const doc = await M.findOne(filter as Record<string, unknown>).lean();\n if (!doc) return null;\n return normalise(doc as Record<string, unknown>) as T & { id: string };\n },\n\n async create(data) {\n const M = await getModel(name, mongooseSchema);\n const doc = await M.create(data);\n return normalise(doc.toObject() as Record<string, unknown>) as T & { id: string };\n },\n\n async update(id, data) {\n const M = await getModel(name, mongooseSchema);\n const doc = await M.findByIdAndUpdate(id, data, { new: true }).lean();\n if (!doc) return null;\n return normalise(doc as Record<string, unknown>) as T & { id: string };\n },\n\n async delete(id) {\n const M = await getModel(name, mongooseSchema);\n await M.findByIdAndDelete(id);\n },\n\n async count(filter = {}) {\n const M = await getModel(name, mongooseSchema);\n return M.countDocuments(filter as Record<string, unknown>);\n },\n };\n}\n","// Thread-local DB client populated during Pre-Flight.\n// The Proxy ensures callers import `db` once and never null-check it —\n// it throws with a helpful message if accessed before Pre-Flight completes.\n\ntype AnyClient = Record<string, unknown>;\n\nlet _client: AnyClient | null = null;\n\nexport function setDbClient(client: AnyClient): void {\n _client = client;\n}\n\nexport function getDbClient(): AnyClient {\n if (!_client) {\n throw new Error('[EFC] db not ready — accessed before Pre-Flight completed');\n }\n return _client;\n}\n\nexport const db: AnyClient = new Proxy({} as AnyClient, {\n get(_target, prop: string) {\n return getDbClient()[prop];\n },\n set(_target, prop: string, value: unknown) {\n getDbClient()[prop] = value;\n return true;\n },\n});\n\nexport { connectMongo } from './mongo.js';\nexport { defineModel } from './model.js';\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { registerTask } from './index.js';\nimport type { TaskDefinition } from '../types.js';\n\nexport async function scanTasks(tasksDir: string): Promise<void> {\n if (!fs.existsSync(tasksDir)) return;\n\n const files = fs.readdirSync(tasksDir).filter((f) => /\\.(ts|js|mts|mjs)$/.test(f));\n\n for (const file of files) {\n const filePath = path.join(tasksDir, file);\n const taskName = path.basename(file, path.extname(file));\n\n try {\n const mod = (await import(filePath)) as Record<string, unknown>;\n const def = mod['default'] as TaskDefinition | undefined;\n\n if (!def || typeof def.handler !== 'function') {\n console.warn(`[EFC] Task file ${file} has no valid default export — skipping`);\n continue;\n }\n\n registerTask(taskName, { ...def, filePath });\n console.log(`[EFC] Registered task: ${taskName}`);\n } catch (err) {\n console.warn(`[EFC] Failed to load task ${file}:`, err);\n }\n }\n}\n","import { Worker, workerData, parentPort, isMainThread } from 'node:worker_threads';\nimport type { TaskDefinition } from '../types.js';\n\n// ── Worker-thread side ────────────────────────────────────────────────────────\nif (!isMainThread) {\n const { handlerPath, payload } = workerData as { handlerPath: string; payload: unknown };\n\n (async () => {\n try {\n const mod = (await import(handlerPath)) as Record<string, unknown>;\n const def = mod['default'] as TaskDefinition | undefined;\n if (!def || typeof def.handler !== 'function') {\n throw new Error(`[EFC] Thread runner: no valid task export in ${handlerPath}`);\n }\n await def.handler(payload);\n parentPort?.postMessage({ ok: true });\n } catch (err) {\n parentPort?.postMessage({ ok: false, error: String(err) });\n }\n })();\n}\n\n// ── Main-thread side ──────────────────────────────────────────────────────────\nexport function runInThread(handlerPath: string, payload: unknown): Promise<void> {\n return new Promise((resolve, reject) => {\n const worker = new Worker(new URL('./thread-runner.js', import.meta.url), {\n workerData: { handlerPath, payload },\n });\n\n worker.on('message', (msg: { ok: boolean; error?: string }) => {\n if (msg.ok) resolve();\n else reject(new Error(msg.error ?? 'Thread task failed'));\n });\n\n worker.on('error', reject);\n worker.on('exit', (code) => {\n if (code !== 0) reject(new Error(`Thread worker exited with code ${code}`));\n });\n });\n}\n","import type { Job } from 'bullmq';\nimport { taskRegistry, setEnqueueImpl } from './index.js';\nimport { runInThread } from './thread-runner.js';\n\ninterface BullMQOpts {\n redisUrl: string;\n concurrency: number;\n}\n\nexport async function initBullMQ(opts: BullMQOpts): Promise<void> {\n let bullmq: typeof import('bullmq');\n try {\n bullmq = await import('bullmq');\n } catch {\n throw new Error('[EFC] bullmq is not installed. Run: npm install bullmq');\n }\n\n const connection = parseRedisUrl(opts.redisUrl);\n const queue = new bullmq.Queue('efc', { connection });\n\n const worker = new bullmq.Worker(\n 'efc',\n async (job: Job) => {\n const def = taskRegistry.get(job.name);\n if (!def) {\n throw new Error(`[EFC] No task registered for name: ${job.name}`);\n }\n\n if (def.options.thread && def.filePath) {\n await runInThread(def.filePath, job.data);\n } else {\n await def.handler(job.data);\n }\n },\n { connection, concurrency: opts.concurrency },\n );\n\n worker.on('failed', (job: Job | undefined, err: Error) => {\n console.error(`[EFC] Task ${job?.name ?? 'unknown'} failed:`, err.message);\n });\n\n worker.on('completed', (job: Job) => {\n console.log(`[EFC] Task ${job.name} completed (id=${job.id})`);\n });\n\n setEnqueueImpl(async (name, payload) => {\n const def = taskRegistry.get(name);\n if (!def) {\n throw new Error(\n `[EFC] Cannot enqueue unknown task: \"${name}\". Is the task file in tasksDir?`,\n );\n }\n\n await queue.add(name, payload, {\n attempts: def.options.retries ?? 3,\n backoff: {\n type: def.options.backoff ?? 'exponential',\n delay: 1000,\n },\n });\n });\n\n console.log('[EFC] BullMQ backend initialised');\n}\n\nfunction parseRedisUrl(url: string): { host: string; port: number; password?: string } {\n try {\n const u = new URL(url);\n const result: { host: string; port: number; password?: string } = {\n host: u.hostname || 'localhost',\n port: parseInt(u.port || '6379', 10),\n };\n if (u.password) result.password = u.password;\n return result;\n } catch {\n return { host: 'localhost', port: 6379 };\n }\n}\n","import type { MountedRoute } from './types.js';\n\nconst HTTP_STATUS: Record<number, string> = {\n 200: 'OK',\n 201: 'Created',\n 202: 'Accepted',\n 204: 'No Content',\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Not Found',\n 405: 'Method Not Allowed',\n 409: 'Conflict',\n 422: 'Unprocessable Entity',\n 500: 'Internal Server Error',\n 503: 'Service Unavailable',\n};\n\nexport function generateDashboard(\n routes: MountedRoute[],\n basePath: string,\n port: number,\n projectName: string,\n projectVersion: string,\n): string {\n const safeRoutes = JSON.stringify(\n routes.map((r) => ({\n urlPath: r.urlPath,\n methods: r.methods,\n params: r.params,\n meta: r.meta ?? null,\n })),\n ).replace(/<\\/script>/gi, '<\\\\/script>');\n const safeHttpStatus = JSON.stringify(HTTP_STATUS);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${projectName} — API Reference</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap\" rel=\"stylesheet\" />\n <style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n :root {\n --bg: #080b0f;\n --bg-card: #0d1117;\n --bg-raised: #111820;\n --border: #1e2832;\n --border-lit: #263040;\n --text: #e2e8f0;\n --text-dim: #64748b;\n --text-muted: #334155;\n --accent: #F0A030;\n --accent-dim: rgba(240,160,48,.12);\n --accent-glow: rgba(240,160,48,.22);\n --purple: #a78bfa;\n --blue: #60a5fa;\n --orange: #fb923c;\n --green: #7AAE8E;\n --radius: 10px;\n --radius-lg: 16px;\n --font-mono: 'JetBrains Mono', monospace;\n --font-sans: 'Inter', sans-serif;\n }\n html { scroll-behavior: smooth; }\n body {\n background: var(--bg); color: var(--text);\n font-family: var(--font-sans); font-size: 16px;\n line-height: 1.6; -webkit-font-smoothing: antialiased;\n position: relative;\n }\n body::before {\n content: ''; position: fixed; inset: 0; z-index: 0;\n background-image:\n linear-gradient(var(--border) 1px, transparent 1px),\n linear-gradient(90deg, var(--border) 1px, transparent 1px);\n background-size: 60px 60px; opacity: .28; pointer-events: none;\n }\n section, footer, .page-hero, .docs-layout { position: relative; z-index: 1; }\n a { text-decoration: none; color: inherit; }\n em { font-style: italic; }\n code {\n font-family: var(--font-mono); font-size: .85em;\n background: var(--bg-raised); border: 1px solid var(--border);\n border-radius: 4px; padding: 1px 6px; color: var(--accent);\n }\n .container { max-width: 1140px; margin: 0 auto; padding: 0 24px; }\n ::-webkit-scrollbar { width: 6px; }\n ::-webkit-scrollbar-track { background: var(--bg); }\n ::-webkit-scrollbar-thumb { background: var(--border-lit); border-radius: 3px; }\n\n /* ── PAGE HERO ── */\n .page-hero {\n padding: 48px 0 56px; border-bottom: 1px solid var(--border);\n }\n .page-hero .hero-glow {\n position: absolute; top: -40px; left: 10%; width: 500px; height: 350px;\n background: radial-gradient(ellipse, rgba(240,160,48,.1) 0%, transparent 68%);\n pointer-events: none;\n }\n .page-title {\n font-size: clamp(2rem, 5vw, 3.4rem); font-weight: 800;\n line-height: 1.1; letter-spacing: -.02em; margin-bottom: 24px;\n display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap;\n }\n .project-version {\n font-family: var(--font-mono); font-size: clamp(.85rem, 2vw, 1.1rem);\n font-weight: 500; color: var(--accent);\n background: var(--accent-dim); border: 1px solid rgba(240,160,48,.3);\n border-radius: 6px; padding: 2px 10px; letter-spacing: .5px;\n }\n .hero-meta { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 40px; }\n .base-url-pill {\n display: flex; align-items: center; gap: 8px;\n background: var(--bg-card); border: 1px solid var(--border);\n border-radius: 7px; padding: 8px 14px;\n }\n .base-url-label { font-size: .72rem; font-family: var(--font-mono);\n color: var(--text-muted); text-transform: uppercase; letter-spacing: .5px; }\n .base-url-val { font-family: var(--font-mono); font-size: .8rem; color: var(--accent); }\n .count-pill {\n font-family: var(--font-mono); font-size: .72rem; color: var(--text-dim);\n background: var(--bg-card); border: 1px solid var(--border);\n border-radius: 7px; padding: 8px 14px;\n }\n\n /* ── TOOLBAR ── */\n .toolbar {\n display: flex; align-items: center; gap: 12px; flex-wrap: wrap;\n }\n .method-filters { display: flex; gap: 4px; flex-wrap: wrap; }\n .filter-btn {\n background: var(--bg-card); border: 1px solid var(--border);\n color: var(--text-dim); font-family: var(--font-mono);\n font-size: .72rem; font-weight: 600; letter-spacing: .5px;\n padding: 6px 12px; border-radius: 6px; cursor: pointer;\n transition: all .15s;\n }\n .filter-btn:hover { border-color: var(--border-lit); color: var(--text); }\n .filter-btn.active { background: var(--accent); color: #0d0d1a; border-color: var(--accent); }\n .filter-btn[data-method=\"GET\"].active { background: rgba(96,165,250,.18); color: #60a5fa; border-color: rgba(96,165,250,.4); }\n .filter-btn[data-method=\"POST\"].active { background: rgba(122,174,142,.18); color: #7AAE8E; border-color: rgba(122,174,142,.4); }\n .filter-btn[data-method=\"PUT\"].active { background: rgba(240,160,48,.18); color: #F0A030; border-color: rgba(240,160,48,.4); }\n .filter-btn[data-method=\"PATCH\"].active { background: rgba(251,191,36,.18); color: #fbbf24; border-color: rgba(251,191,36,.4); }\n .filter-btn[data-method=\"DELETE\"].active { background: rgba(248,113,113,.18); color: #f87171; border-color: rgba(248,113,113,.4); }\n .filter-btn[data-method=\"HEAD\"].active { background: rgba(167,139,250,.18); color: #a78bfa; border-color: rgba(167,139,250,.4); }\n .search-wrap { margin-left: auto; }\n .search-input {\n background: var(--bg-card); border: 1px solid var(--border);\n color: var(--text); font-family: var(--font-sans); font-size: .85rem;\n padding: 7px 14px; border-radius: 7px; width: 240px;\n outline: none; transition: border-color .2s;\n }\n .search-input::placeholder { color: var(--text-muted); }\n .search-input:focus { border-color: var(--border-lit); }\n\n /* ── DOCS LAYOUT ── */\n .docs-layout {\n display: grid; grid-template-columns: 260px 1fr;\n gap: 48px; padding: 52px 0 80px; max-width: 1140px;\n margin: 0 auto; padding-left: 24px; padding-right: 24px;\n align-items: start;\n }\n\n /* ── SIDEBAR ── */\n .docs-sidebar {\n position: sticky; top: 24px;\n max-height: calc(100vh - 48px);\n overflow-y: auto;\n overflow-x: hidden;\n scrollbar-width: none;\n padding-right: 4px;\n }\n .docs-sidebar::-webkit-scrollbar { display: none; }\n .sidebar-section { margin-bottom: 28px; }\n .sidebar-label {\n font-family: var(--font-mono); font-size: .68rem; font-weight: 600;\n color: var(--text-muted); letter-spacing: 1.2px; text-transform: uppercase;\n margin-bottom: 8px; padding: 0 4px;\n }\n .sidebar-link {\n display: flex; align-items: center; gap: 8px;\n padding: 7px 10px; border-radius: 6px;\n border-left: 2px solid transparent;\n font-size: .82rem; color: var(--text-dim); cursor: pointer;\n transition: background .15s, color .15s, border-color .15s;\n overflow: hidden; white-space: nowrap;\n }\n .sidebar-link:hover { background: var(--bg-raised); color: var(--text); }\n .sidebar-link.active { color: var(--accent); border-left-color: var(--accent); background: var(--accent-dim); }\n .sidebar-method-dot {\n width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0;\n }\n .sidebar-path { font-family: var(--font-mono); font-size: .72rem; }\n\n /* ── ENDPOINT CARDS ── */\n .endpoint-card {\n background: var(--bg-card); border: 1px solid var(--border);\n border-radius: var(--radius-lg); margin-bottom: 20px;\n overflow: hidden; transition: border-color .2s;\n animation: fadeUp .45s ease both;\n }\n .endpoint-card:hover { border-color: var(--border-lit); }\n @keyframes fadeUp {\n from { opacity: 0; transform: translateY(16px); }\n to { opacity: 1; transform: translateY(0); }\n }\n @media (prefers-reduced-motion: reduce) {\n .endpoint-card { animation: none; }\n }\n .endpoint-header {\n display: flex; align-items: center; gap: 14px;\n padding: 20px 24px 18px; border-bottom: 1px solid var(--border);\n flex-wrap: wrap;\n }\n .method-badges { display: flex; gap: 6px; flex-wrap: wrap; }\n .method-badge {\n font-family: var(--font-mono); font-size: .7rem; font-weight: 600;\n letter-spacing: .8px; padding: 4px 10px; border-radius: 5px;\n border: 1px solid; text-transform: uppercase;\n }\n .endpoint-path {\n font-family: var(--font-mono); font-size: .9rem; color: var(--text);\n background: none; border: none; padding: 0;\n word-break: break-all;\n }\n .endpoint-body { padding: 18px 24px 24px; }\n .endpoint-desc { font-size: .9rem; color: var(--text-dim); line-height: 1.7; margin-bottom: 20px; }\n\n /* ── CODE BLOCKS ── */\n .example-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }\n .code-block-wrap {\n background: var(--bg-raised); border: 1px solid var(--border);\n border-radius: var(--radius); overflow: hidden;\n }\n .code-bar {\n display: flex; align-items: center; gap: 6px;\n padding: 9px 14px; border-bottom: 1px solid var(--border);\n background: var(--bg-card);\n }\n .dot { width: 11px; height: 11px; border-radius: 50%; }\n .dot-r { background: #ff5f57; }\n .dot-y { background: #febc2e; }\n .dot-g { background: #28c840; }\n .code-label { font-family: var(--font-mono); font-size: .68rem; color: var(--text-dim); margin-left: 4px; }\n pre.code-pre {\n font-family: var(--font-mono); font-size: .78rem; line-height: 1.7;\n padding: 16px 18px; overflow-x: auto; tab-size: 2; white-space: pre;\n }\n .c-dim { color: var(--text-muted); }\n .c-green { color: #7AAE8E; }\n .c-blue { color: #60a5fa; }\n .c-orange { color: #fb923c; }\n .c-purple { color: #a78bfa; }\n .c-accent { color: var(--accent); }\n .status-2xx { color: #7AAE8E; }\n .status-4xx { color: #f87171; }\n .status-5xx { color: #fb923c; }\n\n /* ── EMPTY STATE ── */\n .empty-state {\n text-align: center; padding: 80px 24px;\n color: var(--text-muted); font-size: .9rem;\n }\n\n /* ── RESPONSIVE ── */\n @media (max-width: 900px) {\n .docs-layout { grid-template-columns: 1fr; }\n .docs-sidebar { position: static; max-height: none; overflow-y: visible; padding-right: 0; }\n .example-grid { grid-template-columns: 1fr; }\n .search-wrap { margin-left: 0; width: 100%; }\n .search-input { width: 100%; }\n }\n @media (max-width: 600px) {\n .page-hero { padding: 32px 0 32px; }\n .page-title { font-size: 2rem; }\n }\n </style>\n</head>\n<body>\n\n <div class=\"page-hero\">\n <div class=\"hero-glow\"></div>\n <div class=\"container\">\n <h1 class=\"page-title\">${projectName}${projectVersion ? `<span class=\"project-version\">v${projectVersion}</span>` : ''}</h1>\n <div class=\"hero-meta\">\n <div class=\"base-url-pill\">\n <span class=\"base-url-label\">Base</span>\n <span class=\"base-url-val\">http://localhost:${port}${basePath}</span>\n </div>\n <div class=\"count-pill\" id=\"countPill\"></div>\n </div>\n <div class=\"toolbar\">\n <div class=\"method-filters\" id=\"methodFilters\">\n <button class=\"filter-btn active\" data-method=\"ALL\">ALL</button>\n </div>\n <div class=\"search-wrap\">\n <input type=\"text\" id=\"searchInput\" class=\"search-input\" placeholder=\"Search endpoints…\" />\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"docs-layout\">\n <aside class=\"docs-sidebar\" id=\"sidebar\"></aside>\n <main id=\"endpoints\"></main>\n </div>\n\n <script>\n const ROUTES = ${safeRoutes};\n const BASE_PATH = ${JSON.stringify(basePath)};\n const HTTP_STATUS = ${safeHttpStatus};\n\n const METHOD_STYLE = {\n GET: { bg: 'rgba(96,165,250,.14)', color: '#60a5fa', border: 'rgba(96,165,250,.35)' },\n POST: { bg: 'rgba(122,174,142,.14)', color: '#7AAE8E', border: 'rgba(122,174,142,.35)' },\n PUT: { bg: 'rgba(240,160,48,.14)', color: '#F0A030', border: 'rgba(240,160,48,.35)' },\n PATCH: { bg: 'rgba(251,191,36,.14)', color: '#fbbf24', border: 'rgba(251,191,36,.35)' },\n DELETE: { bg: 'rgba(248,113,113,.14)', color: '#f87171', border: 'rgba(248,113,113,.35)' },\n HEAD: { bg: 'rgba(167,139,250,.14)', color: '#a78bfa', border: 'rgba(167,139,250,.35)' },\n OPTIONS: { bg: 'rgba(148,163,184,.14)', color: '#94a3b8', border: 'rgba(148,163,184,.35)' },\n };\n\n function esc(s) {\n return String(s)\n .replace(/&/g,'&').replace(/</g,'<')\n .replace(/>/g,'>').replace(/\"/g,'"');\n }\n\n function methodBadge(m) {\n const s = METHOD_STYLE[m] || METHOD_STYLE.GET;\n return '<span class=\"method-badge\" style=\"background:' + s.bg + ';color:' + s.color + ';border-color:' + s.border + '\">' + m + '</span>';\n }\n\n function joinPath(base, p) {\n return (base.replace(/\\\\/+$/, '') + p) || '/';\n }\n\n function sidebarDot(method) {\n const s = METHOD_STYLE[method] || METHOD_STYLE.GET;\n return '<span class=\"sidebar-method-dot\" style=\"background:' + s.color + '\"></span>';\n }\n\n function statusClass(code) {\n if (code >= 200 && code < 300) return 'status-2xx';\n if (code >= 400 && code < 500) return 'status-4xx';\n if (code >= 500) return 'status-5xx';\n return '';\n }\n\n function renderSchema(val, indent) {\n const pad = ' '.repeat(indent || 0);\n if (val === null) return '<span class=\"c-dim\">null</span>';\n if (typeof val === 'string') {\n const isDate = /^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}/.test(val);\n return isDate ? '<span class=\"c-green\">Date</span>' : '<span class=\"c-purple\">String</span>';\n }\n if (typeof val === 'number') return '<span class=\"c-orange\">Number</span>';\n if (typeof val === 'boolean') return '<span class=\"c-purple\">Boolean</span>';\n if (Array.isArray(val)) {\n if (!val.length) return '[]';\n return '[\\\\n' + pad + ' ' + renderSchema(val[0], (indent || 0) + 1) + '\\\\n' + pad + ']';\n }\n if (typeof val === 'object') {\n const keys = Object.keys(val);\n if (!keys.length) return '{}';\n const inner = keys.map(function(k) {\n return pad + ' <span class=\"c-blue\">"' + esc(k) + '"</span>: ' + renderSchema(val[k], (indent || 0) + 1);\n }).join(',\\\\n');\n return '{\\\\n' + inner + '\\\\n' + pad + '}';\n }\n return esc(String(val));\n }\n\n function codeBlock(label, content) {\n return '<div class=\"code-block-wrap\">' +\n '<div class=\"code-bar\">' +\n '<span class=\"dot dot-r\"></span>' +\n '<span class=\"dot dot-y\"></span>' +\n '<span class=\"dot dot-g\"></span>' +\n '<span class=\"code-label\">' + esc(label) + '</span>' +\n '</div>' +\n '<pre class=\"code-pre\">' + content + '</pre>' +\n '</div>';\n }\n\n function buildRequestContent(route, example) {\n const req = example || {};\n const method = route.methods[0] || 'GET';\n let path = joinPath(BASE_PATH, route.urlPath);\n if (req.params) {\n for (const [k, v] of Object.entries(req.params)) {\n path = path.replace(':' + k, String(v));\n }\n }\n if (req.query && Object.keys(req.query).length) {\n path += '?' + new URLSearchParams(req.query).toString();\n }\n let lines = '<span class=\"c-accent\">' + esc(method) + '</span> <span class=\"c-blue\">' + esc(path) + '</span>';\n const headers = { ...(req.headers || {}) };\n if (req.body !== undefined && !headers['Content-Type']) {\n headers['Content-Type'] = 'application/json';\n }\n if (Object.keys(headers).length) {\n lines += '\\\\n' + Object.entries(headers)\n .map(([k, v]) => '<span class=\"c-dim\">' + esc(k) + ':</span> ' + esc(String(v)))\n .join('\\\\n');\n }\n if (req.body !== undefined) {\n lines += '\\\\n\\\\n' + renderSchema(req.body, 0);\n }\n return lines;\n }\n\n function buildResponseContent(example) {\n const resp = example || {};\n const code = resp.status || 200;\n const text = HTTP_STATUS[code] || 'OK';\n const cls = statusClass(code);\n let lines = 'HTTP/1.1 <span class=\"' + cls + '\">' + code + ' ' + esc(text) + '</span>';\n if (resp.body !== undefined) {\n lines += '\\\\n<span class=\"c-dim\">Content-Type:</span> application/json\\\\n\\\\n';\n lines += renderSchema(resp.body, 0);\n }\n return lines;\n }\n\n function renderCard(route, index) {\n const id = 'ep-' + index;\n const fullPath = joinPath(BASE_PATH, route.urlPath);\n const badges = route.methods.map(methodBadge).join('');\n const meta = route.meta;\n const desc = meta && meta.description\n ? '<p class=\"endpoint-desc\">' + esc(meta.description) + '</p>'\n : '';\n let examples = '';\n if (meta) {\n const reqHtml = meta.request !== undefined\n ? codeBlock('request', buildRequestContent(route, meta.request))\n : '';\n const respHtml = meta.response !== undefined\n ? codeBlock('response', buildResponseContent(meta.response))\n : '';\n if (reqHtml || respHtml) {\n examples = '<div class=\"example-grid\">' + reqHtml + respHtml + '</div>';\n }\n }\n const delay = (index * 0.06).toFixed(2) + 's';\n return '<div class=\"endpoint-card\" id=\"' + id + '\" data-methods=\"' + route.methods.join(',') + '\" data-path=\"' + esc(fullPath) + '\" style=\"animation-delay:' + delay + '\">' +\n '<div class=\"endpoint-header\">' +\n '<div class=\"method-badges\">' + badges + '</div>' +\n '<code class=\"endpoint-path\">' + esc(fullPath) + '</code>' +\n '</div>' +\n '<div class=\"endpoint-body\">' + desc + examples + '</div>' +\n '</div>';\n }\n\n function buildMethodFilters() {\n const allMethods = [...new Set(ROUTES.flatMap(r => r.methods))].sort();\n const filtersEl = document.getElementById('methodFilters');\n allMethods.forEach(m => {\n const btn = document.createElement('button');\n btn.className = 'filter-btn';\n btn.dataset.method = m;\n btn.textContent = m;\n filtersEl.appendChild(btn);\n });\n }\n\n function buildSidebar(routes) {\n const sidebar = document.getElementById('sidebar');\n if (!routes.length) { sidebar.innerHTML = ''; return; }\n\n const label = document.createElement('div');\n label.className = 'sidebar-label';\n label.textContent = 'Endpoints';\n sidebar.appendChild(label);\n\n routes.forEach((route, i) => {\n const link = document.createElement('div');\n link.className = 'sidebar-link';\n link.dataset.target = 'ep-' + ROUTES.indexOf(route);\n link.innerHTML = sidebarDot(route.methods[0] || 'GET') +\n '<span class=\"sidebar-path\">' + esc(route.urlPath || '/') + '</span>';\n link.addEventListener('click', () => {\n const target = document.getElementById(link.dataset.target);\n if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n });\n sidebar.appendChild(link);\n });\n }\n\n let activeFilter = 'ALL';\n let searchQuery = '';\n\n function applyFilters() {\n const cards = document.querySelectorAll('.endpoint-card');\n let visible = 0;\n cards.forEach(card => {\n const methods = card.dataset.methods.split(',');\n const path = card.dataset.path || '';\n const matchMethod = activeFilter === 'ALL' || methods.includes(activeFilter);\n const matchSearch = !searchQuery || path.toLowerCase().includes(searchQuery);\n const show = matchMethod && matchSearch;\n card.style.display = show ? '' : 'none';\n if (show) visible++;\n });\n document.getElementById('empty-state').style.display = visible ? 'none' : 'block';\n\n // Sync sidebar visibility\n document.querySelectorAll('.sidebar-link').forEach(link => {\n const targetId = link.dataset.target;\n const targetCard = document.getElementById(targetId);\n link.style.display = targetCard && targetCard.style.display !== 'none' ? '' : 'none';\n });\n }\n\n function init() {\n const count = ROUTES.length;\n document.getElementById('countPill').textContent = count + ' endpoint' + (count !== 1 ? 's' : '');\n\n buildMethodFilters();\n\n const container = document.getElementById('endpoints');\n container.innerHTML = ROUTES.map((r, i) => renderCard(r, i)).join('');\n\n // Add empty state after cards\n const emptyEl = document.createElement('div');\n emptyEl.id = 'empty-state';\n emptyEl.className = 'empty-state';\n emptyEl.style.display = 'none';\n emptyEl.innerHTML = '<p>No endpoints match your filter.</p>';\n container.after(emptyEl);\n\n buildSidebar(ROUTES);\n\n // Method filter clicks\n document.getElementById('methodFilters').addEventListener('click', e => {\n const btn = e.target.closest('.filter-btn');\n if (!btn) return;\n document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));\n btn.classList.add('active');\n activeFilter = btn.dataset.method;\n applyFilters();\n });\n\n // Search\n document.getElementById('searchInput').addEventListener('input', e => {\n searchQuery = e.target.value.trim().toLowerCase();\n applyFilters();\n });\n\n // Scroll spy for sidebar\n const cardEls = document.querySelectorAll('.endpoint-card');\n const sidebarLinks = document.querySelectorAll('.sidebar-link');\n const observer = new IntersectionObserver(entries => {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n const id = entry.target.id;\n sidebarLinks.forEach(l => l.classList.toggle('active', l.dataset.target === id));\n const sidebar = document.getElementById('sidebar');\n const activeLink = sidebar && sidebar.querySelector('.sidebar-link.active');\n if (sidebar && activeLink) {\n const linkTop = activeLink.getBoundingClientRect().top - sidebar.getBoundingClientRect().top + sidebar.scrollTop;\n sidebar.scrollTo({ top: linkTop, behavior: 'smooth' });\n }\n }\n });\n }, { rootMargin: '-80px 0px -60% 0px' });\n cardEls.forEach(el => observer.observe(el));\n\n }\n\n try {\n init();\n } catch (e) {\n document.body.insertAdjacentHTML('afterbegin',\n '<div style=\"position:fixed;top:60px;left:0;right:0;z-index:9999;background:#7f1d1d;color:#fca5a5;padding:16px 24px;font-family:monospace;font-size:13px;border-bottom:1px solid #ef4444\">' +\n '<strong>Dashboard JS error:</strong> ' + (e && e.message ? e.message : String(e)) +\n (e && e.stack ? '<pre style=\"margin-top:8px;white-space:pre-wrap\">' + e.stack + '</pre>' : '') +\n '</div>'\n );\n }\n </script>\n</body>\n</html>`;\n}\n","export class HttpError extends Error {\n readonly statusCode: number;\n\n constructor(statusCode: number, message: string) {\n super(message);\n this.name = 'HttpError';\n this.statusCode = statusCode;\n Error.captureStackTrace(this, HttpError);\n }\n}\n","import type { RequestHandler } from 'express';\n\nexport function compose(...handlers: RequestHandler[]): RequestHandler {\n return (req, res, next) => {\n let index = 0;\n\n function dispatch(i: number): void {\n if (i >= handlers.length) {\n next();\n return;\n }\n const handler = handlers[i];\n if (!handler) {\n next();\n return;\n }\n try {\n Promise.resolve(handler(req, res, () => dispatch(i + 1))).catch(next);\n } catch (err) {\n next(err);\n }\n }\n\n dispatch(index);\n };\n}\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["fs","cluster","os","path","path","fs","cluster","os"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/router/mount.ts","../src/cluster/index.ts","../src/db/mongo.ts","../src/db/model.ts","../src/db/index.ts","../src/tasks/scanner.ts","../src/tasks/thread-runner.ts","../src/tasks/bullmq-backend.ts","../src/dashboard.ts","../src/errors.ts","../src/compose.ts"],"sourcesContent":["import http from 'node:http';\nimport fs from 'node:fs';\nimport express from 'express';\nimport cors from 'cors';\nimport cookieParser from 'cookie-parser';\nimport cluster from 'node:cluster';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { EFCConfig, MountedRoute } from './types.js';\nimport { scanDir } from './router/scan.js';\nimport { mountRoutes } from './router/mount.js';\nimport { runMaster, shutdownMaster } from './cluster/index.js';\nimport { configureAuth } from './auth/index.js';\nimport { HttpError } from './errors.js';\nimport { connectMongo } from './db/mongo.js';\nimport { setDbClient } from './db/index.js';\nimport { scanTasks } from './tasks/scanner.js';\nimport { initBullMQ } from './tasks/bullmq-backend.js';\nimport { generateDashboard } from './dashboard.js';\n\nfunction detectDatabase(url?: string): 'mongodb' | 'postgresql' | undefined {\n if (!url) return undefined;\n if (url.startsWith('mongodb')) return 'mongodb';\n if (url.startsWith('postgres')) return 'postgresql';\n return undefined;\n}\n\nexport async function ignite(config: EFCConfig): Promise<http.Server | undefined> {\n const {\n port: _port,\n workers,\n globalMiddlewares = [],\n onWorkerReady,\n onWorkerCrash,\n onError,\n } = config;\n\n // Resolve api/tasks dirs from convention. argv[1]-based resolution is unreliable in tsx watch\n // + cluster workers (argv[1] can point to the tsx binary instead of the user's script), so we\n // probe multiple candidates and use the first that exists on disk.\n function resolveConventionDir(name: string): string {\n const candidates: string[] = [\n ...(process.argv[1] ? [path.join(path.dirname(process.argv[1]), name)] : []),\n path.join(process.cwd(), 'src', name),\n path.join(process.cwd(), name),\n path.join(process.cwd(), 'dist', name),\n ];\n return candidates.find((c) => fs.existsSync(c)) ?? candidates[0]!;\n }\n const apiDir = resolveConventionDir('api');\n const tasksDir = resolveConventionDir('tasks');\n const basePath = config.basePath ?? '/v1/api';\n\n function readProjectMeta(): { name: string; version: string } {\n try {\n const raw = fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8');\n const pkg = JSON.parse(raw) as { name?: string; version?: string };\n return { name: pkg.name ?? 'API', version: pkg.version ?? '' };\n } catch {\n return { name: 'API', version: '' };\n }\n }\n\n const envPort = process.env['PORT'] != null ? Number(process.env['PORT']) : NaN;\n const port =\n _port != null && !Number.isNaN(_port) ? _port : !Number.isNaN(envPort) ? envPort : 3000;\n const databaseUrl = config.databaseUrl ?? process.env['DATABASE_URL'];\n const database = config.database ?? detectDatabase(databaseUrl);\n const jwtSecret = config.jwtSecret ?? process.env['JWT_SECRET'];\n const authStrategy = config.authStrategy ?? 'http-only';\n const clusterEnabled = config.cluster ?? process.env['NODE_ENV'] === 'production';\n\n if (clusterEnabled && cluster.isPrimary) {\n runMaster({\n workers: workers ?? os.cpus().length,\n ...(onWorkerReady !== undefined && { onWorkerReady }),\n ...(onWorkerCrash !== undefined && { onWorkerCrash }),\n });\n return;\n }\n\n const app = express();\n\n const corsOption = config.cors ?? true;\n if (corsOption !== false) {\n const envOrigins = process.env['CORS_ORIGINS'];\n let origin: string | string[] | boolean;\n if (envOrigins) {\n const list = envOrigins\n .split(',')\n .map((o) => o.trim())\n .filter(Boolean);\n origin = list; // always array so cors validates against the request Origin\n } else if (typeof corsOption === 'object' && corsOption.origin !== undefined) {\n origin = corsOption.origin;\n } else {\n origin = true;\n }\n const corsOpts = typeof corsOption === 'object' ? { ...corsOption, origin } : { origin };\n app.use(cors(corsOpts));\n }\n\n app.use(express.json());\n app.use(express.urlencoded({ extended: true }));\n app.use(cookieParser());\n\n for (const mw of globalMiddlewares) {\n app.use(mw);\n }\n\n // Pre-Flight step 1: Connect database\n if (database === 'mongodb' && databaseUrl) {\n const conn = await connectMongo(databaseUrl);\n setDbClient(conn as unknown as Record<string, unknown>);\n }\n\n // Pre-Flight step 2: Configure auth\n if (jwtSecret) {\n const cookieDomain = process.env['COOKIE_DOMAIN'];\n configureAuth({\n secret: jwtSecret,\n strategy: authStrategy,\n expiresIn: process.env['JWT_EXPIRES_IN'] ?? '7d',\n ...(cookieDomain !== undefined && { cookieDomain }),\n });\n }\n\n // Pre-Flight step 3: Scan and register tasks\n if (tasksDir) {\n await scanTasks(tasksDir);\n }\n\n // Pre-Flight step 4: Start task queue backend\n if (config.tasks) {\n if (config.tasks.backend === 'bullmq') {\n await initBullMQ({\n redisUrl: config.tasks.redisUrl ?? 'redis://localhost:6379',\n concurrency: config.tasks.concurrency ?? 5,\n });\n }\n }\n\n // Pre-Flight step 5: Scan routes and mount\n const routes = scanDir(apiDir);\n const apiRouter = express.Router();\n const mounted: MountedRoute[] = await mountRoutes(apiRouter, routes);\n app.use(basePath, apiRouter);\n\n if (config.dashboard !== false && process.env['NODE_ENV'] === 'development') {\n app.get('/', (_req, res) => {\n const { name, version } = readProjectMeta();\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.send(generateDashboard(mounted, basePath, port, name, version));\n });\n }\n\n if (onError) {\n app.use(onError);\n } else {\n app.use(\n (err: unknown, _req: express.Request, res: express.Response, _next: express.NextFunction) => {\n const asHttp = err instanceof Error && 'statusCode' in err ? (err as HttpError) : null;\n if (asHttp) {\n res\n .status(asHttp.statusCode)\n .json({ error: asHttp.message, statusCode: asHttp.statusCode });\n } else {\n console.error('[EFC] Unhandled error:', err);\n res.status(500).json({ error: 'Internal Server Error', statusCode: 500 });\n }\n },\n );\n }\n\n return new Promise<http.Server>((resolve, reject) => {\n const server = app.listen(port);\n\n server.once('listening', () => {\n const wid = (cluster.worker as { id: number } | undefined)?.id ?? 'primary';\n const addr = server.address() as { port: number } | null;\n console.log(`[EFC] Worker ${wid} listening on :${addr?.port ?? port}`);\n resolve(server);\n });\n\n server.once('error', reject);\n });\n}\n\nexport function gracefulShutdown(server: http.Server | undefined, timeoutMs = 10_000): void {\n const shutdown = (signal: string) => {\n console.log(`[EFC] ${signal} received — closing server gracefully…`);\n if (server) {\n server.closeIdleConnections();\n server.close(() => {\n console.log('[EFC] Server closed');\n process.exit(0);\n });\n setTimeout(() => {\n console.error('[EFC] Forced exit after timeout');\n process.exit(1);\n }, timeoutMs).unref();\n } else {\n shutdownMaster();\n }\n };\n process.once('SIGTERM', () => shutdown('SIGTERM'));\n process.once('SIGINT', () => shutdown('SIGINT'));\n}\n\nexport { HttpError } from './errors.js';\nexport { compose } from './compose.js';\nexport { db, setDbClient, getDbClient, defineModel } from './db/index.js';\nexport { scanDir } from './router/scan.js';\nexport type {\n EFCConfig,\n CorsConfig,\n RouteEntry,\n RouteMeta,\n RouteMethodMeta,\n TaskOptions,\n TaskDefinition,\n DatabaseEngine,\n AuthStrategy,\n ModelSchema,\n ModelCRUD,\n} from './types.js';\n","import type { IRouter, RequestHandler } from 'express';\nimport type { MountedRoute, RouteMeta, RouteEntry } from '../types.js';\n\nconst HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'] as const;\ntype HttpMethod = (typeof HTTP_METHODS)[number];\n\nfunction asyncWrap(handler: RequestHandler): RequestHandler {\n return (req, res, next) => {\n Promise.resolve(handler(req, res, next)).catch(next);\n };\n}\n\nexport async function mountRoutes(\n app: IRouter,\n routes: RouteEntry[],\n): Promise<MountedRoute[]> {\n const mounted: MountedRoute[] = [];\n\n for (const route of routes) {\n const mod = (await import(route.filePath)) as Record<string, unknown>;\n const routeMiddlewares: RequestHandler[] = Array.isArray(mod['middlewares'])\n ? (mod['middlewares'] as RequestHandler[])\n : [];\n\n const raw = mod['meta'];\n const meta: RouteMeta | undefined =\n raw != null && typeof raw === 'object' && !Array.isArray(raw)\n ? (raw as RouteMeta)\n : undefined;\n\n const implemented: HttpMethod[] = [];\n const unimplemented: HttpMethod[] = [];\n\n for (const method of HTTP_METHODS) {\n if (typeof mod[method] === 'function') {\n implemented.push(method);\n app[method.toLowerCase() as Lowercase<HttpMethod>](\n route.urlPath,\n ...routeMiddlewares,\n asyncWrap(mod[method] as RequestHandler),\n );\n } else {\n unimplemented.push(method);\n }\n }\n\n if (implemented.length > 0 && unimplemented.length > 0) {\n const allowHeader = implemented.join(', ');\n app.all(route.urlPath, (req, res, next) => {\n if ((unimplemented as string[]).includes(req.method)) {\n res.set('Allow', allowHeader).status(405).json({ error: 'Method Not Allowed' });\n } else {\n next();\n }\n });\n }\n\n mounted.push({ ...route, methods: implemented, ...(meta !== undefined && { meta }) });\n }\n\n return mounted;\n}\n\nexport { asyncWrap };\n","import cluster from 'node:cluster';\nimport os from 'node:os';\n\ninterface ClusterOptions {\n workers?: number;\n onWorkerReady?: ((id: number) => void) | undefined;\n onWorkerCrash?: ((id: number, code: number) => void) | undefined;\n}\n\nlet isShuttingDown = false;\n\nexport function shutdownMaster(): void {\n isShuttingDown = true;\n console.log('[EFC] Cluster master shutting down, waiting for workers…');\n}\n\nexport function runMaster(options: ClusterOptions = {}): void {\n const count = options.workers ?? os.cpus().length;\n\n console.log(`[EFC] Primary ${process.pid} starting ${count} workers`);\n\n for (let i = 0; i < count; i++) {\n cluster.fork();\n }\n\n cluster.on('online', (worker) => {\n options.onWorkerReady?.(worker.id);\n });\n\n cluster.on('exit', (worker, code, signal) => {\n const exitCode = code ?? (signal ? -1 : 0);\n if (isShuttingDown) {\n console.log(`[EFC] Worker ${worker.id} gracefully exited`);\n if (Object.keys(cluster.workers ?? {}).length === 0) {\n console.log('[EFC] All workers exited. Primary exiting.');\n process.exit(0);\n }\n return;\n }\n\n console.warn(`[EFC] Worker ${worker.id} exited (code=${exitCode}), respawning…`);\n options.onWorkerCrash?.(worker.id, exitCode);\n cluster.fork();\n });\n}\n\nexport { cluster };\n","// Wraps mongoose connection for Pre-Flight. Throws clearly if mongoose isn't installed.\n\nlet mongoose: typeof import('mongoose');\n\nasync function loadMongoose(): Promise<typeof import('mongoose')> {\n if (mongoose) return mongoose;\n try {\n mongoose = await import('mongoose');\n return mongoose;\n } catch {\n throw new Error('[EFC] mongoose is not installed. Run: npm install mongoose');\n }\n}\n\nexport async function connectMongo(url: string): Promise<import('mongoose').Connection> {\n const mg = await loadMongoose();\n await mg.connect(url);\n console.log('[EFC] MongoDB connected');\n return mg.connection;\n}\n","import type { ModelSchema, ModelCRUD } from '../types.js';\n\ntype AnyRecord = Record<string, unknown> & { id: string };\n\nfunction normalise(doc: Record<string, unknown>): AnyRecord {\n const id = doc['_id'] ? String(doc['_id']) : '';\n return { ...doc, id } as AnyRecord;\n}\n\nasync function getModel(\n name: string,\n schemaObj: Record<string, unknown>,\n): Promise<import('mongoose').Model<Record<string, unknown>>> {\n let mg: typeof import('mongoose');\n try {\n const mod = await import('mongoose');\n mg = (mod.default || mod) as typeof import('mongoose');\n } catch {\n throw new Error('[EFC] mongoose is not installed. Run: npm install mongoose');\n }\n\n if (mg.models[name]) return mg.models[name] as import('mongoose').Model<Record<string, unknown>>;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const schema = new mg.Schema(schemaObj as any, { timestamps: true });\n return mg.model<Record<string, unknown>>(name, schema);\n}\n\nfunction buildMongooseSchema(schema: ModelSchema): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, def] of Object.entries(schema)) {\n const entry: Record<string, unknown> = {};\n switch (def.type) {\n case 'string':\n entry['type'] = String;\n break;\n case 'number':\n entry['type'] = Number;\n break;\n case 'boolean':\n entry['type'] = Boolean;\n break;\n case 'date':\n entry['type'] = Date;\n break;\n case 'object':\n entry['type'] = Object;\n break;\n case 'array':\n entry['type'] = Array;\n break;\n }\n if (def.required !== undefined) entry['required'] = def.required;\n if (def.unique !== undefined) entry['unique'] = def.unique;\n if (def.default !== undefined) entry['default'] = def.default;\n out[key] = entry;\n }\n return out;\n}\n\nexport function defineModel<T extends Record<string, any>>(\n name: string,\n schema: ModelSchema,\n): ModelCRUD<T> {\n const mongooseSchema = buildMongooseSchema(schema);\n\n return {\n async find(filter = {}) {\n const M = await getModel(name, mongooseSchema);\n const docs = await M.find(filter as Record<string, unknown>).lean();\n return (docs as Record<string, unknown>[]).map(normalise) as (T & { id: string })[];\n },\n\n async findById(id) {\n const M = await getModel(name, mongooseSchema);\n const doc = await M.findById(id).lean();\n if (!doc) return null;\n return normalise(doc as Record<string, unknown>) as T & { id: string };\n },\n\n async findOne(filter) {\n const M = await getModel(name, mongooseSchema);\n const doc = await M.findOne(filter as Record<string, unknown>).lean();\n if (!doc) return null;\n return normalise(doc as Record<string, unknown>) as T & { id: string };\n },\n\n async create(data) {\n const M = await getModel(name, mongooseSchema);\n const doc = await M.create(data);\n return normalise(doc.toObject() as Record<string, unknown>) as T & { id: string };\n },\n\n async update(id, data) {\n const M = await getModel(name, mongooseSchema);\n const doc = await M.findByIdAndUpdate(id, data, { new: true }).lean();\n if (!doc) return null;\n return normalise(doc as Record<string, unknown>) as T & { id: string };\n },\n\n async delete(id) {\n const M = await getModel(name, mongooseSchema);\n await M.findByIdAndDelete(id);\n },\n\n async count(filter = {}) {\n const M = await getModel(name, mongooseSchema);\n return M.countDocuments(filter as Record<string, unknown>);\n },\n };\n}\n","// Thread-local DB client populated during Pre-Flight.\n// The Proxy ensures callers import `db` once and never null-check it —\n// it throws with a helpful message if accessed before Pre-Flight completes.\n\ntype AnyClient = Record<string, unknown>;\n\nlet _client: AnyClient | null = null;\n\nexport function setDbClient(client: AnyClient): void {\n _client = client;\n}\n\nexport function getDbClient(): AnyClient {\n if (!_client) {\n throw new Error('[EFC] db not ready — accessed before Pre-Flight completed');\n }\n return _client;\n}\n\nexport const db: AnyClient = new Proxy({} as AnyClient, {\n get(_target, prop: string) {\n return getDbClient()[prop];\n },\n set(_target, prop: string, value: unknown) {\n getDbClient()[prop] = value;\n return true;\n },\n});\n\nexport { connectMongo } from './mongo.js';\nexport { defineModel } from './model.js';\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { registerTask } from './index.js';\nimport type { TaskDefinition } from '../types.js';\n\nexport async function scanTasks(tasksDir: string): Promise<void> {\n if (!fs.existsSync(tasksDir)) return;\n\n const files = fs.readdirSync(tasksDir).filter((f) => /\\.(ts|js|mts|mjs)$/.test(f));\n\n for (const file of files) {\n const filePath = path.join(tasksDir, file);\n const taskName = path.basename(file, path.extname(file));\n\n try {\n const mod = (await import(filePath)) as Record<string, unknown>;\n const def = mod['default'] as TaskDefinition | undefined;\n\n if (!def || typeof def.handler !== 'function') {\n console.warn(`[EFC] Task file ${file} has no valid default export — skipping`);\n continue;\n }\n\n registerTask(taskName, { ...def, filePath });\n console.log(`[EFC] Registered task: ${taskName}`);\n } catch (err) {\n console.warn(`[EFC] Failed to load task ${file}:`, err);\n }\n }\n}\n","import { Worker, workerData, parentPort, isMainThread } from 'node:worker_threads';\nimport type { TaskDefinition } from '../types.js';\n\n// ── Worker-thread side ────────────────────────────────────────────────────────\nif (!isMainThread) {\n const { handlerPath, payload } = workerData as { handlerPath: string; payload: unknown };\n\n (async () => {\n try {\n const mod = (await import(handlerPath)) as Record<string, unknown>;\n const def = mod['default'] as TaskDefinition | undefined;\n if (!def || typeof def.handler !== 'function') {\n throw new Error(`[EFC] Thread runner: no valid task export in ${handlerPath}`);\n }\n await def.handler(payload);\n parentPort?.postMessage({ ok: true });\n } catch (err) {\n parentPort?.postMessage({ ok: false, error: String(err) });\n }\n })();\n}\n\n// ── Main-thread side ──────────────────────────────────────────────────────────\nexport function runInThread(handlerPath: string, payload: unknown): Promise<void> {\n return new Promise((resolve, reject) => {\n const worker = new Worker(new URL('./thread-runner.js', import.meta.url), {\n workerData: { handlerPath, payload },\n });\n\n worker.on('message', (msg: { ok: boolean; error?: string }) => {\n if (msg.ok) resolve();\n else reject(new Error(msg.error ?? 'Thread task failed'));\n });\n\n worker.on('error', reject);\n worker.on('exit', (code) => {\n if (code !== 0) reject(new Error(`Thread worker exited with code ${code}`));\n });\n });\n}\n","import type { Job } from 'bullmq';\nimport { taskRegistry, setEnqueueImpl } from './index.js';\nimport { runInThread } from './thread-runner.js';\n\ninterface BullMQOpts {\n redisUrl: string;\n concurrency: number;\n}\n\nexport async function initBullMQ(opts: BullMQOpts): Promise<void> {\n let bullmq: typeof import('bullmq');\n try {\n bullmq = await import('bullmq');\n } catch {\n throw new Error('[EFC] bullmq is not installed. Run: npm install bullmq');\n }\n\n const connection = parseRedisUrl(opts.redisUrl);\n const queue = new bullmq.Queue('efc', { connection });\n\n const worker = new bullmq.Worker(\n 'efc',\n async (job: Job) => {\n const def = taskRegistry.get(job.name);\n if (!def) {\n throw new Error(`[EFC] No task registered for name: ${job.name}`);\n }\n\n if (def.options.thread && def.filePath) {\n await runInThread(def.filePath, job.data);\n } else {\n await def.handler(job.data);\n }\n },\n { connection, concurrency: opts.concurrency },\n );\n\n worker.on('failed', (job: Job | undefined, err: Error) => {\n console.error(`[EFC] Task ${job?.name ?? 'unknown'} failed:`, err.message);\n });\n\n worker.on('completed', (job: Job) => {\n console.log(`[EFC] Task ${job.name} completed (id=${job.id})`);\n });\n\n setEnqueueImpl(async (name, payload) => {\n const def = taskRegistry.get(name);\n if (!def) {\n throw new Error(\n `[EFC] Cannot enqueue unknown task: \"${name}\". Is the task file in tasksDir?`,\n );\n }\n\n await queue.add(name, payload, {\n attempts: def.options.retries ?? 3,\n backoff: {\n type: def.options.backoff ?? 'exponential',\n delay: 1000,\n },\n });\n });\n\n console.log('[EFC] BullMQ backend initialised');\n}\n\nfunction parseRedisUrl(url: string): { host: string; port: number; password?: string } {\n try {\n const u = new URL(url);\n const result: { host: string; port: number; password?: string } = {\n host: u.hostname || 'localhost',\n port: parseInt(u.port || '6379', 10),\n };\n if (u.password) result.password = u.password;\n return result;\n } catch {\n return { host: 'localhost', port: 6379 };\n }\n}\n","import type { MountedRoute } from './types.js';\n\nconst HTTP_STATUS: Record<number, string> = {\n 200: 'OK',\n 201: 'Created',\n 202: 'Accepted',\n 204: 'No Content',\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Not Found',\n 405: 'Method Not Allowed',\n 409: 'Conflict',\n 422: 'Unprocessable Entity',\n 500: 'Internal Server Error',\n 503: 'Service Unavailable',\n};\n\nexport function generateDashboard(\n routes: MountedRoute[],\n basePath: string,\n port: number,\n projectName: string,\n projectVersion: string,\n): string {\n const safeRoutes = JSON.stringify(\n routes.map((r) => ({\n urlPath: r.urlPath,\n methods: r.methods,\n params: r.params,\n meta: r.meta ?? null,\n })),\n ).replace(/<\\/script>/gi, '<\\\\/script>');\n const safeHttpStatus = JSON.stringify(HTTP_STATUS);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${projectName} — API Reference</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap\" rel=\"stylesheet\" />\n <style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n :root {\n --bg: #080b0f;\n --bg-card: #0d1117;\n --bg-raised: #111820;\n --border: #1e2832;\n --border-lit: #263040;\n --text: #e2e8f0;\n --text-dim: #64748b;\n --text-muted: #334155;\n --accent: #F0A030;\n --accent-dim: rgba(240,160,48,.12);\n --accent-glow: rgba(240,160,48,.22);\n --purple: #a78bfa;\n --blue: #60a5fa;\n --orange: #fb923c;\n --green: #7AAE8E;\n --radius: 10px;\n --radius-lg: 16px;\n --font-mono: 'JetBrains Mono', monospace;\n --font-sans: 'Inter', sans-serif;\n }\n html { scroll-behavior: smooth; }\n body {\n background: var(--bg); color: var(--text);\n font-family: var(--font-sans); font-size: 16px;\n line-height: 1.6; -webkit-font-smoothing: antialiased;\n position: relative;\n }\n body::before {\n content: ''; position: fixed; inset: 0; z-index: 0;\n background-image:\n linear-gradient(var(--border) 1px, transparent 1px),\n linear-gradient(90deg, var(--border) 1px, transparent 1px);\n background-size: 60px 60px; opacity: .28; pointer-events: none;\n }\n section, footer, .page-hero, .docs-layout { position: relative; z-index: 1; }\n a { text-decoration: none; color: inherit; }\n em { font-style: italic; }\n code {\n font-family: var(--font-mono); font-size: .85em;\n background: var(--bg-raised); border: 1px solid var(--border);\n border-radius: 4px; padding: 1px 6px; color: var(--accent);\n }\n .container { max-width: 1140px; margin: 0 auto; padding: 0 24px; }\n ::-webkit-scrollbar { width: 6px; }\n ::-webkit-scrollbar-track { background: var(--bg); }\n ::-webkit-scrollbar-thumb { background: var(--border-lit); border-radius: 3px; }\n\n /* ── PAGE HERO ── */\n .page-hero {\n padding: 48px 0 56px; border-bottom: 1px solid var(--border);\n }\n .page-hero .hero-glow {\n position: absolute; top: -40px; left: 10%; width: 500px; height: 350px;\n background: radial-gradient(ellipse, rgba(240,160,48,.1) 0%, transparent 68%);\n pointer-events: none;\n }\n .page-title {\n font-size: clamp(2rem, 5vw, 3.4rem); font-weight: 800;\n line-height: 1.1; letter-spacing: -.02em; margin-bottom: 24px;\n display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap;\n }\n .project-version {\n font-family: var(--font-mono); font-size: clamp(.85rem, 2vw, 1.1rem);\n font-weight: 500; color: var(--accent);\n background: var(--accent-dim); border: 1px solid rgba(240,160,48,.3);\n border-radius: 6px; padding: 2px 10px; letter-spacing: .5px;\n }\n .hero-meta { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 40px; }\n .base-url-pill {\n display: flex; align-items: center; gap: 8px;\n background: var(--bg-card); border: 1px solid var(--border);\n border-radius: 7px; padding: 8px 14px;\n }\n .base-url-label { font-size: .72rem; font-family: var(--font-mono);\n color: var(--text-muted); text-transform: uppercase; letter-spacing: .5px; }\n .base-url-val { font-family: var(--font-mono); font-size: .8rem; color: var(--accent); }\n .count-pill {\n font-family: var(--font-mono); font-size: .72rem; color: var(--text-dim);\n background: var(--bg-card); border: 1px solid var(--border);\n border-radius: 7px; padding: 8px 14px;\n }\n\n /* ── TOOLBAR ── */\n .toolbar {\n display: flex; align-items: center; gap: 12px; flex-wrap: wrap;\n }\n .method-filters { display: flex; gap: 4px; flex-wrap: wrap; }\n .filter-btn {\n background: var(--bg-card); border: 1px solid var(--border);\n color: var(--text-dim); font-family: var(--font-mono);\n font-size: .72rem; font-weight: 600; letter-spacing: .5px;\n padding: 6px 12px; border-radius: 6px; cursor: pointer;\n transition: all .15s;\n }\n .filter-btn:hover { border-color: var(--border-lit); color: var(--text); }\n .filter-btn.active { background: var(--accent); color: #0d0d1a; border-color: var(--accent); }\n .filter-btn[data-method=\"GET\"].active { background: rgba(96,165,250,.18); color: #60a5fa; border-color: rgba(96,165,250,.4); }\n .filter-btn[data-method=\"POST\"].active { background: rgba(122,174,142,.18); color: #7AAE8E; border-color: rgba(122,174,142,.4); }\n .filter-btn[data-method=\"PUT\"].active { background: rgba(240,160,48,.18); color: #F0A030; border-color: rgba(240,160,48,.4); }\n .filter-btn[data-method=\"PATCH\"].active { background: rgba(251,191,36,.18); color: #fbbf24; border-color: rgba(251,191,36,.4); }\n .filter-btn[data-method=\"DELETE\"].active { background: rgba(248,113,113,.18); color: #f87171; border-color: rgba(248,113,113,.4); }\n .filter-btn[data-method=\"HEAD\"].active { background: rgba(167,139,250,.18); color: #a78bfa; border-color: rgba(167,139,250,.4); }\n .search-wrap { margin-left: auto; }\n .search-input {\n background: var(--bg-card); border: 1px solid var(--border);\n color: var(--text); font-family: var(--font-sans); font-size: .85rem;\n padding: 7px 14px; border-radius: 7px; width: 240px;\n outline: none; transition: border-color .2s;\n }\n .search-input::placeholder { color: var(--text-muted); }\n .search-input:focus { border-color: var(--border-lit); }\n\n /* ── DOCS LAYOUT ── */\n .docs-layout {\n display: grid; grid-template-columns: 260px 1fr;\n gap: 48px; padding: 52px 0 80px; max-width: 1140px;\n margin: 0 auto; padding-left: 24px; padding-right: 24px;\n align-items: start;\n }\n\n /* ── SIDEBAR ── */\n .docs-sidebar {\n position: sticky; top: 24px;\n max-height: calc(100vh - 48px);\n overflow-y: auto;\n overflow-x: hidden;\n scrollbar-width: none;\n padding-right: 4px;\n }\n .docs-sidebar::-webkit-scrollbar { display: none; }\n .sidebar-section { margin-bottom: 28px; }\n .sidebar-label {\n font-family: var(--font-mono); font-size: .68rem; font-weight: 600;\n color: var(--text-muted); letter-spacing: 1.2px; text-transform: uppercase;\n margin-bottom: 8px; padding: 0 4px;\n }\n .sidebar-link {\n display: flex; align-items: center; gap: 8px;\n padding: 7px 10px; border-radius: 6px;\n border-left: 2px solid transparent;\n font-size: .82rem; color: var(--text-dim); cursor: pointer;\n transition: background .15s, color .15s, border-color .15s;\n overflow: hidden; white-space: nowrap;\n }\n .sidebar-link:hover { background: var(--bg-raised); color: var(--text); }\n .sidebar-link.active { color: var(--accent); border-left-color: var(--accent); background: var(--accent-dim); }\n .sidebar-method-dot {\n width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0;\n }\n .sidebar-path { font-family: var(--font-mono); font-size: .72rem; }\n\n /* ── ENDPOINT CARDS ── */\n .endpoint-card {\n background: var(--bg-card); border: 1px solid var(--border);\n border-radius: var(--radius-lg); margin-bottom: 20px;\n overflow: hidden; transition: border-color .2s;\n animation: fadeUp .45s ease both;\n }\n .endpoint-card:hover { border-color: var(--border-lit); }\n @keyframes fadeUp {\n from { opacity: 0; transform: translateY(16px); }\n to { opacity: 1; transform: translateY(0); }\n }\n @media (prefers-reduced-motion: reduce) {\n .endpoint-card { animation: none; }\n }\n .endpoint-header {\n display: flex; align-items: center; gap: 14px;\n padding: 20px 24px 18px; border-bottom: 1px solid var(--border);\n flex-wrap: wrap;\n }\n .method-badges { display: flex; gap: 6px; flex-wrap: wrap; }\n .method-badge {\n font-family: var(--font-mono); font-size: .7rem; font-weight: 600;\n letter-spacing: .8px; padding: 4px 10px; border-radius: 5px;\n border: 1px solid; text-transform: uppercase;\n }\n .endpoint-path {\n font-family: var(--font-mono); font-size: .9rem; color: var(--text);\n background: none; border: none; padding: 0;\n word-break: break-all;\n }\n .endpoint-body { padding: 6px 24px 24px; }\n .endpoint-desc { font-size: .9rem; color: var(--text-dim); line-height: 1.7; margin-bottom: 20px; }\n\n /* ── PER-METHOD DOC BLOCKS ── */\n .method-block { padding-top: 20px; }\n .method-block + .method-block { margin-top: 4px; border-top: 1px solid var(--border); }\n .method-block-header { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }\n .method-block-header .method-badge { font-size: .68rem; }\n .method-block-empty { font-size: .82rem; color: var(--text-muted); }\n\n /* ── CODE BLOCKS ── */\n .example-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }\n .code-block-wrap {\n background: var(--bg-raised); border: 1px solid var(--border);\n border-radius: var(--radius); overflow: hidden;\n }\n .code-bar {\n display: flex; align-items: center; gap: 6px;\n padding: 9px 14px; border-bottom: 1px solid var(--border);\n background: var(--bg-card);\n }\n .dot { width: 11px; height: 11px; border-radius: 50%; }\n .dot-r { background: #ff5f57; }\n .dot-y { background: #febc2e; }\n .dot-g { background: #28c840; }\n .code-label { font-family: var(--font-mono); font-size: .68rem; color: var(--text-dim); margin-left: 4px; }\n pre.code-pre {\n font-family: var(--font-mono); font-size: .78rem; line-height: 1.7;\n padding: 16px 18px; overflow-x: auto; tab-size: 2; white-space: pre;\n }\n .c-dim { color: var(--text-muted); }\n .c-green { color: #7AAE8E; }\n .c-blue { color: #60a5fa; }\n .c-orange { color: #fb923c; }\n .c-purple { color: #a78bfa; }\n .c-accent { color: var(--accent); }\n .status-2xx { color: #7AAE8E; }\n .status-4xx { color: #f87171; }\n .status-5xx { color: #fb923c; }\n\n /* ── EMPTY STATE ── */\n .empty-state {\n text-align: center; padding: 80px 24px;\n color: var(--text-muted); font-size: .9rem;\n }\n\n /* ── RESPONSIVE ── */\n @media (max-width: 900px) {\n .docs-layout { grid-template-columns: 1fr; }\n .docs-sidebar { position: static; max-height: none; overflow-y: visible; padding-right: 0; }\n .example-grid { grid-template-columns: 1fr; }\n .search-wrap { margin-left: 0; width: 100%; }\n .search-input { width: 100%; }\n }\n @media (max-width: 600px) {\n .page-hero { padding: 32px 0 32px; }\n .page-title { font-size: 2rem; }\n }\n </style>\n</head>\n<body>\n\n <div class=\"page-hero\">\n <div class=\"hero-glow\"></div>\n <div class=\"container\">\n <h1 class=\"page-title\">${projectName}${projectVersion ? `<span class=\"project-version\">v${projectVersion}</span>` : ''}</h1>\n <div class=\"hero-meta\">\n <div class=\"base-url-pill\">\n <span class=\"base-url-label\">Base</span>\n <span class=\"base-url-val\">http://localhost:${port}${basePath}</span>\n </div>\n <div class=\"count-pill\" id=\"countPill\"></div>\n </div>\n <div class=\"toolbar\">\n <div class=\"method-filters\" id=\"methodFilters\">\n <button class=\"filter-btn active\" data-method=\"ALL\">ALL</button>\n </div>\n <div class=\"search-wrap\">\n <input type=\"text\" id=\"searchInput\" class=\"search-input\" placeholder=\"Search endpoints…\" />\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"docs-layout\">\n <aside class=\"docs-sidebar\" id=\"sidebar\"></aside>\n <main id=\"endpoints\"></main>\n </div>\n\n <script>\n const ROUTES = ${safeRoutes};\n const BASE_PATH = ${JSON.stringify(basePath)};\n const HTTP_STATUS = ${safeHttpStatus};\n\n const METHOD_STYLE = {\n GET: { bg: 'rgba(96,165,250,.14)', color: '#60a5fa', border: 'rgba(96,165,250,.35)' },\n POST: { bg: 'rgba(122,174,142,.14)', color: '#7AAE8E', border: 'rgba(122,174,142,.35)' },\n PUT: { bg: 'rgba(240,160,48,.14)', color: '#F0A030', border: 'rgba(240,160,48,.35)' },\n PATCH: { bg: 'rgba(251,191,36,.14)', color: '#fbbf24', border: 'rgba(251,191,36,.35)' },\n DELETE: { bg: 'rgba(248,113,113,.14)', color: '#f87171', border: 'rgba(248,113,113,.35)' },\n HEAD: { bg: 'rgba(167,139,250,.14)', color: '#a78bfa', border: 'rgba(167,139,250,.35)' },\n OPTIONS: { bg: 'rgba(148,163,184,.14)', color: '#94a3b8', border: 'rgba(148,163,184,.35)' },\n };\n\n function esc(s) {\n return String(s)\n .replace(/&/g,'&').replace(/</g,'<')\n .replace(/>/g,'>').replace(/\"/g,'"');\n }\n\n function methodBadge(m) {\n const s = METHOD_STYLE[m] || METHOD_STYLE.GET;\n return '<span class=\"method-badge\" style=\"background:' + s.bg + ';color:' + s.color + ';border-color:' + s.border + '\">' + m + '</span>';\n }\n\n function joinPath(base, p) {\n return (base.replace(/\\\\/+$/, '') + p) || '/';\n }\n\n function sidebarDot(method) {\n const s = METHOD_STYLE[method] || METHOD_STYLE.GET;\n return '<span class=\"sidebar-method-dot\" style=\"background:' + s.color + '\"></span>';\n }\n\n function statusClass(code) {\n if (code >= 200 && code < 300) return 'status-2xx';\n if (code >= 400 && code < 500) return 'status-4xx';\n if (code >= 500) return 'status-5xx';\n return '';\n }\n\n function renderSchema(val, indent) {\n const pad = ' '.repeat(indent || 0);\n if (val === null) return '<span class=\"c-dim\">null</span>';\n if (typeof val === 'string') {\n const isDate = /^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}/.test(val);\n return isDate ? '<span class=\"c-green\">Date</span>' : '<span class=\"c-purple\">String</span>';\n }\n if (typeof val === 'number') return '<span class=\"c-orange\">Number</span>';\n if (typeof val === 'boolean') return '<span class=\"c-purple\">Boolean</span>';\n if (Array.isArray(val)) {\n if (!val.length) return '[]';\n return '[\\\\n' + pad + ' ' + renderSchema(val[0], (indent || 0) + 1) + '\\\\n' + pad + ']';\n }\n if (typeof val === 'object') {\n const keys = Object.keys(val);\n if (!keys.length) return '{}';\n const inner = keys.map(function(k) {\n return pad + ' <span class=\"c-blue\">"' + esc(k) + '"</span>: ' + renderSchema(val[k], (indent || 0) + 1);\n }).join(',\\\\n');\n return '{\\\\n' + inner + '\\\\n' + pad + '}';\n }\n return esc(String(val));\n }\n\n function codeBlock(label, content) {\n return '<div class=\"code-block-wrap\">' +\n '<div class=\"code-bar\">' +\n '<span class=\"dot dot-r\"></span>' +\n '<span class=\"dot dot-y\"></span>' +\n '<span class=\"dot dot-g\"></span>' +\n '<span class=\"code-label\">' + esc(label) + '</span>' +\n '</div>' +\n '<pre class=\"code-pre\">' + content + '</pre>' +\n '</div>';\n }\n\n function buildRequestContent(route, method, example) {\n const req = example || {};\n let path = joinPath(BASE_PATH, route.urlPath);\n if (req.params) {\n for (const [k, v] of Object.entries(req.params)) {\n path = path.replace(':' + k, String(v));\n }\n }\n if (req.query && Object.keys(req.query).length) {\n path += '?' + new URLSearchParams(req.query).toString();\n }\n let lines = '<span class=\"c-accent\">' + esc(method) + '</span> <span class=\"c-blue\">' + esc(path) + '</span>';\n const headers = { ...(req.headers || {}) };\n if (req.body !== undefined && !headers['Content-Type']) {\n headers['Content-Type'] = 'application/json';\n }\n if (Object.keys(headers).length) {\n lines += '\\\\n' + Object.entries(headers)\n .map(([k, v]) => '<span class=\"c-dim\">' + esc(k) + ':</span> ' + esc(String(v)))\n .join('\\\\n');\n }\n if (req.body !== undefined) {\n lines += '\\\\n\\\\n' + renderSchema(req.body, 0);\n }\n return lines;\n }\n\n function buildResponseContent(example) {\n const resp = example || {};\n const code = resp.status || 200;\n const text = HTTP_STATUS[code] || 'OK';\n const cls = statusClass(code);\n let lines = 'HTTP/1.1 <span class=\"' + cls + '\">' + code + ' ' + esc(text) + '</span>';\n if (resp.body !== undefined) {\n lines += '\\\\n<span class=\"c-dim\">Content-Type:</span> application/json\\\\n\\\\n';\n lines += renderSchema(resp.body, 0);\n }\n return lines;\n }\n\n function renderMethodBlock(route, method) {\n const methodMeta = route.meta ? route.meta[method] : null;\n const header = '<div class=\"method-block-header\">' + methodBadge(method) + '</div>';\n\n if (!methodMeta) {\n return '<div class=\"method-block\" data-method=\"' + esc(method) + '\">' + header +\n '<p class=\"method-block-empty\">No documentation provided for this method.</p></div>';\n }\n\n const desc = methodMeta.description\n ? '<p class=\"endpoint-desc\">' + esc(methodMeta.description) + '</p>'\n : '';\n const reqHtml = methodMeta.request !== undefined\n ? codeBlock('request', buildRequestContent(route, method, methodMeta.request))\n : '';\n const respHtml = methodMeta.response !== undefined\n ? codeBlock('response', buildResponseContent(methodMeta.response))\n : '';\n const examples = reqHtml || respHtml\n ? '<div class=\"example-grid\">' + reqHtml + respHtml + '</div>'\n : '';\n\n return '<div class=\"method-block\" data-method=\"' + esc(method) + '\">' + header + desc + examples + '</div>';\n }\n\n function renderCard(route, index) {\n const id = 'ep-' + index;\n const fullPath = joinPath(BASE_PATH, route.urlPath);\n const badges = route.methods.map(methodBadge).join('');\n const body = route.methods.map(function(m) { return renderMethodBlock(route, m); }).join('');\n const delay = (index * 0.06).toFixed(2) + 's';\n return '<div class=\"endpoint-card\" id=\"' + id + '\" data-methods=\"' + route.methods.join(',') + '\" data-path=\"' + esc(fullPath) + '\" style=\"animation-delay:' + delay + '\">' +\n '<div class=\"endpoint-header\">' +\n '<div class=\"method-badges\">' + badges + '</div>' +\n '<code class=\"endpoint-path\">' + esc(fullPath) + '</code>' +\n '</div>' +\n '<div class=\"endpoint-body\">' + body + '</div>' +\n '</div>';\n }\n\n function buildMethodFilters() {\n const allMethods = [...new Set(ROUTES.flatMap(r => r.methods))].sort();\n const filtersEl = document.getElementById('methodFilters');\n allMethods.forEach(m => {\n const btn = document.createElement('button');\n btn.className = 'filter-btn';\n btn.dataset.method = m;\n btn.textContent = m;\n filtersEl.appendChild(btn);\n });\n }\n\n function buildSidebar(routes) {\n const sidebar = document.getElementById('sidebar');\n if (!routes.length) { sidebar.innerHTML = ''; return; }\n\n const label = document.createElement('div');\n label.className = 'sidebar-label';\n label.textContent = 'Endpoints';\n sidebar.appendChild(label);\n\n routes.forEach((route, i) => {\n const link = document.createElement('div');\n link.className = 'sidebar-link';\n link.dataset.target = 'ep-' + ROUTES.indexOf(route);\n link.innerHTML = sidebarDot(route.methods[0] || 'GET') +\n '<span class=\"sidebar-path\">' + esc(route.urlPath || '/') + '</span>';\n link.addEventListener('click', () => {\n const target = document.getElementById(link.dataset.target);\n if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n });\n sidebar.appendChild(link);\n });\n }\n\n let activeFilter = 'ALL';\n let searchQuery = '';\n\n function applyFilters() {\n const cards = document.querySelectorAll('.endpoint-card');\n let visible = 0;\n cards.forEach(card => {\n const methods = card.dataset.methods.split(',');\n const path = card.dataset.path || '';\n const matchMethod = activeFilter === 'ALL' || methods.includes(activeFilter);\n const matchSearch = !searchQuery || path.toLowerCase().includes(searchQuery);\n const show = matchMethod && matchSearch;\n card.style.display = show ? '' : 'none';\n if (show) visible++;\n });\n document.getElementById('empty-state').style.display = visible ? 'none' : 'block';\n\n // Sync sidebar visibility\n document.querySelectorAll('.sidebar-link').forEach(link => {\n const targetId = link.dataset.target;\n const targetCard = document.getElementById(targetId);\n link.style.display = targetCard && targetCard.style.display !== 'none' ? '' : 'none';\n });\n }\n\n function init() {\n const count = ROUTES.length;\n document.getElementById('countPill').textContent = count + ' endpoint' + (count !== 1 ? 's' : '');\n\n buildMethodFilters();\n\n const container = document.getElementById('endpoints');\n container.innerHTML = ROUTES.map((r, i) => renderCard(r, i)).join('');\n\n // Add empty state after cards\n const emptyEl = document.createElement('div');\n emptyEl.id = 'empty-state';\n emptyEl.className = 'empty-state';\n emptyEl.style.display = 'none';\n emptyEl.innerHTML = '<p>No endpoints match your filter.</p>';\n container.after(emptyEl);\n\n buildSidebar(ROUTES);\n\n // Method filter clicks\n document.getElementById('methodFilters').addEventListener('click', e => {\n const btn = e.target.closest('.filter-btn');\n if (!btn) return;\n document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));\n btn.classList.add('active');\n activeFilter = btn.dataset.method;\n applyFilters();\n });\n\n // Search\n document.getElementById('searchInput').addEventListener('input', e => {\n searchQuery = e.target.value.trim().toLowerCase();\n applyFilters();\n });\n\n // Scroll spy for sidebar\n const cardEls = document.querySelectorAll('.endpoint-card');\n const sidebarLinks = document.querySelectorAll('.sidebar-link');\n const observer = new IntersectionObserver(entries => {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n const id = entry.target.id;\n sidebarLinks.forEach(l => l.classList.toggle('active', l.dataset.target === id));\n const sidebar = document.getElementById('sidebar');\n const activeLink = sidebar && sidebar.querySelector('.sidebar-link.active');\n if (sidebar && activeLink) {\n const linkTop = activeLink.getBoundingClientRect().top - sidebar.getBoundingClientRect().top + sidebar.scrollTop;\n sidebar.scrollTo({ top: linkTop, behavior: 'smooth' });\n }\n }\n });\n }, { rootMargin: '-80px 0px -60% 0px' });\n cardEls.forEach(el => observer.observe(el));\n\n }\n\n try {\n init();\n } catch (e) {\n document.body.insertAdjacentHTML('afterbegin',\n '<div style=\"position:fixed;top:60px;left:0;right:0;z-index:9999;background:#7f1d1d;color:#fca5a5;padding:16px 24px;font-family:monospace;font-size:13px;border-bottom:1px solid #ef4444\">' +\n '<strong>Dashboard JS error:</strong> ' + (e && e.message ? e.message : String(e)) +\n (e && e.stack ? '<pre style=\"margin-top:8px;white-space:pre-wrap\">' + e.stack + '</pre>' : '') +\n '</div>'\n );\n }\n </script>\n</body>\n</html>`;\n}\n","export class HttpError extends Error {\n readonly statusCode: number;\n\n constructor(statusCode: number, message: string) {\n super(message);\n this.name = 'HttpError';\n this.statusCode = statusCode;\n Error.captureStackTrace(this, HttpError);\n }\n}\n","import type { RequestHandler } from 'express';\n\nexport function compose(...handlers: RequestHandler[]): RequestHandler {\n return (req, res, next) => {\n let index = 0;\n\n function dispatch(i: number): void {\n if (i >= handlers.length) {\n next();\n return;\n }\n const handler = handlers[i];\n if (!handler) {\n next();\n return;\n }\n try {\n Promise.resolve(handler(req, res, () => dispatch(i + 1))).catch(next);\n } catch (err) {\n next(err);\n }\n }\n\n dispatch(index);\n };\n}\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["fs","cluster","os","path","path","fs","cluster","os"]}
|
package/dist/tasks/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/
|
|
1
|
+
{"version":3,"sources":["/home/prashant/Projects/temp-nodejs/packages/core/dist/tasks/index.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACF,yDAA8B;AAC9B;AACE;AACA;AACA;AACA;AACA;AACF,wQAAC","file":"/home/prashant/Projects/temp-nodejs/packages/core/dist/tasks/index.cjs"}
|
package/dist/tasks/index.d.cts
CHANGED
package/dist/tasks/index.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ interface RouteEntry {
|
|
|
37
37
|
filePath: string;
|
|
38
38
|
params: string[];
|
|
39
39
|
}
|
|
40
|
-
interface
|
|
40
|
+
interface RouteMethodMeta {
|
|
41
41
|
description?: string;
|
|
42
42
|
request?: {
|
|
43
43
|
headers?: Record<string, string>;
|
|
@@ -50,6 +50,11 @@ interface RouteMeta {
|
|
|
50
50
|
body?: unknown;
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Keyed by HTTP method (`GET`, `POST`, ...) so each method implemented in a
|
|
55
|
+
* route file gets its own documentation block in the dashboard.
|
|
56
|
+
*/
|
|
57
|
+
type RouteMeta = Partial<Record<string, RouteMethodMeta>>;
|
|
53
58
|
interface TaskOptions {
|
|
54
59
|
thread?: boolean;
|
|
55
60
|
retries?: number;
|
|
@@ -88,4 +93,4 @@ interface ModelCRUD<T extends Record<string, any>> {
|
|
|
88
93
|
count(filter?: Partial<T>): Promise<number>;
|
|
89
94
|
}
|
|
90
95
|
|
|
91
|
-
export type { AuthStrategy as A, CorsConfig as C, DatabaseEngine as D, EFCConfig as E, ModelSchema as M, RouteEntry as R, TaskDefinition as T, TaskOptions as a, ModelCRUD as b, RouteMeta as c };
|
|
96
|
+
export type { AuthStrategy as A, CorsConfig as C, DatabaseEngine as D, EFCConfig as E, ModelSchema as M, RouteEntry as R, TaskDefinition as T, TaskOptions as a, ModelCRUD as b, RouteMeta as c, RouteMethodMeta as d };
|
|
@@ -37,7 +37,7 @@ interface RouteEntry {
|
|
|
37
37
|
filePath: string;
|
|
38
38
|
params: string[];
|
|
39
39
|
}
|
|
40
|
-
interface
|
|
40
|
+
interface RouteMethodMeta {
|
|
41
41
|
description?: string;
|
|
42
42
|
request?: {
|
|
43
43
|
headers?: Record<string, string>;
|
|
@@ -50,6 +50,11 @@ interface RouteMeta {
|
|
|
50
50
|
body?: unknown;
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Keyed by HTTP method (`GET`, `POST`, ...) so each method implemented in a
|
|
55
|
+
* route file gets its own documentation block in the dashboard.
|
|
56
|
+
*/
|
|
57
|
+
type RouteMeta = Partial<Record<string, RouteMethodMeta>>;
|
|
53
58
|
interface TaskOptions {
|
|
54
59
|
thread?: boolean;
|
|
55
60
|
retries?: number;
|
|
@@ -88,4 +93,4 @@ interface ModelCRUD<T extends Record<string, any>> {
|
|
|
88
93
|
count(filter?: Partial<T>): Promise<number>;
|
|
89
94
|
}
|
|
90
95
|
|
|
91
|
-
export type { AuthStrategy as A, CorsConfig as C, DatabaseEngine as D, EFCConfig as E, ModelSchema as M, RouteEntry as R, TaskDefinition as T, TaskOptions as a, ModelCRUD as b, RouteMeta as c };
|
|
96
|
+
export type { AuthStrategy as A, CorsConfig as C, DatabaseEngine as D, EFCConfig as E, ModelSchema as M, RouteEntry as R, TaskDefinition as T, TaskOptions as a, ModelCRUD as b, RouteMeta as c, RouteMethodMeta as d };
|
package/package.json
CHANGED