@syntay/fastay 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Syntay
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
File without changes
package/dist/app.d.ts ADDED
@@ -0,0 +1,123 @@
1
+ import express from 'express';
2
+ import { MiddlewareMap } from './middleware.js';
3
+ import type { ServeStaticOptions } from 'serve-static';
4
+ /**
5
+ * Express configuration options applied automatically by Fastay
6
+ * before internal middleware and route loading.
7
+ */
8
+ export interface ExpressOptions {
9
+ /**
10
+ * Global middlewares applied to all routes.
11
+ * Example: [cors(), helmet()]
12
+ */
13
+ middlewares?: express.RequestHandler[];
14
+ /**
15
+ * Options passed to express.json().
16
+ * Useful for customizing JSON payload limits or behavior.
17
+ */
18
+ jsonOptions?: Parameters<typeof express.json>[0];
19
+ /**
20
+ * Options passed to express.urlencoded().
21
+ * Useful when handling form submissions or URL-encoded bodies.
22
+ */
23
+ urlencodedOptions?: Parameters<typeof express.urlencoded>[0];
24
+ /**
25
+ * Custom global error handler.
26
+ * If provided, Fastay will use this instead of the default one.
27
+ */
28
+ errorHandler?: express.ErrorRequestHandler;
29
+ /**
30
+ * Static file serving configuration.
31
+ * Example:
32
+ * {
33
+ * path: "public",
34
+ * options: { maxAge: "1d" }
35
+ * }
36
+ */
37
+ static?: {
38
+ path: string;
39
+ options?: ServeStaticOptions;
40
+ };
41
+ /**
42
+ * View engine configuration for Express.
43
+ * Example:
44
+ * {
45
+ * engine: "pug",
46
+ * dir: "views"
47
+ * }
48
+ */
49
+ views?: {
50
+ engine: string;
51
+ dir: string;
52
+ };
53
+ /**
54
+ * Enables or disables Express' "trust proxy" mode.
55
+ * Typically required when using reverse proxies (Nginx, Cloudflare, etc.).
56
+ */
57
+ trustProxy?: boolean;
58
+ /**
59
+ * Local variables available to all templates and responses.
60
+ * Fastay automatically injects them into `response.locals`.
61
+ */
62
+ locals?: Record<string, any>;
63
+ }
64
+ /**
65
+ * Options applied when creating a Fastay.js application.
66
+ */
67
+ export type CreateAppOptions = {
68
+ /**
69
+ * Directory where API route modules are located.
70
+ * Default: "src/api"
71
+ */
72
+ apiDir?: string;
73
+ /**
74
+ * Base route where all API routes will be mounted.
75
+ * Default: "/api"
76
+ */
77
+ baseRoute?: string;
78
+ /**
79
+ * Port on which `.listen()` will run the server.
80
+ * Default: 3000
81
+ */
82
+ port?: number;
83
+ /**
84
+ * Express-level configuration such as middleware, body parsers,
85
+ * view engine, static assets, error handler, etc.
86
+ */
87
+ expressOptions?: ExpressOptions;
88
+ /**
89
+ * Internal Fastay middlewares applied after Express initialization
90
+ * but before route mounting.
91
+ */
92
+ middlewares?: MiddlewareMap;
93
+ };
94
+ /**
95
+ * Bootstraps and configures a Fastay application.
96
+ *
97
+ * Fastay automatically:
98
+ * - Discovers and registers routes defined in `apiDir`.
99
+ * - Applies both built-in and user-provided middlewares.
100
+ * - Exposes a health-check endpoint at `/_health`.
101
+ *
102
+ * @param opts - Configuration options for the Fastay application.
103
+ * @returns A Promise that resolves to an Express `Application` instance.
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * import { createApp } from '@syntay/fastay';
108
+ * import cors from 'cors';
109
+ * import helmet from 'helmet';
110
+ *
111
+ * void (async () => {
112
+ * await createApp({
113
+ * apiDir: './src/api',
114
+ * baseRoute: '/api',
115
+ * port: 5555,
116
+ * expressOptions: {
117
+ * middlewares: [cors(), helmet()],
118
+ * },
119
+ * });
120
+ * })();
121
+ * ```
122
+ */
123
+ export declare function createApp(opts?: CreateAppOptions): Promise<import("express-serve-static-core").Express>;
package/dist/app.js ADDED
@@ -0,0 +1,106 @@
1
+ import express from 'express';
2
+ import path from 'path';
3
+ import { loadApiRoutes } from './router.js';
4
+ import { loadFastayMiddlewares, createMiddleware, } from './middleware.js';
5
+ import { logger } from './logger.js';
6
+ import { printBanner } from './banner.js';
7
+ /**
8
+ * Bootstraps and configures a Fastay application.
9
+ *
10
+ * Fastay automatically:
11
+ * - Discovers and registers routes defined in `apiDir`.
12
+ * - Applies both built-in and user-provided middlewares.
13
+ * - Exposes a health-check endpoint at `/_health`.
14
+ *
15
+ * @param opts - Configuration options for the Fastay application.
16
+ * @returns A Promise that resolves to an Express `Application` instance.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { createApp } from '@syntay/fastay';
21
+ * import cors from 'cors';
22
+ * import helmet from 'helmet';
23
+ *
24
+ * void (async () => {
25
+ * await createApp({
26
+ * apiDir: './src/api',
27
+ * baseRoute: '/api',
28
+ * port: 5555,
29
+ * expressOptions: {
30
+ * middlewares: [cors(), helmet()],
31
+ * },
32
+ * });
33
+ * })();
34
+ * ```
35
+ */
36
+ export async function createApp(opts) {
37
+ const start = logger.timeStart();
38
+ printBanner();
39
+ // logger.group('Fastay');
40
+ logger.info('Initializing server...');
41
+ const apiDir = opts?.apiDir ?? path.resolve(process.cwd(), 'src', 'api');
42
+ const baseRoute = opts?.baseRoute ?? '/api';
43
+ logger.success(`API directory: ${apiDir}`);
44
+ logger.success(`Base route: ${baseRoute}`);
45
+ const app = express();
46
+ if (opts?.expressOptions) {
47
+ for (const [key, value] of Object.entries(opts.expressOptions)) {
48
+ // Se for array → assume middleware global
49
+ if (Array.isArray(value)) {
50
+ value.forEach((mw) => app.use(mw));
51
+ }
52
+ // Se o app tiver método com esse nome
53
+ else if (typeof app[key] === 'function') {
54
+ // TS-safe
55
+ app[key](value);
56
+ }
57
+ // special cases
58
+ else if (key === 'static' && value && typeof value === 'object') {
59
+ const v = value;
60
+ app.use(express.static(v.path, v.options));
61
+ }
62
+ else if (key === 'jsonOptions') {
63
+ app.use(express.json(value));
64
+ }
65
+ else if (key === 'urlencodedOptions') {
66
+ app.use(express.urlencoded(value));
67
+ }
68
+ }
69
+ }
70
+ app.use(express.json());
71
+ const defaltPort = opts?.port ? opts.port : 6000;
72
+ app.listen(defaltPort, () => {
73
+ logger.success(`Server running at http://localhost:${defaltPort}${baseRoute}`);
74
+ });
75
+ // external middlewares
76
+ if (opts?.expressOptions?.middlewares) {
77
+ logger.group('Express Middlewares');
78
+ for (const mw of opts.expressOptions.middlewares) {
79
+ logger.gear(`Loaded: ${mw.name || 'anonymous'}`);
80
+ app.use(mw);
81
+ }
82
+ }
83
+ // Fastay middlewares
84
+ if (opts?.middlewares) {
85
+ logger.group('Fastay Middlewares');
86
+ const apply = createMiddleware(opts.middlewares);
87
+ apply(app);
88
+ }
89
+ // automatic middlewares
90
+ // logger.group('Fastay Auto-Middlewares');
91
+ const isMiddleware = await loadFastayMiddlewares(app);
92
+ // health check
93
+ app.get('/_health', (_, res) => res.json({ ok: true }));
94
+ app.use((_req, res, next) => {
95
+ res.setHeader('X-Powered-By', 'Syntay Engine');
96
+ next();
97
+ });
98
+ // load routes
99
+ // logger.group('Routes Loaded');
100
+ const totalRoutes = await loadApiRoutes(app, baseRoute, apiDir);
101
+ logger.success(`Total routes loaded: ${totalRoutes}`);
102
+ // app.use(errorHandler);
103
+ const time = logger.timeEnd(start);
104
+ logger.success(`Boot completed in ${time}ms`);
105
+ return app;
106
+ }
@@ -0,0 +1 @@
1
+ export declare function printBanner(): void;
package/dist/banner.js ADDED
@@ -0,0 +1,9 @@
1
+ import { logger } from './logger.js';
2
+ export function printBanner() {
3
+ const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
4
+ const white = (s) => `\x1b[37m${s}\x1b[0m`;
5
+ logger.raw('');
6
+ logger.raw(`${cyan('⥨ Fastay.js')} ${white('1.0.0')}`);
7
+ logger.raw(` ${white('- Runtime: Node.js\n')}`);
8
+ // logger.raw('\n');
9
+ }
@@ -0,0 +1,2 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ export declare function errorHandler(err: any, req: Request, res: Response, next: NextFunction): Response<any, Record<string, any>>;
@@ -0,0 +1,36 @@
1
+ import { logger } from './logger';
2
+ import fs from 'fs';
3
+ export function errorHandler(err, req, res, next) {
4
+ const isSyntaxError = err.name === 'SyntaxError' || err.message?.includes('Unexpected');
5
+ const route = `${req.method} ${req.originalUrl}`;
6
+ // Tenta extrair arquivo, linha e coluna (quando stack estiver presente)
7
+ let fileInfo = '';
8
+ if (err.stack) {
9
+ const stackLine = err.stack.split('\n')[1]; // pega primeira linha depois do erro
10
+ const match = stackLine.match(/\((.*):(\d+):(\d+)\)/);
11
+ if (match) {
12
+ const [_, file, line, col] = match;
13
+ fileInfo = `${file}:${line}:${col}`;
14
+ // Tenta mostrar o trecho da linha que deu erro
15
+ if (fs.existsSync(file)) {
16
+ const codeLines = fs.readFileSync(file, 'utf-8').split('\n');
17
+ const codeSnippet = codeLines[parseInt(line) - 1].trim();
18
+ fileInfo += ` → ${codeSnippet}`;
19
+ }
20
+ }
21
+ }
22
+ logger.group(`✗ Runtime Error in route [${route}]`);
23
+ logger.error(`${err.name}: ${err.message}`);
24
+ if (fileInfo)
25
+ logger.error(`Location: ${fileInfo}`);
26
+ if (process.env.NODE_ENV === 'production') {
27
+ return res.status(500).json({
28
+ error: 'Internal server error',
29
+ });
30
+ }
31
+ return res.status(500).json({
32
+ error: err.message,
33
+ stack: err.stack,
34
+ file: fileInfo || undefined,
35
+ });
36
+ }
@@ -0,0 +1,4 @@
1
+ export { createApp } from './app.js';
2
+ export { createMiddleware } from './middleware.js';
3
+ export type { CreateAppOptions } from './app.js';
4
+ export { Request, Response, Next } from './types';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { createApp } from './app.js';
2
+ export { createMiddleware } from './middleware.js';
@@ -0,0 +1,12 @@
1
+ export declare const logger: {
2
+ info: (msg: string) => void;
3
+ warn: (msg: string) => void;
4
+ error: (msg: string) => void;
5
+ success: (msg: string) => void;
6
+ gear: (msg: string) => void;
7
+ space(lines?: number): void;
8
+ group(title: string): void;
9
+ raw(msg: string): void;
10
+ timeStart(): number;
11
+ timeEnd(start: number): string;
12
+ };
package/dist/logger.js ADDED
@@ -0,0 +1,61 @@
1
+ import pino from 'pino';
2
+ import pretty from 'pino-pretty';
3
+ // stream configurado para remover INFO:, timestamps e etc
4
+ const stream = pretty({
5
+ colorize: true,
6
+ ignore: 'pid,hostname,time,level',
7
+ levelFirst: false,
8
+ // Remove "INFO: " antes da msg
9
+ messageKey: 'msg',
10
+ // Maneira correta TS-safe
11
+ messageFormat: (log, messageKey) => {
12
+ const msg = log[messageKey];
13
+ return typeof msg === 'string' ? msg : String(msg);
14
+ },
15
+ });
16
+ const base = pino({
17
+ level: 'info',
18
+ timestamp: false, // remove [HH:mm:ss]
19
+ base: undefined, // remove pid, hostname
20
+ }, stream);
21
+ // helpers para cores ANSI
22
+ const colors = {
23
+ white: (s) => `\x1b[37m${s}\x1b[0m`,
24
+ green: (s) => `\x1b[32m${s}\x1b[0m`,
25
+ red: (s) => `\x1b[31m${s}\x1b[0m`,
26
+ cyan: (s) => `\x1b[36m${s}\x1b[0m`,
27
+ gray: (s) => `\x1b[90m${s}\x1b[0m`,
28
+ };
29
+ // emojis Fastay
30
+ const ICONS = {
31
+ info: '○',
32
+ success: '✓',
33
+ error: '✗',
34
+ gear: '⚙️',
35
+ };
36
+ export const logger = {
37
+ info: (msg) => base.info(` ${colors.white(ICONS.info)} ${colors.white(msg)}`),
38
+ warn: (msg) => base.info(` ${colors.red('⚠')} ${colors.white(msg)}`),
39
+ error: (msg) => base.info(` ${colors.red(ICONS.error)} ${colors.white(msg)}`),
40
+ success: (msg) => base.info(` ${colors.green(ICONS.success)} ${colors.white(msg)}`),
41
+ gear: (msg) => base.info(` ${ICONS.gear} ${colors.white(msg)}`),
42
+ space(lines = 1) {
43
+ for (let i = 0; i < lines; i++)
44
+ base.info(' ');
45
+ },
46
+ group(title) {
47
+ this.space();
48
+ base.info('');
49
+ base.info(colors.cyan(title));
50
+ // this.space();
51
+ },
52
+ raw(msg) {
53
+ base.info(msg);
54
+ },
55
+ timeStart() {
56
+ return performance.now();
57
+ },
58
+ timeEnd(start) {
59
+ return (performance.now() - start).toFixed(1);
60
+ },
61
+ };
@@ -0,0 +1,62 @@
1
+ import { Application, Request, Response, NextFunction } from 'express';
2
+ type MiddlewareFn = (req: Request, res: Response, next: NextFunction) => any;
3
+ /**
4
+ * Defines a map of routes and the middleware functions that Fastay
5
+ * will automatically load and attach during boot.
6
+ *
7
+ * Keys represent route prefixes (e.g. `/auth`, `/admin`), and
8
+ * values are arrays of Fastay middleware functions.
9
+ *
10
+ * Middleware functions use Fastay’s extended `Request`, `Response`,
11
+ * and `Next` types — not the raw Express versions.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * // src/middleware.ts
16
+ * import { createMiddleware } from '@syntay/fastay';
17
+ * import { authMiddleware } from './auth';
18
+ * import { auditLogger } from './audit';
19
+ *
20
+ * export const middleware = createMiddleware({
21
+ * '/auth': [authMiddleware],
22
+ * '/admin': [auditLogger]
23
+ * });
24
+ * ```
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * // src/auth.ts
29
+ * import { Request, Response, Next } from '@syntay/fastay';
30
+ *
31
+ * export async function authMiddleware(req: Request, _res: Response, next: Next) {
32
+ * // Custom logic using extended Fastay types
33
+ * req.user = { id: 1, role: "admin" };
34
+ * next();
35
+ * }
36
+ * ```
37
+ */
38
+ export type MiddlewareMap = Record<string, MiddlewareFn[]>;
39
+ /**
40
+ * Creates a Fastay middleware loader.
41
+ *
42
+ * Fastay uses this internally to attach user-defined middleware to the
43
+ * Express application during boot. The framework automatically discovers
44
+ * and loads any `middleware` exported from the project's `src/` directory.
45
+ *
46
+ * Middleware functions are wrapped so both synchronous and asynchronous
47
+ * handlers behave consistently.
48
+ *
49
+ * @param map - A map of route prefixes and the middleware stack for each route.
50
+ * @returns A function that Fastay will call to register the mapped middleware.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * export const middleware = createMiddleware({
55
+ * '/auth': [authMiddleware],
56
+ * '/admin': [adminGuard, auditLogger]
57
+ * });
58
+ * ```
59
+ */
60
+ export declare function createMiddleware(map: Record<string, MiddlewareFn[]>): (app: Application) => void;
61
+ export declare function loadFastayMiddlewares(app: Application): Promise<void>;
62
+ export {};
@@ -0,0 +1,59 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ import { pathToFileURL } from 'url';
4
+ import { logger } from './logger.js';
5
+ import { wrapMiddleware } from './utils/wrapMiddleware.js';
6
+ /**
7
+ * Creates a Fastay middleware loader.
8
+ *
9
+ * Fastay uses this internally to attach user-defined middleware to the
10
+ * Express application during boot. The framework automatically discovers
11
+ * and loads any `middleware` exported from the project's `src/` directory.
12
+ *
13
+ * Middleware functions are wrapped so both synchronous and asynchronous
14
+ * handlers behave consistently.
15
+ *
16
+ * @param map - A map of route prefixes and the middleware stack for each route.
17
+ * @returns A function that Fastay will call to register the mapped middleware.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * export const middleware = createMiddleware({
22
+ * '/auth': [authMiddleware],
23
+ * '/admin': [adminGuard, auditLogger]
24
+ * });
25
+ * ```
26
+ */
27
+ export function createMiddleware(map) {
28
+ return (app) => {
29
+ for (const [route, middlewares] of Object.entries(map)) {
30
+ for (const mw of middlewares) {
31
+ const wrapped = wrapMiddleware(mw);
32
+ app.use(route, wrapped);
33
+ }
34
+ }
35
+ };
36
+ }
37
+ export async function loadFastayMiddlewares(app) {
38
+ const isDev = process.env.NODE_ENV !== 'production';
39
+ const mwDir = path.resolve(process.cwd(), isDev ? 'src/middlewares' : 'dist/middlewares');
40
+ const file = path.join(mwDir, isDev ? 'middleware.ts' : 'middleware.js');
41
+ if (!fs.existsSync(file))
42
+ return;
43
+ const mod = await import(pathToFileURL(file).href);
44
+ if (!mod.middleware)
45
+ return;
46
+ logger.group('Fastay Auto-Middlewares');
47
+ if (typeof mod.middleware === 'function') {
48
+ mod.middleware(app);
49
+ logger.info('Loading Fastay core middleware...');
50
+ }
51
+ else {
52
+ const map = mod.middleware;
53
+ for (const [route, middlewares] of Object.entries(map)) {
54
+ for (const mw of middlewares) {
55
+ app.use(route, mw);
56
+ }
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,13 @@
1
+ import { Application } from 'express';
2
+ /**
3
+ * Converte caminho do arquivo em rota Express (somente arquivos route.ts)
4
+ */
5
+ export declare function filePathToRoute(apiDir: string, filePath: string, baseRoute: string): string | null;
6
+ /**
7
+ * Retorna todos arquivos .ts/.js recursivamente
8
+ */
9
+ export declare function collectFiles(dir: string): string[];
10
+ /**
11
+ * Carrega todas as rotas do diretório apiDir
12
+ */
13
+ export declare function loadApiRoutes(app: Application, baseRoute: string, apiDirectory: string): Promise<number>;
package/dist/router.js ADDED
@@ -0,0 +1,151 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { pathToFileURL } from 'url';
4
+ import { logger } from './logger.js';
5
+ /**
6
+ * Converte caminho do arquivo em rota Express (somente arquivos route.ts)
7
+ */
8
+ export function filePathToRoute(apiDir, filePath, baseRoute) {
9
+ const rel = path.relative(apiDir, filePath);
10
+ const parts = rel.split(path.sep);
11
+ const filename = parts.pop();
12
+ if (filename !== 'route.ts' && filename !== 'route.js')
13
+ return null;
14
+ const segments = parts
15
+ .map((s) => s.startsWith('[') && s.endsWith(']') ? `:${s.slice(1, -1)}` : s)
16
+ .filter(Boolean);
17
+ return `${baseRoute}/${segments.join('/')}`.replace(/\/+/g, '/');
18
+ }
19
+ /**
20
+ * Retorna todos arquivos .ts/.js recursivamente
21
+ */
22
+ export function collectFiles(dir) {
23
+ let out = [];
24
+ const items = fs.readdirSync(dir, { withFileTypes: true });
25
+ for (const it of items) {
26
+ const full = path.join(dir, it.name);
27
+ if (it.isDirectory())
28
+ out = out.concat(collectFiles(full));
29
+ else if (/\.(ts|js|mts|mjs)$/.test(it.name))
30
+ out.push(full);
31
+ }
32
+ return out;
33
+ }
34
+ /**
35
+ * Wrapper para suportar return JSON/string/number e capturar erros runtime
36
+ */
37
+ function wrapHandler(fn, routePath, filePath) {
38
+ return async (req, res, next) => {
39
+ try {
40
+ const result = fn.length >= 2 ? await fn(req, res) : await fn(req);
41
+ if (res.headersSent)
42
+ return;
43
+ if (result === undefined)
44
+ return;
45
+ if (typeof result === 'string')
46
+ return res.send(result);
47
+ if (typeof result === 'number')
48
+ return res.send(String(result));
49
+ return res.json(result);
50
+ }
51
+ catch (err) {
52
+ const stack = err?.stack?.split('\n').slice(0, 3).join('\n') || '';
53
+ // logger.error(
54
+ // `✗ Runtime Error in route [${req.method} ${routePath}]\n` +
55
+ // ` File: ${filePath}\n` +
56
+ // ` ${err.name}: ${err.message || 'Unknown error'}\n` +
57
+ // ` Stack: ${stack}`
58
+ // );
59
+ let fileInfo = '';
60
+ if (err.stack) {
61
+ const stackLine = err.stack.split('\n')[1]; // pega primeira linha depois do erro
62
+ const match = stackLine.match(/\((.*):(\d+):(\d+)\)/);
63
+ if (match) {
64
+ const [_, file, line, col] = match;
65
+ fileInfo = `${file}:${line}:${col}`;
66
+ // Tenta mostrar o trecho da linha que deu erro
67
+ if (fs.existsSync(file)) {
68
+ const codeLines = fs.readFileSync(file, 'utf-8').split('\n');
69
+ const codeSnippet = codeLines[parseInt(line) - 1].trim();
70
+ fileInfo += ` → ${codeSnippet}`;
71
+ }
72
+ }
73
+ }
74
+ // logger.group(`✗ Runtime Error in route [${req.method} ${routePath}]`);
75
+ logger.error(`${err.name}: ${err.message}`);
76
+ if (fileInfo)
77
+ logger.error(`Location: ${fileInfo}`);
78
+ next(err);
79
+ }
80
+ };
81
+ }
82
+ /**
83
+ * Carrega todas as rotas do diretório apiDir
84
+ */
85
+ export async function loadApiRoutes(app, baseRoute, apiDirectory) {
86
+ const isDev = process.env.NODE_ENV !== 'production';
87
+ const apiDir = path.join(process.cwd(), isDev ? apiDirectory : 'dist/api');
88
+ if (!fs.existsSync(apiDir))
89
+ return 0;
90
+ const files = collectFiles(apiDir);
91
+ let cont = 0;
92
+ logger.group('Routes Loaded');
93
+ for (const file of files) {
94
+ const route = filePathToRoute(apiDir, file, baseRoute);
95
+ if (!route)
96
+ continue;
97
+ try {
98
+ const fileUrl = pathToFileURL(file).href;
99
+ const mod = await import(fileUrl);
100
+ const httpMethods = [
101
+ 'GET',
102
+ 'POST',
103
+ 'PUT',
104
+ 'DELETE',
105
+ 'PATCH',
106
+ 'OPTIONS',
107
+ 'HEAD',
108
+ ];
109
+ for (const m of httpMethods) {
110
+ if (typeof mod[m] === 'function') {
111
+ app[m.toLowerCase()](route, wrapHandler(mod[m], route, file));
112
+ cont++;
113
+ logger.success(`Route: [${m}] ${route}`);
114
+ }
115
+ }
116
+ if (mod.default && typeof mod.default === 'function') {
117
+ app.get(route, wrapHandler(mod.default, route, file));
118
+ cont++;
119
+ logger.success(`Route: [GET] ${route}`);
120
+ }
121
+ }
122
+ catch (err) {
123
+ const stack = err?.stack?.split('\n').slice(0, 3).join('\n') || '';
124
+ // logger.error(
125
+ // `✗ Boot Error importing ${file}\n` +
126
+ // ` Message: ${err.message || 'Unknown error'}\n` +
127
+ // ` Stack: ${stack}`
128
+ // );
129
+ let fileInfo = '';
130
+ if (err.stack) {
131
+ const stackLine = err.stack.split('\n')[1]; // pega primeira linha depois do erro
132
+ const match = stackLine.match(/\((.*):(\d+):(\d+)\)/);
133
+ if (match) {
134
+ const [_, file, line, col] = match;
135
+ fileInfo = `${file}:${line}:${col}`;
136
+ // Tenta mostrar o trecho da linha que deu erro
137
+ if (fs.existsSync(file)) {
138
+ const codeLines = fs.readFileSync(file, 'utf-8').split('\n');
139
+ const codeSnippet = codeLines[parseInt(line) - 1].trim();
140
+ fileInfo += ` → ${codeSnippet}`;
141
+ }
142
+ }
143
+ }
144
+ // logger.group(`✗ Boot Error importing ${file}`);
145
+ logger.error(`${err.name}: ${err.message}`);
146
+ if (fileInfo)
147
+ logger.error(`Location: ${fileInfo}`);
148
+ }
149
+ }
150
+ return cont;
151
+ }