barejs 0.1.9 → 0.1.12
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/README.md +93 -88
- package/dist/benchmarks/index.d.ts +1 -0
- package/dist/example.d.ts +1 -0
- package/dist/index.js +7 -0
- package/dist/scripts/build.d.ts +1 -0
- package/dist/scripts/update-readme.d.ts +1 -0
- package/dist/src/auth.d.ts +23 -0
- package/dist/src/bare.d.ts +32 -0
- package/dist/src/context.d.ts +26 -0
- package/dist/src/cors.d.ts +5 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/logger.d.ts +2 -0
- package/dist/src/static.d.ts +8 -0
- package/dist/src/validators.d.ts +13 -0
- package/package.json +67 -61
- package/src/auth.ts +100 -0
- package/src/bare.ts +125 -139
- package/src/context.ts +30 -48
- package/src/cors.ts +27 -0
- package/src/index.ts +23 -3
- package/src/logger.ts +23 -0
- package/src/static.ts +45 -0
- package/src/validators.ts +58 -21
package/package.json
CHANGED
|
@@ -1,61 +1,67 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "barejs",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"author": "xarhang",
|
|
5
|
-
"description": "High-performance JIT-specialized web framework for Bun",
|
|
6
|
-
"main": "./
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"bun",
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"zod":
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"zod":
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "barejs",
|
|
3
|
+
"version": "0.1.12",
|
|
4
|
+
"author": "xarhang",
|
|
5
|
+
"description": "High-performance JIT-specialized web framework for Bun",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/xarhang/barejs.git"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "bun test",
|
|
23
|
+
"bench": "bun run benchmarks/index.ts",
|
|
24
|
+
"dev": "bun --watch src/index.ts",
|
|
25
|
+
"build": "bun ./scripts/build.ts",
|
|
26
|
+
"prepublishOnly": "bun run build",
|
|
27
|
+
"release": "bun run build && npm version patch && npm publish --access public"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"bun",
|
|
31
|
+
"framework",
|
|
32
|
+
"fastest",
|
|
33
|
+
"jit",
|
|
34
|
+
"typebox",
|
|
35
|
+
"zod",
|
|
36
|
+
"barejs",
|
|
37
|
+
"http-server"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@sinclair/typebox": "^0.34.46",
|
|
42
|
+
"zod": "^3.0.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependenciesMeta": {
|
|
45
|
+
"@sinclair/typebox": {
|
|
46
|
+
"optional": true
|
|
47
|
+
},
|
|
48
|
+
"zod": {
|
|
49
|
+
"optional": true
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@sinclair/typebox": "^0.34.46",
|
|
54
|
+
"zod": "^3.23.8",
|
|
55
|
+
"@types/bun": "latest",
|
|
56
|
+
"typescript": "latest",
|
|
57
|
+
"mitata": "^1.0.34",
|
|
58
|
+
"elysia": "latest",
|
|
59
|
+
"hono": "latest"
|
|
60
|
+
},
|
|
61
|
+
"files": [
|
|
62
|
+
"dist",
|
|
63
|
+
"src",
|
|
64
|
+
"README.md",
|
|
65
|
+
"LICENSE"
|
|
66
|
+
]
|
|
67
|
+
}
|
package/src/auth.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Next } from './context';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* UTILS: Internal Crypto Helpers using Native Web Crypto
|
|
5
|
+
* This approach is Type-safe and highly compatible with Bun
|
|
6
|
+
*/
|
|
7
|
+
const encoder = new TextEncoder();
|
|
8
|
+
|
|
9
|
+
const signData = async (data: string, secret: string): Promise<string> => {
|
|
10
|
+
// Using Bun.crypto.hmac with 'any' cast to bypass version-specific type issues
|
|
11
|
+
const hmac = (Bun as any).crypto.hmac("sha256", secret, data);
|
|
12
|
+
return Buffer.from(hmac).toString("hex");
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const verifyData = async (data: string, signature: string, secret: string): Promise<boolean> => {
|
|
16
|
+
const expectedSignature = await signData(data, secret);
|
|
17
|
+
// Using crypto.subtle.timingSafeEqual or Bun's native timingSafeEqual
|
|
18
|
+
return (Bun as any).crypto.timingSafeEqual(
|
|
19
|
+
encoder.encode(signature),
|
|
20
|
+
encoder.encode(expectedSignature)
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 1. BARE TOKEN AUTH (High Performance JWT-like)
|
|
26
|
+
*/
|
|
27
|
+
export const bareAuth = (secret: string) => {
|
|
28
|
+
return async (ctx: any, next: Next) => {
|
|
29
|
+
const authHeader = ctx.req.headers.get('Authorization');
|
|
30
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
31
|
+
return ctx.status(401).json({ status: 'error', message: 'Bearer token required' });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const token = authHeader.split(' ')[1];
|
|
35
|
+
if (!token) return ctx.status(401).json({ status: 'error', message: 'Invalid token format' });
|
|
36
|
+
|
|
37
|
+
const parts = token.split('.');
|
|
38
|
+
if (parts.length !== 2) {
|
|
39
|
+
return ctx.status(401).json({ status: 'error', message: 'Malformed token' });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const [payloadBase64, signature] = parts;
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const payloadRaw = Buffer.from(payloadBase64!, 'base64').toString();
|
|
46
|
+
const isValid = await verifyData(payloadRaw, signature!, secret);
|
|
47
|
+
|
|
48
|
+
if (!isValid) {
|
|
49
|
+
return ctx.status(401).json({ status: 'error', message: 'Invalid signature' });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
ctx.set('user', JSON.parse(payloadRaw));
|
|
53
|
+
return next();
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return ctx.status(401).json({ status: 'error', message: 'Token verification failed' });
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 2. BASIC AUTH
|
|
62
|
+
*/
|
|
63
|
+
export const basicAuth = (credentials: { user: string; pass: string }) => {
|
|
64
|
+
return async (ctx: any, next: Next) => {
|
|
65
|
+
const authHeader = ctx.req.headers.get('Authorization');
|
|
66
|
+
if (!authHeader?.startsWith('Basic ')) {
|
|
67
|
+
return ctx.status(401).json({ status: 'error', message: 'Basic Auth required' });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const token = authHeader.split(' ')[1];
|
|
71
|
+
if (!token) return ctx.status(401).json({ status: 'error', message: 'Missing credentials' });
|
|
72
|
+
|
|
73
|
+
const decoded = Buffer.from(token, 'base64').toString();
|
|
74
|
+
const [user, pass] = decoded.split(':');
|
|
75
|
+
|
|
76
|
+
if (user === credentials.user && pass === credentials.pass) {
|
|
77
|
+
return next();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return ctx.status(401).json({ status: 'error', message: 'Invalid credentials' });
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 3. PASSWORD UTILS (Bun Native)
|
|
86
|
+
*/
|
|
87
|
+
export const Password = {
|
|
88
|
+
hash: (password: string) => Bun.password.hash(password, { algorithm: "argon2id" }),
|
|
89
|
+
verify: (password: string, hash: string) => Bun.password.verify(password, hash)
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 4. TOKEN GENERATOR
|
|
94
|
+
*/
|
|
95
|
+
export const createToken = async (payload: object, secret: string): Promise<string> => {
|
|
96
|
+
const payloadStr = JSON.stringify(payload);
|
|
97
|
+
const payloadBase64 = Buffer.from(payloadStr).toString('base64');
|
|
98
|
+
const signature = await signData(payloadStr, secret);
|
|
99
|
+
return `${payloadBase64}.${signature}`;
|
|
100
|
+
};
|
package/src/bare.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
//
|
|
2
|
-
export * from './context';
|
|
1
|
+
// All comments in English
|
|
2
|
+
export * from './context';
|
|
3
3
|
export * from './validators';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import type {
|
|
5
|
+
import type { Middleware, WSHandlers } from './context';
|
|
6
|
+
import type { Server, ServerWebSocket } from "bun";
|
|
7
7
|
|
|
8
8
|
export interface BarePlugin {
|
|
9
9
|
name: string;
|
|
@@ -12,10 +12,14 @@ export interface BarePlugin {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export class BareJS {
|
|
15
|
-
private routes: Array<{ method: string; path: string; handlers: Array<
|
|
15
|
+
private routes: Array<{ method: string; path: string; handlers: Array<any> }> = [];
|
|
16
16
|
private globalMiddlewares: Array<Middleware> = [];
|
|
17
17
|
private compiledFetch?: Function;
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
private _reusePort: boolean = true;
|
|
20
|
+
// ✅ Explicitly provide the generic argument to satisfy strict TS rules
|
|
21
|
+
private _server: Server<any> | null = null;
|
|
22
|
+
|
|
19
23
|
private staticMap: Map<string, Function> = new Map();
|
|
20
24
|
private dynamicRoutes: Array<{
|
|
21
25
|
m: string;
|
|
@@ -23,39 +27,28 @@ export class BareJS {
|
|
|
23
27
|
p: string[];
|
|
24
28
|
c: Function;
|
|
25
29
|
}> = [];
|
|
26
|
-
|
|
30
|
+
|
|
27
31
|
private wsHandler: { path: string; handlers: WSHandlers } | null = null;
|
|
28
|
-
|
|
29
|
-
public get
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
public
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
public put = (path: string, ...h: Array<Middleware | Handler>) => {
|
|
40
|
-
this.routes.push({ method: "PUT", path, handlers: h });
|
|
41
|
-
return this;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
public patch = (path: string, ...h: Array<Middleware | Handler>) => {
|
|
45
|
-
this.routes.push({ method: "PATCH", path, handlers: h });
|
|
46
|
-
return this;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
public delete = (path: string, ...h: Array<Middleware | Handler>) => {
|
|
50
|
-
this.routes.push({ method: "DELETE", path, handlers: h });
|
|
51
|
-
return this;
|
|
52
|
-
};
|
|
53
|
-
|
|
32
|
+
|
|
33
|
+
public get server(): Server<any> | null { return this._server; }
|
|
34
|
+
public set server(value: Server<any> | null) { this._server = value; }
|
|
35
|
+
|
|
36
|
+
public get = (path: string, ...h: any[]) => { this.routes.push({ method: "GET", path, handlers: h }); return this; };
|
|
37
|
+
public post = (path: string, ...h: any[]) => { this.routes.push({ method: "POST", path, handlers: h }); return this; };
|
|
38
|
+
public put = (path: string, ...h: any[]) => { this.routes.push({ method: "PUT", path, handlers: h }); return this; };
|
|
39
|
+
public patch = (path: string, ...h: any[]) => { this.routes.push({ method: "PATCH", path, handlers: h }); return this; };
|
|
40
|
+
public delete = (path: string, ...h: any[]) => { this.routes.push({ method: "DELETE", path, handlers: h }); return this; };
|
|
41
|
+
|
|
54
42
|
public ws = (path: string, handlers: WSHandlers) => {
|
|
55
43
|
this.wsHandler = { path, handlers };
|
|
56
44
|
return this;
|
|
57
45
|
};
|
|
58
|
-
|
|
46
|
+
|
|
47
|
+
public reusePort = (enabled: boolean) => {
|
|
48
|
+
this._reusePort = enabled;
|
|
49
|
+
return this;
|
|
50
|
+
};
|
|
51
|
+
|
|
59
52
|
public use = (arg: Middleware | BarePlugin) => {
|
|
60
53
|
if (typeof arg === 'object' && 'install' in arg) {
|
|
61
54
|
arg.install(this);
|
|
@@ -64,134 +57,127 @@ export class BareJS {
|
|
|
64
57
|
}
|
|
65
58
|
return this;
|
|
66
59
|
};
|
|
67
|
-
|
|
60
|
+
|
|
68
61
|
private compile() {
|
|
69
62
|
this.staticMap.clear();
|
|
70
63
|
this.dynamicRoutes.length = 0;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const hasGlobal = gLen > 0;
|
|
74
|
-
const rLen = this.routes.length;
|
|
75
|
-
|
|
76
|
-
for (let i = 0; i < rLen; i++) {
|
|
64
|
+
|
|
65
|
+
for (let i = 0; i < this.routes.length; i++) {
|
|
77
66
|
const route = this.routes[i]!;
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
let composed: Function;
|
|
95
|
-
|
|
96
|
-
if (total === 1) {
|
|
97
|
-
const h = handlers[0] || this.globalMiddlewares[0];
|
|
98
|
-
composed = (ctx: BareContext) => {
|
|
99
|
-
const r = h!(ctx, () => ctx._finalize());
|
|
100
|
-
return r instanceof Response ? r : ctx._finalize();
|
|
101
|
-
};
|
|
102
|
-
} else {
|
|
103
|
-
const pipeline = Array(total);
|
|
104
|
-
for (let j = 0; j < gLen; j++) pipeline[j] = this.globalMiddlewares[j];
|
|
105
|
-
for (let j = 0; j < hLen; j++) pipeline[gLen + j] = handlers[j];
|
|
106
|
-
|
|
107
|
-
composed = (ctx: BareContext) => {
|
|
108
|
-
let idx = 0;
|
|
109
|
-
const exec = (): any => {
|
|
110
|
-
if (idx >= total) return ctx._finalize();
|
|
111
|
-
const fn = pipeline[idx++]!;
|
|
112
|
-
const r = fn(ctx, exec);
|
|
113
|
-
if (r instanceof Response) ctx.res = r;
|
|
114
|
-
return ctx.res || (idx >= total ? ctx._finalize() : exec());
|
|
115
|
-
};
|
|
116
|
-
return exec();
|
|
67
|
+
const pipeline = [...this.globalMiddlewares, ...route.handlers];
|
|
68
|
+
const pLen = pipeline.length;
|
|
69
|
+
|
|
70
|
+
const composed = (req: Request, params: Record<string, string>): any => {
|
|
71
|
+
let idx = 0;
|
|
72
|
+
const next = (): any => {
|
|
73
|
+
if (idx < pLen) {
|
|
74
|
+
const result = pipeline[idx++]!(req, params, next);
|
|
75
|
+
if (result && result.constructor === Object) {
|
|
76
|
+
return new Response(JSON.stringify(result), {
|
|
77
|
+
headers: { 'Content-Type': 'application/json' }
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
return new Response(null, { status: 404 });
|
|
117
83
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
84
|
+
return next();
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
if (route.path.indexOf(':') !== -1) {
|
|
121
88
|
const pNames: string[] = [];
|
|
122
|
-
const regexPath = route.path.replace(/:([^/]+)/g, (_, n) => {
|
|
123
|
-
|
|
124
|
-
return "([^/]+)";
|
|
125
|
-
});
|
|
126
|
-
this.dynamicRoutes.push({
|
|
127
|
-
m: route.method,
|
|
128
|
-
r: new RegExp(`^${regexPath}$`),
|
|
129
|
-
p: pNames,
|
|
130
|
-
c: composed
|
|
131
|
-
});
|
|
89
|
+
const regexPath = route.path.replace(/:([^/]+)/g, (_, n) => { pNames.push(n); return "([^/]+)"; });
|
|
90
|
+
this.dynamicRoutes.push({ m: route.method, r: new RegExp(`^${regexPath}$`), p: pNames, c: composed });
|
|
132
91
|
} else {
|
|
133
92
|
this.staticMap.set(route.method + route.path, composed);
|
|
134
93
|
}
|
|
135
94
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const path = i === url.length ? '/' : url.substring(i);
|
|
148
|
-
const key = req.method + path;
|
|
149
|
-
|
|
150
|
-
const runner = sMap.get(key);
|
|
151
|
-
if (runner) {
|
|
152
|
-
const ctx = new BareContext(req);
|
|
153
|
-
return runner(ctx);
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* ✅ Added <any> to Server type in the fetch signature
|
|
98
|
+
*/
|
|
99
|
+
this.compiledFetch = (req: Request, server?: Server<any>): any => {
|
|
100
|
+
if (this.wsHandler && req.headers.get("upgrade") === "websocket") {
|
|
101
|
+
const s = req.url.indexOf('/', 8);
|
|
102
|
+
const path = s === -1 ? '/' : req.url.substring(s);
|
|
103
|
+
if (path === this.wsHandler.path) {
|
|
104
|
+
if (server?.upgrade(req)) return;
|
|
105
|
+
}
|
|
154
106
|
}
|
|
155
|
-
|
|
107
|
+
|
|
108
|
+
const url = req.url;
|
|
109
|
+
const s = url.indexOf('/', 8);
|
|
110
|
+
const path = s === -1 ? '/' : url.substring(s);
|
|
156
111
|
const method = req.method;
|
|
157
|
-
|
|
112
|
+
|
|
113
|
+
const runner = this.staticMap.get(method + path);
|
|
114
|
+
if (runner) return runner(req, {});
|
|
115
|
+
|
|
116
|
+
const dRoutes = this.dynamicRoutes;
|
|
117
|
+
for (let j = 0; j < dRoutes.length; j++) {
|
|
158
118
|
const d = dRoutes[j]!;
|
|
159
119
|
if (d.m === method) {
|
|
160
120
|
const m = d.r.exec(path);
|
|
161
121
|
if (m) {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
ctx.params[d.p[k]!] = m[k + 1]!;
|
|
166
|
-
}
|
|
167
|
-
return d.c(ctx);
|
|
122
|
+
const params: Record<string, string> = {};
|
|
123
|
+
for (let k = 0; k < d.p.length; k++) params[d.p[k]!] = m[k + 1]!;
|
|
124
|
+
return d.c(req, params);
|
|
168
125
|
}
|
|
169
126
|
}
|
|
170
127
|
}
|
|
171
|
-
|
|
172
|
-
return new Response('404', { status: 404 });
|
|
128
|
+
return new Response('404 Not Found', { status: 404 });
|
|
173
129
|
};
|
|
174
130
|
}
|
|
175
|
-
|
|
176
|
-
|
|
131
|
+
|
|
132
|
+
// ✅ Maintain consistency in the public fetch method
|
|
133
|
+
public fetch = (req: Request, server?: Server<any>) => {
|
|
177
134
|
if (!this.compiledFetch) this.compile();
|
|
178
|
-
return this.compiledFetch!(req);
|
|
135
|
+
return this.compiledFetch!(req, server);
|
|
179
136
|
};
|
|
180
|
-
|
|
181
|
-
public listen(
|
|
137
|
+
|
|
138
|
+
public async listen(arg1?: number | string, arg2?: number | string) {
|
|
182
139
|
this.compile();
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
140
|
+
let port = 3000, host = '0.0.0.0';
|
|
141
|
+
|
|
142
|
+
if (typeof arg1 === 'number') {
|
|
143
|
+
port = arg1;
|
|
144
|
+
if (typeof arg2 === 'string') host = arg2;
|
|
145
|
+
} else if (typeof arg1 === 'string') {
|
|
146
|
+
if (!isNaN(Number(arg1))) port = Number(arg1);
|
|
147
|
+
else host = arg1;
|
|
148
|
+
if (typeof arg2 === 'number') port = arg2;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ✅ Cast the configuration to 'any' or explicitly use the correct Bun Options type
|
|
152
|
+
const serveOptions: any = {
|
|
153
|
+
hostname: host,
|
|
154
|
+
port: port,
|
|
155
|
+
reusePort: this._reusePort,
|
|
156
|
+
fetch: (req: Request, server: Server<any>) => this.fetch(req, server),
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
if (this.wsHandler) {
|
|
160
|
+
serveOptions.websocket = {
|
|
161
|
+
open: (ws: ServerWebSocket<any>) => this.wsHandler?.handlers.open?.(ws),
|
|
162
|
+
message: (ws: ServerWebSocket<any>, msg: string | Buffer) => this.wsHandler?.handlers.message?.(ws, msg),
|
|
163
|
+
close: (ws: ServerWebSocket<any>, code: number, res: string) => this.wsHandler?.handlers.close?.(ws, code, res),
|
|
164
|
+
drain: (ws: ServerWebSocket<any>) => this.wsHandler?.handlers.drain?.(ws),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.server = Bun.serve(serveOptions);
|
|
169
|
+
console.log(`[BAREJS] 🚀 Server started at http://${host}:${port}`);
|
|
170
|
+
|
|
171
|
+
const shutdown = () => {
|
|
172
|
+
if (this.server) {
|
|
173
|
+
this.server.stop();
|
|
174
|
+
process.exit(0);
|
|
194
175
|
}
|
|
195
|
-
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
process.on('SIGINT', shutdown);
|
|
179
|
+
process.on('SIGTERM', shutdown);
|
|
180
|
+
|
|
181
|
+
return this.server;
|
|
196
182
|
}
|
|
197
|
-
}
|
|
183
|
+
}
|
package/src/context.ts
CHANGED
|
@@ -1,54 +1,36 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* High-performance Middleware signature
|
|
4
|
+
* Arguments are passed directly to avoid object allocation overhead.
|
|
5
|
+
*/
|
|
6
|
+
export type Middleware = (
|
|
7
|
+
req: Request,
|
|
8
|
+
params: Record<string, string>,
|
|
9
|
+
next: Next
|
|
10
|
+
) => any;
|
|
11
|
+
// All comments in English
|
|
12
|
+
export type Next = () => any;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The Elysia-style Context
|
|
16
|
+
* Extends the native Request with params and high-speed state
|
|
17
|
+
*/
|
|
18
|
+
export type Context = Request & {
|
|
19
|
+
params: Record<string, string>;
|
|
20
|
+
set: (key: string, value: any) => void;
|
|
21
|
+
get: <T>(key: string) => T;
|
|
22
|
+
// Metadata for internal use
|
|
23
|
+
store: Map<string, any>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Unified Handler type for auto-inference
|
|
28
|
+
*/
|
|
29
|
+
export type Handler = (c: Context, next: Next) => any;
|
|
30
|
+
|
|
3
31
|
export interface WSHandlers {
|
|
4
32
|
open?: (ws: any) => void;
|
|
5
33
|
message?: (ws: any, message: string | Buffer) => void;
|
|
6
34
|
close?: (ws: any, code: number, reason: string) => void;
|
|
7
35
|
drain?: (ws: any) => void;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export type Next = () => any;
|
|
11
|
-
export type Middleware = (ctx: Context, next: Next) => any;
|
|
12
|
-
export type Handler = (ctx: Context, next: Next) => any;
|
|
13
|
-
|
|
14
|
-
export interface Context {
|
|
15
|
-
req: Request;
|
|
16
|
-
res?: Response;
|
|
17
|
-
params: Record<string, string>;
|
|
18
|
-
body?: any;
|
|
19
|
-
[key: string]: any;
|
|
20
|
-
json: (data: any) => void;
|
|
21
|
-
status: (code: number) => this;
|
|
22
|
-
setResHeader: (key: string, value: string) => void;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export class BareContext {
|
|
26
|
-
public res?: Response;
|
|
27
|
-
public params: Record<string, string> = {};
|
|
28
|
-
public body: any;
|
|
29
|
-
public _status = 200;
|
|
30
|
-
public _headers: Record<string, string> = {};
|
|
31
|
-
|
|
32
|
-
constructor(public req: Request) {}
|
|
33
|
-
|
|
34
|
-
json(data: any) {
|
|
35
|
-
this._headers["Content-Type"] = "application/json";
|
|
36
|
-
this.res = new Response(JSON.stringify(data), {
|
|
37
|
-
status: this._status,
|
|
38
|
-
headers: this._headers,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
status(code: number) {
|
|
43
|
-
this._status = code;
|
|
44
|
-
return this;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
setResHeader(k: string, v: string) {
|
|
48
|
-
this._headers[k] = v;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
_finalize() {
|
|
52
|
-
return this.res || new Response(null, { status: this._status, headers: this._headers });
|
|
53
|
-
}
|
|
54
|
-
}
|
|
36
|
+
}
|
package/src/cors.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Middleware } from './context';
|
|
2
|
+
|
|
3
|
+
export const cors = (options: { origin?: string; methods?: string } = {}) => {
|
|
4
|
+
const origin = options.origin || '*';
|
|
5
|
+
const methods = options.methods || 'GET,POST,PUT,PATCH,DELETE,OPTIONS';
|
|
6
|
+
|
|
7
|
+
const mw: Middleware = async (req, params, next) => {
|
|
8
|
+
if (req.method === 'OPTIONS') {
|
|
9
|
+
return new Response(null, {
|
|
10
|
+
status: 204,
|
|
11
|
+
headers: {
|
|
12
|
+
'Access-Control-Allow-Origin': origin,
|
|
13
|
+
'Access-Control-Allow-Methods': methods,
|
|
14
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const res = await next();
|
|
20
|
+
if (res instanceof Response) {
|
|
21
|
+
res.headers.set('Access-Control-Allow-Origin', origin);
|
|
22
|
+
res.headers.set('Access-Control-Allow-Methods', methods);
|
|
23
|
+
}
|
|
24
|
+
return res;
|
|
25
|
+
};
|
|
26
|
+
return mw;
|
|
27
|
+
};
|