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/package.json CHANGED
@@ -1,61 +1,67 @@
1
- {
2
- "name": "barejs",
3
- "version": "0.1.9",
4
- "author": "xarhang",
5
- "description": "High-performance JIT-specialized web framework for Bun",
6
- "main": "./src/bare.ts",
7
- "types": "./src/bare.ts",
8
- "type": "module",
9
- "exports": {
10
- ".": "./src/bare.ts",
11
- "./middleware": "./src/validators.ts"
12
- },
13
- "sideEffects": false,
14
- "repository": {
15
- "type": "git",
16
- "url": "git+https://github.com/xarhang/barejs.git"
17
- },
18
- "scripts": {
19
- "test": "echo \"Error: no test specified\" && exit 1",
20
- "bench": "bun run benchmarks/index.ts",
21
- "dev": "bun --watch src/bare.ts",
22
- "build": "bun ./scripts/build.ts",
23
- "release": "bun run build && npm version patch && npm publish --access public"
24
- },
25
- "keywords": [
26
- "bun",
27
- "framework",
28
- "fastest",
29
- "jit",
30
- "typebox",
31
- "zod"
32
- ],
33
- "license": "MIT",
34
- "peerDependencies": {
35
- "@sinclair/typebox": "^0.34.46",
36
- "zod": "^3.0.0"
37
- },
38
- "peerDependenciesMeta": {
39
- "@sinclair/typebox": {
40
- "optional": true
41
- },
42
- "zod": {
43
- "optional": true
44
- }
45
- },
46
- "devDependencies": {
47
- "@sinclair/typebox": "^0.34.46",
48
- "zod": "^3.23.8",
49
- "@types/bun": "latest",
50
- "elysia": "^1.4.21",
51
- "hono": "^4.11.3",
52
- "mitata": "^1.0.34",
53
- "typescript": "latest"
54
- },
55
- "files": [
56
- "src",
57
- "dist",
58
- "README.md",
59
- "LICENSE"
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
- // src/bare.ts
2
- export * from './context';
1
+ // All comments in English
2
+ export * from './context';
3
3
  export * from './validators';
4
4
 
5
- import { BareContext } from './context';
6
- import type { Context, Middleware, Handler, WSHandlers } from './context';
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<Middleware | Handler> }> = [];
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 = (path: string, ...h: Array<Middleware | Handler>) => {
30
- this.routes.push({ method: "GET", path, handlers: h });
31
- return this;
32
- };
33
-
34
- public post = (path: string, ...h: Array<Middleware | Handler>) => {
35
- this.routes.push({ method: "POST", path, handlers: h });
36
- return this;
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
- const gLen = this.globalMiddlewares.length;
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 handlers = route.handlers;
79
- const hLen = handlers.length;
80
- const hasDynamic = route.path.indexOf(':') !== -1;
81
-
82
- // ULTRA FAST PATH: No middleware, single handler, no params
83
- if (!hasGlobal && hLen === 1 && !hasDynamic) {
84
- const h = handlers[0]!;
85
- this.staticMap.set(route.method + route.path, (ctx: BareContext) => {
86
- const r = h(ctx, () => ctx._finalize());
87
- return r instanceof Response ? r : ctx._finalize();
88
- });
89
- continue;
90
- }
91
-
92
- // FAST PATH: Single handler with params OR multiple handlers
93
- const total = gLen + hLen;
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
- if (hasDynamic) {
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
- pNames.push(n);
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
- // MAXIMUM JIT: Inline everything, minimal allocations
138
- const sMap = this.staticMap;
139
- const dRoutes = this.dynamicRoutes;
140
- const dLen = dRoutes.length;
141
-
142
- this.compiledFetch = (req: Request) => {
143
- const url = req.url;
144
- let i = url.indexOf('/', 8);
145
- if (i === -1) i = url.length;
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
- for (let j = 0; j < dLen; j++) {
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 ctx = new BareContext(req);
163
- const pLen = d.p.length;
164
- for (let k = 0; k < pLen; k++) {
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
- public fetch = (req: Request) => {
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(ip = '0.0.0.0', port = 3000) {
137
+
138
+ public async listen(arg1?: number | string, arg2?: number | string) {
182
139
  this.compile();
183
- console.log(`\x1b[32m⚡ BareJS MAX at http://${ip}:${port}\x1b[0m`);
184
-
185
- return Bun.serve({
186
- hostname: ip,
187
- port,
188
- reusePort: true,
189
- fetch: this.fetch.bind(this),
190
- websocket: {
191
- open: (ws) => this.wsHandler?.handlers.open?.(ws),
192
- message: (ws, msg) => this.wsHandler?.handlers.message?.(ws, msg),
193
- close: (ws, code, res) => this.wsHandler?.handlers.close?.(ws, code, res),
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
- // src/context.ts
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
+ };