bro-framework 2.2.0 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,88 +1,88 @@
1
- {
2
- "name": "bro-framework",
3
- "version": "2.2.0",
4
- "description": "The No-BS Backend Framework for Node.js",
5
- "repository": {
6
- "type": "git",
7
- "url": "https://github.com/medyass1ne/bro.js"
8
- },
9
- "homepage": "https://brojs.yessindevs.me",
10
- "author": "Yessindevs",
11
- "license": "MIT",
12
- "main": "src/index.js",
13
- "types": "src/index.d.ts",
14
- "type": "module",
15
- "exports": {
16
- ".": {
17
- "types": "./src/index.d.ts",
18
- "default": "./src/index.js"
19
- }
20
- },
21
- "bin": {
22
- "bro": "./bin/bro.js"
23
- },
24
- "files": [
25
- "src",
26
- "bin"
27
- ],
28
- "dependencies": {
29
- "@scalar/express-api-reference": "^0.10.18",
30
- "chokidar": "^5.0.0",
31
- "cors": "^2.8.6",
32
- "dotenv": "^16.4.5",
33
- "express": "^4.21.1",
34
- "express-rate-limit": "^8.7.0",
35
- "jsonwebtoken": "^9.0.2",
36
- "multer": "^2.3.0",
37
- "node-cron": "^4.6.0",
38
- "socket.io": "^4.8.3",
39
- "tsx": "^4.23.13",
40
- "zod": "^3.23.8",
41
- "zod-to-json-schema": "^3.25.2"
42
- },
43
- "keywords": [
44
- "bro",
45
- "brojs",
46
- "bro-framework",
47
- "framework",
48
- "backend",
49
- "api",
50
- "server",
51
- "node",
52
- "nodejs",
53
- "express",
54
- "rest",
55
- "zero-config",
56
- "zero-boilerplate",
57
- "file-based-routing",
58
- "auto-routing",
59
- "zod",
60
- "validation",
61
- "jwt",
62
- "authentication",
63
- "auth",
64
- "websocket",
65
- "websockets",
66
- "socket.io",
67
- "realtime",
68
- "openapi",
69
- "swagger",
70
- "scalar",
71
- "docs",
72
- "auto-docs",
73
- "sdk",
74
- "sdk-generator",
75
- "rpc",
76
- "trpc-alternative",
77
- "express-alternative",
78
- "cron",
79
- "scheduler",
80
- "background-tasks",
81
- "file-upload",
82
- "rate-limit",
83
- "esm",
84
- "ai-friendly",
85
- "developer-experience",
86
- "dx"
87
- ]
88
- }
1
+ {
2
+ "name": "bro-framework",
3
+ "version": "2.2.2",
4
+ "description": "The No-BS Backend Framework for Node.js",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/medyass1ne/bro.js"
8
+ },
9
+ "homepage": "https://brojs.yessindevs.me",
10
+ "author": "Yessindevs",
11
+ "license": "MIT",
12
+ "main": "src/index.js",
13
+ "types": "src/index.d.ts",
14
+ "type": "module",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./src/index.d.ts",
18
+ "default": "./src/index.js"
19
+ }
20
+ },
21
+ "bin": {
22
+ "bro": "./bin/bro.js"
23
+ },
24
+ "files": [
25
+ "src",
26
+ "bin"
27
+ ],
28
+ "dependencies": {
29
+ "@scalar/express-api-reference": "^0.10.18",
30
+ "chokidar": "^5.0.0",
31
+ "cors": "^2.8.6",
32
+ "dotenv": "^16.4.5",
33
+ "express": "^4.21.1",
34
+ "express-rate-limit": "^8.7.0",
35
+ "jsonwebtoken": "^9.0.2",
36
+ "multer": "^2.3.0",
37
+ "node-cron": "^4.6.0",
38
+ "socket.io": "^4.8.3",
39
+ "tsx": "^4.23.13",
40
+ "zod": "^3.23.8",
41
+ "zod-to-json-schema": "^3.25.2"
42
+ },
43
+ "keywords": [
44
+ "bro",
45
+ "brojs",
46
+ "bro-framework",
47
+ "framework",
48
+ "backend",
49
+ "api",
50
+ "server",
51
+ "node",
52
+ "nodejs",
53
+ "express",
54
+ "rest",
55
+ "zero-config",
56
+ "zero-boilerplate",
57
+ "file-based-routing",
58
+ "auto-routing",
59
+ "zod",
60
+ "validation",
61
+ "jwt",
62
+ "authentication",
63
+ "auth",
64
+ "websocket",
65
+ "websockets",
66
+ "socket.io",
67
+ "realtime",
68
+ "openapi",
69
+ "swagger",
70
+ "scalar",
71
+ "docs",
72
+ "auto-docs",
73
+ "sdk",
74
+ "sdk-generator",
75
+ "rpc",
76
+ "trpc-alternative",
77
+ "express-alternative",
78
+ "cron",
79
+ "scheduler",
80
+ "background-tasks",
81
+ "file-upload",
82
+ "rate-limit",
83
+ "esm",
84
+ "ai-friendly",
85
+ "developer-experience",
86
+ "dx"
87
+ ]
88
+ }
package/src/auth.js CHANGED
@@ -1,33 +1,33 @@
1
- import jwt from 'jsonwebtoken';
2
-
3
- /**
4
- * Signs a JWT payload.
5
- * @param {Object} payload - The data to embed in the token.
6
- * @param {string} secret - The JWT secret key.
7
- * @param {jwt.SignOptions} [options] - jsonwebtoken sign options.
8
- * @returns {string} The signed JWT token.
9
- */
10
- export function signJwt(payload, secret, options = { expiresIn: '1d' }) {
11
- return jwt.sign(payload, secret, options);
12
- }
13
-
14
- /**
15
- * Verifies and decodes a JWT token.
16
- * @param {string} token - The JWT token to verify.
17
- * @param {string} secret - The JWT secret key.
18
- * @returns {{ valid: boolean, payload?: any, error?: string }}
19
- */
20
- export function verifyJwt(token, secret) {
21
- try {
22
- const payload = jwt.verify(token, secret);
23
- return { valid: true, payload };
24
- } catch (error) {
25
- let message = 'Invalid token';
26
- if (error.name === 'TokenExpiredError') {
27
- message = 'Token has expired';
28
- } else if (error.name === 'JsonWebTokenError') {
29
- message = 'Signature verification failed';
30
- }
31
- return { valid: false, error: message };
32
- }
33
- }
1
+ import jwt from 'jsonwebtoken';
2
+
3
+ /**
4
+ * Signs a JWT payload.
5
+ * @param {Object} payload - The data to embed in the token.
6
+ * @param {string} secret - The JWT secret key.
7
+ * @param {jwt.SignOptions} [options] - jsonwebtoken sign options.
8
+ * @returns {string} The signed JWT token.
9
+ */
10
+ export function signJwt(payload, secret, options = { expiresIn: '1d' }) {
11
+ return jwt.sign(payload, secret, options);
12
+ }
13
+
14
+ /**
15
+ * Verifies and decodes a JWT token.
16
+ * @param {string} token - The JWT token to verify.
17
+ * @param {string} secret - The JWT secret key.
18
+ * @returns {{ valid: boolean, payload?: any, error?: string }}
19
+ */
20
+ export function verifyJwt(token, secret) {
21
+ try {
22
+ const payload = jwt.verify(token, secret);
23
+ return { valid: true, payload };
24
+ } catch (error) {
25
+ let message = 'Invalid token';
26
+ if (error.name === 'TokenExpiredError') {
27
+ message = 'Token has expired';
28
+ } else if (error.name === 'JsonWebTokenError') {
29
+ message = 'Signature verification failed';
30
+ }
31
+ return { valid: false, error: message };
32
+ }
33
+ }
package/src/index.d.ts CHANGED
@@ -1,66 +1,66 @@
1
- import { z, ZodTypeAny } from 'zod';
2
-
3
- type InferZod<T> = T extends ZodTypeAny ? z.infer<T> : any;
4
-
5
- export interface BroContext<Body = any, Params = any, Query = any> {
6
- env?: any;
7
- jwt?: { sign: (payload: any, options?: any) => string };
8
- body: InferZod<Body>;
9
- params: InferZod<Params>;
10
- query: InferZod<Query>;
11
- user?: any;
12
- db?: any;
13
- io?: any;
14
- files?: any[];
15
- error?: any;
16
- }
17
-
18
- export interface RouteConfig<Body = any, Params = any, Query = any> {
19
- auth?: boolean;
20
- upload?: boolean | { limits?: any, fields?: { name: string, maxCount?: number }[], single?: string, array?: string, fileFilter?: any, storage?: any };
21
- schema?: { body?: Body; params?: Params; query?: Query; };
22
- body?: Body;
23
- params?: Params;
24
- query?: Query;
25
- rateLimit?: {
26
- windowMs: number;
27
- max: number;
28
- };
29
- summary?: string;
30
- handler: (ctx: BroContext<Body, Params, Query>) => Promise<any> | any;
31
- }
32
-
33
- export function defineRoute<Body = any, Params = any, Query = any>(
34
- config: RouteConfig<Body, Params, Query>
35
- ): RouteConfig<Body, Params, Query>;
36
-
37
- export interface BroConfig {
38
- env?: ZodTypeAny;
39
- server?: {
40
- port?: number;
41
- cors?: boolean | object;
42
- };
43
- auth?: {
44
- jwtSecret?: string;
45
- expiresIn?: string | number;
46
- };
47
- docs?: boolean | { auth?: { user: string; pass: string } };
48
- rateLimit?: {
49
- windowMs: number;
50
- max: number;
51
- };
52
- upload?: {
53
- limits?: {
54
- fileSize?: number;
55
- files?: number;
56
- fields?: number;
57
- [key: string]: any;
58
- };
59
- };
60
- db?: () => Promise<any> | any;
61
- sockets?: (io: any, db: any) => Promise<void> | void;
62
- }
63
-
64
- export function defineConfig(config: BroConfig): BroConfig;
65
-
66
- export { z } from 'zod';
1
+ import { z, ZodTypeAny } from 'zod';
2
+
3
+ type InferZod<T> = T extends ZodTypeAny ? z.infer<T> : any;
4
+
5
+ export interface BroContext<Body = any, Params = any, Query = any> {
6
+ env?: any;
7
+ jwt?: { sign: (payload: any, options?: any) => string };
8
+ body: InferZod<Body>;
9
+ params: InferZod<Params>;
10
+ query: InferZod<Query>;
11
+ user?: any;
12
+ db?: any;
13
+ io?: any;
14
+ files?: any[];
15
+ error?: any;
16
+ }
17
+
18
+ export interface RouteConfig<Body = any, Params = any, Query = any> {
19
+ auth?: boolean;
20
+ upload?: boolean | { limits?: any, fields?: { name: string, maxCount?: number }[], single?: string, array?: string, fileFilter?: any, storage?: any };
21
+ schema?: { body?: Body; params?: Params; query?: Query; };
22
+ body?: Body;
23
+ params?: Params;
24
+ query?: Query;
25
+ rateLimit?: {
26
+ windowMs: number;
27
+ max: number;
28
+ };
29
+ summary?: string;
30
+ handler: (ctx: BroContext<Body, Params, Query>) => Promise<any> | any;
31
+ }
32
+
33
+ export function defineRoute<Body = any, Params = any, Query = any>(
34
+ config: RouteConfig<Body, Params, Query>
35
+ ): RouteConfig<Body, Params, Query>;
36
+
37
+ export interface BroConfig {
38
+ env?: ZodTypeAny;
39
+ server?: {
40
+ port?: number;
41
+ cors?: boolean | object;
42
+ };
43
+ auth?: {
44
+ jwtSecret?: string;
45
+ expiresIn?: string | number;
46
+ };
47
+ docs?: boolean | { auth?: { user: string; pass: string } };
48
+ rateLimit?: {
49
+ windowMs: number;
50
+ max: number;
51
+ };
52
+ upload?: {
53
+ limits?: {
54
+ fileSize?: number;
55
+ files?: number;
56
+ fields?: number;
57
+ [key: string]: any;
58
+ };
59
+ };
60
+ db?: () => Promise<any> | any;
61
+ sockets?: (io: any, db: any) => Promise<void> | void;
62
+ }
63
+
64
+ export function defineConfig(config: BroConfig): BroConfig;
65
+
66
+ export { z } from 'zod';
package/src/logger.js CHANGED
@@ -1,66 +1,79 @@
1
- export const colors = {
2
- reset: "\x1b[0m",
3
- bold: "\x1b[1m",
4
- dim: "\x1b[2m",
5
- red: "\x1b[31m",
6
- green: "\x1b[32m",
7
- yellow: "\x1b[33m",
8
- blue: "\x1b[34m",
9
- magenta: "\x1b[35m",
10
- cyan: "\x1b[36m",
11
- white: "\x1b[37m"
12
- };
13
-
14
- export function formatMethod(method) {
15
- const m = method.toUpperCase().padEnd(6);
16
- switch(method.toUpperCase()) {
17
- case 'GET': return `${colors.green}${colors.bold}${m}${colors.reset}`;
18
- case 'POST': return `${colors.blue}${colors.bold}${m}${colors.reset}`;
19
- case 'PUT': return `${colors.yellow}${colors.bold}${m}${colors.reset}`;
20
- case 'DELETE': return `${colors.red}${colors.bold}${m}${colors.reset}`;
21
- case 'PATCH': return `${colors.magenta}${colors.bold}${m}${colors.reset}`;
22
- default: return `${colors.white}${colors.bold}${m}${colors.reset}`;
23
- }
24
- }
25
-
26
- export function printBanner(port, durationMs) {
27
- const time = durationMs.toFixed(0);
28
- const version = "2.0.1";
29
- const innerWidth = 47;
30
-
31
- const stripAnsi = (str) => str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
32
-
33
- const formatLine = (content = "") => {
34
- const visibleLength = stripAnsi(content).length;
35
- const padding = Math.max(0, innerWidth - visibleLength);
36
- return `${colors.green}│${colors.reset}${content}${" ".repeat(padding)}${colors.green}│${colors.reset}`;
37
- };
38
-
39
- const borderTop = `${colors.green}╭${"─".repeat(innerWidth)}╮${colors.reset}`;
40
- const borderBottom = `${colors.green}╰${"─".repeat(innerWidth)}╯${colors.reset}`;
41
-
42
- console.log("");
43
- console.log(borderTop);
44
- console.log(formatLine());
45
- console.log(formatLine(` ${colors.bold}bro.js${colors.reset} v${version}`));
46
- console.log(formatLine());
47
- console.log(formatLine(` ➜ ${colors.bold}Local:${colors.reset} ${colors.cyan}http://localhost:${port}${colors.reset}`));
48
- console.log(formatLine(` ➜ ${colors.bold}Ready in:${colors.reset} ${colors.yellow}${time}ms${colors.reset}`));
49
- console.log(formatLine());
50
- console.log(borderBottom);
51
- console.log("");
52
- }
53
-
54
- export function printRoute(method, routePath, hasAuth, isLast = false) {
55
- const branch = isLast ? "╰──" : "├──";
56
- const coloredMethod = formatMethod(method);
57
- const authIcon = hasAuth ? " 🔒" : "";
58
-
59
- console.log(` ${colors.dim}${branch}${colors.reset} ${coloredMethod} ${routePath}${authIcon}`);
60
- }
61
-
62
- export function printHotReload(fileName, event, reloadTimeMs) {
63
- const time = typeof reloadTimeMs === 'number' ? reloadTimeMs.toFixed(0) : reloadTimeMs;
64
- console.log(`\n ${colors.cyan}Route updated:${colors.reset} ${colors.bold}${fileName}${colors.reset} ${colors.dim}(${event})${colors.reset}`);
65
- console.log(` ${colors.dim}Remapped in ${time}ms${colors.reset}\n`);
66
- }
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ export const colors = {
6
+ reset: "\x1b[0m",
7
+ bold: "\x1b[1m",
8
+ dim: "\x1b[2m",
9
+ red: "\x1b[31m",
10
+ green: "\x1b[32m",
11
+ yellow: "\x1b[33m",
12
+ blue: "\x1b[34m",
13
+ magenta: "\x1b[35m",
14
+ cyan: "\x1b[36m",
15
+ white: "\x1b[37m"
16
+ };
17
+
18
+ export function formatMethod(method) {
19
+ const m = method.toUpperCase().padEnd(6);
20
+ switch(method.toUpperCase()) {
21
+ case 'GET': return `${colors.green}${colors.bold}${m}${colors.reset}`;
22
+ case 'POST': return `${colors.blue}${colors.bold}${m}${colors.reset}`;
23
+ case 'PUT': return `${colors.yellow}${colors.bold}${m}${colors.reset}`;
24
+ case 'DELETE': return `${colors.red}${colors.bold}${m}${colors.reset}`;
25
+ case 'PATCH': return `${colors.magenta}${colors.bold}${m}${colors.reset}`;
26
+ default: return `${colors.white}${colors.bold}${m}${colors.reset}`;
27
+ }
28
+ }
29
+
30
+ export function printBanner(port, durationMs) {
31
+ const time = durationMs.toFixed(0);
32
+
33
+ let version = "2.2.0";
34
+ try {
35
+ const __filename = fileURLToPath(import.meta.url);
36
+ const __dirname = path.dirname(__filename);
37
+ const pkgPath = path.join(__dirname, '..', 'package.json');
38
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
39
+ version = pkg.version;
40
+ } catch(e) {}
41
+
42
+ const innerWidth = 47;
43
+
44
+ const stripAnsi = (str) => str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
45
+
46
+ const formatLine = (content = "") => {
47
+ const visibleLength = stripAnsi(content).length;
48
+ const padding = Math.max(0, innerWidth - visibleLength);
49
+ return `${colors.green}│${colors.reset}${content}${" ".repeat(padding)}${colors.green}│${colors.reset}`;
50
+ };
51
+
52
+ const borderTop = `${colors.green}╭${"─".repeat(innerWidth)}╮${colors.reset}`;
53
+ const borderBottom = `${colors.green}╰${"─".repeat(innerWidth)}╯${colors.reset}`;
54
+
55
+ console.log("");
56
+ console.log(borderTop);
57
+ console.log(formatLine());
58
+ console.log(formatLine(` ${colors.bold}bro.js${colors.reset} v${version}`));
59
+ console.log(formatLine());
60
+ console.log(formatLine(` ➜ ${colors.bold}Local:${colors.reset} ${colors.cyan}http://localhost:${port}${colors.reset}`));
61
+ console.log(formatLine(` ➜ ${colors.bold}Ready in:${colors.reset} ${colors.yellow}${time}ms${colors.reset}`));
62
+ console.log(formatLine());
63
+ console.log(borderBottom);
64
+ console.log("");
65
+ }
66
+
67
+ export function printRoute(method, routePath, hasAuth, isLast = false) {
68
+ const branch = isLast ? "╰──" : "├──";
69
+ const coloredMethod = formatMethod(method);
70
+ const authIcon = hasAuth ? " 🔒" : "";
71
+
72
+ console.log(` ${colors.dim}${branch}${colors.reset} ${coloredMethod} ${routePath}${authIcon}`);
73
+ }
74
+
75
+ export function printHotReload(fileName, event, reloadTimeMs) {
76
+ const time = typeof reloadTimeMs === 'number' ? reloadTimeMs.toFixed(0) : reloadTimeMs;
77
+ console.log(`\n ${colors.cyan}Route updated:${colors.reset} ${colors.bold}${fileName}${colors.reset} ${colors.dim}(${event})${colors.reset}`);
78
+ console.log(` ${colors.dim}Remapped in ${time}ms${colors.reset}\n`);
79
+ }