@bejibun/core 0.1.64 → 0.1.66

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/CHANGELOG.md CHANGED
@@ -3,6 +3,51 @@ All notable changes to this project will be documented in this file.
3
3
 
4
4
  ---
5
5
 
6
+ ## [v0.1.66](https://github.com/crenata/bejibun-core/compare/v0.1.65...v0.1.66) - 2025-12-31
7
+
8
+ ### 🩹 Fixes
9
+
10
+ ### 📖 Changes
11
+ #### What's New :
12
+ - Added `Router.resource()`
13
+
14
+ Single line code that automatically generates a full set of CRUD.
15
+
16
+ #### How to use?
17
+ ```ts
18
+ import Router from "@bejibun/core/facades/Router";
19
+ import YourController from "@/app/controllers/YourController";
20
+
21
+ Router.resource("path", YourController);
22
+ Router.resource("path", YourController, {
23
+ only: ["index", "store"] // "index" | "store" | "show" | "update" | "destroy"
24
+ });
25
+ Router.resource("path", YourController, {
26
+ except: ["index", "store"] // "index" | "store" | "show" | "update" | "destroy"
27
+ });
28
+ ```
29
+
30
+ ### ❤️Contributors
31
+ - Havea Crenata ([@crenata](https://github.com/crenata))
32
+
33
+ **Full Changelog**: https://github.com/crenata/bejibun-core/blob/master/CHANGELOG.md
34
+
35
+ ---
36
+
37
+ ## [v0.1.65](https://github.com/crenata/bejibun-core/compare/v0.1.64...v0.1.65) - 2025-12-29
38
+
39
+ ### 🩹 Fixes
40
+ - Router namespace on group - [#6](https://github.com/crenata/bejibun-core/issues/6)
41
+
42
+ ### 📖 Changes
43
+
44
+ ### ❤️Contributors
45
+ - Havea Crenata ([@crenata](https://github.com/crenata))
46
+
47
+ **Full Changelog**: https://github.com/crenata/bejibun-core/blob/master/CHANGELOG.md
48
+
49
+ ---
50
+
6
51
  ## [v0.1.64](https://github.com/crenata/bejibun-core/compare/v0.1.61...v0.1.64) - 2025-12-25
7
52
 
8
53
  ### 🩹 Fixes
@@ -1,7 +1,8 @@
1
1
  import type { TFacilitator, TPaywall, TX402Config } from "@bejibun/x402";
2
2
  import type { IMiddleware } from "../types/middleware";
3
- import type { HandlerType, ResourceAction, RouterGroup } from "../types/router";
3
+ import type { HandlerType, ResourceAction, Route, RouterGroup } from "../types/router";
4
4
  import HttpMethodEnum from "@bejibun/utils/enums/HttpMethodEnum";
5
+ import BaseController from "../bases/BaseController";
5
6
  export interface ResourceOptions {
6
7
  only?: Array<ResourceAction>;
7
8
  except?: Array<ResourceAction>;
@@ -14,21 +15,26 @@ export default class RouterBuilder {
14
15
  middleware(...middlewares: Array<IMiddleware>): RouterBuilder;
15
16
  namespace(baseNamespace: string): RouterBuilder;
16
17
  x402(config?: TX402Config, facilitatorConfig?: TFacilitator, paywallConfig?: TPaywall): RouterBuilder;
17
- group(routes: RouterGroup | Array<RouterGroup>): RouterGroup;
18
- resources(controller: Record<string, HandlerType>, options?: ResourceOptions): RouterGroup;
19
- buildSingle(method: HttpMethodEnum, path: string, handler: string | HandlerType): RouterGroup;
20
- connect(path: string, handler: string | HandlerType): RouterGroup;
21
- delete(path: string, handler: string | HandlerType): RouterGroup;
22
- get(path: string, handler: string | HandlerType): RouterGroup;
23
- head(path: string, handler: string | HandlerType): RouterGroup;
24
- options(path: string, handler: string | HandlerType): RouterGroup;
25
- patch(path: string, handler: string | HandlerType): RouterGroup;
26
- post(path: string, handler: string | HandlerType): RouterGroup;
27
- put(path: string, handler: string | HandlerType): RouterGroup;
28
- trace(path: string, handler: string | HandlerType): RouterGroup;
18
+ group(routes: Route | Array<Route> | RouterGroup): RouterGroup | Array<RouterGroup>;
19
+ resource(path: string, controller: typeof BaseController, options?: ResourceOptions): RouterGroup;
20
+ buildSingle(method: HttpMethodEnum, path: string, handler: string | HandlerType): Route;
21
+ connect(path: string, handler: string | HandlerType): Route;
22
+ delete(path: string, handler: string | HandlerType): Route;
23
+ get(path: string, handler: string | HandlerType): Route;
24
+ head(path: string, handler: string | HandlerType): Route;
25
+ options(path: string, handler: string | HandlerType): Route;
26
+ patch(path: string, handler: string | HandlerType): Route;
27
+ post(path: string, handler: string | HandlerType): Route;
28
+ put(path: string, handler: string | HandlerType): Route;
29
+ trace(path: string, handler: string | HandlerType): Route;
29
30
  match(methods: Array<HttpMethodEnum>, path: string, handler: string | HandlerType): RouterGroup;
30
31
  any(path: string, handler: string | HandlerType): RouterGroup;
32
+ serialize(routes: Route | Array<Route> | RouterGroup | Array<RouterGroup>): RouterGroup;
33
+ private mergeRoutes;
31
34
  private joinPaths;
32
35
  private resolveControllerString;
33
36
  private resolveIncludedActions;
37
+ private hasRaw;
38
+ private isMethodMap;
39
+ private applyGroup;
34
40
  }
@@ -1,5 +1,6 @@
1
1
  import App from "@bejibun/app";
2
- import { isEmpty, isModuleExists } from "@bejibun/utils";
2
+ import Logger from "@bejibun/logger";
3
+ import { defineValue, isEmpty, isModuleExists, isNotEmpty } from "@bejibun/utils";
3
4
  import HttpMethodEnum from "@bejibun/utils/enums/HttpMethodEnum";
4
5
  import Enum from "@bejibun/utils/facades/Enum";
5
6
  import path from "path";
@@ -28,67 +29,124 @@ export default class RouterBuilder {
28
29
  return this;
29
30
  }
30
31
  group(routes) {
31
- const routeList = Array.isArray(routes) ? routes : [routes];
32
- const newRoutes = {};
33
- for (const route of routeList) {
34
- for (const path in route) {
35
- const cleanPath = this.joinPaths(this.basePath, path);
36
- const routeHandlers = route[path];
37
- const wrappedHandlers = {};
38
- for (const method in routeHandlers) {
39
- let handler = routeHandlers[method];
40
- for (const middleware of this.middlewares) {
41
- handler = middleware.handle(handler);
42
- }
43
- wrappedHandlers[method] = handler;
32
+ const rawGroups = [];
33
+ let routeGroups = {};
34
+ if (this.hasRaw(routes)) {
35
+ const routeList = Array.isArray(routes) ? routes.flat() : [routes];
36
+ const routerGroups = routeList.filter((value) => !this.hasRaw(value));
37
+ const rawRoutes = routeList.filter((value) => this.hasRaw(value));
38
+ const newRoutes = {};
39
+ for (const route of rawRoutes) {
40
+ const middlewares = this.middlewares.concat(defineValue(route.raw.middlewares, []));
41
+ const effectiveNamespace = defineValue(this.baseNamespace, route.raw.namespace);
42
+ const cleanPath = this.joinPaths(defineValue(route.raw.prefix, this.basePath), route.raw.path);
43
+ let resolvedHandler = typeof route.raw.handler === "string" ?
44
+ this.resolveControllerString(route.raw.handler, effectiveNamespace) :
45
+ route.raw.handler;
46
+ for (const middleware of [...middlewares].reverse()) {
47
+ resolvedHandler = middleware.handle(resolvedHandler);
44
48
  }
45
49
  if (isEmpty(newRoutes[cleanPath]))
46
50
  newRoutes[cleanPath] = {};
47
- Object.assign(newRoutes[cleanPath], wrappedHandlers);
51
+ Object.assign(newRoutes[cleanPath], {
52
+ [route.raw.method]: resolvedHandler
53
+ });
54
+ route.raw.middlewares = middlewares;
55
+ route.raw.namespace = effectiveNamespace;
56
+ route.raw.path = cleanPath;
57
+ rawGroups.push(route);
48
58
  }
59
+ routeGroups = Object.assign({}, ...routerGroups.map((value) => this.applyGroup(value)), newRoutes);
60
+ }
61
+ if (isNotEmpty(routeGroups))
62
+ return {
63
+ raws: rawGroups,
64
+ routes: routeGroups
65
+ };
66
+ if (isEmpty(routes))
67
+ return {};
68
+ if (Array.isArray(routes)) {
69
+ return routes.map((value) => {
70
+ if (isNotEmpty(value.raws))
71
+ return value.raws;
72
+ return value;
73
+ })
74
+ .flat()
75
+ .map((route) => this.applyGroup(route));
49
76
  }
50
- return newRoutes;
77
+ return this.applyGroup(routes);
51
78
  }
52
- resources(controller, options) {
79
+ resource(path, controller, options) {
80
+ const ClassController = new controller();
81
+ const cleanPath = this.joinPaths(this.basePath, path);
53
82
  const allRoutes = {
54
- "": {
83
+ [cleanPath]: {
55
84
  GET: "index",
56
85
  POST: "store"
57
86
  },
58
- ":id": {
87
+ [`${cleanPath}/:id`]: {
59
88
  GET: "show",
60
89
  PUT: "update",
61
90
  DELETE: "destroy"
62
91
  }
63
92
  };
64
93
  const includedActions = this.resolveIncludedActions(options);
65
- const filteredRoutes = {};
94
+ const raws = [];
95
+ const routes = {};
66
96
  for (const path in allRoutes) {
67
97
  const methods = allRoutes[path];
68
- const methodHandlers = {};
98
+ const methodMap = {};
69
99
  for (const method in methods) {
70
100
  const action = methods[method];
71
- if (includedActions.has(action) && controller[action]) {
72
- methodHandlers[method] = controller[action];
101
+ let handler = ClassController[action];
102
+ if (includedActions.has(action) && isNotEmpty(handler)) {
103
+ raws.push({
104
+ raw: {
105
+ prefix: this.basePath,
106
+ middlewares: [],
107
+ namespace: this.baseNamespace,
108
+ method,
109
+ path,
110
+ handler
111
+ },
112
+ route: {
113
+ [path]: {
114
+ [method]: handler
115
+ }
116
+ }
117
+ });
73
118
  }
74
119
  }
75
- if (Object.keys(methodHandlers).length > 0) {
76
- filteredRoutes[path] = methodHandlers;
120
+ if (Object.keys(methodMap).length > 0) {
121
+ routes[path] = methodMap;
77
122
  }
78
123
  }
79
- return this.group(filteredRoutes);
124
+ return this.applyGroup({
125
+ raws,
126
+ routes
127
+ });
80
128
  }
81
129
  buildSingle(method, path, handler) {
82
130
  const cleanPath = this.joinPaths(this.basePath, path);
83
131
  let resolvedHandler = typeof handler === "string" ?
84
132
  this.resolveControllerString(handler) :
85
133
  handler;
86
- for (const middleware of this.middlewares) {
134
+ for (const middleware of [...this.middlewares].reverse()) {
87
135
  resolvedHandler = middleware.handle(resolvedHandler);
88
136
  }
89
137
  return {
90
- [cleanPath]: {
91
- [method]: resolvedHandler
138
+ raw: {
139
+ prefix: this.basePath,
140
+ middlewares: this.middlewares,
141
+ namespace: this.baseNamespace,
142
+ method,
143
+ path,
144
+ handler
145
+ },
146
+ route: {
147
+ [cleanPath]: {
148
+ [method]: resolvedHandler
149
+ }
92
150
  }
93
151
  };
94
152
  }
@@ -122,7 +180,7 @@ export default class RouterBuilder {
122
180
  match(methods, path, handler) {
123
181
  const routeMap = {};
124
182
  for (const method of methods) {
125
- const single = this.buildSingle(method, path, handler);
183
+ const single = this.buildSingle(method, path, handler).route;
126
184
  const fullPath = Object.keys(single)[0];
127
185
  const handlers = single[fullPath];
128
186
  if (isEmpty(routeMap[fullPath]))
@@ -134,18 +192,60 @@ export default class RouterBuilder {
134
192
  any(path, handler) {
135
193
  return this.match(Enum.setEnums(HttpMethodEnum).toArray().map((value) => value.value), path, handler);
136
194
  }
195
+ serialize(routes) {
196
+ if (Array.isArray(routes)) {
197
+ if (this.hasRaw(routes))
198
+ routes = routes.map((value) => value.route);
199
+ }
200
+ else {
201
+ if (this.hasRaw(routes))
202
+ routes = routes.route;
203
+ }
204
+ const mergedRoutes = this.mergeRoutes(routes);
205
+ if (Array.isArray(mergedRoutes))
206
+ return Object.assign({}, ...mergedRoutes);
207
+ return mergedRoutes;
208
+ }
209
+ mergeRoutes(routes) {
210
+ const merged = {};
211
+ const routeEntries = Array.isArray(routes) ?
212
+ routes :
213
+ Object.entries(routes).map(([path, methods]) => ({
214
+ [path]: methods
215
+ }));
216
+ for (const route of routeEntries) {
217
+ for (const [path, methods] of Object.entries(route)) {
218
+ if (isEmpty(merged[path]))
219
+ merged[path] = {};
220
+ for (const [method, handler] of Object.entries(methods)) {
221
+ if (isNotEmpty(merged[path][method]))
222
+ Logger.setContext("Router").warn(`Duplicate route: ${method} ${path} - overwriting.`);
223
+ merged[path][method] = handler;
224
+ }
225
+ }
226
+ }
227
+ return merged;
228
+ }
137
229
  joinPaths(base, path) {
138
230
  base = base.replace(/\/+$/, "");
139
231
  path = path.replace(/^\/+/, "");
140
232
  return `/${[base, path].filter(Boolean).join("/")}`;
141
233
  }
142
- resolveControllerString(definition) {
234
+ resolveControllerString(definition, overrideNamespace) {
143
235
  const [controllerName, methodName] = definition.split("@");
144
236
  if (isEmpty(controllerName) || isEmpty(methodName)) {
145
237
  throw new RouterException(`Invalid router controller definition: ${definition}.`);
146
238
  }
147
- const controllerPath = path.resolve(App.Path.rootPath(), this.baseNamespace);
148
- const location = Bun.resolveSync(`./${controllerName}.ts`, controllerPath);
239
+ const controllerPath = path.resolve(App.Path.rootPath(), defineValue(overrideNamespace, this.baseNamespace));
240
+ let location = null;
241
+ try {
242
+ location = Bun.resolveSync(`./${controllerName}.ts`, controllerPath);
243
+ }
244
+ catch {
245
+ return async () => {
246
+ throw new RouterException(`Invalid router for controller location [${controllerPath}]`);
247
+ };
248
+ }
149
249
  let ControllerClass;
150
250
  try {
151
251
  ControllerClass = require(location).default;
@@ -180,4 +280,66 @@ export default class RouterBuilder {
180
280
  }
181
281
  return new Set(all);
182
282
  }
283
+ hasRaw(routes) {
284
+ if (Array.isArray(routes))
285
+ return routes.flat().some(route => isNotEmpty(route) && "raw" in route);
286
+ return (isNotEmpty(routes) &&
287
+ typeof routes === "object" &&
288
+ "raw" in routes);
289
+ }
290
+ isMethodMap(value) {
291
+ return (isNotEmpty(value) &&
292
+ typeof value === "object" &&
293
+ Object.values(value).every(v => typeof v === "function"));
294
+ }
295
+ applyGroup(route) {
296
+ if (isEmpty(route))
297
+ return route;
298
+ if (this.hasRaw(route)) {
299
+ const routeList = Array.isArray(route) ? route.flat() : [route];
300
+ const rawRoutes = routeList.filter((value) => this.hasRaw(value));
301
+ const newRoutes = {};
302
+ for (const route of rawRoutes) {
303
+ const middlewares = route.raw.middlewares.concat(defineValue(this.middlewares, []));
304
+ const cleanPath = this.joinPaths(defineValue(route.raw.prefix, this.basePath), route.raw.path);
305
+ const effectiveNamespace = defineValue(this.baseNamespace === "app/controllers" ?
306
+ null :
307
+ this.baseNamespace, route.raw.namespace);
308
+ let resolvedHandler = typeof route.raw.handler === "string" ?
309
+ this.resolveControllerString(route.raw.handler, effectiveNamespace) :
310
+ route.raw.handler;
311
+ for (const middleware of [...middlewares].reverse()) {
312
+ resolvedHandler = middleware.handle(resolvedHandler);
313
+ }
314
+ if (isEmpty(newRoutes[cleanPath]))
315
+ newRoutes[cleanPath] = {};
316
+ Object.assign(newRoutes[cleanPath], {
317
+ [route.raw.method]: resolvedHandler
318
+ });
319
+ }
320
+ return newRoutes;
321
+ }
322
+ const result = {};
323
+ for (const [key, value] of Object.entries(route)) {
324
+ const newKey = key.startsWith("/") ? this.joinPaths(this.basePath, key) : key;
325
+ if (this.isMethodMap(value)) {
326
+ const wrappedMethods = {};
327
+ for (const [method, handler] of Object.entries(value)) {
328
+ let resolvedHandler = handler;
329
+ for (const middleware of [...this.middlewares].reverse()) {
330
+ resolvedHandler = middleware.handle(resolvedHandler);
331
+ }
332
+ wrappedMethods[method] = resolvedHandler;
333
+ }
334
+ result[newKey] = wrappedMethods;
335
+ continue;
336
+ }
337
+ if (isNotEmpty(value) && typeof value === "object") {
338
+ result[newKey] = this.applyGroup(value);
339
+ continue;
340
+ }
341
+ result[newKey] = value;
342
+ }
343
+ return result;
344
+ }
183
345
  }
@@ -1,23 +1,27 @@
1
+ import type { ResourceOptions } from "../builders/RouterBuilder";
1
2
  import type { IMiddleware } from "../types/middleware";
2
3
  import type { HandlerType, RouterGroup } from "../types/router";
3
4
  import HttpMethodEnum from "@bejibun/utils/enums/HttpMethodEnum";
4
- import RouterBuilder, { ResourceOptions } from "../builders/RouterBuilder";
5
+ import BaseController from "../bases/BaseController";
6
+ import RouterBuilder from "../builders/RouterBuilder";
7
+ import { Route } from "../types/router";
5
8
  export default class Router {
6
9
  static prefix(basePath: string): RouterBuilder;
7
10
  static middleware(...middlewares: Array<IMiddleware>): RouterBuilder;
8
11
  static namespace(baseNamespace: string): RouterBuilder;
9
12
  static x402(): RouterBuilder;
10
- static resources(controller: Record<string, HandlerType>, options?: ResourceOptions): RouterGroup;
11
- static group(routes: RouterGroup, prefix?: string, middlewares?: Array<IMiddleware>): RouterGroup;
12
- static connect(path: string, handler: string | HandlerType): RouterGroup;
13
- static delete(path: string, handler: string | HandlerType): RouterGroup;
14
- static get(path: string, handler: string | HandlerType): RouterGroup;
15
- static head(path: string, handler: string | HandlerType): RouterGroup;
16
- static options(path: string, handler: string | HandlerType): RouterGroup;
17
- static patch(path: string, handler: string | HandlerType): RouterGroup;
18
- static post(path: string, handler: string | HandlerType): RouterGroup;
19
- static put(path: string, handler: string | HandlerType): RouterGroup;
20
- static trace(path: string, handler: string | HandlerType): RouterGroup;
13
+ static resource(path: string, controller: typeof BaseController, options?: ResourceOptions): RouterGroup;
14
+ static group(routes: Route | Array<Route> | RouterGroup): RouterGroup | Array<RouterGroup>;
15
+ static connect(path: string, handler: string | HandlerType): Route;
16
+ static delete(path: string, handler: string | HandlerType): Route;
17
+ static get(path: string, handler: string | HandlerType): Route;
18
+ static head(path: string, handler: string | HandlerType): Route;
19
+ static options(path: string, handler: string | HandlerType): Route;
20
+ static patch(path: string, handler: string | HandlerType): Route;
21
+ static post(path: string, handler: string | HandlerType): Route;
22
+ static put(path: string, handler: string | HandlerType): Route;
23
+ static trace(path: string, handler: string | HandlerType): Route;
21
24
  static match(methods: Array<HttpMethodEnum>, path: string, handler: string | HandlerType): RouterGroup;
22
25
  static any(path: string, handler: string | HandlerType): RouterGroup;
26
+ static serialize(routes: Route | Array<Route> | RouterGroup | Array<RouterGroup>): RouterGroup;
23
27
  }
package/facades/Router.js CHANGED
@@ -12,16 +12,11 @@ export default class Router {
12
12
  static x402() {
13
13
  return new RouterBuilder().x402();
14
14
  }
15
- static resources(controller, options) {
16
- return new RouterBuilder().resources(controller, options);
15
+ static resource(path, controller, options) {
16
+ return new RouterBuilder().resource(path, controller, options);
17
17
  }
18
- static group(routes, prefix, middlewares) {
19
- const builder = new RouterBuilder();
20
- if (prefix)
21
- builder.prefix(prefix);
22
- if (middlewares?.length)
23
- builder.middleware(...middlewares);
24
- return builder.group(routes);
18
+ static group(routes) {
19
+ return new RouterBuilder().group(routes);
25
20
  }
26
21
  static connect(path, handler) {
27
22
  return new RouterBuilder().connect(path, handler);
@@ -56,4 +51,7 @@ export default class Router {
56
51
  static any(path, handler) {
57
52
  return new RouterBuilder().any(path, handler);
58
53
  }
54
+ static serialize(routes) {
55
+ return new RouterBuilder().serialize(routes);
56
+ }
59
57
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bejibun/core",
3
- "version": "0.1.64",
3
+ "version": "0.1.66",
4
4
  "author": "Havea Crenata <havea.crenata@gmail.com>",
5
5
  "repository": {
6
6
  "type": "git",
package/server.d.ts CHANGED
@@ -1 +1,6 @@
1
- export {};
1
+ export default class Server {
2
+ private get exceptionHandler();
3
+ private get apiRoutes();
4
+ private get webRoutes();
5
+ run(): void;
6
+ }
package/server.js CHANGED
@@ -1,50 +1,59 @@
1
1
  import App from "@bejibun/app";
2
2
  import Logger from "@bejibun/logger";
3
+ import { defineValue } from "@bejibun/utils";
3
4
  import RuntimeException from "./exceptions/RuntimeException";
4
5
  import Router from "./facades/Router";
5
6
  import MaintenanceMiddleware from "./middlewares/MaintenanceMiddleware";
6
7
  import RateLimiterMiddleware from "./middlewares/RateLimiterMiddleware";
7
8
  import(App.Path.rootPath("bootstrap.ts"));
8
- const exceptionHandlerPath = App.Path.appPath("exceptions/handler.ts");
9
- let ExceptionHandler;
10
- try {
11
- ExceptionHandler = require(exceptionHandlerPath).default;
12
- }
13
- catch (error) {
14
- throw new RuntimeException(`Missing exception handler class [${exceptionHandlerPath}].`, null, error.message);
15
- }
16
- const apiRoutesPath = App.Path.routesPath("api.ts");
17
- let ApiRoutes;
18
- try {
19
- ApiRoutes = require(apiRoutesPath).default;
20
- }
21
- catch (error) {
22
- throw new RuntimeException(`Missing api file on routes directory [${apiRoutesPath}].`, null, error.message);
23
- }
24
- const webRoutesPath = App.Path.routesPath("web.ts");
25
- let WebRoutes;
26
- try {
27
- WebRoutes = require(webRoutesPath).default;
28
- }
29
- catch (error) {
30
- throw new RuntimeException(`Missing web file on routes directory [${webRoutesPath}].`, null, error.message);
31
- }
32
- const server = Bun.serve({
33
- development: Bun.env.NODE_ENV !== "production" && {
34
- // Enable browser hot reloading in development
35
- hmr: true,
36
- // Echo console logs from the browser to the server
37
- console: true
38
- },
39
- error: new ExceptionHandler().handle,
40
- port: Bun.env.APP_PORT,
41
- routes: {
42
- "/": require(App.Path.publicPath("index.html")),
43
- ...Router.middleware(new MaintenanceMiddleware(), new RateLimiterMiddleware()).group([
44
- Router.namespace("app/exceptions").any("/*", "Handler@route"),
45
- ApiRoutes,
46
- WebRoutes
47
- ])
9
+ export default class Server {
10
+ get exceptionHandler() {
11
+ const exceptionHandlerPath = App.Path.appPath("exceptions/handler.ts");
12
+ try {
13
+ return require(exceptionHandlerPath).default;
14
+ }
15
+ catch (error) {
16
+ throw new RuntimeException(`Missing exception handler class [${exceptionHandlerPath}].`, null, error.message);
17
+ }
18
+ }
19
+ get apiRoutes() {
20
+ const apiRoutesPath = App.Path.routesPath("api.ts");
21
+ try {
22
+ return require(apiRoutesPath).default;
23
+ }
24
+ catch (error) {
25
+ throw new RuntimeException(`Missing api file on routes directory [${apiRoutesPath}].`, null, error.message);
26
+ }
27
+ }
28
+ get webRoutes() {
29
+ const webRoutesPath = App.Path.routesPath("web.ts");
30
+ try {
31
+ return require(webRoutesPath).default;
32
+ }
33
+ catch (error) {
34
+ throw new RuntimeException(`Missing web file on routes directory [${webRoutesPath}].`, null, error.message);
35
+ }
48
36
  }
49
- });
50
- Logger.setContext("APP").info(`🚀 Server running at ${server.url.origin}`);
37
+ run() {
38
+ const server = Bun.serve({
39
+ development: Bun.env.NODE_ENV !== "production" && {
40
+ // Enable browser hot reloading in development
41
+ hmr: true,
42
+ // Echo console logs from the browser to the server
43
+ console: true
44
+ },
45
+ error: new this.exceptionHandler().handle,
46
+ port: Bun.env.APP_PORT,
47
+ routes: {
48
+ "/": require(App.Path.publicPath("index.html")),
49
+ ...Object.assign({}, ...defineValue(Router.middleware(new MaintenanceMiddleware(), new RateLimiterMiddleware()).group([
50
+ Router.namespace("app/exceptions").any("/*", "Handler@route"),
51
+ Router.serialize(this.apiRoutes),
52
+ Router.serialize(this.webRoutes)
53
+ ]), []))
54
+ }
55
+ });
56
+ Logger.setContext("APP").info(`🚀 Server running at ${server.url.origin}`);
57
+ }
58
+ }
59
+ new Server().run();
package/types/router.d.ts CHANGED
@@ -1,3 +1,18 @@
1
+ import {IMiddleware} from "../types/middleware";
2
+
1
3
  export type HandlerType = (request: Bun.BunRequest, server: Bun.Server) => Promise<Response>;
2
- export type RouterGroup = Record<string, Record<string, HandlerType>>;
4
+ export type RouterMethodMap = Record<string, HandlerType>;
5
+ export type RouterGroup = Record<string, RouterMethodMap | RouterGroup>;
6
+ export type RawRoute = {
7
+ prefix: string,
8
+ middlewares: Array<IMiddleware>,
9
+ namespace: string,
10
+ method: string,
11
+ path: string,
12
+ handler: string | HandlerType
13
+ };
14
+ export type Route = {
15
+ raw: RawRoute,
16
+ route: RouterGroup
17
+ };
3
18
  export type ResourceAction = "index" | "store" | "show" | "update" | "destroy";