@bejibun/core 0.1.65 → 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,37 @@ 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
+
6
37
  ## [v0.1.65](https://github.com/crenata/bejibun-core/compare/v0.1.64...v0.1.65) - 2025-12-29
7
38
 
8
39
  ### 🩹 Fixes
@@ -2,6 +2,7 @@ import type { TFacilitator, TPaywall, TX402Config } from "@bejibun/x402";
2
2
  import type { IMiddleware } from "../types/middleware";
3
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>;
@@ -15,7 +16,7 @@ export default class RouterBuilder {
15
16
  namespace(baseNamespace: string): RouterBuilder;
16
17
  x402(config?: TX402Config, facilitatorConfig?: TFacilitator, paywallConfig?: TPaywall): RouterBuilder;
17
18
  group(routes: Route | Array<Route> | RouterGroup): RouterGroup | Array<RouterGroup>;
18
- resources(controller: Record<string, HandlerType>, options?: ResourceOptions): RouterGroup;
19
+ resource(path: string, controller: typeof BaseController, options?: ResourceOptions): RouterGroup;
19
20
  buildSingle(method: HttpMethodEnum, path: string, handler: string | HandlerType): Route;
20
21
  connect(path: string, handler: string | HandlerType): Route;
21
22
  delete(path: string, handler: string | HandlerType): Route;
@@ -28,7 +29,8 @@ export default class RouterBuilder {
28
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;
31
- serialize(routes: Route | Array<Route> | RouterGroup | Array<RouterGroup>): RouterGroup | Array<RouterGroup>;
32
+ serialize(routes: Route | Array<Route> | RouterGroup | Array<RouterGroup>): RouterGroup;
33
+ private mergeRoutes;
32
34
  private joinPaths;
33
35
  private resolveControllerString;
34
36
  private resolveIncludedActions;
@@ -1,4 +1,5 @@
1
1
  import App from "@bejibun/app";
2
+ import Logger from "@bejibun/logger";
2
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";
@@ -65,43 +66,65 @@ export default class RouterBuilder {
65
66
  if (isEmpty(routes))
66
67
  return {};
67
68
  if (Array.isArray(routes)) {
68
- if (routes.some(value => isNotEmpty(value.raws)))
69
- return routes.map(value => value.raws)
70
- .flat()
71
- .map((route) => this.applyGroup(route));
72
- return routes.map((route) => this.applyGroup(route));
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));
73
76
  }
74
77
  return this.applyGroup(routes);
75
78
  }
76
- resources(controller, options) {
79
+ resource(path, controller, options) {
80
+ const ClassController = new controller();
81
+ const cleanPath = this.joinPaths(this.basePath, path);
77
82
  const allRoutes = {
78
- "": {
83
+ [cleanPath]: {
79
84
  GET: "index",
80
85
  POST: "store"
81
86
  },
82
- ":id": {
87
+ [`${cleanPath}/:id`]: {
83
88
  GET: "show",
84
89
  PUT: "update",
85
90
  DELETE: "destroy"
86
91
  }
87
92
  };
88
93
  const includedActions = this.resolveIncludedActions(options);
89
- const filteredRoutes = {};
94
+ const raws = [];
95
+ const routes = {};
90
96
  for (const path in allRoutes) {
91
97
  const methods = allRoutes[path];
92
- const methodHandlers = {};
98
+ const methodMap = {};
93
99
  for (const method in methods) {
94
100
  const action = methods[method];
95
- if (includedActions.has(action) && controller[action]) {
96
- 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
+ });
97
118
  }
98
119
  }
99
- if (Object.keys(methodHandlers).length > 0) {
100
- filteredRoutes[path] = methodHandlers;
120
+ if (Object.keys(methodMap).length > 0) {
121
+ routes[path] = methodMap;
101
122
  }
102
123
  }
103
- return filteredRoutes;
104
- // return this.group(filteredRoutes);
124
+ return this.applyGroup({
125
+ raws,
126
+ routes
127
+ });
105
128
  }
106
129
  buildSingle(method, path, handler) {
107
130
  const cleanPath = this.joinPaths(this.basePath, path);
@@ -172,13 +195,36 @@ export default class RouterBuilder {
172
195
  serialize(routes) {
173
196
  if (Array.isArray(routes)) {
174
197
  if (this.hasRaw(routes))
175
- return routes.map((value) => value.route);
198
+ routes = routes.map((value) => value.route);
176
199
  }
177
200
  else {
178
201
  if (this.hasRaw(routes))
179
- return routes.route;
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
+ }
180
226
  }
181
- return routes;
227
+ return merged;
182
228
  }
183
229
  joinPaths(base, path) {
184
230
  base = base.replace(/\/+$/, "");
@@ -2,6 +2,7 @@ import type { ResourceOptions } from "../builders/RouterBuilder";
2
2
  import type { IMiddleware } from "../types/middleware";
3
3
  import type { HandlerType, RouterGroup } from "../types/router";
4
4
  import HttpMethodEnum from "@bejibun/utils/enums/HttpMethodEnum";
5
+ import BaseController from "../bases/BaseController";
5
6
  import RouterBuilder from "../builders/RouterBuilder";
6
7
  import { Route } from "../types/router";
7
8
  export default class Router {
@@ -9,7 +10,7 @@ export default class Router {
9
10
  static middleware(...middlewares: Array<IMiddleware>): RouterBuilder;
10
11
  static namespace(baseNamespace: string): RouterBuilder;
11
12
  static x402(): RouterBuilder;
12
- static resources(controller: Record<string, HandlerType>, options?: ResourceOptions): RouterGroup;
13
+ static resource(path: string, controller: typeof BaseController, options?: ResourceOptions): RouterGroup;
13
14
  static group(routes: Route | Array<Route> | RouterGroup): RouterGroup | Array<RouterGroup>;
14
15
  static connect(path: string, handler: string | HandlerType): Route;
15
16
  static delete(path: string, handler: string | HandlerType): Route;
@@ -22,5 +23,5 @@ export default class Router {
22
23
  static trace(path: string, handler: string | HandlerType): Route;
23
24
  static match(methods: Array<HttpMethodEnum>, path: string, handler: string | HandlerType): RouterGroup;
24
25
  static any(path: string, handler: string | HandlerType): RouterGroup;
25
- static serialize(routes: Route | Array<Route> | RouterGroup | Array<RouterGroup>): RouterGroup | Array<RouterGroup>;
26
+ static serialize(routes: Route | Array<Route> | RouterGroup | Array<RouterGroup>): RouterGroup;
26
27
  }
package/facades/Router.js CHANGED
@@ -12,8 +12,8 @@ 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
18
  static group(routes) {
19
19
  return new RouterBuilder().group(routes);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bejibun/core",
3
- "version": "0.1.65",
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
@@ -2,6 +2,5 @@ export default class Server {
2
2
  private get exceptionHandler();
3
3
  private get apiRoutes();
4
4
  private get webRoutes();
5
- private routeWrapper;
6
5
  run(): void;
7
6
  }
package/server.js CHANGED
@@ -34,12 +34,6 @@ export default class Server {
34
34
  throw new RuntimeException(`Missing web file on routes directory [${webRoutesPath}].`, null, error.message);
35
35
  }
36
36
  }
37
- routeWrapper(routes) {
38
- routes = Router.serialize(routes);
39
- if (Array.isArray(routes))
40
- return Object.assign({}, ...routes);
41
- return routes;
42
- }
43
37
  run() {
44
38
  const server = Bun.serve({
45
39
  development: Bun.env.NODE_ENV !== "production" && {
@@ -54,8 +48,8 @@ export default class Server {
54
48
  "/": require(App.Path.publicPath("index.html")),
55
49
  ...Object.assign({}, ...defineValue(Router.middleware(new MaintenanceMiddleware(), new RateLimiterMiddleware()).group([
56
50
  Router.namespace("app/exceptions").any("/*", "Handler@route"),
57
- this.routeWrapper(this.apiRoutes),
58
- this.routeWrapper(this.webRoutes)
51
+ Router.serialize(this.apiRoutes),
52
+ Router.serialize(this.webRoutes)
59
53
  ]), []))
60
54
  }
61
55
  });