hanni 1.0.6 → 1.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hanni",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Minimalist Bun web framework with built-in Swagger UI",
5
5
  "type": "module",
6
6
  "main": "./src/hanni.js",
package/src/hanni.js CHANGED
@@ -1,83 +1,87 @@
1
- import { Router as _Router } from './router.js'
2
- import { createContext } from './context.js'
3
- import { buildSpec, swaggerHTML } from './swagger.js'
4
- import { compose } from './middleware.js'
5
- import { parseBody, corsHeaders } from './utils.js'
1
+ import { Router as _Router } from './router.js';
2
+ import { createContext } from './context.js';
3
+ import { buildSpec, swaggerHTML } from './swagger.js';
4
+ import { compose, Static as _Static } from './middleware.js';
5
+ import { parseBody, corsHeaders } from './utils.js';
6
6
 
7
- export { _Router as Router }
7
+ export { _Router as Router, _Static as Static };
8
8
 
9
9
  export function Hanni(config = {}) {
10
- const router = new _Router()
11
- const middlewares = []
12
- const mounts = []
13
- const swaggerCfg = config.swagger
10
+ const router = new _Router();
11
+ const middlewares = [];
12
+ const mounts = [];
13
+ const swaggerCfg = config.swagger;
14
14
 
15
- const app = {}
15
+ const app = {};
16
16
 
17
- const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS', 'ALL']
17
+ const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS', 'ALL'];
18
18
  for (const m of methods) {
19
19
  app[m.toLowerCase()] = (path, handler, meta) => {
20
- router.add(m, path, handler, meta)
21
- return app
22
- }
20
+ router.add(m, path, handler, meta);
21
+ return app;
22
+ };
23
23
  }
24
24
 
25
25
  app.use = (arg1, arg2) => {
26
26
  if (typeof arg1 === 'string' && arg2 && arg2.list) {
27
- mounts.push({ path: arg1, router: arg2 })
27
+ mounts.push({ path: arg1, router: arg2 });
28
28
  } else if (typeof arg1 === 'function') {
29
- middlewares.push(arg1)
29
+ middlewares.push(arg1);
30
30
  }
31
- return app
32
- }
31
+ return app;
32
+ };
33
33
 
34
- app.listen = port => {
34
+ app.listen = (port) => {
35
35
  Bun.serve({
36
36
  port,
37
37
  async fetch(req) {
38
- const url = new URL(req.url)
38
+ const url = new URL(req.url);
39
39
 
40
40
  if (swaggerCfg) {
41
- const base = swaggerCfg.path
41
+ const base = swaggerCfg.path;
42
42
  if (url.pathname === base || url.pathname === base + '/') {
43
- return new Response(swaggerHTML(base + '/json', swaggerCfg), { headers: { 'Content-Type': 'text/html' } })
43
+ return new Response(swaggerHTML(base + '/json', swaggerCfg), { headers: { 'Content-Type': 'text/html' } });
44
44
  }
45
45
  if (url.pathname === base + '/json') {
46
46
  const allRoutes = [
47
47
  ...router.list(),
48
48
  ...mounts.flatMap(mount => mount.router.list().map(r => ({ ...r, path: mount.path + r.path })))
49
- ]
50
- return Response.json(buildSpec(allRoutes, swaggerCfg))
49
+ ];
50
+ return Response.json(buildSpec(allRoutes, swaggerCfg));
51
51
  }
52
52
  }
53
53
 
54
- if (config.cors && req.method === 'OPTIONS') return new Response(null, { headers: corsHeaders() })
54
+ if (config.cors && req.method === 'OPTIONS') {
55
+ return new Response(null, { headers: corsHeaders() });
56
+ }
55
57
 
56
- let match = router.match(req.method, url.pathname)
58
+ let match = router.match(req.method, url.pathname);
57
59
  if (!match) {
58
60
  for (const mount of mounts) {
59
61
  if (url.pathname.startsWith(mount.path)) {
60
- const subPath = url.pathname.slice(mount.path.length) || '/'
61
- match = mount.router.match(req.method, subPath)
62
- if (match) break
62
+ const subPath = url.pathname.slice(mount.path.length) || '/';
63
+ match = mount.router.match(req.method, subPath);
64
+ if (match) break;
63
65
  }
64
66
  }
65
67
  }
66
68
 
67
- if (!match) return new Response('Not Found', { status: 404 })
69
+ if (!match) return new Response('Not Found', { status: 404 });
68
70
 
69
- const ctx = createContext(req, match.params)
70
- ctx.body = await parseBody(req)
71
+ const ctx = createContext(req, match.params);
72
+ ctx.body = await parseBody(req);
71
73
 
72
- const handler = compose(middlewares, match.handler)
73
- const res = await handler(ctx)
74
+ const handler = compose(middlewares, match.handler);
75
+ const res = await handler(ctx);
74
76
 
75
- if (config.cors) Object.entries(corsHeaders()).forEach(([k, v]) => res.headers.set(k, v))
77
+ if (config.cors) {
78
+ Object.entries(corsHeaders()).forEach(([k, v]) => res.headers.set(k, v));
79
+ }
76
80
 
77
- return res
81
+ return res;
78
82
  }
79
- })
80
- }
83
+ });
84
+ };
81
85
 
82
- return app
86
+ return app;
83
87
  }
package/src/middleware.js CHANGED
@@ -1,13 +1,44 @@
1
+ import { existsSync } from "fs";
2
+ import { join } from "path";
3
+
4
+ export function Static({ path: urlPath, folder }) {
5
+ return async function (ctx, next) {
6
+ const reqPath = ctx.req.url;
7
+ if (!reqPath.startsWith(urlPath)) return next();
8
+
9
+ const filePath = join(folder, reqPath.slice(urlPath.length));
10
+ if (!existsSync(filePath)) {
11
+ ctx.res.status = 404;
12
+ ctx.res.body = "File not found";
13
+ return;
14
+ }
15
+
16
+ const file = Bun.file(filePath);
17
+ ctx.res.body = file;
18
+ ctx.res.headers.set("Content-Type", getContentType(filePath));
19
+ };
20
+ }
21
+
22
+ function getContentType(filePath) {
23
+ if (filePath.endsWith(".html")) return "text/html";
24
+ if (filePath.endsWith(".css")) return "text/css";
25
+ if (filePath.endsWith(".js")) return "application/javascript";
26
+ if (filePath.endsWith(".json")) return "application/json";
27
+ if (filePath.endsWith(".png")) return "image/png";
28
+ if (filePath.endsWith(".jpg") || filePath.endsWith(".jpeg")) return "image/jpeg";
29
+ return "text/plain";
30
+ }
31
+
1
32
  export function compose(middlewares, handler) {
2
33
  return function (ctx) {
3
- let i = -1
4
- const dispatch = idx => {
5
- if (idx <= i) return Promise.reject()
6
- i = idx
7
- const fn = idx === middlewares.length ? handler : middlewares[idx]
8
- if (!fn) return Promise.resolve()
9
- return Promise.resolve(fn(ctx, () => dispatch(idx + 1)))
10
- }
11
- return dispatch(0)
12
- }
34
+ let i = -1;
35
+ const dispatch = (idx) => {
36
+ if (idx <= i) return Promise.reject();
37
+ i = idx;
38
+ const fn = idx === middlewares.length ? handler : middlewares[idx];
39
+ if (!fn) return Promise.resolve();
40
+ return Promise.resolve(fn(ctx, () => dispatch(idx + 1)));
41
+ };
42
+ return dispatch(0);
43
+ };
13
44
  }