express-file-cluster 0.2.1 → 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.
@@ -4,12 +4,12 @@
4
4
 
5
5
 
6
6
 
7
- var _chunkQB3X6LKZcjs = require('../chunk-QB3X6LKZ.cjs');
7
+ var _chunkUUF2OYRWcjs = require('../chunk-UUF2OYRW.cjs');
8
8
 
9
9
 
10
10
 
11
11
 
12
12
 
13
13
 
14
- exports.configureAuth = _chunkQB3X6LKZcjs.configureAuth; exports.issueToken = _chunkQB3X6LKZcjs.issueToken; exports.requireAuth = _chunkQB3X6LKZcjs.requireAuth; exports.revokeToken = _chunkQB3X6LKZcjs.revokeToken; exports.signToken = _chunkQB3X6LKZcjs.signToken;
14
+ exports.configureAuth = _chunkUUF2OYRWcjs.configureAuth; exports.issueToken = _chunkUUF2OYRWcjs.issueToken; exports.requireAuth = _chunkUUF2OYRWcjs.requireAuth; exports.revokeToken = _chunkUUF2OYRWcjs.revokeToken; exports.signToken = _chunkUUF2OYRWcjs.signToken;
15
15
  //# sourceMappingURL=index.cjs.map
@@ -1,5 +1,5 @@
1
1
  import { Response, RequestHandler } from 'express';
2
- import { A as AuthStrategy } from '../types-Dj9-sLOK.cjs';
2
+ import { A as AuthStrategy } from '../types-DpegkYh2.cjs';
3
3
 
4
4
  interface AuthConfig {
5
5
  secret: string;
@@ -1,5 +1,5 @@
1
1
  import { Response, RequestHandler } from 'express';
2
- import { A as AuthStrategy } from '../types-Dj9-sLOK.js';
2
+ import { A as AuthStrategy } from '../types-DpegkYh2.js';
3
3
 
4
4
  interface AuthConfig {
5
5
  secret: string;
@@ -4,7 +4,7 @@ import {
4
4
  requireAuth,
5
5
  revokeToken,
6
6
  signToken
7
- } from "../chunk-DQGWFWBV.js";
7
+ } from "../chunk-BHMFPULA.js";
8
8
  export {
9
9
  configureAuth,
10
10
  issueToken,
@@ -60,4 +60,4 @@ export {
60
60
  signToken,
61
61
  requireAuth
62
62
  };
63
- //# sourceMappingURL=chunk-DQGWFWBV.js.map
63
+ //# sourceMappingURL=chunk-BHMFPULA.js.map
@@ -0,0 +1 @@
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":[]}
@@ -22,9 +22,7 @@ function setEnqueueImpl(impl) {
22
22
  }
23
23
  async function enqueue(name, payload) {
24
24
  if (!_impl) {
25
- throw new Error(
26
- `[EFC] Task queue not initialised. Set tasks.backend in ignite() config.`
27
- );
25
+ throw new Error(`[EFC] Task queue not initialised. Set tasks.backend in ignite() config.`);
28
26
  }
29
27
  return _impl(name, payload);
30
28
  }
@@ -39,4 +37,4 @@ export {
39
37
  enqueue,
40
38
  registerTask
41
39
  };
42
- //# sourceMappingURL=chunk-Z5YNIVQG.js.map
40
+ //# sourceMappingURL=chunk-LPZEKFVN.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tasks/index.ts"],"sourcesContent":["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"],"mappings":";AAEO,IAAM,eAAe,oBAAI,IAA4B;AAQrD,IAAM,aAAiC,CAC5C,kBACA,iBACsB;AACtB,MAAI,UAAuB,CAAC;AAC5B,MAAI;AAEJ,MAAI,OAAO,qBAAqB,YAAY;AAC1C,cAAU;AAAA,EACZ,OAAO;AACL,cAAU;AACV,QAAI,CAAC,aAAc,OAAM,IAAI,MAAM,gDAAgD;AACnF,cAAU;AAAA,EACZ;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,MAAM;AAAA,EACR;AACF;AAGA,IAAI,QAA4B;AAEzB,SAAS,eAAe,MAAyB;AACtD,UAAQ;AACV;AAEA,eAAsB,QAAW,MAAc,SAA2B;AACxE,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,MAAM,MAAM,OAAkB;AACvC;AAEO,SAAS,aAAa,MAAc,KAA2B;AACpE,eAAa,IAAI,MAAM,EAAE,GAAG,KAAK,KAAK,CAAC;AACzC;","names":[]}
1
+ {"version":3,"sources":["../src/tasks/index.ts"],"sourcesContent":["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(`[EFC] Task queue not initialised. Set tasks.backend in ignite() config.`);\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"],"mappings":";AAEO,IAAM,eAAe,oBAAI,IAA4B;AAQrD,IAAM,aAAiC,CAC5C,kBACA,iBACsB;AACtB,MAAI,UAAuB,CAAC;AAC5B,MAAI;AAEJ,MAAI,OAAO,qBAAqB,YAAY;AAC1C,cAAU;AAAA,EACZ,OAAO;AACL,cAAU;AACV,QAAI,CAAC,aAAc,OAAM,IAAI,MAAM,gDAAgD;AACnF,cAAU;AAAA,EACZ;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,MAAM;AAAA,EACR;AACF;AAGA,IAAI,QAA4B;AAEzB,SAAS,eAAe,MAAyB;AACtD,UAAQ;AACV;AAEA,eAAsB,QAAW,MAAc,SAA2B;AACxE,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,yEAAyE;AAAA,EAC3F;AACA,SAAO,MAAM,MAAM,OAAkB;AACvC;AAEO,SAAS,aAAa,MAAc,KAA2B;AACpE,eAAa,IAAI,MAAM,EAAE,GAAG,KAAK,KAAK,CAAC;AACzC;","names":[]}
@@ -60,4 +60,4 @@ var requireAuth = async (req, res, next) => {
60
60
 
61
61
 
62
62
  exports.configureAuth = configureAuth; exports.issueToken = issueToken; exports.revokeToken = revokeToken; exports.signToken = signToken; exports.requireAuth = requireAuth;
63
- //# sourceMappingURL=chunk-QB3X6LKZ.cjs.map
63
+ //# sourceMappingURL=chunk-UUF2OYRW.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/efc.js/efc.js/packages/core/dist/chunk-QB3X6LKZ.cjs","../src/auth/index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/efc.js/efc.js/packages/core/dist/chunk-QB3X6LKZ.cjs","sourcesContent":[null,"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"]}
1
+ {"version":3,"sources":["/home/runner/work/efc.js/efc.js/packages/core/dist/chunk-UUF2OYRW.cjs","../src/auth/index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/efc.js/efc.js/packages/core/dist/chunk-UUF2OYRW.cjs","sourcesContent":[null,"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"]}
@@ -22,9 +22,7 @@ function setEnqueueImpl(impl) {
22
22
  }
23
23
  async function enqueue(name, payload) {
24
24
  if (!_impl) {
25
- throw new Error(
26
- `[EFC] Task queue not initialised. Set tasks.backend in ignite() config.`
27
- );
25
+ throw new Error(`[EFC] Task queue not initialised. Set tasks.backend in ignite() config.`);
28
26
  }
29
27
  return _impl(name, payload);
30
28
  }
@@ -39,4 +37,4 @@ function registerTask(name, def) {
39
37
 
40
38
 
41
39
  exports.taskRegistry = taskRegistry; exports.defineTask = defineTask; exports.setEnqueueImpl = setEnqueueImpl; exports.enqueue = enqueue; exports.registerTask = registerTask;
42
- //# sourceMappingURL=chunk-UDE6S53C.cjs.map
40
+ //# sourceMappingURL=chunk-XH7O25BL.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/efc.js/efc.js/packages/core/dist/chunk-XH7O25BL.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,CAAM,CAAA,uEAAA,CAAyE,CAAA;AAAA,EAC3F;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-XH7O25BL.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(`[EFC] Task queue not initialised. Set tasks.backend in ignite() config.`);\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"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/efc.js/efc.js/packages/core/dist/cli/index.cjs","../../src/cli/index.ts","../../src/cli/commands/start.ts","../../src/cli/commands/build.ts","../../src/cli/commands/run.ts","../../src/cli/commands/generate.ts","../../src/cli/commands/diagnostics.ts"],"names":["path","pc"],"mappings":"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","file":"/home/runner/work/efc.js/efc.js/packages/core/dist/cli/index.cjs","sourcesContent":[null,"#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { startCommand } from './commands/start.js';\nimport { buildCommand } from './commands/build.js';\nimport { runCommand } from './commands/run.js';\nimport { generateCommand } from './commands/generate.js';\nimport { diagnosticsCommands } from './commands/diagnostics.js';\n\nconst program = new Command('efc')\n .description('Express File Cluster CLI')\n .version('0.1.0');\n\nprogram.addCommand(startCommand());\nprogram.addCommand(buildCommand());\nprogram.addCommand(runCommand());\nprogram.addCommand(generateCommand());\n\nfor (const cmd of diagnosticsCommands()) {\n program.addCommand(cmd);\n}\n\nprogram.parse(process.argv);\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport pc from 'picocolors';\n\nexport function startCommand(): Command {\n const cmd = new Command('start');\n\n cmd\n .argument('<mode>', 'dev | prod')\n .description('Start the EFC server')\n .action((mode: string) => {\n if (mode === 'dev') {\n startDev();\n } else if (mode === 'prod') {\n startProd();\n } else {\n console.error(pc.red(`Unknown mode: ${mode}. Use 'dev' or 'prod'.`));\n process.exit(1);\n }\n });\n\n return cmd;\n}\n\nfunction parseDotenv(cwd: string): Record<string, string> {\n const envFile = path.join(cwd, '.env');\n if (!fs.existsSync(envFile)) return {};\n const vars: Record<string, string> = {};\n for (const line of fs.readFileSync(envFile, 'utf8').split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eq = trimmed.indexOf('=');\n if (eq === -1) continue;\n const key = trimmed.slice(0, eq).trim();\n const raw = trimmed.slice(eq + 1).trim();\n vars[key] = raw.replace(/^(['\"])(.*)\\1$/, '$2');\n }\n return vars;\n}\n\nfunction startDev(): void {\n const entry = resolveEntry();\n if (!entry) {\n console.error(pc.red('[EFC] Could not find entry point. Expected src/index.ts or index.ts'));\n process.exit(1);\n }\n\n console.log(pc.cyan('[EFC] Starting development server…'));\n console.log(pc.dim(` Entry: ${entry}`));\n\n const cwd = process.cwd();\n const localTsx = path.join(cwd, 'node_modules', '.bin', 'tsx');\n const tsx = fs.existsSync(localTsx) ? localTsx : 'tsx';\n\n // .env values are base; existing process.env takes precedence (same as dotenv default)\n const env: NodeJS.ProcessEnv = { ...parseDotenv(cwd), ...process.env, NODE_ENV: 'development' };\n\n const child = spawn(tsx, ['watch', '--include', 'src', entry], { stdio: 'inherit', env });\n child.on('exit', (code) => process.exit(code ?? 0));\n}\n\nfunction startProd(): void {\n const cwd = process.cwd();\n const distEntry = path.join(cwd, 'dist', 'index.js');\n\n if (!fs.existsSync(distEntry)) {\n console.error(pc.red('[EFC] dist/index.js not found. Run `efc build prod` first.'));\n process.exit(1);\n }\n\n console.log(pc.cyan('[EFC] Starting production server…'));\n\n // Production env comes exclusively from process.env — no .env file loading.\n // Platforms (Docker, Kubernetes, Railway, Heroku, etc.) inject vars directly.\n const child = spawn('node', [distEntry], {\n stdio: 'inherit',\n env: { ...process.env, NODE_ENV: 'production' },\n });\n child.on('exit', (code) => process.exit(code ?? 0));\n}\n\nfunction resolveEntry(): string | null {\n const cwd = process.cwd();\n const candidates = [\n path.join(cwd, 'src', 'index.ts'),\n path.join(cwd, 'index.ts'),\n path.join(cwd, 'src', 'index.js'),\n path.join(cwd, 'index.js'),\n ];\n return candidates.find((f) => fs.existsSync(f)) ?? null;\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport pc from 'picocolors';\n\nexport function buildCommand(): Command {\n const cmd = new Command('build');\n\n cmd\n .argument('<mode>', 'prod')\n .description('Build the application for production')\n .action((mode: string) => {\n if (mode !== 'prod') {\n console.error(pc.red(`Unknown build mode: ${mode}. Use 'prod'.`));\n process.exit(1);\n }\n buildProd();\n });\n\n return cmd;\n}\n\nfunction buildProd(): void {\n console.log(pc.cyan('[EFC] Building for production…'));\n\n // Type-check first\n const tsc = spawn('npx', ['tsc', '--noEmit'], { stdio: 'inherit' });\n\n tsc.on('exit', (code) => {\n if (code !== 0) {\n console.error(pc.red('[EFC] TypeScript errors found. Fix them before building.'));\n process.exit(1);\n }\n\n const tsup = spawn('npx', ['tsup'], { stdio: 'inherit' });\n tsup.on('exit', (tsupCode) => {\n if (tsupCode === 0) {\n console.log(pc.green('[EFC] Build complete → dist/'));\n } else {\n process.exit(tsupCode ?? 1);\n }\n });\n });\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport pc from 'picocolors';\n\nexport function runCommand(): Command {\n const cmd = new Command('run');\n\n cmd\n .argument('<runner>', 'tests')\n .description('Run EFC sub-commands (tests)')\n .allowUnknownOption()\n .action((runner: string) => {\n if (runner === 'tests') {\n runTests(cmd.args.slice(1));\n } else {\n console.error(pc.red(`Unknown runner: ${runner}. Use 'tests'.`));\n process.exit(1);\n }\n });\n\n return cmd;\n}\n\nfunction runTests(extraArgs: string[]): void {\n console.log(pc.cyan('[EFC] Running tests via Vitest…'));\n const child = spawn('npx', ['vitest', 'run', ...extraArgs], { stdio: 'inherit' });\n child.on('exit', (code) => process.exit(code ?? 0));\n}\n","import { Command } from 'commander';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport pc from 'picocolors';\n\nexport function generateCommand(): Command {\n const cmd = new Command('generate').alias('g').description('Scaffold EFC modules');\n\n cmd\n .command('route <routePath>')\n .description('Scaffold a route module, e.g. users/[id]')\n .action((routePath: string) => generateRoute(routePath));\n\n cmd\n .command('task <name>')\n .description('Scaffold a background task module')\n .action((name: string) => generateTask(name));\n\n cmd\n .command('middleware <name>')\n .description('Scaffold a middleware module')\n .action((name: string) => generateMiddleware(name));\n\n return cmd;\n}\n\nfunction writeFile(filePath: string, content: string): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n if (fs.existsSync(filePath)) {\n console.error(pc.red(`File already exists: ${filePath}`));\n process.exit(1);\n }\n fs.writeFileSync(filePath, content, 'utf8');\n console.log(pc.green(` created ${path.relative(process.cwd(), filePath)}`));\n}\n\nfunction generateRoute(routePath: string): void {\n const cwd = process.cwd();\n const filePath = path.join(cwd, 'src', 'api', `${routePath}.ts`);\n\n const content = `import type { Request, Response } from 'express';\n\nexport const GET = async (req: Request, res: Response) => {\n res.json({ message: 'OK' });\n};\n\nexport const POST = async (req: Request, res: Response) => {\n res.status(201).json({ message: 'Created' });\n};\n`;\n\n writeFile(filePath, content);\n console.log(pc.cyan(`[EFC] Route scaffolded`));\n}\n\nfunction generateTask(name: string): void {\n const cwd = process.cwd();\n const filePath = path.join(cwd, 'src', 'tasks', `${name}.ts`);\n\n const content = `import { defineTask } from 'express-file-cluster/tasks';\n\ninterface ${name}Payload {\n // TODO: define payload fields\n}\n\nexport default defineTask<${name}Payload>(async (payload) => {\n // TODO: implement task logic\n console.log('[Task:${name}]', payload);\n});\n`;\n\n writeFile(filePath, content);\n console.log(pc.cyan(`[EFC] Task scaffolded`));\n}\n\nfunction generateMiddleware(name: string): void {\n const cwd = process.cwd();\n const filePath = path.join(cwd, 'src', 'middlewares', `${name}.ts`);\n\n const content = `import type { Request, Response, NextFunction } from 'express';\n\nexport function ${name}(req: Request, res: Response, next: NextFunction): void {\n // TODO: implement middleware logic\n next();\n}\n`;\n\n writeFile(filePath, content);\n console.log(pc.cyan(`[EFC] Middleware scaffolded`));\n}\n","import { Command } from 'commander';\nimport { scanDir } from '../../router/scan.js';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport pc from 'picocolors';\n\nexport function diagnosticsCommands(): Command[] {\n return [routesCommand(), tasksCommand(), doctorCommand()];\n}\n\nfunction routesCommand(): Command {\n return new Command('routes')\n .description('Print the resolved route table')\n .action(() => {\n const apiDir = resolveApiDir();\n if (!apiDir) {\n console.error(pc.red('[EFC] Could not find apiDir (expected src/api)'));\n process.exit(1);\n }\n\n const routes = scanDir(apiDir);\n if (routes.length === 0) {\n console.log(pc.yellow('No routes found.'));\n return;\n }\n\n console.log(pc.bold('\\n Route Table\\n'));\n console.log(pc.dim(' ' + '─'.repeat(60)));\n for (const route of routes) {\n const rel = path.relative(process.cwd(), route.filePath);\n console.log(` ${pc.cyan(route.urlPath.padEnd(35))} ${pc.dim(rel)}`);\n }\n console.log(pc.dim(' ' + '─'.repeat(60)));\n console.log(pc.dim(`\\n ${routes.length} route(s) found\\n`));\n });\n}\n\nfunction tasksCommand(): Command {\n return new Command('tasks')\n .description('List registered background tasks')\n .action(() => {\n const tasksDir = resolveTasksDir();\n if (!tasksDir || !fs.existsSync(tasksDir)) {\n console.log(pc.yellow('No tasks directory found.'));\n return;\n }\n\n const files = fs.readdirSync(tasksDir).filter((f) => /\\.(ts|js)$/.test(f));\n if (files.length === 0) {\n console.log(pc.yellow('No tasks found.'));\n return;\n }\n\n console.log(pc.bold('\\n Background Tasks\\n'));\n for (const file of files) {\n console.log(` ${pc.cyan(path.basename(file, path.extname(file)))}`);\n }\n console.log();\n });\n}\n\nfunction doctorCommand(): Command {\n return new Command('doctor')\n .description('Validate config, env vars, and project setup')\n .action(() => {\n const cwd = process.cwd();\n const checks: { label: string; ok: boolean; hint?: string }[] = [\n {\n label: 'package.json exists',\n ok: fs.existsSync(path.join(cwd, 'package.json')),\n },\n {\n label: 'tsconfig.json exists',\n ok: fs.existsSync(path.join(cwd, 'tsconfig.json')),\n hint: 'Run `tsc --init` to create one',\n },\n {\n label: 'src/api directory exists',\n ok: fs.existsSync(path.join(cwd, 'src', 'api')),\n hint: 'Create src/api/ and add route files',\n },\n {\n label: 'DATABASE_URL set',\n ok: Boolean(process.env['DATABASE_URL']),\n hint: 'Add DATABASE_URL to .env',\n },\n {\n label: 'JWT_SECRET set',\n ok: Boolean(process.env['JWT_SECRET']),\n hint: 'Add JWT_SECRET to .env (generate: openssl rand -hex 64)',\n },\n ];\n\n console.log(pc.bold('\\n EFC Doctor\\n'));\n let allOk = true;\n for (const check of checks) {\n const icon = check.ok ? pc.green('✓') : pc.red('✗');\n console.log(` ${icon} ${check.label}`);\n if (!check.ok) {\n allOk = false;\n if (check.hint) console.log(pc.dim(` → ${check.hint}`));\n }\n }\n console.log();\n if (allOk) {\n console.log(pc.green(' All checks passed!\\n'));\n } else {\n console.log(pc.yellow(' Some checks failed. Fix the issues above.\\n'));\n process.exit(1);\n }\n });\n}\n\nfunction resolveApiDir(): string | null {\n const cwd = process.cwd();\n const candidates = [path.join(cwd, 'src', 'api'), path.join(cwd, 'api')];\n return candidates.find((d) => fs.existsSync(d)) ?? null;\n}\n\nfunction resolveTasksDir(): string | null {\n const cwd = process.cwd();\n const candidates = [path.join(cwd, 'src', 'tasks'), path.join(cwd, 'tasks')];\n return candidates.find((d) => fs.existsSync(d)) ?? null;\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/efc.js/efc.js/packages/core/dist/cli/index.cjs","../../src/cli/index.ts","../../src/cli/commands/start.ts","../../src/cli/commands/build.ts","../../src/cli/commands/run.ts","../../src/cli/commands/generate.ts","../../src/cli/commands/diagnostics.ts"],"names":["path","pc"],"mappings":"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","file":"/home/runner/work/efc.js/efc.js/packages/core/dist/cli/index.cjs","sourcesContent":[null,"#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { startCommand } from './commands/start.js';\nimport { buildCommand } from './commands/build.js';\nimport { runCommand } from './commands/run.js';\nimport { generateCommand } from './commands/generate.js';\nimport { diagnosticsCommands } from './commands/diagnostics.js';\n\nconst program = new Command('efc').description('Express File Cluster CLI').version('0.1.0');\n\nprogram.addCommand(startCommand());\nprogram.addCommand(buildCommand());\nprogram.addCommand(runCommand());\nprogram.addCommand(generateCommand());\n\nfor (const cmd of diagnosticsCommands()) {\n program.addCommand(cmd);\n}\n\nprogram.parse(process.argv);\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport pc from 'picocolors';\n\nexport function startCommand(): Command {\n const cmd = new Command('start');\n\n cmd\n .argument('<mode>', 'dev | prod')\n .description('Start the EFC server')\n .action((mode: string) => {\n if (mode === 'dev') {\n startDev();\n } else if (mode === 'prod') {\n startProd();\n } else {\n console.error(pc.red(`Unknown mode: ${mode}. Use 'dev' or 'prod'.`));\n process.exit(1);\n }\n });\n\n return cmd;\n}\n\nfunction parseDotenv(cwd: string): Record<string, string> {\n const envFile = path.join(cwd, '.env');\n if (!fs.existsSync(envFile)) return {};\n const vars: Record<string, string> = {};\n for (const line of fs.readFileSync(envFile, 'utf8').split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eq = trimmed.indexOf('=');\n if (eq === -1) continue;\n const key = trimmed.slice(0, eq).trim();\n const raw = trimmed.slice(eq + 1).trim();\n vars[key] = raw.replace(/^(['\"])(.*)\\1$/, '$2');\n }\n return vars;\n}\n\nfunction startDev(): void {\n const entry = resolveEntry();\n if (!entry) {\n console.error(pc.red('[EFC] Could not find entry point. Expected src/index.ts or index.ts'));\n process.exit(1);\n }\n\n console.log(pc.cyan('[EFC] Starting development server…'));\n console.log(pc.dim(` Entry: ${entry}`));\n\n const cwd = process.cwd();\n const localTsx = path.join(cwd, 'node_modules', '.bin', 'tsx');\n const tsx = fs.existsSync(localTsx) ? localTsx : 'tsx';\n\n // .env values are base; existing process.env takes precedence (same as dotenv default)\n const env: NodeJS.ProcessEnv = { ...parseDotenv(cwd), ...process.env, NODE_ENV: 'development' };\n\n const child = spawn(tsx, ['watch', '--include', 'src', entry], { stdio: 'inherit', env });\n child.on('exit', (code) => process.exit(code ?? 0));\n}\n\nfunction startProd(): void {\n const cwd = process.cwd();\n const distEntry = path.join(cwd, 'dist', 'index.js');\n\n if (!fs.existsSync(distEntry)) {\n console.error(pc.red('[EFC] dist/index.js not found. Run `efc build prod` first.'));\n process.exit(1);\n }\n\n console.log(pc.cyan('[EFC] Starting production server…'));\n\n // Production env comes exclusively from process.env — no .env file loading.\n // Platforms (Docker, Kubernetes, Railway, Heroku, etc.) inject vars directly.\n const child = spawn('node', [distEntry], {\n stdio: 'inherit',\n env: { ...process.env, NODE_ENV: 'production' },\n });\n child.on('exit', (code) => process.exit(code ?? 0));\n}\n\nfunction resolveEntry(): string | null {\n const cwd = process.cwd();\n const candidates = [\n path.join(cwd, 'src', 'index.ts'),\n path.join(cwd, 'index.ts'),\n path.join(cwd, 'src', 'index.js'),\n path.join(cwd, 'index.js'),\n ];\n return candidates.find((f) => fs.existsSync(f)) ?? null;\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport pc from 'picocolors';\n\nexport function buildCommand(): Command {\n const cmd = new Command('build');\n\n cmd\n .argument('<mode>', 'prod')\n .description('Build the application for production')\n .action((mode: string) => {\n if (mode !== 'prod') {\n console.error(pc.red(`Unknown build mode: ${mode}. Use 'prod'.`));\n process.exit(1);\n }\n buildProd();\n });\n\n return cmd;\n}\n\nfunction buildProd(): void {\n console.log(pc.cyan('[EFC] Building for production…'));\n\n // Type-check first\n const tsc = spawn('npx', ['tsc', '--noEmit'], { stdio: 'inherit' });\n\n tsc.on('exit', (code) => {\n if (code !== 0) {\n console.error(pc.red('[EFC] TypeScript errors found. Fix them before building.'));\n process.exit(1);\n }\n\n const tsup = spawn('npx', ['tsup'], { stdio: 'inherit' });\n tsup.on('exit', (tsupCode) => {\n if (tsupCode === 0) {\n console.log(pc.green('[EFC] Build complete → dist/'));\n } else {\n process.exit(tsupCode ?? 1);\n }\n });\n });\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport pc from 'picocolors';\n\nexport function runCommand(): Command {\n const cmd = new Command('run');\n\n cmd\n .argument('<runner>', 'tests')\n .description('Run EFC sub-commands (tests)')\n .allowUnknownOption()\n .action((runner: string) => {\n if (runner === 'tests') {\n runTests(cmd.args.slice(1));\n } else {\n console.error(pc.red(`Unknown runner: ${runner}. Use 'tests'.`));\n process.exit(1);\n }\n });\n\n return cmd;\n}\n\nfunction runTests(extraArgs: string[]): void {\n console.log(pc.cyan('[EFC] Running tests via Vitest…'));\n const child = spawn('npx', ['vitest', 'run', ...extraArgs], { stdio: 'inherit' });\n child.on('exit', (code) => process.exit(code ?? 0));\n}\n","import { Command } from 'commander';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport pc from 'picocolors';\n\nexport function generateCommand(): Command {\n const cmd = new Command('generate').alias('g').description('Scaffold EFC modules');\n\n cmd\n .command('route <routePath>')\n .description('Scaffold a route module, e.g. users/[id]')\n .action((routePath: string) => generateRoute(routePath));\n\n cmd\n .command('task <name>')\n .description('Scaffold a background task module')\n .action((name: string) => generateTask(name));\n\n cmd\n .command('middleware <name>')\n .description('Scaffold a middleware module')\n .action((name: string) => generateMiddleware(name));\n\n return cmd;\n}\n\nfunction writeFile(filePath: string, content: string): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n if (fs.existsSync(filePath)) {\n console.error(pc.red(`File already exists: ${filePath}`));\n process.exit(1);\n }\n fs.writeFileSync(filePath, content, 'utf8');\n console.log(pc.green(` created ${path.relative(process.cwd(), filePath)}`));\n}\n\nfunction generateRoute(routePath: string): void {\n const cwd = process.cwd();\n const filePath = path.join(cwd, 'src', 'api', `${routePath}.ts`);\n\n const content = `import type { Request, Response } from 'express';\n\nexport const GET = async (req: Request, res: Response) => {\n res.json({ message: 'OK' });\n};\n\nexport const POST = async (req: Request, res: Response) => {\n res.status(201).json({ message: 'Created' });\n};\n`;\n\n writeFile(filePath, content);\n console.log(pc.cyan(`[EFC] Route scaffolded`));\n}\n\nfunction generateTask(name: string): void {\n const cwd = process.cwd();\n const filePath = path.join(cwd, 'src', 'tasks', `${name}.ts`);\n\n const content = `import { defineTask } from 'express-file-cluster/tasks';\n\ninterface ${name}Payload {\n // TODO: define payload fields\n}\n\nexport default defineTask<${name}Payload>(async (payload) => {\n // TODO: implement task logic\n console.log('[Task:${name}]', payload);\n});\n`;\n\n writeFile(filePath, content);\n console.log(pc.cyan(`[EFC] Task scaffolded`));\n}\n\nfunction generateMiddleware(name: string): void {\n const cwd = process.cwd();\n const filePath = path.join(cwd, 'src', 'middlewares', `${name}.ts`);\n\n const content = `import type { Request, Response, NextFunction } from 'express';\n\nexport function ${name}(req: Request, res: Response, next: NextFunction): void {\n // TODO: implement middleware logic\n next();\n}\n`;\n\n writeFile(filePath, content);\n console.log(pc.cyan(`[EFC] Middleware scaffolded`));\n}\n","import { Command } from 'commander';\nimport { scanDir } from '../../router/scan.js';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport pc from 'picocolors';\n\nexport function diagnosticsCommands(): Command[] {\n return [routesCommand(), tasksCommand(), doctorCommand()];\n}\n\nfunction routesCommand(): Command {\n return new Command('routes').description('Print the resolved route table').action(() => {\n const apiDir = resolveApiDir();\n if (!apiDir) {\n console.error(pc.red('[EFC] Could not find apiDir (expected src/api)'));\n process.exit(1);\n }\n\n const routes = scanDir(apiDir);\n if (routes.length === 0) {\n console.log(pc.yellow('No routes found.'));\n return;\n }\n\n console.log(pc.bold('\\n Route Table\\n'));\n console.log(pc.dim(' ' + '─'.repeat(60)));\n for (const route of routes) {\n const rel = path.relative(process.cwd(), route.filePath);\n console.log(` ${pc.cyan(route.urlPath.padEnd(35))} ${pc.dim(rel)}`);\n }\n console.log(pc.dim(' ' + '─'.repeat(60)));\n console.log(pc.dim(`\\n ${routes.length} route(s) found\\n`));\n });\n}\n\nfunction tasksCommand(): Command {\n return new Command('tasks').description('List registered background tasks').action(() => {\n const tasksDir = resolveTasksDir();\n if (!tasksDir || !fs.existsSync(tasksDir)) {\n console.log(pc.yellow('No tasks directory found.'));\n return;\n }\n\n const files = fs.readdirSync(tasksDir).filter((f) => /\\.(ts|js)$/.test(f));\n if (files.length === 0) {\n console.log(pc.yellow('No tasks found.'));\n return;\n }\n\n console.log(pc.bold('\\n Background Tasks\\n'));\n for (const file of files) {\n console.log(` ${pc.cyan(path.basename(file, path.extname(file)))}`);\n }\n console.log();\n });\n}\n\nfunction doctorCommand(): Command {\n return new Command('doctor')\n .description('Validate config, env vars, and project setup')\n .action(() => {\n const cwd = process.cwd();\n const checks: { label: string; ok: boolean; hint?: string }[] = [\n {\n label: 'package.json exists',\n ok: fs.existsSync(path.join(cwd, 'package.json')),\n },\n {\n label: 'tsconfig.json exists',\n ok: fs.existsSync(path.join(cwd, 'tsconfig.json')),\n hint: 'Run `tsc --init` to create one',\n },\n {\n label: 'src/api directory exists',\n ok: fs.existsSync(path.join(cwd, 'src', 'api')),\n hint: 'Create src/api/ and add route files',\n },\n {\n label: 'DATABASE_URL set',\n ok: Boolean(process.env['DATABASE_URL']),\n hint: 'Add DATABASE_URL to .env',\n },\n {\n label: 'JWT_SECRET set',\n ok: Boolean(process.env['JWT_SECRET']),\n hint: 'Add JWT_SECRET to .env (generate: openssl rand -hex 64)',\n },\n ];\n\n console.log(pc.bold('\\n EFC Doctor\\n'));\n let allOk = true;\n for (const check of checks) {\n const icon = check.ok ? pc.green('✓') : pc.red('✗');\n console.log(` ${icon} ${check.label}`);\n if (!check.ok) {\n allOk = false;\n if (check.hint) console.log(pc.dim(` → ${check.hint}`));\n }\n }\n console.log();\n if (allOk) {\n console.log(pc.green(' All checks passed!\\n'));\n } else {\n console.log(pc.yellow(' Some checks failed. Fix the issues above.\\n'));\n process.exit(1);\n }\n });\n}\n\nfunction resolveApiDir(): string | null {\n const cwd = process.cwd();\n const candidates = [path.join(cwd, 'src', 'api'), path.join(cwd, 'api')];\n return candidates.find((d) => fs.existsSync(d)) ?? null;\n}\n\nfunction resolveTasksDir(): string | null {\n const cwd = process.cwd();\n const candidates = [path.join(cwd, 'src', 'tasks'), path.join(cwd, 'tasks')];\n return candidates.find((d) => fs.existsSync(d)) ?? null;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/commands/start.ts","../../src/cli/commands/build.ts","../../src/cli/commands/run.ts","../../src/cli/commands/generate.ts","../../src/cli/commands/diagnostics.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { startCommand } from './commands/start.js';\nimport { buildCommand } from './commands/build.js';\nimport { runCommand } from './commands/run.js';\nimport { generateCommand } from './commands/generate.js';\nimport { diagnosticsCommands } from './commands/diagnostics.js';\n\nconst program = new Command('efc')\n .description('Express File Cluster CLI')\n .version('0.1.0');\n\nprogram.addCommand(startCommand());\nprogram.addCommand(buildCommand());\nprogram.addCommand(runCommand());\nprogram.addCommand(generateCommand());\n\nfor (const cmd of diagnosticsCommands()) {\n program.addCommand(cmd);\n}\n\nprogram.parse(process.argv);\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport pc from 'picocolors';\n\nexport function startCommand(): Command {\n const cmd = new Command('start');\n\n cmd\n .argument('<mode>', 'dev | prod')\n .description('Start the EFC server')\n .action((mode: string) => {\n if (mode === 'dev') {\n startDev();\n } else if (mode === 'prod') {\n startProd();\n } else {\n console.error(pc.red(`Unknown mode: ${mode}. Use 'dev' or 'prod'.`));\n process.exit(1);\n }\n });\n\n return cmd;\n}\n\nfunction parseDotenv(cwd: string): Record<string, string> {\n const envFile = path.join(cwd, '.env');\n if (!fs.existsSync(envFile)) return {};\n const vars: Record<string, string> = {};\n for (const line of fs.readFileSync(envFile, 'utf8').split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eq = trimmed.indexOf('=');\n if (eq === -1) continue;\n const key = trimmed.slice(0, eq).trim();\n const raw = trimmed.slice(eq + 1).trim();\n vars[key] = raw.replace(/^(['\"])(.*)\\1$/, '$2');\n }\n return vars;\n}\n\nfunction startDev(): void {\n const entry = resolveEntry();\n if (!entry) {\n console.error(pc.red('[EFC] Could not find entry point. Expected src/index.ts or index.ts'));\n process.exit(1);\n }\n\n console.log(pc.cyan('[EFC] Starting development server…'));\n console.log(pc.dim(` Entry: ${entry}`));\n\n const cwd = process.cwd();\n const localTsx = path.join(cwd, 'node_modules', '.bin', 'tsx');\n const tsx = fs.existsSync(localTsx) ? localTsx : 'tsx';\n\n // .env values are base; existing process.env takes precedence (same as dotenv default)\n const env: NodeJS.ProcessEnv = { ...parseDotenv(cwd), ...process.env, NODE_ENV: 'development' };\n\n const child = spawn(tsx, ['watch', '--include', 'src', entry], { stdio: 'inherit', env });\n child.on('exit', (code) => process.exit(code ?? 0));\n}\n\nfunction startProd(): void {\n const cwd = process.cwd();\n const distEntry = path.join(cwd, 'dist', 'index.js');\n\n if (!fs.existsSync(distEntry)) {\n console.error(pc.red('[EFC] dist/index.js not found. Run `efc build prod` first.'));\n process.exit(1);\n }\n\n console.log(pc.cyan('[EFC] Starting production server…'));\n\n // Production env comes exclusively from process.env — no .env file loading.\n // Platforms (Docker, Kubernetes, Railway, Heroku, etc.) inject vars directly.\n const child = spawn('node', [distEntry], {\n stdio: 'inherit',\n env: { ...process.env, NODE_ENV: 'production' },\n });\n child.on('exit', (code) => process.exit(code ?? 0));\n}\n\nfunction resolveEntry(): string | null {\n const cwd = process.cwd();\n const candidates = [\n path.join(cwd, 'src', 'index.ts'),\n path.join(cwd, 'index.ts'),\n path.join(cwd, 'src', 'index.js'),\n path.join(cwd, 'index.js'),\n ];\n return candidates.find((f) => fs.existsSync(f)) ?? null;\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport pc from 'picocolors';\n\nexport function buildCommand(): Command {\n const cmd = new Command('build');\n\n cmd\n .argument('<mode>', 'prod')\n .description('Build the application for production')\n .action((mode: string) => {\n if (mode !== 'prod') {\n console.error(pc.red(`Unknown build mode: ${mode}. Use 'prod'.`));\n process.exit(1);\n }\n buildProd();\n });\n\n return cmd;\n}\n\nfunction buildProd(): void {\n console.log(pc.cyan('[EFC] Building for production…'));\n\n // Type-check first\n const tsc = spawn('npx', ['tsc', '--noEmit'], { stdio: 'inherit' });\n\n tsc.on('exit', (code) => {\n if (code !== 0) {\n console.error(pc.red('[EFC] TypeScript errors found. Fix them before building.'));\n process.exit(1);\n }\n\n const tsup = spawn('npx', ['tsup'], { stdio: 'inherit' });\n tsup.on('exit', (tsupCode) => {\n if (tsupCode === 0) {\n console.log(pc.green('[EFC] Build complete → dist/'));\n } else {\n process.exit(tsupCode ?? 1);\n }\n });\n });\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport pc from 'picocolors';\n\nexport function runCommand(): Command {\n const cmd = new Command('run');\n\n cmd\n .argument('<runner>', 'tests')\n .description('Run EFC sub-commands (tests)')\n .allowUnknownOption()\n .action((runner: string) => {\n if (runner === 'tests') {\n runTests(cmd.args.slice(1));\n } else {\n console.error(pc.red(`Unknown runner: ${runner}. Use 'tests'.`));\n process.exit(1);\n }\n });\n\n return cmd;\n}\n\nfunction runTests(extraArgs: string[]): void {\n console.log(pc.cyan('[EFC] Running tests via Vitest…'));\n const child = spawn('npx', ['vitest', 'run', ...extraArgs], { stdio: 'inherit' });\n child.on('exit', (code) => process.exit(code ?? 0));\n}\n","import { Command } from 'commander';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport pc from 'picocolors';\n\nexport function generateCommand(): Command {\n const cmd = new Command('generate').alias('g').description('Scaffold EFC modules');\n\n cmd\n .command('route <routePath>')\n .description('Scaffold a route module, e.g. users/[id]')\n .action((routePath: string) => generateRoute(routePath));\n\n cmd\n .command('task <name>')\n .description('Scaffold a background task module')\n .action((name: string) => generateTask(name));\n\n cmd\n .command('middleware <name>')\n .description('Scaffold a middleware module')\n .action((name: string) => generateMiddleware(name));\n\n return cmd;\n}\n\nfunction writeFile(filePath: string, content: string): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n if (fs.existsSync(filePath)) {\n console.error(pc.red(`File already exists: ${filePath}`));\n process.exit(1);\n }\n fs.writeFileSync(filePath, content, 'utf8');\n console.log(pc.green(` created ${path.relative(process.cwd(), filePath)}`));\n}\n\nfunction generateRoute(routePath: string): void {\n const cwd = process.cwd();\n const filePath = path.join(cwd, 'src', 'api', `${routePath}.ts`);\n\n const content = `import type { Request, Response } from 'express';\n\nexport const GET = async (req: Request, res: Response) => {\n res.json({ message: 'OK' });\n};\n\nexport const POST = async (req: Request, res: Response) => {\n res.status(201).json({ message: 'Created' });\n};\n`;\n\n writeFile(filePath, content);\n console.log(pc.cyan(`[EFC] Route scaffolded`));\n}\n\nfunction generateTask(name: string): void {\n const cwd = process.cwd();\n const filePath = path.join(cwd, 'src', 'tasks', `${name}.ts`);\n\n const content = `import { defineTask } from 'express-file-cluster/tasks';\n\ninterface ${name}Payload {\n // TODO: define payload fields\n}\n\nexport default defineTask<${name}Payload>(async (payload) => {\n // TODO: implement task logic\n console.log('[Task:${name}]', payload);\n});\n`;\n\n writeFile(filePath, content);\n console.log(pc.cyan(`[EFC] Task scaffolded`));\n}\n\nfunction generateMiddleware(name: string): void {\n const cwd = process.cwd();\n const filePath = path.join(cwd, 'src', 'middlewares', `${name}.ts`);\n\n const content = `import type { Request, Response, NextFunction } from 'express';\n\nexport function ${name}(req: Request, res: Response, next: NextFunction): void {\n // TODO: implement middleware logic\n next();\n}\n`;\n\n writeFile(filePath, content);\n console.log(pc.cyan(`[EFC] Middleware scaffolded`));\n}\n","import { Command } from 'commander';\nimport { scanDir } from '../../router/scan.js';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport pc from 'picocolors';\n\nexport function diagnosticsCommands(): Command[] {\n return [routesCommand(), tasksCommand(), doctorCommand()];\n}\n\nfunction routesCommand(): Command {\n return new Command('routes')\n .description('Print the resolved route table')\n .action(() => {\n const apiDir = resolveApiDir();\n if (!apiDir) {\n console.error(pc.red('[EFC] Could not find apiDir (expected src/api)'));\n process.exit(1);\n }\n\n const routes = scanDir(apiDir);\n if (routes.length === 0) {\n console.log(pc.yellow('No routes found.'));\n return;\n }\n\n console.log(pc.bold('\\n Route Table\\n'));\n console.log(pc.dim(' ' + '─'.repeat(60)));\n for (const route of routes) {\n const rel = path.relative(process.cwd(), route.filePath);\n console.log(` ${pc.cyan(route.urlPath.padEnd(35))} ${pc.dim(rel)}`);\n }\n console.log(pc.dim(' ' + '─'.repeat(60)));\n console.log(pc.dim(`\\n ${routes.length} route(s) found\\n`));\n });\n}\n\nfunction tasksCommand(): Command {\n return new Command('tasks')\n .description('List registered background tasks')\n .action(() => {\n const tasksDir = resolveTasksDir();\n if (!tasksDir || !fs.existsSync(tasksDir)) {\n console.log(pc.yellow('No tasks directory found.'));\n return;\n }\n\n const files = fs.readdirSync(tasksDir).filter((f) => /\\.(ts|js)$/.test(f));\n if (files.length === 0) {\n console.log(pc.yellow('No tasks found.'));\n return;\n }\n\n console.log(pc.bold('\\n Background Tasks\\n'));\n for (const file of files) {\n console.log(` ${pc.cyan(path.basename(file, path.extname(file)))}`);\n }\n console.log();\n });\n}\n\nfunction doctorCommand(): Command {\n return new Command('doctor')\n .description('Validate config, env vars, and project setup')\n .action(() => {\n const cwd = process.cwd();\n const checks: { label: string; ok: boolean; hint?: string }[] = [\n {\n label: 'package.json exists',\n ok: fs.existsSync(path.join(cwd, 'package.json')),\n },\n {\n label: 'tsconfig.json exists',\n ok: fs.existsSync(path.join(cwd, 'tsconfig.json')),\n hint: 'Run `tsc --init` to create one',\n },\n {\n label: 'src/api directory exists',\n ok: fs.existsSync(path.join(cwd, 'src', 'api')),\n hint: 'Create src/api/ and add route files',\n },\n {\n label: 'DATABASE_URL set',\n ok: Boolean(process.env['DATABASE_URL']),\n hint: 'Add DATABASE_URL to .env',\n },\n {\n label: 'JWT_SECRET set',\n ok: Boolean(process.env['JWT_SECRET']),\n hint: 'Add JWT_SECRET to .env (generate: openssl rand -hex 64)',\n },\n ];\n\n console.log(pc.bold('\\n EFC Doctor\\n'));\n let allOk = true;\n for (const check of checks) {\n const icon = check.ok ? pc.green('✓') : pc.red('✗');\n console.log(` ${icon} ${check.label}`);\n if (!check.ok) {\n allOk = false;\n if (check.hint) console.log(pc.dim(` → ${check.hint}`));\n }\n }\n console.log();\n if (allOk) {\n console.log(pc.green(' All checks passed!\\n'));\n } else {\n console.log(pc.yellow(' Some checks failed. Fix the issues above.\\n'));\n process.exit(1);\n }\n });\n}\n\nfunction resolveApiDir(): string | null {\n const cwd = process.cwd();\n const candidates = [path.join(cwd, 'src', 'api'), path.join(cwd, 'api')];\n return candidates.find((d) => fs.existsSync(d)) ?? null;\n}\n\nfunction resolveTasksDir(): string | null {\n const cwd = process.cwd();\n const candidates = [path.join(cwd, 'src', 'tasks'), path.join(cwd, 'tasks')];\n return candidates.find((d) => fs.existsSync(d)) ?? null;\n}\n"],"mappings":"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","names":["Command","Command","spawn","pc","Command","spawn","pc","Command","fs","path","pc","Command","path","fs","pc","Command","Command"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/commands/start.ts","../../src/cli/commands/build.ts","../../src/cli/commands/run.ts","../../src/cli/commands/generate.ts","../../src/cli/commands/diagnostics.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { startCommand } from './commands/start.js';\nimport { buildCommand } from './commands/build.js';\nimport { runCommand } from './commands/run.js';\nimport { generateCommand } from './commands/generate.js';\nimport { diagnosticsCommands } from './commands/diagnostics.js';\n\nconst program = new Command('efc').description('Express File Cluster CLI').version('0.1.0');\n\nprogram.addCommand(startCommand());\nprogram.addCommand(buildCommand());\nprogram.addCommand(runCommand());\nprogram.addCommand(generateCommand());\n\nfor (const cmd of diagnosticsCommands()) {\n program.addCommand(cmd);\n}\n\nprogram.parse(process.argv);\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport pc from 'picocolors';\n\nexport function startCommand(): Command {\n const cmd = new Command('start');\n\n cmd\n .argument('<mode>', 'dev | prod')\n .description('Start the EFC server')\n .action((mode: string) => {\n if (mode === 'dev') {\n startDev();\n } else if (mode === 'prod') {\n startProd();\n } else {\n console.error(pc.red(`Unknown mode: ${mode}. Use 'dev' or 'prod'.`));\n process.exit(1);\n }\n });\n\n return cmd;\n}\n\nfunction parseDotenv(cwd: string): Record<string, string> {\n const envFile = path.join(cwd, '.env');\n if (!fs.existsSync(envFile)) return {};\n const vars: Record<string, string> = {};\n for (const line of fs.readFileSync(envFile, 'utf8').split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eq = trimmed.indexOf('=');\n if (eq === -1) continue;\n const key = trimmed.slice(0, eq).trim();\n const raw = trimmed.slice(eq + 1).trim();\n vars[key] = raw.replace(/^(['\"])(.*)\\1$/, '$2');\n }\n return vars;\n}\n\nfunction startDev(): void {\n const entry = resolveEntry();\n if (!entry) {\n console.error(pc.red('[EFC] Could not find entry point. Expected src/index.ts or index.ts'));\n process.exit(1);\n }\n\n console.log(pc.cyan('[EFC] Starting development server…'));\n console.log(pc.dim(` Entry: ${entry}`));\n\n const cwd = process.cwd();\n const localTsx = path.join(cwd, 'node_modules', '.bin', 'tsx');\n const tsx = fs.existsSync(localTsx) ? localTsx : 'tsx';\n\n // .env values are base; existing process.env takes precedence (same as dotenv default)\n const env: NodeJS.ProcessEnv = { ...parseDotenv(cwd), ...process.env, NODE_ENV: 'development' };\n\n const child = spawn(tsx, ['watch', '--include', 'src', entry], { stdio: 'inherit', env });\n child.on('exit', (code) => process.exit(code ?? 0));\n}\n\nfunction startProd(): void {\n const cwd = process.cwd();\n const distEntry = path.join(cwd, 'dist', 'index.js');\n\n if (!fs.existsSync(distEntry)) {\n console.error(pc.red('[EFC] dist/index.js not found. Run `efc build prod` first.'));\n process.exit(1);\n }\n\n console.log(pc.cyan('[EFC] Starting production server…'));\n\n // Production env comes exclusively from process.env — no .env file loading.\n // Platforms (Docker, Kubernetes, Railway, Heroku, etc.) inject vars directly.\n const child = spawn('node', [distEntry], {\n stdio: 'inherit',\n env: { ...process.env, NODE_ENV: 'production' },\n });\n child.on('exit', (code) => process.exit(code ?? 0));\n}\n\nfunction resolveEntry(): string | null {\n const cwd = process.cwd();\n const candidates = [\n path.join(cwd, 'src', 'index.ts'),\n path.join(cwd, 'index.ts'),\n path.join(cwd, 'src', 'index.js'),\n path.join(cwd, 'index.js'),\n ];\n return candidates.find((f) => fs.existsSync(f)) ?? null;\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport pc from 'picocolors';\n\nexport function buildCommand(): Command {\n const cmd = new Command('build');\n\n cmd\n .argument('<mode>', 'prod')\n .description('Build the application for production')\n .action((mode: string) => {\n if (mode !== 'prod') {\n console.error(pc.red(`Unknown build mode: ${mode}. Use 'prod'.`));\n process.exit(1);\n }\n buildProd();\n });\n\n return cmd;\n}\n\nfunction buildProd(): void {\n console.log(pc.cyan('[EFC] Building for production…'));\n\n // Type-check first\n const tsc = spawn('npx', ['tsc', '--noEmit'], { stdio: 'inherit' });\n\n tsc.on('exit', (code) => {\n if (code !== 0) {\n console.error(pc.red('[EFC] TypeScript errors found. Fix them before building.'));\n process.exit(1);\n }\n\n const tsup = spawn('npx', ['tsup'], { stdio: 'inherit' });\n tsup.on('exit', (tsupCode) => {\n if (tsupCode === 0) {\n console.log(pc.green('[EFC] Build complete → dist/'));\n } else {\n process.exit(tsupCode ?? 1);\n }\n });\n });\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport pc from 'picocolors';\n\nexport function runCommand(): Command {\n const cmd = new Command('run');\n\n cmd\n .argument('<runner>', 'tests')\n .description('Run EFC sub-commands (tests)')\n .allowUnknownOption()\n .action((runner: string) => {\n if (runner === 'tests') {\n runTests(cmd.args.slice(1));\n } else {\n console.error(pc.red(`Unknown runner: ${runner}. Use 'tests'.`));\n process.exit(1);\n }\n });\n\n return cmd;\n}\n\nfunction runTests(extraArgs: string[]): void {\n console.log(pc.cyan('[EFC] Running tests via Vitest…'));\n const child = spawn('npx', ['vitest', 'run', ...extraArgs], { stdio: 'inherit' });\n child.on('exit', (code) => process.exit(code ?? 0));\n}\n","import { Command } from 'commander';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport pc from 'picocolors';\n\nexport function generateCommand(): Command {\n const cmd = new Command('generate').alias('g').description('Scaffold EFC modules');\n\n cmd\n .command('route <routePath>')\n .description('Scaffold a route module, e.g. users/[id]')\n .action((routePath: string) => generateRoute(routePath));\n\n cmd\n .command('task <name>')\n .description('Scaffold a background task module')\n .action((name: string) => generateTask(name));\n\n cmd\n .command('middleware <name>')\n .description('Scaffold a middleware module')\n .action((name: string) => generateMiddleware(name));\n\n return cmd;\n}\n\nfunction writeFile(filePath: string, content: string): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n if (fs.existsSync(filePath)) {\n console.error(pc.red(`File already exists: ${filePath}`));\n process.exit(1);\n }\n fs.writeFileSync(filePath, content, 'utf8');\n console.log(pc.green(` created ${path.relative(process.cwd(), filePath)}`));\n}\n\nfunction generateRoute(routePath: string): void {\n const cwd = process.cwd();\n const filePath = path.join(cwd, 'src', 'api', `${routePath}.ts`);\n\n const content = `import type { Request, Response } from 'express';\n\nexport const GET = async (req: Request, res: Response) => {\n res.json({ message: 'OK' });\n};\n\nexport const POST = async (req: Request, res: Response) => {\n res.status(201).json({ message: 'Created' });\n};\n`;\n\n writeFile(filePath, content);\n console.log(pc.cyan(`[EFC] Route scaffolded`));\n}\n\nfunction generateTask(name: string): void {\n const cwd = process.cwd();\n const filePath = path.join(cwd, 'src', 'tasks', `${name}.ts`);\n\n const content = `import { defineTask } from 'express-file-cluster/tasks';\n\ninterface ${name}Payload {\n // TODO: define payload fields\n}\n\nexport default defineTask<${name}Payload>(async (payload) => {\n // TODO: implement task logic\n console.log('[Task:${name}]', payload);\n});\n`;\n\n writeFile(filePath, content);\n console.log(pc.cyan(`[EFC] Task scaffolded`));\n}\n\nfunction generateMiddleware(name: string): void {\n const cwd = process.cwd();\n const filePath = path.join(cwd, 'src', 'middlewares', `${name}.ts`);\n\n const content = `import type { Request, Response, NextFunction } from 'express';\n\nexport function ${name}(req: Request, res: Response, next: NextFunction): void {\n // TODO: implement middleware logic\n next();\n}\n`;\n\n writeFile(filePath, content);\n console.log(pc.cyan(`[EFC] Middleware scaffolded`));\n}\n","import { Command } from 'commander';\nimport { scanDir } from '../../router/scan.js';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport pc from 'picocolors';\n\nexport function diagnosticsCommands(): Command[] {\n return [routesCommand(), tasksCommand(), doctorCommand()];\n}\n\nfunction routesCommand(): Command {\n return new Command('routes').description('Print the resolved route table').action(() => {\n const apiDir = resolveApiDir();\n if (!apiDir) {\n console.error(pc.red('[EFC] Could not find apiDir (expected src/api)'));\n process.exit(1);\n }\n\n const routes = scanDir(apiDir);\n if (routes.length === 0) {\n console.log(pc.yellow('No routes found.'));\n return;\n }\n\n console.log(pc.bold('\\n Route Table\\n'));\n console.log(pc.dim(' ' + '─'.repeat(60)));\n for (const route of routes) {\n const rel = path.relative(process.cwd(), route.filePath);\n console.log(` ${pc.cyan(route.urlPath.padEnd(35))} ${pc.dim(rel)}`);\n }\n console.log(pc.dim(' ' + '─'.repeat(60)));\n console.log(pc.dim(`\\n ${routes.length} route(s) found\\n`));\n });\n}\n\nfunction tasksCommand(): Command {\n return new Command('tasks').description('List registered background tasks').action(() => {\n const tasksDir = resolveTasksDir();\n if (!tasksDir || !fs.existsSync(tasksDir)) {\n console.log(pc.yellow('No tasks directory found.'));\n return;\n }\n\n const files = fs.readdirSync(tasksDir).filter((f) => /\\.(ts|js)$/.test(f));\n if (files.length === 0) {\n console.log(pc.yellow('No tasks found.'));\n return;\n }\n\n console.log(pc.bold('\\n Background Tasks\\n'));\n for (const file of files) {\n console.log(` ${pc.cyan(path.basename(file, path.extname(file)))}`);\n }\n console.log();\n });\n}\n\nfunction doctorCommand(): Command {\n return new Command('doctor')\n .description('Validate config, env vars, and project setup')\n .action(() => {\n const cwd = process.cwd();\n const checks: { label: string; ok: boolean; hint?: string }[] = [\n {\n label: 'package.json exists',\n ok: fs.existsSync(path.join(cwd, 'package.json')),\n },\n {\n label: 'tsconfig.json exists',\n ok: fs.existsSync(path.join(cwd, 'tsconfig.json')),\n hint: 'Run `tsc --init` to create one',\n },\n {\n label: 'src/api directory exists',\n ok: fs.existsSync(path.join(cwd, 'src', 'api')),\n hint: 'Create src/api/ and add route files',\n },\n {\n label: 'DATABASE_URL set',\n ok: Boolean(process.env['DATABASE_URL']),\n hint: 'Add DATABASE_URL to .env',\n },\n {\n label: 'JWT_SECRET set',\n ok: Boolean(process.env['JWT_SECRET']),\n hint: 'Add JWT_SECRET to .env (generate: openssl rand -hex 64)',\n },\n ];\n\n console.log(pc.bold('\\n EFC Doctor\\n'));\n let allOk = true;\n for (const check of checks) {\n const icon = check.ok ? pc.green('✓') : pc.red('✗');\n console.log(` ${icon} ${check.label}`);\n if (!check.ok) {\n allOk = false;\n if (check.hint) console.log(pc.dim(` → ${check.hint}`));\n }\n }\n console.log();\n if (allOk) {\n console.log(pc.green(' All checks passed!\\n'));\n } else {\n console.log(pc.yellow(' Some checks failed. Fix the issues above.\\n'));\n process.exit(1);\n }\n });\n}\n\nfunction resolveApiDir(): string | null {\n const cwd = process.cwd();\n const candidates = [path.join(cwd, 'src', 'api'), path.join(cwd, 'api')];\n return candidates.find((d) => fs.existsSync(d)) ?? null;\n}\n\nfunction resolveTasksDir(): string | null {\n const cwd = process.cwd();\n const candidates = [path.join(cwd, 'src', 'tasks'), path.join(cwd, 'tasks')];\n return candidates.find((d) => fs.existsSync(d)) ?? null;\n}\n"],"mappings":"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","names":["Command","Command","spawn","pc","Command","spawn","pc","Command","fs","path","pc","Command","path","fs","pc","Command","Command"]}
package/dist/index.cjs CHANGED
@@ -1,11 +1,11 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunkQB3X6LKZcjs = require('./chunk-QB3X6LKZ.cjs');
3
+ var _chunkUUF2OYRWcjs = require('./chunk-UUF2OYRW.cjs');
4
4
 
5
5
 
6
6
 
7
7
 
8
- var _chunkUDE6S53Ccjs = require('./chunk-UDE6S53C.cjs');
8
+ var _chunkXH7O25BLcjs = require('./chunk-XH7O25BL.cjs');
9
9
 
10
10
 
11
11
  var _chunkMQNVNOH5cjs = require('./chunk-MQNVNOH5.cjs');
@@ -16,6 +16,7 @@ var _cors = require('cors'); var _cors2 = _interopRequireDefault(_cors);
16
16
  var _cookieparser = require('cookie-parser'); var _cookieparser2 = _interopRequireDefault(_cookieparser);
17
17
  var _cluster = require('cluster'); var _cluster2 = _interopRequireDefault(_cluster);
18
18
  var _os = require('os'); var _os2 = _interopRequireDefault(_os);
19
+ var _path = require('path'); var _path2 = _interopRequireDefault(_path);
19
20
 
20
21
  // src/router/mount.ts
21
22
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
@@ -96,9 +97,7 @@ async function loadMongoose() {
96
97
  mongoose = await Promise.resolve().then(() => _interopRequireWildcard(require("mongoose")));
97
98
  return mongoose;
98
99
  } catch (e) {
99
- throw new Error(
100
- "[EFC] mongoose is not installed. Run: npm install mongoose"
101
- );
100
+ throw new Error("[EFC] mongoose is not installed. Run: npm install mongoose");
102
101
  }
103
102
  }
104
103
  async function connectMongo(url) {
@@ -221,7 +220,7 @@ var db = new Proxy({}, {
221
220
 
222
221
  // src/tasks/scanner.ts
223
222
  var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs);
224
- var _path = require('path'); var _path2 = _interopRequireDefault(_path);
223
+
225
224
  async function scanTasks(tasksDir) {
226
225
  if (!_fs2.default.existsSync(tasksDir)) return;
227
226
  const files = _fs2.default.readdirSync(tasksDir).filter((f) => /\.(ts|js|mts|mjs)$/.test(f));
@@ -235,7 +234,7 @@ async function scanTasks(tasksDir) {
235
234
  console.warn(`[EFC] Task file ${file} has no valid default export \u2014 skipping`);
236
235
  continue;
237
236
  }
238
- _chunkUDE6S53Ccjs.registerTask.call(void 0, taskName, { ...def, filePath });
237
+ _chunkXH7O25BLcjs.registerTask.call(void 0, taskName, { ...def, filePath });
239
238
  console.log(`[EFC] Registered task: ${taskName}`);
240
239
  } catch (err) {
241
240
  console.warn(`[EFC] Failed to load task ${file}:`, err);
@@ -290,7 +289,7 @@ async function initBullMQ(opts) {
290
289
  const worker = new bullmq.Worker(
291
290
  "efc",
292
291
  async (job) => {
293
- const def = _chunkUDE6S53Ccjs.taskRegistry.get(job.name);
292
+ const def = _chunkXH7O25BLcjs.taskRegistry.get(job.name);
294
293
  if (!def) {
295
294
  throw new Error(`[EFC] No task registered for name: ${job.name}`);
296
295
  }
@@ -308,10 +307,12 @@ async function initBullMQ(opts) {
308
307
  worker.on("completed", (job) => {
309
308
  console.log(`[EFC] Task ${job.name} completed (id=${job.id})`);
310
309
  });
311
- _chunkUDE6S53Ccjs.setEnqueueImpl.call(void 0, async (name, payload) => {
312
- const def = _chunkUDE6S53Ccjs.taskRegistry.get(name);
310
+ _chunkXH7O25BLcjs.setEnqueueImpl.call(void 0, async (name, payload) => {
311
+ const def = _chunkXH7O25BLcjs.taskRegistry.get(name);
313
312
  if (!def) {
314
- throw new Error(`[EFC] Cannot enqueue unknown task: "${name}". Is the task file in tasksDir?`);
313
+ throw new Error(
314
+ `[EFC] Cannot enqueue unknown task: "${name}". Is the task file in tasksDir?`
315
+ );
315
316
  }
316
317
  await queue.add(name, payload, {
317
318
  attempts: _nullishCoalesce(def.options.retries, () => ( 3)),
@@ -383,12 +384,15 @@ async function ignite(config) {
383
384
  const {
384
385
  port: _port,
385
386
  workers,
386
- apiDir,
387
387
  globalMiddlewares = [],
388
388
  onWorkerReady,
389
389
  onWorkerCrash,
390
390
  onError
391
391
  } = config;
392
+ const baseDir = process.argv[1] ? _path2.default.dirname(process.argv[1]) : process.cwd();
393
+ const apiDir = _path2.default.join(baseDir, "api");
394
+ const tasksDir = _path2.default.join(baseDir, "tasks");
395
+ const basePath = _nullishCoalesce(config.basePath, () => ( "/v1/api"));
392
396
  const envPort = process.env["PORT"] != null ? Number(process.env["PORT"]) : NaN;
393
397
  const port = _port != null && !Number.isNaN(_port) ? _port : !Number.isNaN(envPort) ? envPort : 3e3;
394
398
  const databaseUrl = _nullishCoalesce(config.databaseUrl, () => ( process.env["DATABASE_URL"]));
@@ -432,15 +436,15 @@ async function ignite(config) {
432
436
  }
433
437
  if (jwtSecret) {
434
438
  const cookieDomain = process.env["COOKIE_DOMAIN"];
435
- _chunkQB3X6LKZcjs.configureAuth.call(void 0, {
439
+ _chunkUUF2OYRWcjs.configureAuth.call(void 0, {
436
440
  secret: jwtSecret,
437
441
  strategy: authStrategy,
438
442
  expiresIn: _nullishCoalesce(process.env["JWT_EXPIRES_IN"], () => ( "7d")),
439
443
  ...cookieDomain !== void 0 && { cookieDomain }
440
444
  });
441
445
  }
442
- if (config.tasksDir) {
443
- await scanTasks(config.tasksDir);
446
+ if (tasksDir) {
447
+ await scanTasks(tasksDir);
444
448
  }
445
449
  if (config.tasks) {
446
450
  if (config.tasks.backend === "bullmq") {
@@ -451,7 +455,9 @@ async function ignite(config) {
451
455
  }
452
456
  }
453
457
  const routes = _chunkMQNVNOH5cjs.scanDir.call(void 0, apiDir);
454
- await mountRoutes(app, routes);
458
+ const apiRouter = _express2.default.Router();
459
+ await mountRoutes(apiRouter, routes);
460
+ app.use(basePath, apiRouter);
455
461
  if (onError) {
456
462
  app.use(onError);
457
463
  } else {
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/efc.js/efc.js/packages/core/dist/index.cjs","../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"],"names":[],"mappings":"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","file":"/home/runner/work/efc.js/efc.js/packages/core/dist/index.cjs","sourcesContent":[null,"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"]}
1
+ {"version":3,"sources":["/home/runner/work/efc.js/efc.js/packages/core/dist/index.cjs","../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"],"names":[],"mappings":"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","file":"/home/runner/work/efc.js/efc.js/packages/core/dist/index.cjs","sourcesContent":[null,"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"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import http from 'node:http';
2
- import { M as ModelSchema, b as ModelCRUD, R as RouteEntry, E as EFCConfig } from './types-Dj9-sLOK.cjs';
3
- export { A as AuthStrategy, C as CorsConfig, D as DatabaseEngine, T as TaskDefinition, a as TaskOptions } from './types-Dj9-sLOK.cjs';
2
+ import { M as ModelSchema, b as ModelCRUD, R as RouteEntry, E as EFCConfig } from './types-DpegkYh2.cjs';
3
+ export { A as AuthStrategy, C as CorsConfig, D as DatabaseEngine, T as TaskDefinition, a as TaskOptions } from './types-DpegkYh2.cjs';
4
4
  import { RequestHandler } from 'express';
5
5
 
6
6
  declare class HttpError extends Error {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import http from 'node:http';
2
- import { M as ModelSchema, b as ModelCRUD, R as RouteEntry, E as EFCConfig } from './types-Dj9-sLOK.js';
3
- export { A as AuthStrategy, C as CorsConfig, D as DatabaseEngine, T as TaskDefinition, a as TaskOptions } from './types-Dj9-sLOK.js';
2
+ import { M as ModelSchema, b as ModelCRUD, R as RouteEntry, E as EFCConfig } from './types-DpegkYh2.js';
3
+ export { A as AuthStrategy, C as CorsConfig, D as DatabaseEngine, T as TaskDefinition, a as TaskOptions } from './types-DpegkYh2.js';
4
4
  import { RequestHandler } from 'express';
5
5
 
6
6
  declare class HttpError extends Error {
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  configureAuth
3
- } from "./chunk-DQGWFWBV.js";
3
+ } from "./chunk-BHMFPULA.js";
4
4
  import {
5
5
  registerTask,
6
6
  setEnqueueImpl,
7
7
  taskRegistry
8
- } from "./chunk-Z5YNIVQG.js";
8
+ } from "./chunk-LPZEKFVN.js";
9
9
  import {
10
10
  scanDir
11
11
  } from "./chunk-MZPG5HXL.js";
@@ -16,6 +16,7 @@ import cors from "cors";
16
16
  import cookieParser from "cookie-parser";
17
17
  import cluster2 from "cluster";
18
18
  import os2 from "os";
19
+ import path2 from "path";
19
20
 
20
21
  // src/router/mount.ts
21
22
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
@@ -96,9 +97,7 @@ async function loadMongoose() {
96
97
  mongoose = await import("mongoose");
97
98
  return mongoose;
98
99
  } catch {
99
- throw new Error(
100
- "[EFC] mongoose is not installed. Run: npm install mongoose"
101
- );
100
+ throw new Error("[EFC] mongoose is not installed. Run: npm install mongoose");
102
101
  }
103
102
  }
104
103
  async function connectMongo(url) {
@@ -311,7 +310,9 @@ async function initBullMQ(opts) {
311
310
  setEnqueueImpl(async (name, payload) => {
312
311
  const def = taskRegistry.get(name);
313
312
  if (!def) {
314
- throw new Error(`[EFC] Cannot enqueue unknown task: "${name}". Is the task file in tasksDir?`);
313
+ throw new Error(
314
+ `[EFC] Cannot enqueue unknown task: "${name}". Is the task file in tasksDir?`
315
+ );
315
316
  }
316
317
  await queue.add(name, payload, {
317
318
  attempts: def.options.retries ?? 3,
@@ -383,12 +384,15 @@ async function ignite(config) {
383
384
  const {
384
385
  port: _port,
385
386
  workers,
386
- apiDir,
387
387
  globalMiddlewares = [],
388
388
  onWorkerReady,
389
389
  onWorkerCrash,
390
390
  onError
391
391
  } = config;
392
+ const baseDir = process.argv[1] ? path2.dirname(process.argv[1]) : process.cwd();
393
+ const apiDir = path2.join(baseDir, "api");
394
+ const tasksDir = path2.join(baseDir, "tasks");
395
+ const basePath = config.basePath ?? "/v1/api";
392
396
  const envPort = process.env["PORT"] != null ? Number(process.env["PORT"]) : NaN;
393
397
  const port = _port != null && !Number.isNaN(_port) ? _port : !Number.isNaN(envPort) ? envPort : 3e3;
394
398
  const databaseUrl = config.databaseUrl ?? process.env["DATABASE_URL"];
@@ -439,8 +443,8 @@ async function ignite(config) {
439
443
  ...cookieDomain !== void 0 && { cookieDomain }
440
444
  });
441
445
  }
442
- if (config.tasksDir) {
443
- await scanTasks(config.tasksDir);
446
+ if (tasksDir) {
447
+ await scanTasks(tasksDir);
444
448
  }
445
449
  if (config.tasks) {
446
450
  if (config.tasks.backend === "bullmq") {
@@ -451,7 +455,9 @@ async function ignite(config) {
451
455
  }
452
456
  }
453
457
  const routes = scanDir(apiDir);
454
- await mountRoutes(app, routes);
458
+ const apiRouter = express.Router();
459
+ await mountRoutes(apiRouter, routes);
460
+ app.use(basePath, apiRouter);
455
461
  if (onError) {
456
462
  app.use(onError);
457
463
  } else {
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":"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","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":";;;;;;;;;;;;;AACA,OAAO,aAAa;AACpB,OAAO,UAAU;AACjB,OAAO,kBAAkB;AACzB,OAAOA,cAAa;AACpB,OAAOC,SAAQ;AACf,OAAOC,WAAU;;;ACHjB,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,KAAc,QAAqC;AACnF,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,MAAM,4DAA4D;AAAA,EAC9E;AACF;AAEA,eAAsB,aAAa,KAAqD;AACtF,QAAM,KAAK,MAAM,aAAa;AAC9B,QAAM,GAAG,QAAQ,GAAG;AACpB,UAAQ,IAAI,yBAAyB;AACrC,SAAO,GAAG;AACZ;;;ACfA,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,MAAO,MAAM,OAAO;AAC1B,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,MAAO,MAAM,OAAO;AAC1B,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;AAAA,QACR,uCAAuC,IAAI;AAAA,MAC7C;AAAA,IACF;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;;;AC7EO,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;;;AVPA,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,oBAAoB,CAAC;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAGJ,QAAM,UAAU,QAAQ,KAAK,CAAC,IAAIC,MAAK,QAAQ,QAAQ,KAAK,CAAC,CAAC,IAAI,QAAQ,IAAI;AAC9E,QAAM,SAASA,MAAK,KAAK,SAAS,KAAK;AACvC,QAAM,WAAWA,MAAK,KAAK,SAAS,OAAO;AAC3C,QAAM,WAAW,OAAO,YAAY;AAEpC,QAAM,UAAU,QAAQ,IAAI,MAAM,KAAK,OAAO,OAAO,QAAQ,IAAI,MAAM,CAAC,IAAI;AAC5E,QAAM,OACJ,SAAS,QAAQ,CAAC,OAAO,MAAM,KAAK,IAAI,QAAQ,CAAC,OAAO,MAAM,OAAO,IAAI,UAAU;AACrF,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,WACV,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AACjB,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,UAAU;AACZ,UAAM,UAAU,QAAQ;AAAA,EAC1B;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,QAAQ,OAAO;AACjC,QAAM,YAAY,WAAW,MAAM;AACnC,MAAI,IAAI,UAAU,SAAS;AAE3B,MAAI,SAAS;AACX,QAAI,IAAI,OAAO;AAAA,EACjB,OAAO;AACL,QAAI;AAAA,MACF,CAAC,KAAc,MAAuB,KAAuB,UAAgC;AAC3F,cAAM,SAAS,eAAe,SAAS,gBAAgB,MAAO,MAAoB;AAClF,YAAI,QAAQ;AACV,cACG,OAAO,OAAO,UAAU,EACxB,KAAK,EAAE,OAAO,OAAO,SAAS,YAAY,OAAO,WAAW,CAAC;AAAA,QAClE,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","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.1",
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"]}