express-file-cluster 0.2.0 → 0.2.2

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/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/errors.ts","../src/compose.ts"],"sourcesContent":["import http from 'node:http';\nimport express from 'express';\nimport cors from 'cors';\nimport cookieParser from 'cookie-parser';\nimport cluster from 'node:cluster';\nimport os from 'node:os';\nimport type { EFCConfig } 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';\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 apiDir,\n globalMiddlewares = [],\n onWorkerReady,\n onWorkerCrash,\n onError,\n } = config;\n\n // All runtime values fall back to environment variables\n const envPort = process.env['PORT'] != null ? Number(process.env['PORT']) : NaN;\n const port = (_port != null && !Number.isNaN(_port)) ? _port\n : !Number.isNaN(envPort) ? envPort\n : 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.split(',').map((o) => o.trim()).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 (config.tasksDir) {\n await scanTasks(config.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 await mountRoutes(app, routes);\n\n if (onError) {\n app.use(onError);\n } else {\n app.use(\n (\n err: unknown,\n _req: express.Request,\n res: express.Response,\n _next: express.NextFunction,\n ) => {\n const asHttp = err instanceof Error && 'statusCode' in err\n ? (err as HttpError) : null;\n if (asHttp) {\n res.status(asHttp.statusCode).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 TaskOptions,\n TaskDefinition,\n DatabaseEngine,\n AuthStrategy,\n ModelSchema,\n ModelCRUD,\n} from './types.js';\n","import type { Application, RequestHandler } from 'express';\nimport type { 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(app: Application, routes: RouteEntry[]): Promise<void> {\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 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}\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(\n '[EFC] mongoose is not installed. Run: npm install mongoose',\n );\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(`[EFC] Cannot enqueue unknown task: \"${name}\". Is the task file in tasksDir?`);\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","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":";;;;;;;;;;;;;AACA,OAAO,aAAa;AACpB,OAAO,UAAU;AACjB,OAAO,kBAAkB;AACzB,OAAOA,cAAa;AACpB,OAAOC,SAAQ;;;ACFf,IAAM,eAAe,CAAC,OAAO,QAAQ,OAAO,SAAS,UAAU,QAAQ,SAAS;AAGhF,SAAS,UAAU,SAAyC;AAC1D,SAAO,CAAC,KAAK,KAAK,SAAS;AACzB,YAAQ,QAAQ,QAAQ,KAAK,KAAK,IAAI,CAAC,EAAE,MAAM,IAAI;AAAA,EACrD;AACF;AAEA,eAAsB,YAAY,KAAkB,QAAqC;AACvF,aAAW,SAAS,QAAQ;AAC1B,UAAM,MAAO,MAAM,OAAO,MAAM;AAChC,UAAM,mBAAqC,MAAM,QAAQ,IAAI,aAAa,CAAC,IACtE,IAAI,aAAa,IAClB,CAAC;AAEL,UAAM,cAA4B,CAAC;AACnC,UAAM,gBAA8B,CAAC;AAErC,eAAW,UAAU,cAAc;AACjC,UAAI,OAAO,IAAI,MAAM,MAAM,YAAY;AACrC,oBAAY,KAAK,MAAM;AACvB,YAAI,OAAO,YAAY,CAA0B;AAAA,UAC/C,MAAM;AAAA,UACN,GAAG;AAAA,UACH,UAAU,IAAI,MAAM,CAAmB;AAAA,QACzC;AAAA,MACF,OAAO;AACL,sBAAc,KAAK,MAAM;AAAA,MAC3B;AAAA,IACF;AAEA,QAAI,YAAY,SAAS,KAAK,cAAc,SAAS,GAAG;AACtD,YAAM,cAAc,YAAY,KAAK,IAAI;AACzC,UAAI,IAAI,MAAM,SAAS,CAAC,KAAK,KAAK,SAAS;AACzC,YAAK,cAA2B,SAAS,IAAI,MAAM,GAAG;AACpD,cAAI,IAAI,SAAS,WAAW,EAAE,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,qBAAqB,CAAC;AAAA,QAChF,OAAO;AACL,eAAK;AAAA,QACP;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;;;AC9CA,OAAO,aAAa;AACpB,OAAO,QAAQ;AAQf,IAAI,iBAAiB;AAEd,SAAS,iBAAuB;AACrC,mBAAiB;AACjB,UAAQ,IAAI,+DAA0D;AACxE;AAEO,SAAS,UAAU,UAA0B,CAAC,GAAS;AAC5D,QAAM,QAAQ,QAAQ,WAAW,GAAG,KAAK,EAAE;AAE3C,UAAQ,IAAI,iBAAiB,QAAQ,GAAG,aAAa,KAAK,UAAU;AAEpE,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,YAAQ,KAAK;AAAA,EACf;AAEA,UAAQ,GAAG,UAAU,CAAC,WAAW;AAC/B,YAAQ,gBAAgB,OAAO,EAAE;AAAA,EACnC,CAAC;AAED,UAAQ,GAAG,QAAQ,CAAC,QAAQ,MAAM,WAAW;AAC3C,UAAM,WAAW,SAAS,SAAS,KAAK;AACxC,QAAI,gBAAgB;AAClB,cAAQ,IAAI,gBAAgB,OAAO,EAAE,oBAAoB;AACzD,UAAI,OAAO,KAAK,QAAQ,WAAW,CAAC,CAAC,EAAE,WAAW,GAAG;AACnD,gBAAQ,IAAI,4CAA4C;AACxD,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA;AAAA,IACF;AAEA,YAAQ,KAAK,gBAAgB,OAAO,EAAE,iBAAiB,QAAQ,qBAAgB;AAC/E,YAAQ,gBAAgB,OAAO,IAAI,QAAQ;AAC3C,YAAQ,KAAK;AAAA,EACf,CAAC;AACH;;;AC1CA,IAAI;AAEJ,eAAe,eAAmD;AAChE,MAAI,SAAU,QAAO;AACrB,MAAI;AACF,eAAW,MAAM,OAAO,UAAU;AAClC,WAAO;AAAA,EACT,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAsB,aAAa,KAAqD;AACtF,QAAM,KAAK,MAAM,aAAa;AAC9B,QAAM,GAAG,QAAQ,GAAG;AACpB,UAAQ,IAAI,yBAAyB;AACrC,SAAO,GAAG;AACZ;;;ACjBA,SAAS,UAAU,KAAyC;AAC1D,QAAM,KAAK,IAAI,KAAK,IAAI,OAAO,IAAI,KAAK,CAAC,IAAI;AAC7C,SAAO,EAAE,GAAG,KAAK,GAAG;AACtB;AAEA,eAAe,SACb,MACA,WAC4D;AAC5D,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,UAAU;AACnC,SAAM,IAAI,WAAW;AAAA,EACvB,QAAQ;AACN,UAAM,IAAI,MAAM,4DAA4D;AAAA,EAC9E;AAEA,MAAI,GAAG,OAAO,IAAI,EAAG,QAAO,GAAG,OAAO,IAAI;AAG1C,QAAM,SAAS,IAAI,GAAG,OAAO,WAAkB,EAAE,YAAY,KAAK,CAAC;AACnE,SAAO,GAAG,MAA+B,MAAM,MAAM;AACvD;AAEA,SAAS,oBAAoB,QAA8C;AACzE,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC/C,UAAM,QAAiC,CAAC;AACxC,YAAQ,IAAI,MAAM;AAAA,MAChB,KAAK;AACH,cAAM,MAAM,IAAI;AAChB;AAAA,MACF,KAAK;AACH,cAAM,MAAM,IAAI;AAChB;AAAA,MACF,KAAK;AACH,cAAM,MAAM,IAAI;AAChB;AAAA,MACF,KAAK;AACH,cAAM,MAAM,IAAI;AAChB;AAAA,MACF,KAAK;AACH,cAAM,MAAM,IAAI;AAChB;AAAA,MACF,KAAK;AACH,cAAM,MAAM,IAAI;AAChB;AAAA,IACJ;AACA,QAAI,IAAI,aAAa,OAAW,OAAM,UAAU,IAAI,IAAI;AACxD,QAAI,IAAI,WAAW,OAAW,OAAM,QAAQ,IAAI,IAAI;AACpD,QAAI,IAAI,YAAY,OAAW,OAAM,SAAS,IAAI,IAAI;AACtD,QAAI,GAAG,IAAI;AAAA,EACb;AACA,SAAO;AACT;AAEO,SAAS,YACd,MACA,QACc;AACd,QAAM,iBAAiB,oBAAoB,MAAM;AAEjD,SAAO;AAAA,IACL,MAAM,KAAK,SAAS,CAAC,GAAG;AACtB,YAAM,IAAI,MAAM,SAAS,MAAM,cAAc;AAC7C,YAAM,OAAO,MAAM,EAAE,KAAK,MAAiC,EAAE,KAAK;AAClE,aAAQ,KAAmC,IAAI,SAAS;AAAA,IAC1D;AAAA,IAEA,MAAM,SAAS,IAAI;AACjB,YAAM,IAAI,MAAM,SAAS,MAAM,cAAc;AAC7C,YAAM,MAAM,MAAM,EAAE,SAAS,EAAE,EAAE,KAAK;AACtC,UAAI,CAAC,IAAK,QAAO;AACjB,aAAO,UAAU,GAA8B;AAAA,IACjD;AAAA,IAEA,MAAM,QAAQ,QAAQ;AACpB,YAAM,IAAI,MAAM,SAAS,MAAM,cAAc;AAC7C,YAAM,MAAM,MAAM,EAAE,QAAQ,MAAiC,EAAE,KAAK;AACpE,UAAI,CAAC,IAAK,QAAO;AACjB,aAAO,UAAU,GAA8B;AAAA,IACjD;AAAA,IAEA,MAAM,OAAO,MAAM;AACjB,YAAM,IAAI,MAAM,SAAS,MAAM,cAAc;AAC7C,YAAM,MAAM,MAAM,EAAE,OAAO,IAAI;AAC/B,aAAO,UAAU,IAAI,SAAS,CAA4B;AAAA,IAC5D;AAAA,IAEA,MAAM,OAAO,IAAI,MAAM;AACrB,YAAM,IAAI,MAAM,SAAS,MAAM,cAAc;AAC7C,YAAM,MAAM,MAAM,EAAE,kBAAkB,IAAI,MAAM,EAAE,KAAK,KAAK,CAAC,EAAE,KAAK;AACpE,UAAI,CAAC,IAAK,QAAO;AACjB,aAAO,UAAU,GAA8B;AAAA,IACjD;AAAA,IAEA,MAAM,OAAO,IAAI;AACf,YAAM,IAAI,MAAM,SAAS,MAAM,cAAc;AAC7C,YAAM,EAAE,kBAAkB,EAAE;AAAA,IAC9B;AAAA,IAEA,MAAM,MAAM,SAAS,CAAC,GAAG;AACvB,YAAM,IAAI,MAAM,SAAS,MAAM,cAAc;AAC7C,aAAO,EAAE,eAAe,MAAiC;AAAA,IAC3D;AAAA,EACF;AACF;;;ACxGA,IAAI,UAA4B;AAEzB,SAAS,YAAY,QAAyB;AACnD,YAAU;AACZ;AAEO,SAAS,cAAyB;AACvC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,gEAA2D;AAAA,EAC7E;AACA,SAAO;AACT;AAEO,IAAM,KAAgB,IAAI,MAAM,CAAC,GAAgB;AAAA,EACtD,IAAI,SAAS,MAAc;AACzB,WAAO,YAAY,EAAE,IAAI;AAAA,EAC3B;AAAA,EACA,IAAI,SAAS,MAAc,OAAgB;AACzC,gBAAY,EAAE,IAAI,IAAI;AACtB,WAAO;AAAA,EACT;AACF,CAAC;;;AC3BD,OAAO,QAAQ;AACf,OAAO,UAAU;AAIjB,eAAsB,UAAU,UAAiC;AAC/D,MAAI,CAAC,GAAG,WAAW,QAAQ,EAAG;AAE9B,QAAM,QAAQ,GAAG,YAAY,QAAQ,EAAE,OAAO,CAAC,MAAM,qBAAqB,KAAK,CAAC,CAAC;AAEjF,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAW,KAAK,KAAK,UAAU,IAAI;AACzC,UAAM,WAAW,KAAK,SAAS,MAAM,KAAK,QAAQ,IAAI,CAAC;AAEvD,QAAI;AACF,YAAM,MAAM,MAAM,OAAO;AACzB,YAAM,MAAM,IAAI,SAAS;AAEzB,UAAI,CAAC,OAAO,OAAO,IAAI,YAAY,YAAY;AAC7C,gBAAQ,KAAK,mBAAmB,IAAI,8CAAyC;AAC7E;AAAA,MACF;AAEA,mBAAa,UAAU,EAAE,GAAG,KAAK,SAAS,CAAC;AAC3C,cAAQ,IAAI,0BAA0B,QAAQ,EAAE;AAAA,IAClD,SAAS,KAAK;AACZ,cAAQ,KAAK,6BAA6B,IAAI,KAAK,GAAG;AAAA,IACxD;AAAA,EACF;AACF;;;AC7BA,SAAS,QAAQ,YAAY,YAAY,oBAAoB;AAI7D,IAAI,CAAC,cAAc;AACjB,QAAM,EAAE,aAAa,QAAQ,IAAI;AAEjC,GAAC,YAAY;AACX,QAAI;AACF,YAAM,MAAM,MAAM,OAAO;AACzB,YAAM,MAAM,IAAI,SAAS;AACzB,UAAI,CAAC,OAAO,OAAO,IAAI,YAAY,YAAY;AAC7C,cAAM,IAAI,MAAM,gDAAgD,WAAW,EAAE;AAAA,MAC/E;AACA,YAAM,IAAI,QAAQ,OAAO;AACzB,kBAAY,YAAY,EAAE,IAAI,KAAK,CAAC;AAAA,IACtC,SAAS,KAAK;AACZ,kBAAY,YAAY,EAAE,IAAI,OAAO,OAAO,OAAO,GAAG,EAAE,CAAC;AAAA,IAC3D;AAAA,EACF,GAAG;AACL;AAGO,SAAS,YAAY,aAAqB,SAAiC;AAChF,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAS,IAAI,OAAO,IAAI,IAAI,sBAAsB,YAAY,GAAG,GAAG;AAAA,MACxE,YAAY,EAAE,aAAa,QAAQ;AAAA,IACrC,CAAC;AAED,WAAO,GAAG,WAAW,CAAC,QAAyC;AAC7D,UAAI,IAAI,GAAI,SAAQ;AAAA,UACf,QAAO,IAAI,MAAM,IAAI,SAAS,oBAAoB,CAAC;AAAA,IAC1D,CAAC;AAED,WAAO,GAAG,SAAS,MAAM;AACzB,WAAO,GAAG,QAAQ,CAAC,SAAS;AAC1B,UAAI,SAAS,EAAG,QAAO,IAAI,MAAM,kCAAkC,IAAI,EAAE,CAAC;AAAA,IAC5E,CAAC;AAAA,EACH,CAAC;AACH;;;AC9BA,eAAsB,WAAW,MAAiC;AAChE,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,OAAO,QAAQ;AAAA,EAChC,QAAQ;AACN,UAAM,IAAI,MAAM,wDAAwD;AAAA,EAC1E;AAEA,QAAM,aAAa,cAAc,KAAK,QAAQ;AAC9C,QAAM,QAAQ,IAAI,OAAO,MAAM,OAAO,EAAE,WAAW,CAAC;AAEpD,QAAM,SAAS,IAAI,OAAO;AAAA,IACxB;AAAA,IACA,OAAO,QAAa;AAClB,YAAM,MAAM,aAAa,IAAI,IAAI,IAAI;AACrC,UAAI,CAAC,KAAK;AACR,cAAM,IAAI,MAAM,sCAAsC,IAAI,IAAI,EAAE;AAAA,MAClE;AAEA,UAAI,IAAI,QAAQ,UAAU,IAAI,UAAU;AACtC,cAAM,YAAY,IAAI,UAAU,IAAI,IAAI;AAAA,MAC1C,OAAO;AACL,cAAM,IAAI,QAAQ,IAAI,IAAI;AAAA,MAC5B;AAAA,IACF;AAAA,IACA,EAAE,YAAY,aAAa,KAAK,YAAY;AAAA,EAC9C;AAEA,SAAO,GAAG,UAAU,CAAC,KAAsB,QAAe;AACxD,YAAQ,MAAM,cAAc,KAAK,QAAQ,SAAS,YAAY,IAAI,OAAO;AAAA,EAC3E,CAAC;AAED,SAAO,GAAG,aAAa,CAAC,QAAa;AACnC,YAAQ,IAAI,cAAc,IAAI,IAAI,kBAAkB,IAAI,EAAE,GAAG;AAAA,EAC/D,CAAC;AAED,iBAAe,OAAO,MAAM,YAAY;AACtC,UAAM,MAAM,aAAa,IAAI,IAAI;AACjC,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,uCAAuC,IAAI,kCAAkC;AAAA,IAC/F;AAEA,UAAM,MAAM,IAAI,MAAM,SAAS;AAAA,MAC7B,UAAU,IAAI,QAAQ,WAAW;AAAA,MACjC,SAAS;AAAA,QACP,MAAM,IAAI,QAAQ,WAAW;AAAA,QAC7B,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,UAAQ,IAAI,kCAAkC;AAChD;AAEA,SAAS,cAAc,KAAgE;AACrF,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,GAAG;AACrB,UAAM,SAA4D;AAAA,MAChE,MAAM,EAAE,YAAY;AAAA,MACpB,MAAM,SAAS,EAAE,QAAQ,QAAQ,EAAE;AAAA,IACrC;AACA,QAAI,EAAE,SAAU,QAAO,WAAW,EAAE;AACpC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,EAAE,MAAM,aAAa,MAAM,KAAK;AAAA,EACzC;AACF;;;AC3EO,IAAM,YAAN,MAAM,mBAAkB,MAAM;AAAA,EAC1B;AAAA,EAET,YAAY,YAAoB,SAAiB;AAC/C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,UAAM,kBAAkB,MAAM,UAAS;AAAA,EACzC;AACF;;;ACPO,SAAS,WAAW,UAA4C;AACrE,SAAO,CAAC,KAAK,KAAK,SAAS;AACzB,QAAI,QAAQ;AAEZ,aAAS,SAAS,GAAiB;AACjC,UAAI,KAAK,SAAS,QAAQ;AACxB,aAAK;AACL;AAAA,MACF;AACA,YAAM,UAAU,SAAS,CAAC;AAC1B,UAAI,CAAC,SAAS;AACZ,aAAK;AACL;AAAA,MACF;AACA,UAAI;AACF,gBAAQ,QAAQ,QAAQ,KAAK,KAAK,MAAM,SAAS,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI;AAAA,MACtE,SAAS,KAAK;AACZ,aAAK,GAAG;AAAA,MACV;AAAA,IACF;AAEA,aAAS,KAAK;AAAA,EAChB;AACF;;;AVRA,SAAS,eAAe,KAAoD;AAC1E,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,WAAW,SAAS,EAAG,QAAO;AACtC,MAAI,IAAI,WAAW,UAAU,EAAG,QAAO;AACvC,SAAO;AACT;AAEA,eAAsB,OAAO,QAAqD;AAChF,QAAM;AAAA,IACJ,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,oBAAoB,CAAC;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAGJ,QAAM,UAAU,QAAQ,IAAI,MAAM,KAAK,OAAO,OAAO,QAAQ,IAAI,MAAM,CAAC,IAAI;AAC5E,QAAM,OAAQ,SAAS,QAAQ,CAAC,OAAO,MAAM,KAAK,IAAK,QACnD,CAAC,OAAO,MAAM,OAAO,IAAI,UACzB;AACJ,QAAM,cAAc,OAAO,eAAe,QAAQ,IAAI,cAAc;AACpE,QAAM,WAAW,OAAO,YAAY,eAAe,WAAW;AAC9D,QAAM,YAAY,OAAO,aAAa,QAAQ,IAAI,YAAY;AAC9D,QAAM,eAAe,OAAO,gBAAgB;AAC5C,QAAM,iBAAiB,OAAO,WAAW,QAAQ,IAAI,UAAU,MAAM;AAErE,MAAI,kBAAkBC,SAAQ,WAAW;AACvC,cAAU;AAAA,MACR,SAAS,WAAWC,IAAG,KAAK,EAAE;AAAA,MAC9B,GAAI,kBAAkB,UAAa,EAAE,cAAc;AAAA,MACnD,GAAI,kBAAkB,UAAa,EAAE,cAAc;AAAA,IACrD,CAAC;AACD;AAAA,EACF;AAEA,QAAM,MAAM,QAAQ;AAEpB,QAAM,aAAa,OAAO,QAAQ;AAClC,MAAI,eAAe,OAAO;AACxB,UAAM,aAAa,QAAQ,IAAI,cAAc;AAC7C,QAAI;AACJ,QAAI,YAAY;AACd,YAAM,OAAO,WAAW,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AACtE,eAAS;AAAA,IACX,WAAW,OAAO,eAAe,YAAY,WAAW,WAAW,QAAW;AAC5E,eAAS,WAAW;AAAA,IACtB,OAAO;AACL,eAAS;AAAA,IACX;AACA,UAAM,WAAW,OAAO,eAAe,WAAW,EAAE,GAAG,YAAY,OAAO,IAAI,EAAE,OAAO;AACvF,QAAI,IAAI,KAAK,QAAQ,CAAC;AAAA,EACxB;AAEA,MAAI,IAAI,QAAQ,KAAK,CAAC;AACtB,MAAI,IAAI,QAAQ,WAAW,EAAE,UAAU,KAAK,CAAC,CAAC;AAC9C,MAAI,IAAI,aAAa,CAAC;AAEtB,aAAW,MAAM,mBAAmB;AAClC,QAAI,IAAI,EAAE;AAAA,EACZ;AAGA,MAAI,aAAa,aAAa,aAAa;AACzC,UAAM,OAAO,MAAM,aAAa,WAAW;AAC3C,gBAAY,IAA0C;AAAA,EACxD;AAGA,MAAI,WAAW;AACb,UAAM,eAAe,QAAQ,IAAI,eAAe;AAChD,kBAAc;AAAA,MACZ,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,WAAW,QAAQ,IAAI,gBAAgB,KAAK;AAAA,MAC5C,GAAI,iBAAiB,UAAa,EAAE,aAAa;AAAA,IACnD,CAAC;AAAA,EACH;AAGA,MAAI,OAAO,UAAU;AACnB,UAAM,UAAU,OAAO,QAAQ;AAAA,EACjC;AAGA,MAAI,OAAO,OAAO;AAChB,QAAI,OAAO,MAAM,YAAY,UAAU;AACrC,YAAM,WAAW;AAAA,QACf,UAAU,OAAO,MAAM,YAAY;AAAA,QACnC,aAAa,OAAO,MAAM,eAAe;AAAA,MAC3C,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,SAAS,QAAQ,MAAM;AAC7B,QAAM,YAAY,KAAK,MAAM;AAE7B,MAAI,SAAS;AACX,QAAI,IAAI,OAAO;AAAA,EACjB,OAAO;AACL,QAAI;AAAA,MACF,CACE,KACA,MACA,KACA,UACG;AACH,cAAM,SAAS,eAAe,SAAS,gBAAgB,MAClD,MAAoB;AACzB,YAAI,QAAQ;AACV,cAAI,OAAO,OAAO,UAAU,EAAE,KAAK,EAAE,OAAO,OAAO,SAAS,YAAY,OAAO,WAAW,CAAC;AAAA,QAC7F,OAAO;AACL,kBAAQ,MAAM,0BAA0B,GAAG;AAC3C,cAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,yBAAyB,YAAY,IAAI,CAAC;AAAA,QAC1E;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,IAAI,QAAqB,CAAC,SAAS,WAAW;AACnD,UAAM,SAAS,IAAI,OAAO,IAAI;AAE9B,WAAO,KAAK,aAAa,MAAM;AAC7B,YAAM,MAAOD,SAAQ,QAAuC,MAAM;AAClE,YAAM,OAAO,OAAO,QAAQ;AAC5B,cAAQ,IAAI,gBAAgB,GAAG,kBAAkB,MAAM,QAAQ,IAAI,EAAE;AACrE,cAAQ,MAAM;AAAA,IAChB,CAAC;AAED,WAAO,KAAK,SAAS,MAAM;AAAA,EAC7B,CAAC;AACH;AAEO,SAAS,iBAAiB,QAAiC,YAAY,KAAc;AAC1F,QAAM,WAAW,CAAC,WAAmB;AACnC,YAAQ,IAAI,SAAS,MAAM,kDAAwC;AACnE,QAAI,QAAQ;AACV,aAAO,qBAAqB;AAC5B,aAAO,MAAM,MAAM;AACjB,gBAAQ,IAAI,qBAAqB;AACjC,gBAAQ,KAAK,CAAC;AAAA,MAChB,CAAC;AACD,iBAAW,MAAM;AACf,gBAAQ,MAAM,iCAAiC;AAC/C,gBAAQ,KAAK,CAAC;AAAA,MAChB,GAAG,SAAS,EAAE,MAAM;AAAA,IACtB,OAAO;AACL,qBAAe;AAAA,IACjB;AAAA,EACF;AACA,UAAQ,KAAK,WAAW,MAAM,SAAS,SAAS,CAAC;AACjD,UAAQ,KAAK,UAAU,MAAM,SAAS,QAAQ,CAAC;AACjD;","names":["cluster","os","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/errors.ts","../src/compose.ts"],"sourcesContent":["import http from 'node:http';\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 } 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';\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 // All runtime values fall back to environment variables\n const baseDir = process.argv[1] ? path.dirname(process.argv[1]) : process.cwd();\n const apiDir = path.join(baseDir, 'api');\n const tasksDir = path.join(baseDir, 'tasks');\n const basePath = config.basePath ?? '/v1/api';\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 await mountRoutes(apiRouter, routes);\n app.use(basePath, apiRouter);\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 TaskOptions,\n TaskDefinition,\n DatabaseEngine,\n AuthStrategy,\n ModelSchema,\n ModelCRUD,\n} from './types.js';\n","import type { IRouter, RequestHandler } from 'express';\nimport type { 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(app: IRouter, routes: RouteEntry[]): Promise<void> {\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 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}\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","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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","names":["cluster","os","path","path","cluster","os"]}
@@ -4,12 +4,12 @@
4
4
 
5
5
 
6
6
 
7
- var _chunkUDE6S53Ccjs = require('../chunk-UDE6S53C.cjs');
7
+ var _chunkXH7O25BLcjs = require('../chunk-XH7O25BL.cjs');
8
8
 
9
9
 
10
10
 
11
11
 
12
12
 
13
13
 
14
- exports.defineTask = _chunkUDE6S53Ccjs.defineTask; exports.enqueue = _chunkUDE6S53Ccjs.enqueue; exports.registerTask = _chunkUDE6S53Ccjs.registerTask; exports.setEnqueueImpl = _chunkUDE6S53Ccjs.setEnqueueImpl; exports.taskRegistry = _chunkUDE6S53Ccjs.taskRegistry;
14
+ exports.defineTask = _chunkXH7O25BLcjs.defineTask; exports.enqueue = _chunkXH7O25BLcjs.enqueue; exports.registerTask = _chunkXH7O25BLcjs.registerTask; exports.setEnqueueImpl = _chunkXH7O25BLcjs.setEnqueueImpl; exports.taskRegistry = _chunkXH7O25BLcjs.taskRegistry;
15
15
  //# sourceMappingURL=index.cjs.map
@@ -1,4 +1,4 @@
1
- import { T as TaskDefinition, a as TaskOptions } from '../types-Dj9-sLOK.cjs';
1
+ import { T as TaskDefinition, a as TaskOptions } from '../types-DpegkYh2.cjs';
2
2
  import 'express';
3
3
 
4
4
  declare const taskRegistry: Map<string, TaskDefinition<unknown>>;
@@ -1,4 +1,4 @@
1
- import { T as TaskDefinition, a as TaskOptions } from '../types-Dj9-sLOK.js';
1
+ import { T as TaskDefinition, a as TaskOptions } from '../types-DpegkYh2.js';
2
2
  import 'express';
3
3
 
4
4
  declare const taskRegistry: Map<string, TaskDefinition<unknown>>;
@@ -4,7 +4,7 @@ import {
4
4
  registerTask,
5
5
  setEnqueueImpl,
6
6
  taskRegistry
7
- } from "../chunk-Z5YNIVQG.js";
7
+ } from "../chunk-LPZEKFVN.js";
8
8
  export {
9
9
  defineTask,
10
10
  enqueue,
@@ -17,8 +17,7 @@ interface CorsConfig {
17
17
  }
18
18
  interface EFCConfig {
19
19
  port?: number;
20
- apiDir: string;
21
- tasksDir?: string;
20
+ basePath?: string;
22
21
  database?: DatabaseEngine;
23
22
  databaseUrl?: string;
24
23
  authStrategy?: AuthStrategy;
@@ -17,8 +17,7 @@ interface CorsConfig {
17
17
  }
18
18
  interface EFCConfig {
19
19
  port?: number;
20
- apiDir: string;
21
- tasksDir?: string;
20
+ basePath?: string;
22
21
  database?: DatabaseEngine;
23
22
  databaseUrl?: string;
24
23
  authStrategy?: AuthStrategy;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "express-file-cluster",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Enterprise-grade, zero-boilerplate backend framework with file-based routing and multi-core clustering",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -55,7 +55,7 @@
55
55
  "supertest": "^7.2.2",
56
56
  "tsup": "^8.2.0",
57
57
  "typescript": "^5.5.0",
58
- "vitest": "^2.0.0"
58
+ "vitest": "^4.1.9"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "bullmq": ">=5.0.0",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/auth/index.ts"],"sourcesContent":["import type { RequestHandler, Response } from 'express';\nimport { SignJWT, jwtVerify } from 'jose';\nimport type { AuthStrategy } from '../types.js';\n\ninterface AuthConfig {\n secret: string;\n strategy: AuthStrategy;\n expiresIn: string;\n cookieDomain?: string | undefined;\n}\n\nlet _config: AuthConfig | null = null;\n\nexport function configureAuth(config: AuthConfig): void {\n _config = config;\n}\n\nfunction getConfig(): AuthConfig {\n if (!_config) throw new Error('[EFC] Auth not configured — pass jwtSecret to ignite()');\n return _config;\n}\n\nexport async function issueToken(res: Response, payload: Record<string, unknown>): Promise<void> {\n const { secret, expiresIn, cookieDomain } = getConfig();\n const encodedSecret = new TextEncoder().encode(secret);\n const token = await new SignJWT(payload)\n .setProtectedHeader({ alg: 'HS256' })\n .setExpirationTime(expiresIn)\n .sign(encodedSecret);\n \n res.cookie('efc_token', token, {\n httpOnly: true,\n secure: process.env['NODE_ENV'] === 'production',\n sameSite: 'strict',\n ...(cookieDomain !== undefined && { domain: cookieDomain }),\n });\n}\n\nexport function revokeToken(res: Response): void {\n res.clearCookie('efc_token');\n}\n\nexport async function signToken(payload: Record<string, unknown>): Promise<string> {\n const { secret, expiresIn } = getConfig();\n const encodedSecret = new TextEncoder().encode(secret);\n return await new SignJWT(payload)\n .setProtectedHeader({ alg: 'HS256' })\n .setExpirationTime(expiresIn)\n .sign(encodedSecret);\n}\n\nexport const requireAuth: RequestHandler = async (req, res, next) => {\n const { secret, strategy } = getConfig();\n\n try {\n let token: string | undefined;\n\n if (strategy === 'http-only') {\n const cookies = (req as typeof req & { cookies: Record<string, string> }).cookies;\n token = cookies?.['efc_token'];\n } else {\n const auth = req.headers['authorization'];\n if (typeof auth === 'string' && auth.startsWith('Bearer ')) {\n token = auth.slice(7);\n }\n }\n\n if (!token) {\n res.status(401).json({ error: 'Unauthorized' });\n return;\n }\n\n const encodedSecret = new TextEncoder().encode(secret);\n const { payload } = await jwtVerify(token, encodedSecret);\n (req as typeof req & { user: unknown }).user = payload;\n next();\n } catch {\n res.status(401).json({ error: 'Unauthorized' });\n }\n};\n"],"mappings":";AACA,SAAS,SAAS,iBAAiB;AAUnC,IAAI,UAA6B;AAE1B,SAAS,cAAc,QAA0B;AACtD,YAAU;AACZ;AAEA,SAAS,YAAwB;AAC/B,MAAI,CAAC,QAAS,OAAM,IAAI,MAAM,6DAAwD;AACtF,SAAO;AACT;AAEA,eAAsB,WAAW,KAAe,SAAiD;AAC/F,QAAM,EAAE,QAAQ,WAAW,aAAa,IAAI,UAAU;AACtD,QAAM,gBAAgB,IAAI,YAAY,EAAE,OAAO,MAAM;AACrD,QAAM,QAAQ,MAAM,IAAI,QAAQ,OAAO,EACpC,mBAAmB,EAAE,KAAK,QAAQ,CAAC,EACnC,kBAAkB,SAAS,EAC3B,KAAK,aAAa;AAErB,MAAI,OAAO,aAAa,OAAO;AAAA,IAC7B,UAAU;AAAA,IACV,QAAQ,QAAQ,IAAI,UAAU,MAAM;AAAA,IACpC,UAAU;AAAA,IACV,GAAI,iBAAiB,UAAa,EAAE,QAAQ,aAAa;AAAA,EAC3D,CAAC;AACH;AAEO,SAAS,YAAY,KAAqB;AAC/C,MAAI,YAAY,WAAW;AAC7B;AAEA,eAAsB,UAAU,SAAmD;AACjF,QAAM,EAAE,QAAQ,UAAU,IAAI,UAAU;AACxC,QAAM,gBAAgB,IAAI,YAAY,EAAE,OAAO,MAAM;AACrD,SAAO,MAAM,IAAI,QAAQ,OAAO,EAC7B,mBAAmB,EAAE,KAAK,QAAQ,CAAC,EACnC,kBAAkB,SAAS,EAC3B,KAAK,aAAa;AACvB;AAEO,IAAM,cAA8B,OAAO,KAAK,KAAK,SAAS;AACnE,QAAM,EAAE,QAAQ,SAAS,IAAI,UAAU;AAEvC,MAAI;AACF,QAAI;AAEJ,QAAI,aAAa,aAAa;AAC5B,YAAM,UAAW,IAAyD;AAC1E,cAAQ,UAAU,WAAW;AAAA,IAC/B,OAAO;AACL,YAAM,OAAO,IAAI,QAAQ,eAAe;AACxC,UAAI,OAAO,SAAS,YAAY,KAAK,WAAW,SAAS,GAAG;AAC1D,gBAAQ,KAAK,MAAM,CAAC;AAAA,MACtB;AAAA,IACF;AAEA,QAAI,CAAC,OAAO;AACV,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,eAAe,CAAC;AAC9C;AAAA,IACF;AAEA,UAAM,gBAAgB,IAAI,YAAY,EAAE,OAAO,MAAM;AACrD,UAAM,EAAE,QAAQ,IAAI,MAAM,UAAU,OAAO,aAAa;AACxD,IAAC,IAAuC,OAAO;AAC/C,SAAK;AAAA,EACP,QAAQ;AACN,QAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,eAAe,CAAC;AAAA,EAChD;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/efc.js/efc.js/packages/core/dist/chunk-UDE6S53C.cjs","../src/tasks/index.ts"],"names":[],"mappings":"AAAA;ACEO,IAAM,aAAA,kBAAe,IAAI,GAAA,CAA4B,CAAA;AAQrD,IAAM,WAAA,EAAiC,CAC5C,gBAAA,EACA,YAAA,EAAA,GACsB;AACtB,EAAA,IAAI,QAAA,EAAuB,CAAC,CAAA;AAC5B,EAAA,IAAI,OAAA;AAEJ,EAAA,GAAA,CAAI,OAAO,iBAAA,IAAqB,UAAA,EAAY;AAC1C,IAAA,QAAA,EAAU,gBAAA;AAAA,EACZ,EAAA,KAAO;AACL,IAAA,QAAA,EAAU,gBAAA;AACV,IAAA,GAAA,CAAI,CAAC,YAAA,EAAc,MAAM,IAAI,KAAA,CAAM,gDAAgD,CAAA;AACnF,IAAA,QAAA,EAAU,YAAA;AAAA,EACZ;AAEA,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,OAAA;AAAA,IACA,IAAA,EAAM;AAAA,EACR,CAAA;AACF,CAAA;AAGA,IAAI,MAAA,EAA4B,IAAA;AAEzB,SAAS,cAAA,CAAe,IAAA,EAAyB;AACtD,EAAA,MAAA,EAAQ,IAAA;AACV;AAEA,MAAA,SAAsB,OAAA,CAAW,IAAA,EAAc,OAAA,EAA2B;AACxE,EAAA,GAAA,CAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,uEAAA;AAAA,IACF,CAAA;AAAA,EACF;AACA,EAAA,OAAO,KAAA,CAAM,IAAA,EAAM,OAAkB,CAAA;AACvC;AAEO,SAAS,YAAA,CAAa,IAAA,EAAc,GAAA,EAA2B;AACpE,EAAA,YAAA,CAAa,GAAA,CAAI,IAAA,EAAM,EAAE,GAAG,GAAA,EAAK,KAAK,CAAC,CAAA;AACzC;ADjBA;AACA;AACE;AACA;AACA;AACA;AACA;AACF,8KAAC","file":"/home/runner/work/efc.js/efc.js/packages/core/dist/chunk-UDE6S53C.cjs","sourcesContent":[null,"import type { TaskDefinition, TaskOptions } from '../types.js';\n\nexport const taskRegistry = new Map<string, TaskDefinition>();\n\ntype HandlerFn<T> = (payload: T) => Promise<void>;\ntype DefineTaskOverload = {\n <T>(handler: HandlerFn<T>): TaskDefinition<T>;\n <T>(options: TaskOptions, handler: HandlerFn<T>): TaskDefinition<T>;\n};\n\nexport const defineTask: DefineTaskOverload = <T>(\n handlerOrOptions: HandlerFn<T> | TaskOptions,\n maybeHandler?: HandlerFn<T>,\n): TaskDefinition<T> => {\n let options: TaskOptions = {};\n let handler: HandlerFn<T>;\n\n if (typeof handlerOrOptions === 'function') {\n handler = handlerOrOptions;\n } else {\n options = handlerOrOptions;\n if (!maybeHandler) throw new Error('[EFC] defineTask: handler function is required');\n handler = maybeHandler;\n }\n\n return {\n handler: handler as (payload: unknown) => Promise<void>,\n options,\n name: '',\n };\n};\n\ntype EnqueueImpl = (name: string, payload: unknown) => Promise<void>;\nlet _impl: EnqueueImpl | null = null;\n\nexport function setEnqueueImpl(impl: EnqueueImpl): void {\n _impl = impl;\n}\n\nexport async function enqueue<T>(name: string, payload: T): Promise<void> {\n if (!_impl) {\n throw new Error(\n `[EFC] Task queue not initialised. Set tasks.backend in ignite() config.`,\n );\n }\n return _impl(name, payload as unknown);\n}\n\nexport function registerTask(name: string, def: TaskDefinition): void {\n taskRegistry.set(name, { ...def, name });\n}\n"]}