barejs 0.1.6 → 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/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.8",
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,6 @@
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
+ // src/bare.ts
2
+ import { BareContext } from './context';
3
+ import type { Context, Middleware, Handler, WSHandlers } from './context';
12
4
 
13
5
  export interface BarePlugin {
14
6
  name: string;
@@ -17,19 +9,50 @@ export interface BarePlugin {
17
9
  }
18
10
 
19
11
  export class BareJS {
20
- private routes: { method: string; path: string; handlers: (Middleware | Handler)[] }[] = [];
21
- private globalMiddlewares: Middleware[] = [];
22
- private compiledFetch?: (req: Request) => Promise<Response> | Response;
23
- private staticMap: Record<string, any> = {};
24
-
25
- // --- Routing Methods ---
26
- public get = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "GET", path, handlers: h }); return this; };
27
- public post = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "POST", path, handlers: h }); return this; };
28
- public put = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "PUT", path, handlers: h }); return this; };
29
- public patch = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "PATCH", path, handlers: h }); return this; };
30
- public delete = (path: string, ...h: (Middleware | Handler)[]) => { this.routes.push({ method: "DELETE", path, handlers: h }); return this; };
31
-
32
- // --- Plugin & Middleware System ---
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
+
24
+ private wsHandler: { path: string; handlers: WSHandlers } | null = null;
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
+
51
+ public ws = (path: string, handlers: WSHandlers) => {
52
+ this.wsHandler = { path, handlers };
53
+ return this;
54
+ };
55
+
33
56
  public use = (arg: Middleware | BarePlugin) => {
34
57
  if (typeof arg === 'object' && 'install' in arg) {
35
58
  arg.install(this);
@@ -38,91 +61,134 @@ export class BareJS {
38
61
  }
39
62
  return this;
40
63
  };
41
-
42
- // --- Core Engine ---
43
- public fetch = (req: Request): Promise<Response> | Response => {
44
- if (!this.compiledFetch) this.compile();
45
- return this.compiledFetch!(req);
46
- };
47
-
64
+
48
65
  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
- });
66
-
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" };
66
+ this.staticMap.clear();
67
+ this.dynamicRoutes.length = 0;
68
+
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;
87
+ }
88
+
89
+ // FAST PATH: Single handler with params OR multiple handlers
90
+ const total = gLen + hLen;
91
+ let composed: Function;
72
92
 
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;
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];
78
103
 
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 })
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());
85
112
  };
86
- return await runner(ctx);
113
+ return exec();
114
+ };
115
+ }
116
+
117
+ if (hasDynamic) {
118
+ const pNames: string[] = [];
119
+ const regexPath = route.path.replace(/:([^/]+)/g, (_, n) => {
120
+ pNames.push(n);
121
+ return "([^/]+)";
122
+ });
123
+ this.dynamicRoutes.push({
124
+ m: route.method,
125
+ r: new RegExp(`^${regexPath}$`),
126
+ p: pNames,
127
+ c: composed
128
+ });
129
+ } else {
130
+ this.staticMap.set(route.method + route.path, composed);
131
+ }
132
+ }
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
+ }
87
166
  }
88
- return new Response('404 Not Found', { status: 404 });
89
- };`;
90
-
91
- this.compiledFetch = new Function(fnBody).bind(this)();
167
+ }
168
+
169
+ return new Response('404', { status: 404 });
170
+ };
92
171
  }
93
-
94
- public listen(ip: string = '0.0.0.0', port: number = 3000) {
172
+
173
+ public fetch = (req: Request) => {
174
+ if (!this.compiledFetch) this.compile();
175
+ return this.compiledFetch!(req);
176
+ };
177
+
178
+ public listen(ip = '0.0.0.0', port = 3000) {
95
179
  this.compile();
96
-
97
-
98
-
99
-
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 {
119
-
120
- console.log(`🚀 BareJS running at http://${ip}:${port}`);
121
- }
180
+ console.log(`\x1b[32m⚡ BareJS MAX at http://${ip}:${port}\x1b[0m`);
181
+
122
182
  return Bun.serve({
123
183
  hostname: ip,
124
184
  port,
125
- fetch: (req) => this.fetch(req),
185
+ reusePort: true,
186
+ fetch: this.fetch.bind(this),
187
+ websocket: {
188
+ open: (ws) => this.wsHandler?.handlers.open?.(ws),
189
+ message: (ws, msg) => this.wsHandler?.handlers.message?.(ws, msg),
190
+ close: (ws, code, res) => this.wsHandler?.handlers.close?.(ws, code, res),
191
+ }
126
192
  });
127
193
  }
128
- }
194
+ }
package/src/context.ts ADDED
@@ -0,0 +1,54 @@
1
+
2
+ // src/context.ts
3
+ export interface WSHandlers {
4
+ open?: (ws: any) => void;
5
+ message?: (ws: any, message: string | Buffer) => void;
6
+ close?: (ws: any, code: number, reason: string) => void;
7
+ 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
+ }
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);
@@ -0,0 +1,55 @@
1
+ // src/validators.ts
2
+ import * as Compiler from '@sinclair/typebox/compiler';
3
+ import type { Context, Next } from './context';
4
+
5
+ export const typebox = (schema: any) => {
6
+ const check = Compiler.TypeCompiler.Compile(schema);
7
+ return async (ctx: Context, next: Next) => {
8
+ try {
9
+ const body = await ctx.req.json();
10
+ if (!check.Check(body)) return new Response("Validation Failed", { status: 400 });
11
+ ctx.body = body;
12
+ return next();
13
+ } catch {
14
+ return new Response("Invalid JSON", { status: 400 });
15
+ }
16
+ };
17
+ };
18
+
19
+ export const native = (schema: any) => {
20
+ const props = schema.properties || {};
21
+ const keys = Object.keys(props);
22
+ const kLen = keys.length;
23
+
24
+ return async (ctx: Context, next: Next) => {
25
+ try {
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
+ }
33
+ }
34
+
35
+ ctx.body = body;
36
+ return next();
37
+ } catch {
38
+ return new Response("Invalid JSON", { status: 400 });
39
+ }
40
+ };
41
+ };
42
+
43
+ export const zod = (schema: any) => {
44
+ return async (ctx: Context, next: Next) => {
45
+ try {
46
+ const body = await ctx.req.json();
47
+ const result = schema.safeParse(body);
48
+ if (!result.success) return new Response(JSON.stringify(result.error), { status: 400 });
49
+ ctx.body = result.data;
50
+ return next();
51
+ } catch {
52
+ return new Response("Invalid JSON", { status: 400 });
53
+ }
54
+ };
55
+ };
@@ -1,53 +0,0 @@
1
- import * as Compiler from '@sinclair/typebox/compiler';
2
-
3
- /**
4
- * 1. TypeBox Validator: Highest Performance (JIT Optimized)
5
- * Best for: Production and Benchmarking
6
- */
7
- export const typebox = (schema: any) => {
8
- const check = Compiler.TypeCompiler.Compile(schema);
9
- return async (ctx: any, next: any) => {
10
- try {
11
- const body = await ctx.req.json();
12
- if (!check.Check(body)) return new Response("TypeBox Validation Failed", { status: 400 });
13
- ctx.body = body;
14
- return next();
15
- } catch { return new Response("Invalid JSON", { status: 400 }); }
16
- };
17
- };
18
-
19
- /**
20
- * 2. Native Validator: Zero Dependency
21
- * Best for: Avoiding Runtime bugs or keeping the bundle lightweight.
22
- */
23
- export const native = (schema: any) => {
24
- const properties = schema.properties || {};
25
- const keys = Object.keys(properties);
26
- return async (ctx: any, next: any) => {
27
- try {
28
- const body = await ctx.req.json();
29
- for (const key of keys) {
30
- if (typeof body[key] !== properties[key].type)
31
- return new Response(`Native Validation Failed: ${key} is not ${properties[key].type}`, { status: 400 });
32
- }
33
- ctx.body = body;
34
- return next();
35
- } catch { return new Response("Invalid JSON", { status: 400 }); }
36
- };
37
- };
38
-
39
- /**
40
- * 3. Zod Validator: Best Developer Experience
41
- * Note: Requires 'npm install zod'
42
- */
43
- export const zod = (schema: any) => {
44
- return async (ctx: any, next: any) => {
45
- try {
46
- const body = await ctx.req.json();
47
- const result = schema.safeParse(body);
48
- if (!result.success) return new Response(JSON.stringify(result.error), { status: 400 });
49
- ctx.body = result.data;
50
- return next();
51
- } catch { return new Response("Invalid JSON", { status: 400 }); }
52
- };
53
- };
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
- }