barejs 0.1.7 → 0.1.8

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,6 +1,6 @@
1
1
  {
2
2
  "name": "barejs",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "author": "xarhang",
5
5
  "description": "High-performance JIT-specialized web framework for Bun",
6
6
  "main": "./src/bare.ts",
package/src/bare.ts CHANGED
@@ -1,3 +1,4 @@
1
+ // src/bare.ts
1
2
  import { BareContext } from './context';
2
3
  import type { Context, Middleware, Handler, WSHandlers } from './context';
3
4
 
@@ -8,37 +9,50 @@ export interface BarePlugin {
8
9
  }
9
10
 
10
11
  export class BareJS {
11
- private routes: { method: string; path: string; handlers: (Middleware | Handler)[] }[] = [];
12
- private globalMiddlewares: Middleware[] = [];
13
- private compiledFetch?: (req: Request, server: any) => Promise<Response> | Response;
14
-
15
- private staticMap: Record<string, (ctx: BareContext) => Promise<Response>> = {};
16
-
17
- private dynamicRoutes: {
18
- method: string;
19
- regex: RegExp;
20
- paramNames: string[];
21
- chain: (ctx: BareContext) => Promise<Response>
22
- }[] = [];
12
+ private routes: Array<{ method: string; path: string; handlers: Array<Middleware | Handler> }> = [];
13
+ private globalMiddlewares: Array<Middleware> = [];
14
+ private compiledFetch?: Function;
15
+
16
+ private staticMap: Map<string, Function> = new Map();
17
+ private dynamicRoutes: Array<{
18
+ m: string;
19
+ r: RegExp;
20
+ p: string[];
21
+ c: Function;
22
+ }> = [];
23
23
 
24
24
  private wsHandler: { path: string; handlers: WSHandlers } | null = null;
25
- private ContextClass = BareContext;
26
-
27
- private errorHandler: (err: any, ctx: Context) => Response = (err) =>
28
- new Response(JSON.stringify({ error: err.message }), { status: 500 });
29
-
30
- // --- HTTP Methods ---
31
- public get = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "GET", path, handlers: h }); return this; };
32
- public post = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "POST", path, handlers: h }); return this; };
33
- public put = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "PUT", path, handlers: h }); return this; };
34
- public patch = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "PATCH", path, handlers: h }); return this; };
35
- public delete = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "DELETE", path, handlers: h }); return this; };
36
-
25
+
26
+ public get = (path: string, ...h: Array<Middleware | Handler>) => {
27
+ this.routes.push({ method: "GET", path, handlers: h });
28
+ return this;
29
+ };
30
+
31
+ public post = (path: string, ...h: Array<Middleware | Handler>) => {
32
+ this.routes.push({ method: "POST", path, handlers: h });
33
+ return this;
34
+ };
35
+
36
+ public put = (path: string, ...h: Array<Middleware | Handler>) => {
37
+ this.routes.push({ method: "PUT", path, handlers: h });
38
+ return this;
39
+ };
40
+
41
+ public patch = (path: string, ...h: Array<Middleware | Handler>) => {
42
+ this.routes.push({ method: "PATCH", path, handlers: h });
43
+ return this;
44
+ };
45
+
46
+ public delete = (path: string, ...h: Array<Middleware | Handler>) => {
47
+ this.routes.push({ method: "DELETE", path, handlers: h });
48
+ return this;
49
+ };
50
+
37
51
  public ws = (path: string, handlers: WSHandlers) => {
38
52
  this.wsHandler = { path, handlers };
39
53
  return this;
40
54
  };
41
-
55
+
42
56
  public use = (arg: Middleware | BarePlugin) => {
43
57
  if (typeof arg === 'object' && 'install' in arg) {
44
58
  arg.install(this);
@@ -47,105 +61,129 @@ export class BareJS {
47
61
  }
48
62
  return this;
49
63
  };
50
-
51
64
 
52
- private async handleDynamic(req: Request, method: string, path: string): Promise<Response> {
53
- const dr = this.dynamicRoutes;
54
- const len = dr.length;
65
+ private compile() {
66
+ this.staticMap.clear();
67
+ this.dynamicRoutes.length = 0;
55
68
 
56
- for (let i = 0; i < len; i++) {
57
- const route = dr[i];
58
- if (route && route.method === method) {
59
- const match = path.match(route.regex);
60
- if (match) {
61
- const ctx = new this.ContextClass(req);
62
- const pNames = route.paramNames;
63
-
64
-
65
- for (let j = 0; j < pNames.length; j++) {
66
- const name = pNames[j];
67
- const value = match[j + 1];
68
-
69
-
70
- if (name && value) {
71
- ctx.params[name] = value;
72
- }
73
- }
74
- return await route.chain(ctx);
75
- }
69
+ const gLen = this.globalMiddlewares.length;
70
+ const hasGlobal = gLen > 0;
71
+ const rLen = this.routes.length;
72
+
73
+ for (let i = 0; i < rLen; i++) {
74
+ const route = this.routes[i]!;
75
+ const handlers = route.handlers;
76
+ const hLen = handlers.length;
77
+ const hasDynamic = route.path.indexOf(':') !== -1;
78
+
79
+ // ULTRA FAST PATH: No middleware, single handler, no params
80
+ if (!hasGlobal && hLen === 1 && !hasDynamic) {
81
+ const h = handlers[0]!;
82
+ this.staticMap.set(route.method + route.path, (ctx: BareContext) => {
83
+ const r = h(ctx, () => ctx._finalize());
84
+ return r instanceof Response ? r : ctx._finalize();
85
+ });
86
+ continue;
76
87
  }
77
- }
78
- return new Response('404 Not Found', { status: 404 });
79
- }
80
-
81
- private compile() {
82
- this.staticMap = {};
83
- this.dynamicRoutes = [];
84
-
85
- for (const route of this.routes) {
86
- const pipeline = [...this.globalMiddlewares, ...route.handlers];
87
88
 
88
- // เทคนิค Compose: มัด Middleware จากหลังมาหน้า
89
- // เพื่อให้ตัวแรกสุดกลายเป็นฟังก์ชันเดียวที่เรียกตัวถัดไปได้ทันที
90
- let composed = async (ctx: BareContext): Promise<Response> => ctx._finalize();
91
-
92
- for (let i = pipeline.length - 1; i >= 0; i--) {
93
- const current = pipeline[i];
94
- const nextFn = composed;
95
- composed = async (ctx: BareContext) => {
96
- const res = await (current as any)(ctx, () => nextFn(ctx));
97
- if (res instanceof Response) ctx.res = res;
98
- return ctx.res || ctx._finalize();
89
+ // FAST PATH: Single handler with params OR multiple handlers
90
+ const total = gLen + hLen;
91
+ let composed: Function;
92
+
93
+ if (total === 1) {
94
+ const h = handlers[0] || this.globalMiddlewares[0];
95
+ composed = (ctx: BareContext) => {
96
+ const r = h!(ctx, () => ctx._finalize());
97
+ return r instanceof Response ? r : ctx._finalize();
98
+ };
99
+ } else {
100
+ const pipeline = Array(total);
101
+ for (let j = 0; j < gLen; j++) pipeline[j] = this.globalMiddlewares[j];
102
+ for (let j = 0; j < hLen; j++) pipeline[gLen + j] = handlers[j];
103
+
104
+ composed = (ctx: BareContext) => {
105
+ let idx = 0;
106
+ const exec = (): any => {
107
+ if (idx >= total) return ctx._finalize();
108
+ const fn = pipeline[idx++]!;
109
+ const r = fn(ctx, exec);
110
+ if (r instanceof Response) ctx.res = r;
111
+ return ctx.res || (idx >= total ? ctx._finalize() : exec());
112
+ };
113
+ return exec();
99
114
  };
100
115
  }
101
-
102
- if (route.path.includes(':')) {
103
- const paramNames: string[] = [];
104
- const regexPath = route.path.replace(/:([^\/]+)/g, (_, name) => {
105
- paramNames.push(name);
116
+
117
+ if (hasDynamic) {
118
+ const pNames: string[] = [];
119
+ const regexPath = route.path.replace(/:([^/]+)/g, (_, n) => {
120
+ pNames.push(n);
106
121
  return "([^/]+)";
107
122
  });
108
123
  this.dynamicRoutes.push({
109
- method: route.method,
110
- regex: new RegExp(`^${regexPath}$`),
111
- paramNames,
112
- chain: composed
124
+ m: route.method,
125
+ r: new RegExp(`^${regexPath}$`),
126
+ p: pNames,
127
+ c: composed
113
128
  });
114
129
  } else {
115
- this.staticMap[`${route.method}:${route.path}`] = composed;
130
+ this.staticMap.set(route.method + route.path, composed);
116
131
  }
117
132
  }
118
-
119
- // JIT: ใช้พารามิเตอร์แบบกระจายเพื่อลด Overhead ของ Scope
120
- this.compiledFetch = new Function(
121
- 'staticMap', 'hd', 'Ctx',
122
- `return async (req, server) => {
123
- const url = req.url;
124
- const start = url.indexOf('/', 8);
125
- const path = start === -1 ? '/' : url.substring(start);
126
- const method = req.method;
127
-
128
- const runner = staticMap[method + ":" + path];
129
- if (runner) return await runner(new Ctx(req));
130
-
131
- return await hd(req, method, path);
132
- }`
133
- )(this.staticMap, this.handleDynamic.bind(this), this.ContextClass);
133
+
134
+ // MAXIMUM JIT: Inline everything, minimal allocations
135
+ const sMap = this.staticMap;
136
+ const dRoutes = this.dynamicRoutes;
137
+ const dLen = dRoutes.length;
138
+
139
+ this.compiledFetch = (req: Request) => {
140
+ const url = req.url;
141
+ let i = url.indexOf('/', 8);
142
+ if (i === -1) i = url.length;
143
+
144
+ const path = i === url.length ? '/' : url.substring(i);
145
+ const key = req.method + path;
146
+
147
+ const runner = sMap.get(key);
148
+ if (runner) {
149
+ const ctx = new BareContext(req);
150
+ return runner(ctx);
151
+ }
152
+
153
+ const method = req.method;
154
+ for (let j = 0; j < dLen; j++) {
155
+ const d = dRoutes[j]!;
156
+ if (d.m === method) {
157
+ const m = d.r.exec(path);
158
+ if (m) {
159
+ const ctx = new BareContext(req);
160
+ const pLen = d.p.length;
161
+ for (let k = 0; k < pLen; k++) {
162
+ ctx.params[d.p[k]!] = m[k + 1]!;
163
+ }
164
+ return d.c(ctx);
165
+ }
166
+ }
167
+ }
168
+
169
+ return new Response('404', { status: 404 });
170
+ };
134
171
  }
135
-
136
- public fetch = (req: Request, server: any = {} as any): Promise<Response> | Response => {
172
+
173
+ public fetch = (req: Request) => {
137
174
  if (!this.compiledFetch) this.compile();
138
- return this.compiledFetch!(req, server);
175
+ return this.compiledFetch!(req);
139
176
  };
140
-
141
- public listen(ip: string = '0.0.0.0', port: number = 3000) {
177
+
178
+ public listen(ip = '0.0.0.0', port = 3000) {
142
179
  this.compile();
143
- console.log(`\x1b[32m⚡ BareJS Extreme (2x Optimized) running at http://${ip}:${port}\x1b[0m`);
180
+ console.log(`\x1b[32m⚡ BareJS MAX at http://${ip}:${port}\x1b[0m`);
181
+
144
182
  return Bun.serve({
145
183
  hostname: ip,
146
184
  port,
147
- reusePort: true,
148
- fetch: (req, server) => this.fetch(req, server),
185
+ reusePort: true,
186
+ fetch: this.fetch.bind(this),
149
187
  websocket: {
150
188
  open: (ws) => this.wsHandler?.handlers.open?.(ws),
151
189
  message: (ws, msg) => this.wsHandler?.handlers.message?.(ws, msg),
@@ -153,4 +191,4 @@ export class BareJS {
153
191
  }
154
192
  });
155
193
  }
156
- }
194
+ }
package/src/context.ts CHANGED
@@ -1,3 +1,5 @@
1
+
2
+ // src/context.ts
1
3
  export interface WSHandlers {
2
4
  open?: (ws: any) => void;
3
5
  message?: (ws: any, message: string | Buffer) => void;
@@ -5,14 +7,13 @@ export interface WSHandlers {
5
7
  drain?: (ws: any) => void;
6
8
  }
7
9
 
8
- export type Next = () => Promise<any> | any;
10
+ export type Next = () => any;
9
11
  export type Middleware = (ctx: Context, next: Next) => any;
10
- export type Handler = (ctx: Context) => any;
11
-
12
+ export type Handler = (ctx: Context, next: Next) => any;
12
13
 
13
14
  export interface Context {
14
15
  req: Request;
15
- res: Response;
16
+ res?: Response;
16
17
  params: Record<string, string>;
17
18
  body?: any;
18
19
  [key: string]: any;
@@ -22,14 +23,14 @@ export interface Context {
22
23
  }
23
24
 
24
25
  export class BareContext {
25
- public res: Response | undefined;
26
+ public res?: Response;
26
27
  public params: Record<string, string> = {};
27
- public body: any = undefined;
28
+ public body: any;
28
29
  public _status = 200;
29
30
  public _headers: Record<string, string> = {};
30
-
31
+
31
32
  constructor(public req: Request) {}
32
-
33
+
33
34
  json(data: any) {
34
35
  this._headers["Content-Type"] = "application/json";
35
36
  this.res = new Response(JSON.stringify(data), {
@@ -37,18 +38,17 @@ export class BareContext {
37
38
  headers: this._headers,
38
39
  });
39
40
  }
40
-
41
- status(code: number): this {
41
+
42
+ status(code: number) {
42
43
  this._status = code;
43
44
  return this;
44
45
  }
45
-
46
+
46
47
  setResHeader(k: string, v: string) {
47
48
  this._headers[k] = v;
48
49
  }
49
-
50
- public _finalize(): Response {
51
- if (this.res) return this.res;
52
- return new Response(null, { status: this._status, headers: this._headers });
50
+
51
+ _finalize() {
52
+ return this.res || new Response(null, { status: this._status, headers: this._headers });
53
53
  }
54
- }
54
+ }
package/src/validators.ts CHANGED
@@ -1,5 +1,5 @@
1
+ // src/validators.ts
1
2
  import * as Compiler from '@sinclair/typebox/compiler';
2
- // ใช้ import type สำหรับ Types เพื่อความปลอดภัยของ TS
3
3
  import type { Context, Next } from './context';
4
4
 
5
5
  export const typebox = (schema: any) => {
@@ -7,26 +7,36 @@ export const typebox = (schema: any) => {
7
7
  return async (ctx: Context, next: Next) => {
8
8
  try {
9
9
  const body = await ctx.req.json();
10
- if (!check.Check(body)) return new Response("TypeBox Validation Failed", { status: 400 });
10
+ if (!check.Check(body)) return new Response("Validation Failed", { status: 400 });
11
11
  ctx.body = body;
12
12
  return next();
13
- } catch { return new Response("Invalid JSON", { status: 400 }); }
13
+ } catch {
14
+ return new Response("Invalid JSON", { status: 400 });
15
+ }
14
16
  };
15
17
  };
16
18
 
17
19
  export const native = (schema: any) => {
18
- const properties = schema.properties || {};
19
- const keys = Object.keys(properties);
20
+ const props = schema.properties || {};
21
+ const keys = Object.keys(props);
22
+ const kLen = keys.length;
23
+
20
24
  return async (ctx: Context, next: Next) => {
21
25
  try {
22
- const body = await ctx.req.json() as any; // Cast เป็น any เพื่อแก้ปัญหา 'unknown'
23
- for (const key of keys) {
24
- if (typeof body[key] !== properties[key].type)
25
- return new Response(`Native Validation Failed: ${key}`, { status: 400 });
26
+ const body = await ctx.req.json() as any;
27
+
28
+ for (let i = 0; i < kLen; i++) {
29
+ const k = keys[i]!;
30
+ if (typeof body[k] !== props[k]?.type) {
31
+ return new Response(`Validation Failed: ${k}`, { status: 400 });
32
+ }
26
33
  }
34
+
27
35
  ctx.body = body;
28
36
  return next();
29
- } catch { return new Response("Invalid JSON", { status: 400 }); }
37
+ } catch {
38
+ return new Response("Invalid JSON", { status: 400 });
39
+ }
30
40
  };
31
41
  };
32
42
 
@@ -38,6 +48,8 @@ export const zod = (schema: any) => {
38
48
  if (!result.success) return new Response(JSON.stringify(result.error), { status: 400 });
39
49
  ctx.body = result.data;
40
50
  return next();
41
- } catch { return new Response("Invalid JSON", { status: 400 }); }
51
+ } catch {
52
+ return new Response("Invalid JSON", { status: 400 });
53
+ }
42
54
  };
43
- };
55
+ };