bareserver 0.0.0-stage → 0.1.1

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.
Files changed (4) hide show
  1. package/index.d.ts +118 -0
  2. package/index.js +656 -0
  3. package/package.json +43 -4
  4. package/README.md +0 -3
package/index.d.ts ADDED
@@ -0,0 +1,118 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import { Readable } from 'node:stream';
3
+
4
+ export interface CookieOptions {
5
+ maxAge?: number;
6
+ domain?: string;
7
+ path?: string;
8
+ expires?: Date;
9
+ httpOnly?: boolean;
10
+ secure?: boolean;
11
+ sameSite?: boolean | 'lax' | 'strict' | 'none';
12
+ }
13
+
14
+ export interface BareRequest extends Readable {
15
+ method: string;
16
+ path: string;
17
+ url: string;
18
+ originalUrl: string;
19
+ baseUrl: string;
20
+ query: Record<string, string>;
21
+ params: Record<string, string>;
22
+ headers: Record<string, string | undefined>;
23
+ cookies: Record<string, string>;
24
+ ip: string;
25
+ ips: string[];
26
+ protocol: string;
27
+ secure: boolean;
28
+ hostname: string;
29
+ get(header: string): string | undefined;
30
+ header(header: string): string | undefined;
31
+ is(type: string): boolean;
32
+ body: any;
33
+ [key: string]: any;
34
+ }
35
+
36
+ export interface BareResponse {
37
+ statusCode: number;
38
+ headersSent: boolean;
39
+ status(code: number): this;
40
+ sendStatus(code: number): void;
41
+ set(field: string, val: string | number | string[]): this;
42
+ set(fields: Record<string, string | number | string[]>): this;
43
+ setHeader(field: string, val: string | number | string[]): this;
44
+ getHeader(field: string): any;
45
+ getHeaders(): Record<string, any>;
46
+ hasHeader(field: string): boolean;
47
+ removeHeader(field: string): this;
48
+ writeHead(statusCode: number, headers?: Record<string, any>): this;
49
+ redirect(url: string): void;
50
+ redirect(status: number, url: string): void;
51
+ cookie(name: string, val: string, options?: CookieOptions): this;
52
+ clearCookie(name: string, options?: CookieOptions): this;
53
+ type(type: string): this;
54
+ append(field: string, val: string | string[]): this;
55
+ send(body?: string | Buffer | object | null): void;
56
+ json(body: any): void;
57
+ end(data?: string | Buffer): void;
58
+ }
59
+
60
+ export type NextFunction = (err?: any) => void;
61
+ export type MiddlewareHandler = (req: BareRequest, res: BareResponse, next: NextFunction) => void;
62
+ export type ErrorHandler = (err: any, req: BareRequest, res: BareResponse, next: NextFunction) => void;
63
+ export type RouteHandler = (req: BareRequest, res: BareResponse) => void;
64
+ export type Handler = MiddlewareHandler | ErrorHandler | RouteHandler;
65
+
66
+ export interface BareServerOptions {
67
+ port?: number;
68
+ maxBodySize?: number;
69
+ keepAlive?: boolean;
70
+ }
71
+
72
+ export interface Router {
73
+ use(prefix: string, ...handlers: (Handler | Router)[]): this;
74
+ use(...handlers: (Handler | Router)[]): this;
75
+ get(path: string, ...handlers: Handler[]): this;
76
+ post(path: string, ...handlers: Handler[]): this;
77
+ put(path: string, ...handlers: Handler[]): this;
78
+ delete(path: string, ...handlers: Handler[]): this;
79
+ patch(path: string, ...handlers: Handler[]): this;
80
+ all(path: string, ...handlers: Handler[]): this;
81
+ }
82
+
83
+ export class BareServer {
84
+ port: number;
85
+ constructor(options?: BareServerOptions | number);
86
+ cors(): this;
87
+ static(path: string, body: any): this;
88
+ static(path: string, status: number, body: any): this;
89
+ use(prefix: string, ...handlers: (Handler | Router)[]): this;
90
+ use(...handlers: (Handler | Router)[]): this;
91
+ get(path: string, ...handlers: Handler[]): this;
92
+ post(path: string, ...handlers: Handler[]): this;
93
+ put(path: string, ...handlers: Handler[]): this;
94
+ delete(path: string, ...handlers: Handler[]): this;
95
+ patch(path: string, ...handlers: Handler[]): this;
96
+ all(path: string, ...handlers: Handler[]): this;
97
+ listen(port?: number, callback?: (url: string) => void): Promise<string>;
98
+ listen(callback?: (url: string) => void): Promise<string>;
99
+ setBodyLimit(bytes: number): this;
100
+ setKeepAlive(enabled: boolean): this;
101
+ close(callback?: (err?: Error | null) => void): Promise<void>;
102
+ }
103
+
104
+ export interface ExpressApp extends BareServer {}
105
+
106
+ declare function express(options?: BareServerOptions | number): ExpressApp;
107
+
108
+ declare namespace express {
109
+ export const BareServer: typeof import('./index.js').BareServer;
110
+ export function Router(): Router;
111
+ export function json(): MiddlewareHandler;
112
+ export function urlencoded(options?: any): MiddlewareHandler;
113
+ export function raw(): MiddlewareHandler;
114
+ export function text(): MiddlewareHandler;
115
+ }
116
+
117
+ export default express;
118
+ export { express, Router };
package/index.js ADDED
@@ -0,0 +1,656 @@
1
+ const { Readable } = require('node:stream');
2
+ const { STATUS_CODES } = require('node:http');
3
+
4
+ let native;
5
+ try {
6
+ native = require('./index.node');
7
+ } catch (e) {
8
+ const { platform, arch } = process;
9
+ const candidates = [];
10
+ if (platform === 'darwin') {
11
+ candidates.push(`bareserver-darwin-${arch}`, `@bareserver/darwin-${arch}`);
12
+ } else if (platform === 'win32') {
13
+ candidates.push(`bareserver-win32-${arch}-msvc`, `@bareserver/win32-${arch}-msvc`);
14
+ } else if (platform === 'linux') {
15
+ candidates.push(`bareserver-linux-${arch}-gnu`, `@bareserver/linux-${arch}-gnu`);
16
+ }
17
+
18
+ for (const pkg of candidates) {
19
+ try {
20
+ native = require(pkg);
21
+ if (native) break;
22
+ } catch {}
23
+ }
24
+
25
+ if (!native) {
26
+ throw new Error(`Failed to load bareserver native binary for ${platform}-${arch}: ${e.message}`);
27
+ }
28
+ }
29
+
30
+ class BareRequest extends Readable {
31
+ constructor(raw) {
32
+ super();
33
+ this._raw = raw;
34
+ this._headers = null;
35
+ this._rawBody = raw.rawBody;
36
+ this._parsedBody = undefined;
37
+ this._cookies = undefined;
38
+ this._streamEnded = false;
39
+ this.method = raw.method;
40
+ this.path = raw.path;
41
+ this.url = raw.query ? `${raw.path}?${raw.query}` : raw.path;
42
+ this.originalUrl = this.url;
43
+ this.baseUrl = '';
44
+ this.query = raw.query ? Object.fromEntries(new URLSearchParams(raw.query)) : {};
45
+ const params = raw.params || {};
46
+ if (raw.params) {
47
+ for (const k in raw.params) {
48
+ const val = raw.params[k];
49
+ if (typeof val === 'string' && val.includes('%')) {
50
+ try {
51
+ params[k] = decodeURIComponent(val);
52
+ } catch {
53
+ // Keep raw if invalid encoding
54
+ }
55
+ }
56
+ }
57
+ }
58
+ this.params = params;
59
+ }
60
+
61
+ _read() {
62
+ if (!this._streamEnded) {
63
+ this._streamEnded = true;
64
+ if (this._rawBody && this._rawBody.length > 0) {
65
+ this.push(this._rawBody);
66
+ }
67
+ this.push(null);
68
+ }
69
+ }
70
+
71
+ get headers() {
72
+ if (this._headers !== null) return this._headers;
73
+ const obj = {};
74
+ const arr = this._raw.rawHeaders;
75
+ if (arr) {
76
+ for (let i = 0; i < arr.length; i += 2) {
77
+ obj[arr[i].toLowerCase()] = arr[i + 1];
78
+ }
79
+ }
80
+ return (this._headers = obj);
81
+ }
82
+
83
+ get(field) {
84
+ const lower = field.toLowerCase();
85
+ const arr = this._raw.rawHeaders;
86
+ if (!arr) return undefined;
87
+ for (let i = 0; i < arr.length; i += 2) {
88
+ if (arr[i].toLowerCase() === lower) return arr[i + 1];
89
+ }
90
+ return undefined;
91
+ }
92
+
93
+ header(field) {
94
+ return this.get(field);
95
+ }
96
+
97
+ get body() {
98
+ if (this._parsedBody !== undefined) return this._parsedBody;
99
+ if (this._rawBody && this._rawBody.length > 0) {
100
+ const ct = this.get('content-type') || '';
101
+ if (ct.includes('application/json')) {
102
+ try {
103
+ return (this._parsedBody = JSON.parse(this._rawBody.toString('utf8')));
104
+ } catch {
105
+ return (this._parsedBody = this._rawBody);
106
+ }
107
+ }
108
+ return (this._parsedBody = this._rawBody);
109
+ }
110
+ return (this._parsedBody = {});
111
+ }
112
+
113
+ get cookies() {
114
+ if (this._cookies !== undefined) return this._cookies;
115
+ const cookieHeader = this.get('cookie');
116
+ if (!cookieHeader) return (this._cookies = {});
117
+ const parsed = {};
118
+ const pairs = cookieHeader.split(';');
119
+ for (let i = 0; i < pairs.length; i++) {
120
+ const pair = pairs[i].trim();
121
+ const eqIdx = pair.indexOf('=');
122
+ if (eqIdx !== -1) {
123
+ parsed[decodeURIComponent(pair.slice(0, eqIdx).trim())] = decodeURIComponent(pair.slice(eqIdx + 1).trim());
124
+ }
125
+ }
126
+ return (this._cookies = parsed);
127
+ }
128
+
129
+ set cookies(val) {
130
+ this._cookies = val;
131
+ }
132
+
133
+ get ip() {
134
+ const forwarded = this.get('x-forwarded-for');
135
+ if (forwarded) return forwarded.split(',')[0].trim();
136
+ return '127.0.0.1';
137
+ }
138
+
139
+ get ips() {
140
+ const forwarded = this.get('x-forwarded-for');
141
+ return forwarded ? forwarded.split(',').map((s) => s.trim()) : [];
142
+ }
143
+
144
+ get protocol() {
145
+ return this.get('x-forwarded-proto') || 'http';
146
+ }
147
+
148
+ get secure() {
149
+ return this.protocol === 'https';
150
+ }
151
+
152
+ get hostname() {
153
+ const host = this.get('host') || 'localhost';
154
+ return host.split(':')[0];
155
+ }
156
+
157
+ is(type) {
158
+ const ct = this.get('content-type');
159
+ if (!ct) return false;
160
+ return ct.includes(type);
161
+ }
162
+ }
163
+
164
+ class BareResponse {
165
+ constructor(server, raw) {
166
+ this._server = server;
167
+ this._raw = raw;
168
+ this.statusCode = 200;
169
+ this.contentType = null;
170
+ this.extraHeaders = null;
171
+ this.responded = false;
172
+ }
173
+
174
+ get headersSent() {
175
+ return this.responded;
176
+ }
177
+
178
+ status(code) {
179
+ if (this.responded) return this;
180
+ this.statusCode = code;
181
+ return this;
182
+ }
183
+
184
+ set(field, val) {
185
+ if (this.responded) return this;
186
+ if (typeof field === 'object') {
187
+ for (const [k, v] of Object.entries(field)) {
188
+ this.set(k, v);
189
+ }
190
+ } else {
191
+ const lower = field.toLowerCase();
192
+ if (lower === 'content-type') {
193
+ this.contentType = String(val);
194
+ } else {
195
+ if (!this.extraHeaders) this.extraHeaders = {};
196
+ this.extraHeaders[lower] = Array.isArray(val) ? val.map(String) : String(val);
197
+ }
198
+ }
199
+ return this;
200
+ }
201
+
202
+ setHeader(field, val) {
203
+ return this.set(field, val);
204
+ }
205
+
206
+ getHeader(field) {
207
+ const lower = field.toLowerCase();
208
+ if (lower === 'content-type') return this.contentType;
209
+ return this.extraHeaders ? this.extraHeaders[lower] : undefined;
210
+ }
211
+
212
+ getHeaders() {
213
+ const headers = { ...this.extraHeaders };
214
+ if (this.contentType) headers['content-type'] = this.contentType;
215
+ return headers;
216
+ }
217
+
218
+ hasHeader(field) {
219
+ return this.getHeader(field) !== undefined;
220
+ }
221
+
222
+ removeHeader(field) {
223
+ const lower = field.toLowerCase();
224
+ if (lower === 'content-type') {
225
+ this.contentType = null;
226
+ } else if (this.extraHeaders) {
227
+ delete this.extraHeaders[lower];
228
+ }
229
+ return this;
230
+ }
231
+
232
+ writeHead(statusCode, reasonOrHeaders, maybeHeaders) {
233
+ this.statusCode = statusCode;
234
+ const headers = typeof reasonOrHeaders === 'object' ? reasonOrHeaders : maybeHeaders;
235
+ if (headers) {
236
+ this.set(headers);
237
+ }
238
+ return this;
239
+ }
240
+
241
+ sendStatus(code) {
242
+ const body = STATUS_CODES[code] || String(code);
243
+ this.status(code).type('text/plain').send(body);
244
+ }
245
+
246
+ redirect(statusOrUrl, maybeUrl) {
247
+ let status = 302;
248
+ let url = statusOrUrl;
249
+ if (typeof statusOrUrl === 'number') {
250
+ status = statusOrUrl;
251
+ url = maybeUrl;
252
+ }
253
+ this.status(status);
254
+ this.set('location', url);
255
+ this.send(`Found. Redirecting to ${url}`);
256
+ }
257
+
258
+ cookie(name, val, options = {}) {
259
+ let str = `${encodeURIComponent(name)}=${encodeURIComponent(val)}`;
260
+ if (options.maxAge) str += `; Max-Age=${Math.floor(options.maxAge / 1000)}`;
261
+ if (options.domain) str += `; Domain=${options.domain}`;
262
+ if (options.path) str += `; Path=${options.path}`;
263
+ else str += '; Path=/';
264
+ if (options.expires) str += `; Expires=${options.expires.toUTCString()}`;
265
+ if (options.httpOnly) str += '; HttpOnly';
266
+ if (options.secure) str += '; Secure';
267
+ if (options.sameSite) {
268
+ const ss = typeof options.sameSite === 'string' ? options.sameSite.toLowerCase() : 'strict';
269
+ str += `; SameSite=${ss.charAt(0).toUpperCase() + ss.slice(1)}`;
270
+ }
271
+ const prev = this.getHeader('set-cookie');
272
+ if (!prev) {
273
+ this.set('set-cookie', str);
274
+ } else if (Array.isArray(prev)) {
275
+ this.set('set-cookie', [...prev, str]);
276
+ } else {
277
+ this.set('set-cookie', [prev, str]);
278
+ }
279
+ return this;
280
+ }
281
+
282
+ clearCookie(name, options = {}) {
283
+ return this.cookie(name, '', { ...options, expires: new Date(1), maxAge: 0 });
284
+ }
285
+
286
+ type(type) {
287
+ let ct = type;
288
+ if (!type.includes('/')) {
289
+ if (type === 'json') ct = 'application/json';
290
+ else if (type === 'html') ct = 'text/html; charset=utf-8';
291
+ else if (type === 'text' || type === 'txt') ct = 'text/plain; charset=utf-8';
292
+ else ct = `text/${type}`;
293
+ }
294
+ this.contentType = ct;
295
+ return this;
296
+ }
297
+
298
+ append(field, val) {
299
+ const prev = this.getHeader(field);
300
+ if (!prev) {
301
+ return this.set(field, val);
302
+ }
303
+ if (Array.isArray(prev)) {
304
+ return this.set(field, [...prev, val]);
305
+ }
306
+ return this.set(field, [prev, val]);
307
+ }
308
+
309
+ _getHeadersArray() {
310
+ if (!this.extraHeaders) return null;
311
+ const arr = [];
312
+ for (const k in this.extraHeaders) {
313
+ const v = this.extraHeaders[k];
314
+ if (Array.isArray(v)) {
315
+ for (let i = 0; i < v.length; i++) {
316
+ arr.push(k, v[i]);
317
+ }
318
+ } else {
319
+ arr.push(k, v);
320
+ }
321
+ }
322
+ return arr;
323
+ }
324
+
325
+ send(body) {
326
+ if (this.responded) return;
327
+ this.responded = true;
328
+ const headers = this._getHeadersArray();
329
+
330
+ if (Buffer.isBuffer(body)) {
331
+ this.contentType = this.contentType || 'application/octet-stream';
332
+ this._server.native.respond(this._raw.reqId, this.statusCode, this.contentType, body, headers);
333
+ } else if (typeof body === 'object' && body !== null) {
334
+ this.contentType = this.contentType || 'application/json';
335
+ this._server.native.respond(this._raw.reqId, this.statusCode, this.contentType, JSON.stringify(body), headers);
336
+ } else {
337
+ this.contentType = this.contentType || 'text/plain; charset=utf-8';
338
+ this._server.native.respond(this._raw.reqId, this.statusCode, this.contentType, String(body ?? ''), headers);
339
+ }
340
+ }
341
+
342
+ json(body) {
343
+ if (this.responded) return;
344
+ this.responded = true;
345
+ this.contentType = 'application/json';
346
+ this._server.native.respond(this._raw.reqId, this.statusCode, this.contentType, JSON.stringify(body), this._getHeadersArray());
347
+ }
348
+
349
+ end(data) {
350
+ this.send(data || '');
351
+ }
352
+ }
353
+
354
+ class Router {
355
+ constructor() {
356
+ this.routes = [];
357
+ this.middlewares = [];
358
+ this._mounted = [];
359
+ }
360
+
361
+ use(prefixOrFn, ...handlers) {
362
+ if (typeof prefixOrFn === 'string') {
363
+ const prefix = prefixOrFn;
364
+ for (const h of handlers) {
365
+ if (h instanceof Router) {
366
+ h._mount(this, prefix);
367
+ } else if (typeof h === 'function') {
368
+ this.middlewares.push({ prefix, fn: h });
369
+ }
370
+ }
371
+ } else if (prefixOrFn instanceof Router) {
372
+ prefixOrFn._mount(this, '');
373
+ } else if (typeof prefixOrFn === 'function') {
374
+ const all = [prefixOrFn, ...handlers];
375
+ for (const fn of all) {
376
+ this.middlewares.push({ prefix: '', fn });
377
+ }
378
+ }
379
+ return this;
380
+ }
381
+
382
+ _addRoute(method, path, ...handlers) {
383
+ const handler = handlers.pop();
384
+ const routeMiddlewares = handlers;
385
+ this.routes.push({ method, path, middlewares: routeMiddlewares, handler });
386
+ for (const { target, prefix } of this._mounted) {
387
+ const fullPath = `${prefix}/${path}`.replace(/\/+/g, '/');
388
+ target._addRoute(method, fullPath, ...routeMiddlewares, handler);
389
+ }
390
+ return this;
391
+ }
392
+
393
+ _mount(target, prefix = '') {
394
+ this._mounted.push({ target, prefix });
395
+ for (const r of this.routes) {
396
+ const fullPath = `${prefix}/${r.path}`.replace(/\/+/g, '/');
397
+ const scopedMiddlewares = this.middlewares
398
+ .filter((m) => !m.prefix || fullPath.startsWith(m.prefix))
399
+ .map((m) => m.fn);
400
+ target._addRoute(r.method, fullPath, ...scopedMiddlewares, ...r.middlewares, r.handler);
401
+ }
402
+ }
403
+
404
+ get(path, ...handlers) { return this._addRoute('GET', path, ...handlers); }
405
+ post(path, ...handlers) { return this._addRoute('POST', path, ...handlers); }
406
+ put(path, ...handlers) { return this._addRoute('PUT', path, ...handlers); }
407
+ delete(path, ...handlers) { return this._addRoute('DELETE', path, ...handlers); }
408
+ patch(path, ...handlers) { return this._addRoute('PATCH', path, ...handlers); }
409
+ all(path, ...handlers) {
410
+ for (const m of ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']) {
411
+ this._addRoute(m, path, ...handlers);
412
+ }
413
+ return this;
414
+ }
415
+ }
416
+
417
+ class BareServer {
418
+ constructor(options = {}) {
419
+ const port = typeof options === 'number' ? options : (options.port || 3000);
420
+ const maxBodySize = typeof options === 'object' && options.maxBodySize ? options.maxBodySize : 52428800;
421
+ const keepAlive = typeof options === 'object' && options.keepAlive !== undefined ? options.keepAlive : true;
422
+ this.port = port;
423
+ this.native = new native.NativeServer(this.port, maxBodySize, keepAlive);
424
+ this.middlewares = [];
425
+ this.routes = [];
426
+ this.handlerCount = 0;
427
+ this._pathMiddlewares = [];
428
+
429
+ this.native.setDispatcher((err, raw) => {
430
+ if (err || !raw) return;
431
+ const route = this.routes[raw.handlerId];
432
+ if (!route) {
433
+ this.native.respond(raw.reqId, 404, 'text/plain', 'Not Found');
434
+ return;
435
+ }
436
+
437
+ const req = new BareRequest(raw);
438
+ const res = new BareResponse(this, raw);
439
+
440
+ // Monomorphic fast-path: single handler without middleware
441
+ if (route.chain.length === 1) {
442
+ try {
443
+ const ret = route.handler(req, res);
444
+ if (ret && typeof ret.catch === 'function') {
445
+ ret.catch((e) => {
446
+ if (!res.responded) res.status(500).send(e.message || 'Internal Server Error');
447
+ });
448
+ }
449
+ } catch (e) {
450
+ if (!res.responded) res.status(500).send(e.message || 'Internal Server Error');
451
+ }
452
+ return;
453
+ }
454
+
455
+ // Pre-compiled middleware pipeline with error-handling support
456
+ const chain = route.chain;
457
+ let idx = 0;
458
+
459
+ function next(err) {
460
+ if (idx >= chain.length) {
461
+ if (err && !res.responded) {
462
+ res.status(500).send(err.message || 'Internal Server Error');
463
+ }
464
+ return;
465
+ }
466
+ const fn = chain[idx++];
467
+ let called = false;
468
+ const stepNext = (nextErr) => {
469
+ if (called) return;
470
+ called = true;
471
+ next(nextErr);
472
+ };
473
+
474
+ try {
475
+ let ret;
476
+ if (err) {
477
+ if (fn.length === 4) {
478
+ ret = fn(err, req, res, stepNext);
479
+ } else {
480
+ next(err);
481
+ }
482
+ } else {
483
+ if (fn.length === 4) {
484
+ next();
485
+ } else {
486
+ ret = fn(req, res, stepNext);
487
+ }
488
+ }
489
+ if (ret && typeof ret.catch === 'function') {
490
+ ret.catch(stepNext);
491
+ }
492
+ } catch (e) {
493
+ stepNext(e);
494
+ }
495
+ }
496
+
497
+ next();
498
+ });
499
+ }
500
+
501
+ cors() {
502
+ this.native.enableCors();
503
+ return this;
504
+ }
505
+
506
+ static(path, statusOrBody, maybeBody) {
507
+ let status = 200;
508
+ let body = statusOrBody;
509
+ let contentType = 'text/plain; charset=utf-8';
510
+
511
+ if (typeof statusOrBody === 'number') {
512
+ status = statusOrBody;
513
+ body = maybeBody;
514
+ }
515
+
516
+ let buf;
517
+ if (Buffer.isBuffer(body)) {
518
+ buf = body;
519
+ } else if (typeof body === 'object' && body !== null) {
520
+ contentType = 'application/json';
521
+ buf = Buffer.from(JSON.stringify(body));
522
+ } else {
523
+ buf = Buffer.from(String(body ?? ''));
524
+ }
525
+
526
+ this.native.addStaticRoute('GET', path, status, contentType, buf);
527
+ return this;
528
+ }
529
+
530
+ _buildChain(path, routeMiddlewares, handler) {
531
+ const all = [
532
+ ...this.middlewares,
533
+ ...this._pathMiddlewares.filter((m) => path.startsWith(m.prefix)).map((m) => m.fn),
534
+ ...routeMiddlewares,
535
+ ];
536
+ const normal = all.filter((fn) => fn.length !== 4);
537
+ const errorHandlers = all.filter((fn) => fn.length === 4);
538
+ return [...normal, handler, ...errorHandlers];
539
+ }
540
+
541
+ use(prefixOrFn, ...handlers) {
542
+ if (typeof prefixOrFn === 'string') {
543
+ const prefix = prefixOrFn;
544
+ for (const h of handlers) {
545
+ if (h instanceof Router) {
546
+ h._mount(this, prefix);
547
+ } else if (typeof h === 'function') {
548
+ this._pathMiddlewares.push({ prefix, fn: h });
549
+ }
550
+ }
551
+ } else if (prefixOrFn instanceof Router) {
552
+ prefixOrFn._mount(this, '');
553
+ } else if (typeof prefixOrFn === 'function') {
554
+ const all = [prefixOrFn, ...handlers];
555
+ for (const fn of all) {
556
+ this.middlewares.push(fn);
557
+ }
558
+ // Re-compile chains for existing routes
559
+ for (const route of this.routes) {
560
+ if (route) {
561
+ route.chain = this._buildChain(route.path, route.middlewares, route.handler);
562
+ }
563
+ }
564
+ }
565
+ return this;
566
+ }
567
+
568
+ _addRoute(method, path, ...handlers) {
569
+ const handler = handlers.pop();
570
+ const routeMiddlewares = handlers;
571
+ const handlerId = this.handlerCount++;
572
+
573
+ const normalizedPath = path.replace(/:([a-zA-Z0-9_]+)/g, '{$1}');
574
+ const chain = this._buildChain(path, routeMiddlewares, handler);
575
+
576
+ this.routes[handlerId] = { path, handler, middlewares: routeMiddlewares, chain };
577
+ this.native.addRoute(method, normalizedPath, handlerId);
578
+ return this;
579
+ }
580
+
581
+ get(path, ...handlers) { return this._addRoute('GET', path, ...handlers); }
582
+ post(path, ...handlers) { return this._addRoute('POST', path, ...handlers); }
583
+ put(path, ...handlers) { return this._addRoute('PUT', path, ...handlers); }
584
+ delete(path, ...handlers) { return this._addRoute('DELETE', path, ...handlers); }
585
+ patch(path, ...handlers) { return this._addRoute('PATCH', path, ...handlers); }
586
+ all(path, ...handlers) {
587
+ for (const m of ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']) {
588
+ this._addRoute(m, path, ...handlers);
589
+ }
590
+ return this;
591
+ }
592
+
593
+ async listen(portOrCallback, hostOrCallback, maybeCallback) {
594
+ let port = this.port;
595
+ let callback;
596
+
597
+ if (typeof portOrCallback === 'number') {
598
+ port = portOrCallback;
599
+ this.port = port;
600
+ if (typeof hostOrCallback === 'function') {
601
+ callback = hostOrCallback;
602
+ } else if (typeof maybeCallback === 'function') {
603
+ callback = maybeCallback;
604
+ }
605
+ } else if (typeof portOrCallback === 'function') {
606
+ callback = portOrCallback;
607
+ }
608
+
609
+ const url = await this.native.listen(port);
610
+ if (callback) callback(url);
611
+ return url;
612
+ }
613
+
614
+ setBodyLimit(bytes) {
615
+ this.native.setMaxBodySize(bytes);
616
+ return this;
617
+ }
618
+
619
+ setKeepAlive(enabled) {
620
+ this.native.setKeepAlive(enabled);
621
+ return this;
622
+ }
623
+
624
+ close(callback) {
625
+ const p = this.native.stop();
626
+ if (typeof callback === 'function') {
627
+ p.then(() => callback(null), (err) => callback(err));
628
+ }
629
+ return p;
630
+ }
631
+ }
632
+
633
+ function express(options) {
634
+ return new BareServer(options);
635
+ }
636
+
637
+ express.BareServer = BareServer;
638
+ express.Router = () => new Router();
639
+ express.json = () => (req, res, next) => {
640
+ const ct = req.get('content-type') || '';
641
+ if (ct.includes('application/json') && req._rawBody && req._rawBody.length > 0) {
642
+ try {
643
+ JSON.parse(req._rawBody.toString('utf8'));
644
+ } catch {
645
+ res.status(400).json({ error: 'Invalid JSON payload' });
646
+ return;
647
+ }
648
+ }
649
+ next();
650
+ };
651
+ express.urlencoded = () => (req, res, next) => next();
652
+ express.raw = () => (req, res, next) => next();
653
+ express.text = () => (req, res, next) => next();
654
+ express.default = express;
655
+
656
+ module.exports = express;
package/package.json CHANGED
@@ -1,6 +1,45 @@
1
1
  {
2
2
  "name": "bareserver",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Zero-dependency, native-fast HTTP server for Node.js",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "files": [
8
+ "index.js",
9
+ "index.d.ts",
10
+ "README.md",
11
+ "LICENSE"
12
+ ],
13
+ "optionalDependencies": {
14
+ "bareserver-darwin-arm64": "0.1.1",
15
+ "bareserver-darwin-x64": "0.1.1",
16
+ "bareserver-linux-x64-gnu": "0.1.1",
17
+ "bareserver-win32-x64-msvc": "0.1.1"
18
+ },
19
+ "scripts": {
20
+ "build": "cargo build --release && (cp target/release/libbareserver.dylib index.node 2>/dev/null || cp target/release/libbareserver.so index.node 2>/dev/null || true)",
21
+ "test": "node test.js",
22
+ "test:chaos": "node test_chaos.js"
23
+ },
24
+ "devDependencies": {
25
+ "@hono/node-server": "^2.1.6",
26
+ "autocannon": "^8.0.0",
27
+ "cookie-parser": "^1.4.7",
28
+ "cpeak": "^2.10.0",
29
+ "express": "^4.21.0",
30
+ "fastify": "^5.0.0",
31
+ "helmet": "^8.0.0",
32
+ "hono": "^4.13.17",
33
+ "hyper-express": "^7.0.2",
34
+ "ultimate-express": "^2.2.4"
35
+ },
36
+ "keywords": [
37
+ "http",
38
+ "fastify",
39
+ "express",
40
+ "rust",
41
+ "native"
42
+ ],
43
+ "author": "tarunbtw",
44
+ "license": "MIT"
45
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.