@bool-ts/core 1.7.17 → 1.8.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 (83) hide show
  1. package/.prettierrc +1 -1
  2. package/__test/controller.ts +1 -1
  3. package/__test/index.ts +4 -1
  4. package/__test/module.ts +3 -1
  5. package/__test/tsconfig.json +109 -0
  6. package/__test/webSocket.ts +37 -0
  7. package/__test/webSocketClient.ts +23 -0
  8. package/dist/decorators/controller.d.ts +3 -3
  9. package/dist/decorators/controller.js +1 -2
  10. package/dist/decorators/dispatcher.d.ts +1 -1
  11. package/dist/decorators/http.d.ts +12 -12
  12. package/dist/decorators/http.js +1 -1
  13. package/dist/decorators/index.d.ts +5 -0
  14. package/dist/decorators/index.js +3 -0
  15. package/dist/decorators/module.d.ts +3 -1
  16. package/dist/decorators/module.js +9 -2
  17. package/dist/decorators/webSocket.d.ts +25 -0
  18. package/dist/decorators/webSocket.js +40 -0
  19. package/dist/decorators/webSocketArguments.d.ts +22 -0
  20. package/dist/decorators/webSocketArguments.js +49 -0
  21. package/dist/decorators/webSocketEvent.d.ts +15 -0
  22. package/dist/decorators/webSocketEvent.js +24 -0
  23. package/dist/decorators/zodSchema.js +3 -3
  24. package/dist/entities/{route.d.ts → httpRoute.d.ts} +12 -12
  25. package/dist/entities/{route.js → httpRoute.js} +27 -25
  26. package/dist/entities/httpRouter.d.ts +10 -0
  27. package/dist/entities/{router.js → httpRouter.js} +7 -5
  28. package/dist/entities/{routerGroup.d.ts → httpRouterGroup.d.ts} +4 -4
  29. package/dist/entities/{routerGroup.js → httpRouterGroup.js} +1 -1
  30. package/dist/entities/index.d.ts +7 -4
  31. package/dist/entities/index.js +6 -3
  32. package/dist/entities/webSocketRoute.d.ts +12 -0
  33. package/dist/entities/webSocketRoute.js +22 -0
  34. package/dist/entities/webSocketRouter.d.ts +31 -0
  35. package/dist/entities/webSocketRouter.js +54 -0
  36. package/dist/entities/webSocketRouterGroup.d.ts +25 -0
  37. package/dist/entities/webSocketRouterGroup.js +51 -0
  38. package/dist/hooks/factory.d.ts +0 -39
  39. package/dist/hooks/factory.js +398 -63
  40. package/dist/hooks/injector.js +2 -2
  41. package/dist/index.d.ts +1 -1
  42. package/dist/interfaces/index.d.ts +1 -0
  43. package/dist/interfaces/webSocket.d.ts +2 -0
  44. package/dist/interfaces/webSocket.js +1 -0
  45. package/dist/keys/index.d.ts +10 -1
  46. package/dist/keys/index.js +21 -12
  47. package/dist/ultils/colors.d.ts +30 -0
  48. package/dist/ultils/colors.js +41 -0
  49. package/dist/ultils/index.d.ts +2 -0
  50. package/dist/ultils/index.js +2 -0
  51. package/dist/ultils/socket.d.ts +1 -0
  52. package/dist/ultils/socket.js +7 -0
  53. package/package.json +7 -7
  54. package/src/decorators/controller.ts +5 -4
  55. package/src/decorators/dispatcher.ts +2 -1
  56. package/src/decorators/guard.ts +1 -0
  57. package/src/decorators/http.ts +3 -3
  58. package/src/decorators/index.ts +10 -0
  59. package/src/decorators/middleware.ts +1 -0
  60. package/src/decorators/module.ts +14 -3
  61. package/src/decorators/webSocket.ts +81 -0
  62. package/src/decorators/webSocketArguments.ts +144 -0
  63. package/src/decorators/webSocketEvent.ts +56 -0
  64. package/src/decorators/zodSchema.ts +5 -3
  65. package/src/entities/{route.ts → httpRoute.ts} +71 -57
  66. package/src/entities/{router.ts → httpRouter.ts} +8 -6
  67. package/src/entities/{routerGroup.ts → httpRouterGroup.ts} +4 -4
  68. package/src/entities/index.ts +7 -4
  69. package/src/entities/webSocketRoute.ts +35 -0
  70. package/src/entities/webSocketRouter.ts +64 -0
  71. package/src/entities/webSocketRouterGroup.ts +64 -0
  72. package/src/hooks/factory.ts +622 -95
  73. package/src/hooks/injector.ts +2 -2
  74. package/src/index.ts +1 -1
  75. package/src/interfaces/index.ts +1 -0
  76. package/src/interfaces/webSocket.ts +1 -0
  77. package/src/keys/index.ts +22 -12
  78. package/src/ultils/colors.ts +56 -0
  79. package/src/ultils/index.ts +3 -1
  80. package/src/ultils/socket.ts +9 -0
  81. package/test.ts +0 -0
  82. package/tsconfig.json +3 -2
  83. package/dist/entities/router.d.ts +0 -10
@@ -1,5 +1,6 @@
1
1
  import * as Zod from "zod";
2
- import { controllerRouteZodSchemaKey } from "../keys";
2
+
3
+ import { zodSchemaKey } from "../keys";
3
4
 
4
5
  export const ZodSchema = (schema: Zod.Schema) => {
5
6
  return (target: Object, methodName: string | symbol | undefined, parameterIndex: number) => {
@@ -7,13 +8,14 @@ export const ZodSchema = (schema: Zod.Schema) => {
7
8
  return;
8
9
  }
9
10
 
10
- const zodSchemasMetadata = Reflect.getOwnMetadata(controllerRouteZodSchemaKey, target.constructor, methodName) || {};
11
+ const zodSchemasMetadata =
12
+ Reflect.getOwnMetadata(zodSchemaKey, target.constructor, methodName) || {};
11
13
 
12
14
  zodSchemasMetadata[`paramterIndexes.${parameterIndex}`] = {
13
15
  index: parameterIndex,
14
16
  schema: schema
15
17
  };
16
18
 
17
- Reflect.defineMetadata(controllerRouteZodSchemaKey, zodSchemasMetadata, target.constructor, methodName);
19
+ Reflect.defineMetadata(zodSchemaKey, zodSchemasMetadata, target.constructor, methodName);
18
20
  };
19
21
  };
@@ -2,19 +2,19 @@
2
2
 
3
3
  import type { THttpMethods } from "../http";
4
4
 
5
- export type TRouteModel<T = unknown> = Readonly<{
5
+ export type THttpRouteModel<T = unknown> = Readonly<{
6
6
  class: new (...args: Array<any>) => T;
7
7
  funcName: string | symbol;
8
8
  func: (...args: Array<any>) => unknown;
9
9
  }>;
10
10
 
11
- export class Route {
11
+ export class HttpRoute {
12
12
  public static rootPattern = ":([a-z0-9A-Z_-]{1,})";
13
13
  public static innerRootPattern = "([a-z0-9A-Z_-]{1,})";
14
14
 
15
15
  public readonly alias: string;
16
16
 
17
- private _map = new Map<keyof THttpMethods, TRouteModel>();
17
+ private _map = new Map<keyof THttpMethods, THttpRouteModel>();
18
18
 
19
19
  constructor(alias: string) {
20
20
  this.alias = this._thinAlias(alias);
@@ -23,7 +23,10 @@ export class Route {
23
23
  public test(
24
24
  pathname: string,
25
25
  method: keyof THttpMethods
26
- ): Readonly<{ parameters: Record<string, string>; model: TRouteModel }> | false | undefined {
26
+ ):
27
+ | Readonly<{ parameters: Record<string, string>; model: THttpRouteModel }>
28
+ | false
29
+ | undefined {
27
30
  try {
28
31
  const model = this._map.get(method);
29
32
  const aliasSplitted = this.alias.split("/");
@@ -39,7 +42,10 @@ export class Route {
39
42
  }
40
43
 
41
44
  const parameters: Record<string, string> = Object();
42
- const matchingRegex = this.alias.replace(new RegExp(Route.rootPattern, "g"), Route.innerRootPattern);
45
+ const matchingRegex = this.alias.replace(
46
+ new RegExp(HttpRoute.rootPattern, "g"),
47
+ HttpRoute.innerRootPattern
48
+ );
43
49
 
44
50
  if (!new RegExp(matchingRegex).test(this._thinAlias(pathname))) {
45
51
  return undefined;
@@ -50,29 +56,32 @@ export class Route {
50
56
  const pathnamePart = currentPathNameSplitted[index];
51
57
 
52
58
  // Check pathmane path match a dynamic syntax, if no match => start compare equal or not
53
- if (!new RegExp(Route.rootPattern, "g").test(aliasPart)) {
59
+ if (!new RegExp(HttpRoute.rootPattern, "g").test(aliasPart)) {
54
60
  if (aliasPart !== pathnamePart) return undefined;
55
61
  } else {
56
62
  let isFailed = false;
57
63
 
58
- aliasPart.replace(new RegExp(Route.rootPattern, "g"), (match, key, offset) => {
59
- if (offset === 0) {
60
- pathnamePart.replace(
61
- new RegExp(Route.innerRootPattern, "g"),
62
- (innerMatch, innerKey, innerOffset) => {
63
- if (innerOffset === 0) {
64
- Object.assign(parameters, {
65
- [key]: innerMatch
66
- });
64
+ aliasPart.replace(
65
+ new RegExp(HttpRoute.rootPattern, "g"),
66
+ (match, key, offset) => {
67
+ if (offset === 0) {
68
+ pathnamePart.replace(
69
+ new RegExp(HttpRoute.innerRootPattern, "g"),
70
+ (innerMatch, innerKey, innerOffset) => {
71
+ if (innerOffset === 0) {
72
+ Object.assign(parameters, {
73
+ [key]: innerMatch
74
+ });
75
+ }
76
+
77
+ return innerMatch;
67
78
  }
79
+ );
80
+ }
68
81
 
69
- return innerMatch;
70
- }
71
- );
82
+ return match;
72
83
  }
73
-
74
- return match;
75
- });
84
+ );
76
85
 
77
86
  if (isFailed) {
78
87
  return undefined;
@@ -118,23 +127,26 @@ export class Route {
118
127
  const pathnamePart = currentPathNameSplitted[index];
119
128
 
120
129
  // Check pathmane path match a dynamic syntax, if no match => start compare equal or not
121
- if (!new RegExp(Route.rootPattern, "g").test(aliasPart)) {
130
+ if (!new RegExp(HttpRoute.rootPattern, "g").test(aliasPart)) {
122
131
  if (aliasPart !== pathnamePart) {
123
132
  return false;
124
133
  }
125
134
  } else {
126
135
  let isFailed = false;
127
136
 
128
- aliasPart.replace(new RegExp(Route.rootPattern, "g"), (subString, key, value) => {
129
- if (!new RegExp(value, "g").test(pathnamePart)) {
130
- isFailed = true;
131
- } else {
132
- Object.assign(parameters, {
133
- [key]: pathnamePart
134
- });
137
+ aliasPart.replace(
138
+ new RegExp(HttpRoute.rootPattern, "g"),
139
+ (subString, key, value) => {
140
+ if (!new RegExp(value, "g").test(pathnamePart)) {
141
+ isFailed = true;
142
+ } else {
143
+ Object.assign(parameters, {
144
+ [key]: pathnamePart
145
+ });
146
+ }
147
+ return "";
135
148
  }
136
- return "";
137
- });
149
+ );
138
150
 
139
151
  if (isFailed) {
140
152
  return false;
@@ -156,10 +168,10 @@ export class Route {
156
168
  * @param handlers
157
169
  * @returns
158
170
  */
159
- public get(handler: TRouteModel) {
160
- const currenTRouteModel = this._map.get("GET");
171
+ public get(handler: THttpRouteModel) {
172
+ const currenTHttpRouteModel = this._map.get("GET");
161
173
 
162
- if (!currenTRouteModel) {
174
+ if (!currenTHttpRouteModel) {
163
175
  this._map.set("GET", handler);
164
176
  }
165
177
 
@@ -171,10 +183,10 @@ export class Route {
171
183
  * @param handlers
172
184
  * @returns
173
185
  */
174
- public post(handler: TRouteModel) {
175
- const currenTRouteModel = this._map.get("POST");
186
+ public post(handler: THttpRouteModel) {
187
+ const currenTHttpRouteModel = this._map.get("POST");
176
188
 
177
- if (!currenTRouteModel) {
189
+ if (!currenTHttpRouteModel) {
178
190
  this._map.set("POST", handler);
179
191
  }
180
192
 
@@ -186,10 +198,10 @@ export class Route {
186
198
  * @param handlers
187
199
  * @returns
188
200
  */
189
- public put(handler: TRouteModel) {
190
- const currenTRouteModel = this._map.get("PUT");
201
+ public put(handler: THttpRouteModel) {
202
+ const currenTHttpRouteModel = this._map.get("PUT");
191
203
 
192
- if (!currenTRouteModel) {
204
+ if (!currenTHttpRouteModel) {
193
205
  this._map.set("PUT", handler);
194
206
  }
195
207
 
@@ -201,10 +213,10 @@ export class Route {
201
213
  * @param handlers
202
214
  * @returns
203
215
  */
204
- public delete(handler: TRouteModel) {
205
- const currenTRouteModel = this._map.get("DELETE");
216
+ public delete(handler: THttpRouteModel) {
217
+ const currenTHttpRouteModel = this._map.get("DELETE");
206
218
 
207
- if (!currenTRouteModel) {
219
+ if (!currenTHttpRouteModel) {
208
220
  this._map.set("DELETE", handler);
209
221
  }
210
222
 
@@ -216,10 +228,10 @@ export class Route {
216
228
  * @param handlers
217
229
  * @returns
218
230
  */
219
- public connect(handler: TRouteModel) {
220
- const currenTRouteModel = this._map.get("CONNECT");
231
+ public connect(handler: THttpRouteModel) {
232
+ const currenTHttpRouteModel = this._map.get("CONNECT");
221
233
 
222
- if (!currenTRouteModel) {
234
+ if (!currenTHttpRouteModel) {
223
235
  return this._map.set("CONNECT", handler);
224
236
  }
225
237
 
@@ -231,10 +243,10 @@ export class Route {
231
243
  * @param handlers
232
244
  * @returns
233
245
  */
234
- public options(handler: TRouteModel) {
235
- const currenTRouteModel = this._map.get("OPTIONS");
246
+ public options(handler: THttpRouteModel) {
247
+ const currenTHttpRouteModel = this._map.get("OPTIONS");
236
248
 
237
- if (!currenTRouteModel) {
249
+ if (!currenTHttpRouteModel) {
238
250
  return this._map.set("OPTIONS", handler);
239
251
  }
240
252
 
@@ -246,10 +258,10 @@ export class Route {
246
258
  * @param handlers
247
259
  * @returns
248
260
  */
249
- public trace(handler: TRouteModel) {
250
- const currenTRouteModel = this._map.get("TRACE");
261
+ public trace(handler: THttpRouteModel) {
262
+ const currenTHttpRouteModel = this._map.get("TRACE");
251
263
 
252
- if (!currenTRouteModel) {
264
+ if (!currenTHttpRouteModel) {
253
265
  return this._map.set("TRACE", handler);
254
266
  }
255
267
 
@@ -261,10 +273,10 @@ export class Route {
261
273
  * @param handlers
262
274
  * @returns
263
275
  */
264
- public patch(handler: TRouteModel) {
265
- const currenTRouteModel = this._map.get("PATCH");
276
+ public patch(handler: THttpRouteModel) {
277
+ const currenTHttpRouteModel = this._map.get("PATCH");
266
278
 
267
- if (!currenTRouteModel) {
279
+ if (!currenTHttpRouteModel) {
268
280
  return this._map.set("PATCH", handler);
269
281
  }
270
282
 
@@ -277,7 +289,9 @@ export class Route {
277
289
  * @returns
278
290
  */
279
291
  private _thinAlias(alias: string) {
280
- return alias.replace(new RegExp("[/]{2,}", "g"), "/").replace(new RegExp("^[/]|[/]$", "g"), "");
292
+ return alias
293
+ .replace(new RegExp("[/]{2,}", "g"), "/")
294
+ .replace(new RegExp("^[/]|[/]$", "g"), "");
281
295
  }
282
296
 
283
297
  /**
@@ -339,4 +353,4 @@ export class Route {
339
353
  }
340
354
  }
341
355
 
342
- export default Route;
356
+ export default HttpRoute;
@@ -1,11 +1,11 @@
1
1
  "use strict";
2
2
 
3
- import Route from "./route";
3
+ import HttpRoute from "./httpRoute";
4
4
 
5
- export class Router {
5
+ export class HttpRouter {
6
6
  public readonly alias: string;
7
7
 
8
- private _routes: Map<string, Route> = new Map();
8
+ private _routes: Map<string, HttpRoute> = new Map();
9
9
 
10
10
  constructor(alias: string) {
11
11
  this.alias = this._thinAlias(alias);
@@ -14,7 +14,7 @@ export class Router {
14
14
  public route(alias: string) {
15
15
  const thinAlias = this._thinAlias(`${this.alias}/${alias}`);
16
16
  const route = this._routes.get(thinAlias);
17
- const newRoute = !route ? new Route(`${this.alias}/${alias}`) : route;
17
+ const newRoute = !route ? new HttpRoute(`${this.alias}/${alias}`) : route;
18
18
 
19
19
  if (!route) {
20
20
  this._routes.set(thinAlias, newRoute);
@@ -24,7 +24,9 @@ export class Router {
24
24
  }
25
25
 
26
26
  private _thinAlias(alias: string) {
27
- return alias.replace(new RegExp("[/]{2,}", "g"), "/").replace(new RegExp("^[/]|[/]$", "g"), "");
27
+ return alias
28
+ .replace(new RegExp("[/]{2,}", "g"), "/")
29
+ .replace(new RegExp("^[/]|[/]$", "g"), "");
28
30
  }
29
31
 
30
32
  get routes() {
@@ -32,4 +34,4 @@ export class Router {
32
34
  }
33
35
  }
34
36
 
35
- export default Router;
37
+ export default HttpRouter;
@@ -1,10 +1,10 @@
1
1
  import type { THttpMethods } from "../http";
2
- import type { Router } from "./router";
2
+ import type { HttpRouter } from "./httpRouter";
3
3
 
4
- export class RouterGroup {
5
- private _routers: Map<string, Router> = new Map();
4
+ export class HttpRouterGroup {
5
+ private _routers: Map<string, HttpRouter> = new Map();
6
6
 
7
- public add(...routers: Array<Router>) {
7
+ public add(...routers: Array<HttpRouter>) {
8
8
  for (let i = 0; i < routers.length; i++) {
9
9
  if (this._routers.has(routers[i].alias)) {
10
10
  continue;
@@ -1,5 +1,8 @@
1
- export type { TRouteModel } from "./route";
1
+ export type { THttpRouteModel } from "./httpRoute";
2
2
 
3
- export { Route } from "./route";
4
- export { Router } from "./router";
5
- export { RouterGroup } from "./routerGroup";
3
+ export { HttpRoute } from "./httpRoute";
4
+ export { HttpRouter } from "./httpRouter";
5
+ export { HttpRouterGroup } from "./httpRouterGroup";
6
+ export { WebSocketRoute } from "./webSocketRoute";
7
+ export { WebSocketRouter } from "./webSocketRouter";
8
+ export { WebSocketRouterGroup } from "./webSocketRouterGroup";
@@ -0,0 +1,35 @@
1
+ import type { TWebSocketEventHandlerMetadata } from "../decorators";
2
+
3
+ export class WebSocketRoute {
4
+ public readonly eventName: string;
5
+ public readonly metadata: TWebSocketEventHandlerMetadata;
6
+ private _context: Object | undefined = undefined;
7
+
8
+ constructor({
9
+ eventName,
10
+ metadata
11
+ }: {
12
+ eventName: string;
13
+ metadata: TWebSocketEventHandlerMetadata;
14
+ }) {
15
+ this.eventName = eventName;
16
+ this.metadata = metadata;
17
+ }
18
+
19
+ public bind(instance: Object): ThisType<WebSocketRoute> {
20
+ this._context = instance;
21
+
22
+ return this;
23
+ }
24
+
25
+ public execute(): Readonly<TWebSocketEventHandlerMetadata> {
26
+ return Object.freeze({
27
+ methodName: this.metadata.methodName,
28
+ descriptor:
29
+ !this._context || typeof this.metadata.descriptor.value !== "function"
30
+ ? this.metadata.descriptor
31
+ : this.metadata.descriptor.value.bind(this._context),
32
+ arguments: this.metadata.arguments
33
+ });
34
+ }
35
+ }
@@ -0,0 +1,64 @@
1
+ import type { TWebSocketEventHandlerMetadata } from "../decorators";
2
+ import type { WebSocketRoute } from "./webSocketRoute";
3
+
4
+ export class WebSocketRouter {
5
+ public readonly alias: string;
6
+ public readonly routes: Array<WebSocketRoute> = [];
7
+
8
+ constructor(public readonly rawAlias: string = "/") {
9
+ this.alias = WebSocketRouter.thinAlias(rawAlias);
10
+ }
11
+
12
+ /**
13
+ * Add websocket routes into router and start analysis
14
+ * @param routes
15
+ * @returns
16
+ */
17
+ public addRoutes(...routes: Array<WebSocketRoute>): ThisType<WebSocketRouter> {
18
+ for (const route of routes) {
19
+ if (!this.routes.includes(route)) {
20
+ this.routes.push(route);
21
+ }
22
+ }
23
+
24
+ return this;
25
+ }
26
+
27
+ /**
28
+ * Bind context for descriptor handler in the router
29
+ * @param instance
30
+ * @returns
31
+ */
32
+ public bind(instance: Object): ThisType<WebSocketRouter> {
33
+ for (const route of this.routes) {
34
+ route.bind(instance);
35
+ }
36
+
37
+ return this;
38
+ }
39
+
40
+ /**
41
+ * Generate map for websocket handler metadata
42
+ * @returns
43
+ */
44
+ public execute(): Map<string, TWebSocketEventHandlerMetadata> {
45
+ const map = new Map<string, TWebSocketEventHandlerMetadata>();
46
+
47
+ for (const route of this.routes) {
48
+ map.set(`${this.alias}:::${route.eventName}`, route.execute());
49
+ }
50
+
51
+ return map;
52
+ }
53
+
54
+ /**
55
+ * Handle alias for router, standardize
56
+ * @param alias
57
+ * @returns
58
+ */
59
+ public static thinAlias(alias: string): string {
60
+ return alias
61
+ .replace(new RegExp("[/]{2,}", "g"), "/")
62
+ .replace(new RegExp("^[/]|[/]$", "g"), "");
63
+ }
64
+ }
@@ -0,0 +1,64 @@
1
+ import type { TWebSocketEventHandlerMetadata } from "../decorators";
2
+ import type { WebSocketRouter } from "./webSocketRouter";
3
+
4
+ export class WebSocketRouterGroup {
5
+ public readonly prefix: string;
6
+ public readonly routers: Array<WebSocketRouter> = [];
7
+
8
+ constructor(public readonly rawPrefix: string = "/") {
9
+ this.prefix = WebSocketRouterGroup.thinPrefix(rawPrefix);
10
+ }
11
+
12
+ /**
13
+ *
14
+ * @param routers
15
+ * @returns
16
+ */
17
+ public addRouters(...routers: Array<WebSocketRouter>): ThisType<WebSocketRouterGroup> {
18
+ for (let i = 0; i < routers.length; i++) {
19
+ if (!this.routers.includes(routers[i])) {
20
+ this.routers.push(routers[i]);
21
+ }
22
+ }
23
+
24
+ for (const router of routers) {
25
+ if (!this.routers.includes(router)) {
26
+ this.routers.push(router);
27
+ }
28
+ }
29
+
30
+ return this;
31
+ }
32
+
33
+ /**
34
+ *
35
+ * @returns
36
+ */
37
+ public execute(): Map<string, TWebSocketEventHandlerMetadata> {
38
+ const map = new Map<string, TWebSocketEventHandlerMetadata>();
39
+
40
+ for (const router of this.routers) {
41
+ const routerMap = router.execute();
42
+
43
+ for (const [routerKey, metadata] of routerMap.entries()) {
44
+ map.set(
45
+ `/${WebSocketRouterGroup.thinPrefix(`${this.prefix}/${routerKey}`)}`,
46
+ metadata
47
+ );
48
+ }
49
+ }
50
+
51
+ return map;
52
+ }
53
+
54
+ /**
55
+ *
56
+ * @param prefix
57
+ * @returns
58
+ */
59
+ public static thinPrefix(prefix: string): string {
60
+ return prefix
61
+ .replace(new RegExp("[/]{2,}", "g"), "/")
62
+ .replace(new RegExp("^[/]|[/]$", "g"), "");
63
+ }
64
+ }