barejs 0.1.6 → 0.1.7

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 CHANGED
@@ -158,8 +158,8 @@ app.post('/native', native({ properties: { id: { type: 'number' } } }), (ctx) =>
158
158
  * [x] **JIT Static Routing**: lookup via compiled static maps.
159
159
  * [x] **Validation Integration**: Support for TypeBox, Zod, and Native JSON.
160
160
  * [x] **Full Plugin System**: Modular extensibility with zero overhead.
161
- * [ ] **Dynamic Path JIT**: Compiled Regex for parameterized routes (e.g., `/user/:id`).
162
- * [ ] **Native WebSocket Support**: High-speed binary streaming.
161
+ * [x] **Dynamic Path JIT**: Compiled Regex for parameterized routes (e.g., `/user/:id`).
162
+ * [x] **Native WebSocket Support**: High-speed binary streaming.
163
163
 
164
164
  ---
165
165
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "barejs",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "author": "xarhang",
5
5
  "description": "High-performance JIT-specialized web framework for Bun",
6
6
  "main": "./src/bare.ts",
@@ -58,4 +58,4 @@
58
58
  "README.md",
59
59
  "LICENSE"
60
60
  ]
61
- }
61
+ }
package/src/bare.ts CHANGED
@@ -1,14 +1,5 @@
1
- export interface Context {
2
- req: Request;
3
- params: Record<string, string>;
4
- json: (data: any) => Response;
5
- body?: any;
6
- [key: string]: any;
7
- }
8
-
9
- export type Next = () => Promise<any> | any;
10
- export type Middleware = (ctx: Context, next: Next) => any;
11
- export type Handler = (ctx: Context) => any;
1
+ import { BareContext } from './context';
2
+ import type { Context, Middleware, Handler, WSHandlers } from './context';
12
3
 
13
4
  export interface BarePlugin {
14
5
  name: string;
@@ -19,17 +10,35 @@ export interface BarePlugin {
19
10
  export class BareJS {
20
11
  private routes: { method: string; path: string; handlers: (Middleware | Handler)[] }[] = [];
21
12
  private globalMiddlewares: Middleware[] = [];
22
- private compiledFetch?: (req: Request) => Promise<Response> | Response;
23
- private staticMap: Record<string, any> = {};
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
+ }[] = [];
23
+
24
+ private wsHandler: { path: string; handlers: WSHandlers } | null = null;
25
+ private ContextClass = BareContext;
24
26
 
25
- // --- Routing Methods ---
27
+ private errorHandler: (err: any, ctx: Context) => Response = (err) =>
28
+ new Response(JSON.stringify({ error: err.message }), { status: 500 });
29
+
30
+ // --- HTTP Methods ---
26
31
  public get = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "GET", path, handlers: h }); return this; };
27
32
  public post = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "POST", path, handlers: h }); return this; };
28
33
  public put = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "PUT", path, handlers: h }); return this; };
29
34
  public patch = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "PATCH", path, handlers: h }); return this; };
30
35
  public delete = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "DELETE", path, handlers: h }); return this; };
31
36
 
32
- // --- Plugin & Middleware System ---
37
+ public ws = (path: string, handlers: WSHandlers) => {
38
+ this.wsHandler = { path, handlers };
39
+ return this;
40
+ };
41
+
33
42
  public use = (arg: Middleware | BarePlugin) => {
34
43
  if (typeof arg === 'object' && 'install' in arg) {
35
44
  arg.install(this);
@@ -39,90 +48,109 @@ export class BareJS {
39
48
  return this;
40
49
  };
41
50
 
42
- // --- Core Engine ---
43
- public fetch = (req: Request): Promise<Response> | Response => {
44
- if (!this.compiledFetch) this.compile();
45
- return this.compiledFetch!(req);
46
- };
51
+
52
+ private async handleDynamic(req: Request, method: string, path: string): Promise<Response> {
53
+ const dr = this.dynamicRoutes;
54
+ const len = dr.length;
55
+
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
+ }
76
+ }
77
+ }
78
+ return new Response('404 Not Found', { status: 404 });
79
+ }
47
80
 
48
81
  private compile() {
49
- this.routes.forEach((route) => {
50
- // Middleware Onion Runner
51
- const chain = async (ctx: Context) => {
52
- let idx = 0;
53
- const middlewares = [...this.globalMiddlewares, ...route.handlers];
54
- const next = async (): Promise<any> => {
55
- const handler = middlewares[idx++];
56
- if (!handler) return;
57
- // Support both async and sync handlers
58
- return await (handler.length > 1
59
- ? (handler as Middleware)(ctx, next)
60
- : (handler as Handler)(ctx));
61
- };
62
- return await next();
63
- };
64
- this.staticMap[`${route.method}:${route.path}`] = chain;
65
- });
82
+ this.staticMap = {};
83
+ this.dynamicRoutes = [];
66
84
 
67
- // JIT Optimized Fetch Function
68
- const fnBody = `
69
- const staticMap = this.staticMap;
70
- const EMPTY_PARAMS = Object.freeze({});
71
- const jsonHeader = { "content-type": "application/json" };
85
+ for (const route of this.routes) {
86
+ const pipeline = [...this.globalMiddlewares, ...route.handlers];
72
87
 
73
- return async (req) => {
74
- const url = req.url;
75
- const pathStart = url.indexOf('/', 8);
76
- const path = pathStart === -1 ? '/' : url.substring(pathStart);
77
- const key = req.method + ":" + path;
78
-
79
- const runner = staticMap[key];
80
- if (runner) {
81
- const ctx = {
82
- req,
83
- params: EMPTY_PARAMS,
84
- json: (d) => new Response(JSON.stringify(d), { headers: jsonHeader })
85
- };
86
- return await runner(ctx);
87
- }
88
- return new Response('404 Not Found', { status: 404 });
89
- };`;
88
+ // เทคนิค Compose: มัด Middleware จากหลังมาหน้า
89
+ // เพื่อให้ตัวแรกสุดกลายเป็นฟังก์ชันเดียวที่เรียกตัวถัดไปได้ทันที
90
+ let composed = async (ctx: BareContext): Promise<Response> => ctx._finalize();
90
91
 
91
- this.compiledFetch = new Function(fnBody).bind(this)();
92
- }
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();
99
+ };
100
+ }
93
101
 
94
- public listen(ip: string = '0.0.0.0', port: number = 3000) {
95
- this.compile();
102
+ if (route.path.includes(':')) {
103
+ const paramNames: string[] = [];
104
+ const regexPath = route.path.replace(/:([^\/]+)/g, (_, name) => {
105
+ paramNames.push(name);
106
+ return "([^/]+)";
107
+ });
108
+ this.dynamicRoutes.push({
109
+ method: route.method,
110
+ regex: new RegExp(`^${regexPath}$`),
111
+ paramNames,
112
+ chain: composed
113
+ });
114
+ } else {
115
+ this.staticMap[`${route.method}:${route.path}`] = composed;
116
+ }
117
+ }
96
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;
97
127
 
128
+ const runner = staticMap[method + ":" + path];
129
+ if (runner) return await runner(new Ctx(req));
98
130
 
131
+ return await hd(req, method, path);
132
+ }`
133
+ )(this.staticMap, this.handleDynamic.bind(this), this.ContextClass);
134
+ }
99
135
 
100
- const reset = "\x1b[0m";
101
- const cyan = "\x1b[36m";
102
- const yellow = "\x1b[33m";
103
- const gray = "\x1b[90m";
104
- const bold = "\x1b[1m";
105
- if (process.env.NODE_ENV !== 'production' && process.env.BARE_SILENT !== 'true') {
106
- console.log(`
107
- ${cyan}${bold} ____ _ ____
108
- | __ ) __ _ _ __ ___ | / ___|
109
- | _ \\ / _\` | '__/ _ \\ _ | \\___ \\
110
- | |_) | (_| | | | __/| |_| |___) |
111
- |____/ \\__,_|_| \\___| \\___/|____/
112
- ${reset}
113
- ${yellow}BareJS${reset} ${gray}${reset}
114
- ${gray}-----------------------------------${reset}
115
- 🚀 Running at: ${cyan}http://${ip}:${port}${reset}
116
- ${gray}Ready to build everything awesome!${reset}
117
- `);
118
- } else {
136
+ public fetch = (req: Request, server: any = {} as any): Promise<Response> | Response => {
137
+ if (!this.compiledFetch) this.compile();
138
+ return this.compiledFetch!(req, server);
139
+ };
119
140
 
120
- console.log(`🚀 BareJS running at http://${ip}:${port}`);
121
- }
141
+ public listen(ip: string = '0.0.0.0', port: number = 3000) {
142
+ this.compile();
143
+ console.log(`\x1b[32m⚡ BareJS Extreme (2x Optimized) running at http://${ip}:${port}\x1b[0m`);
122
144
  return Bun.serve({
123
145
  hostname: ip,
124
146
  port,
125
- fetch: (req) => this.fetch(req),
147
+ reusePort: true,
148
+ fetch: (req, server) => this.fetch(req, server),
149
+ websocket: {
150
+ open: (ws) => this.wsHandler?.handlers.open?.(ws),
151
+ message: (ws, msg) => this.wsHandler?.handlers.message?.(ws, msg),
152
+ close: (ws, code, res) => this.wsHandler?.handlers.close?.(ws, code, res),
153
+ }
126
154
  });
127
155
  }
128
156
  }
package/src/context.ts ADDED
@@ -0,0 +1,54 @@
1
+ export interface WSHandlers {
2
+ open?: (ws: any) => void;
3
+ message?: (ws: any, message: string | Buffer) => void;
4
+ close?: (ws: any, code: number, reason: string) => void;
5
+ drain?: (ws: any) => void;
6
+ }
7
+
8
+ export type Next = () => Promise<any> | any;
9
+ export type Middleware = (ctx: Context, next: Next) => any;
10
+ export type Handler = (ctx: Context) => any;
11
+
12
+
13
+ export interface Context {
14
+ req: Request;
15
+ res: Response;
16
+ params: Record<string, string>;
17
+ body?: any;
18
+ [key: string]: any;
19
+ json: (data: any) => void;
20
+ status: (code: number) => this;
21
+ setResHeader: (key: string, value: string) => void;
22
+ }
23
+
24
+ export class BareContext {
25
+ public res: Response | undefined;
26
+ public params: Record<string, string> = {};
27
+ public body: any = undefined;
28
+ public _status = 200;
29
+ public _headers: Record<string, string> = {};
30
+
31
+ constructor(public req: Request) {}
32
+
33
+ json(data: any) {
34
+ this._headers["Content-Type"] = "application/json";
35
+ this.res = new Response(JSON.stringify(data), {
36
+ status: this._status,
37
+ headers: this._headers,
38
+ });
39
+ }
40
+
41
+ status(code: number): this {
42
+ this._status = code;
43
+ return this;
44
+ }
45
+
46
+ setResHeader(k: string, v: string) {
47
+ this._headers[k] = v;
48
+ }
49
+
50
+ public _finalize(): Response {
51
+ if (this.res) return this.res;
52
+ return new Response(null, { status: this._status, headers: this._headers });
53
+ }
54
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { BareJS, type Context } from './bare';
2
- import { typebox, native } from './middleware/validators';
1
+ import { BareJS} from './bare';
2
+ import type { Context } from './context';
3
+ import { typebox, native } from './validators';
3
4
  import * as TB from '@sinclair/typebox';
4
5
 
5
6
  const app = new BareJS();
@@ -15,6 +16,7 @@ app.post('/users-tb', typebox(UserSchema), (ctx: Context) => {
15
16
  return ctx.json({ message: "Saved via TypeBox", user: ctx.body });
16
17
  });
17
18
 
19
+
18
20
  // ✅ Route 2: Using Native Validator (Safe alternative if TypeBox has issues)
19
21
  app.post('/users-native', native(UserSchema), (ctx: Context) => {
20
22
  return ctx.json({ message: "Saved via Native", user: ctx.body });
@@ -22,5 +24,16 @@ app.post('/users-native', native(UserSchema), (ctx: Context) => {
22
24
 
23
25
  // ✅ Route 3: No Validator (Pure speed, 0 ns overhead)
24
26
  app.get('/ping', (ctx: Context) => ctx.json({ message: "pong" }));
27
+ // Dynamic Path
28
+ app.get('/user/:id', (ctx: Context) => {
29
+ const userId = ctx.params.id;
30
+ return ctx.json({ user: userId, status: 'active' });
31
+ });
32
+
33
+ // Multiple Params
34
+ app.get('/post/:category/:id', (ctx: Context) => {
35
+ return ctx.json(ctx.params); // { category: 'tech', id: '1' }
36
+ });
37
+
25
38
 
26
39
  app.listen('0.0.0.0', 3000);
@@ -1,12 +1,10 @@
1
1
  import * as Compiler from '@sinclair/typebox/compiler';
2
+ // ใช้ import type สำหรับ Types เพื่อความปลอดภัยของ TS
3
+ import type { Context, Next } from './context';
2
4
 
3
- /**
4
- * 1. TypeBox Validator: Highest Performance (JIT Optimized)
5
- * Best for: Production and Benchmarking
6
- */
7
5
  export const typebox = (schema: any) => {
8
6
  const check = Compiler.TypeCompiler.Compile(schema);
9
- return async (ctx: any, next: any) => {
7
+ return async (ctx: Context, next: Next) => {
10
8
  try {
11
9
  const body = await ctx.req.json();
12
10
  if (!check.Check(body)) return new Response("TypeBox Validation Failed", { status: 400 });
@@ -16,19 +14,15 @@ export const typebox = (schema: any) => {
16
14
  };
17
15
  };
18
16
 
19
- /**
20
- * 2. Native Validator: Zero Dependency
21
- * Best for: Avoiding Runtime bugs or keeping the bundle lightweight.
22
- */
23
17
  export const native = (schema: any) => {
24
18
  const properties = schema.properties || {};
25
19
  const keys = Object.keys(properties);
26
- return async (ctx: any, next: any) => {
20
+ return async (ctx: Context, next: Next) => {
27
21
  try {
28
- const body = await ctx.req.json();
22
+ const body = await ctx.req.json() as any; // Cast เป็น any เพื่อแก้ปัญหา 'unknown'
29
23
  for (const key of keys) {
30
24
  if (typeof body[key] !== properties[key].type)
31
- return new Response(`Native Validation Failed: ${key} is not ${properties[key].type}`, { status: 400 });
25
+ return new Response(`Native Validation Failed: ${key}`, { status: 400 });
32
26
  }
33
27
  ctx.body = body;
34
28
  return next();
@@ -36,12 +30,8 @@ export const native = (schema: any) => {
36
30
  };
37
31
  };
38
32
 
39
- /**
40
- * 3. Zod Validator: Best Developer Experience
41
- * Note: Requires 'npm install zod'
42
- */
43
33
  export const zod = (schema: any) => {
44
- return async (ctx: any, next: any) => {
34
+ return async (ctx: Context, next: Next) => {
45
35
  try {
46
36
  const body = await ctx.req.json();
47
37
  const result = schema.safeParse(body);
package/src/types.ts DELETED
@@ -1,24 +0,0 @@
1
- // src/context.ts
2
- export class Context {
3
- public res: Response;
4
-
5
- constructor(public req: Request) {
6
- this.res = new Response("Not Found", { status: 404 });
7
- }
8
-
9
- json(data: any) {
10
- this.res = new Response(JSON.stringify(data), {
11
- headers: { "Content-Type": "application/json" },
12
- });
13
- }
14
-
15
-
16
- setResHeader(key: string, value: string) {
17
- const newHeaders = new Headers(this.res.headers);
18
- newHeaders.set(key, value);
19
- this.res = new Response(this.res.body, {
20
- status: this.res.status,
21
- headers: newHeaders,
22
- });
23
- }
24
- }