vector-framework 0.9.9 → 1.1.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 (54) hide show
  1. package/README.md +6 -5
  2. package/dist/cache/manager.d.ts +5 -2
  3. package/dist/cache/manager.d.ts.map +1 -1
  4. package/dist/cache/manager.js +21 -7
  5. package/dist/cache/manager.js.map +1 -1
  6. package/dist/cli/index.js +76 -98
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/cli.js +134 -69
  9. package/dist/core/config-loader.d.ts +2 -2
  10. package/dist/core/config-loader.d.ts.map +1 -1
  11. package/dist/core/config-loader.js +16 -21
  12. package/dist/core/config-loader.js.map +1 -1
  13. package/dist/core/router.d.ts +2 -0
  14. package/dist/core/router.d.ts.map +1 -1
  15. package/dist/core/router.js +52 -16
  16. package/dist/core/router.js.map +1 -1
  17. package/dist/core/server.d.ts +4 -3
  18. package/dist/core/server.d.ts.map +1 -1
  19. package/dist/core/server.js +40 -20
  20. package/dist/core/server.js.map +1 -1
  21. package/dist/core/vector.d.ts +7 -7
  22. package/dist/core/vector.d.ts.map +1 -1
  23. package/dist/core/vector.js +20 -21
  24. package/dist/core/vector.js.map +1 -1
  25. package/dist/dev/route-scanner.d.ts +1 -1
  26. package/dist/dev/route-scanner.d.ts.map +1 -1
  27. package/dist/dev/route-scanner.js +40 -42
  28. package/dist/dev/route-scanner.js.map +1 -1
  29. package/dist/http.d.ts +2 -2
  30. package/dist/http.d.ts.map +1 -1
  31. package/dist/http.js +70 -63
  32. package/dist/http.js.map +1 -1
  33. package/dist/index.d.ts +3 -3
  34. package/dist/index.js +4 -4
  35. package/dist/index.mjs +4 -4
  36. package/dist/middleware/manager.d.ts +1 -1
  37. package/dist/middleware/manager.d.ts.map +1 -1
  38. package/dist/middleware/manager.js.map +1 -1
  39. package/dist/utils/path.d.ts +1 -0
  40. package/dist/utils/path.d.ts.map +1 -1
  41. package/dist/utils/path.js +9 -3
  42. package/dist/utils/path.js.map +1 -1
  43. package/package.json +14 -9
  44. package/src/cache/manager.ts +23 -14
  45. package/src/cli/index.ts +83 -117
  46. package/src/core/config-loader.ts +19 -27
  47. package/src/core/router.ts +52 -18
  48. package/src/core/server.ts +43 -30
  49. package/src/core/vector.ts +25 -35
  50. package/src/dev/route-scanner.ts +41 -47
  51. package/src/http.ts +82 -112
  52. package/src/index.ts +3 -3
  53. package/src/middleware/manager.ts +4 -11
  54. package/src/utils/path.ts +13 -4
package/src/http.ts CHANGED
@@ -1,26 +1,26 @@
1
- import { cors, type IRequest, type RouteEntry, withContent } from "itty-router";
2
- import { CONTENT_TYPES, HTTP_STATUS } from "./constants";
1
+ import { cors, type IRequest, type RouteEntry, withContent } from 'itty-router';
2
+ import { buildRouteRegex } from './utils/path';
3
+ import { CONTENT_TYPES, HTTP_STATUS } from './constants';
3
4
  import type {
4
5
  CacheOptions,
5
6
  DefaultVectorTypes,
6
7
  GetAuthType,
7
8
  VectorRequest,
8
9
  VectorTypes,
9
- } from "./types";
10
- import { getVectorInstance } from "./core/vector";
10
+ } from './types';
11
+ import { getVectorInstance } from './core/vector';
11
12
 
12
- export interface ProtectedRequest<
13
- TTypes extends VectorTypes = DefaultVectorTypes
14
- > extends IRequest {
13
+ export interface ProtectedRequest<TTypes extends VectorTypes = DefaultVectorTypes>
14
+ extends IRequest {
15
15
  authUser?: GetAuthType<TTypes>;
16
16
  }
17
17
 
18
18
  export const { preflight, corsify } = cors({
19
- origin: "*",
19
+ origin: '*',
20
20
  credentials: true,
21
- allowHeaders: "Content-Type, Authorization",
22
- allowMethods: "GET, POST, PUT, PATCH, DELETE, OPTIONS",
23
- exposeHeaders: "Authorization",
21
+ allowHeaders: 'Content-Type, Authorization',
22
+ allowMethods: 'GET, POST, PUT, PATCH, DELETE, OPTIONS',
23
+ exposeHeaders: 'Authorization',
24
24
  maxAge: 86_400,
25
25
  });
26
26
 
@@ -29,9 +29,7 @@ interface ExtendedApiOptions extends ApiOptions {
29
29
  path: string;
30
30
  }
31
31
 
32
- export interface RouteDefinition<
33
- TTypes extends VectorTypes = DefaultVectorTypes
34
- > {
32
+ export interface RouteDefinition<TTypes extends VectorTypes = DefaultVectorTypes> {
35
33
  entry: RouteEntry;
36
34
  options: ExtendedApiOptions;
37
35
  handler: (req: VectorRequest<TTypes>) => Promise<unknown>;
@@ -45,16 +43,7 @@ export function route<TTypes extends VectorTypes = DefaultVectorTypes>(
45
43
 
46
44
  const entry: RouteEntry = [
47
45
  options.method.toUpperCase(),
48
- RegExp(
49
- `^${
50
- options.path
51
- .replace(/\/+(\/|$)/g, "$1") // strip double & trailing splash
52
- .replace(/(\/?\.?):(\w+)\+/g, "($1(?<$2>*))") // greedy params
53
- .replace(/(\/?\.?):(\w+)/g, "($1(?<$2>[^$1/]+?))") // named params and image format
54
- .replace(/\./g, "\\.") // dot in path
55
- .replace(/(\/?)\*/g, "($1.*)?") // wildcard
56
- }/*$`
57
- ),
46
+ buildRouteRegex(options.path),
58
47
  [handler],
59
48
  options.path,
60
49
  ];
@@ -66,24 +55,30 @@ export function route<TTypes extends VectorTypes = DefaultVectorTypes>(
66
55
  };
67
56
  }
68
57
 
58
+ function hasBigInt(value: unknown, depth = 0): boolean {
59
+ if (typeof value === 'bigint') return true;
60
+ if (depth > 4 || value === null || typeof value !== 'object') return false;
61
+ for (const v of Object.values(value as object)) {
62
+ if (hasBigInt(v, depth + 1)) return true;
63
+ }
64
+ return false;
65
+ }
66
+
69
67
  function stringifyData(data: unknown): string {
70
- return JSON.stringify(data ?? null, (_key, value) =>
71
- typeof value === "bigint" ? value.toString() : value
68
+ const val = data ?? null;
69
+ if (!hasBigInt(val)) return JSON.stringify(val);
70
+ return JSON.stringify(val, (_key, value) =>
71
+ typeof value === 'bigint' ? value.toString() : value
72
72
  );
73
73
  }
74
74
 
75
75
  const ApiResponse = {
76
- success: <T>(data: T, contentType?: string) =>
77
- createResponse(HTTP_STATUS.OK, data, contentType),
76
+ success: <T>(data: T, contentType?: string) => createResponse(HTTP_STATUS.OK, data, contentType),
78
77
  created: <T>(data: T, contentType?: string) =>
79
78
  createResponse(HTTP_STATUS.CREATED, data, contentType),
80
79
  };
81
80
 
82
- function createErrorResponse(
83
- code: number,
84
- message: string,
85
- contentType?: string
86
- ): Response {
81
+ function createErrorResponse(code: number, message: string, contentType?: string): Response {
87
82
  const errorBody = {
88
83
  error: true,
89
84
  message,
@@ -96,144 +91,129 @@ function createErrorResponse(
96
91
 
97
92
  export const APIError = {
98
93
  // 4xx Client Errors
99
- badRequest: (msg = "Bad Request", contentType?: string) =>
94
+ badRequest: (msg = 'Bad Request', contentType?: string) =>
100
95
  createErrorResponse(HTTP_STATUS.BAD_REQUEST, msg, contentType),
101
96
 
102
- unauthorized: (msg = "Unauthorized", contentType?: string) =>
97
+ unauthorized: (msg = 'Unauthorized', contentType?: string) =>
103
98
  createErrorResponse(HTTP_STATUS.UNAUTHORIZED, msg, contentType),
104
99
 
105
- paymentRequired: (msg = "Payment Required", contentType?: string) =>
100
+ paymentRequired: (msg = 'Payment Required', contentType?: string) =>
106
101
  createErrorResponse(402, msg, contentType),
107
102
 
108
- forbidden: (msg = "Forbidden", contentType?: string) =>
103
+ forbidden: (msg = 'Forbidden', contentType?: string) =>
109
104
  createErrorResponse(HTTP_STATUS.FORBIDDEN, msg, contentType),
110
105
 
111
- notFound: (msg = "Not Found", contentType?: string) =>
106
+ notFound: (msg = 'Not Found', contentType?: string) =>
112
107
  createErrorResponse(HTTP_STATUS.NOT_FOUND, msg, contentType),
113
108
 
114
- methodNotAllowed: (msg = "Method Not Allowed", contentType?: string) =>
109
+ methodNotAllowed: (msg = 'Method Not Allowed', contentType?: string) =>
115
110
  createErrorResponse(405, msg, contentType),
116
111
 
117
- notAcceptable: (msg = "Not Acceptable", contentType?: string) =>
112
+ notAcceptable: (msg = 'Not Acceptable', contentType?: string) =>
118
113
  createErrorResponse(406, msg, contentType),
119
114
 
120
- requestTimeout: (msg = "Request Timeout", contentType?: string) =>
115
+ requestTimeout: (msg = 'Request Timeout', contentType?: string) =>
121
116
  createErrorResponse(408, msg, contentType),
122
117
 
123
- conflict: (msg = "Conflict", contentType?: string) =>
118
+ conflict: (msg = 'Conflict', contentType?: string) =>
124
119
  createErrorResponse(HTTP_STATUS.CONFLICT, msg, contentType),
125
120
 
126
- gone: (msg = "Gone", contentType?: string) =>
127
- createErrorResponse(410, msg, contentType),
121
+ gone: (msg = 'Gone', contentType?: string) => createErrorResponse(410, msg, contentType),
128
122
 
129
- lengthRequired: (msg = "Length Required", contentType?: string) =>
123
+ lengthRequired: (msg = 'Length Required', contentType?: string) =>
130
124
  createErrorResponse(411, msg, contentType),
131
125
 
132
- preconditionFailed: (msg = "Precondition Failed", contentType?: string) =>
126
+ preconditionFailed: (msg = 'Precondition Failed', contentType?: string) =>
133
127
  createErrorResponse(412, msg, contentType),
134
128
 
135
- payloadTooLarge: (msg = "Payload Too Large", contentType?: string) =>
129
+ payloadTooLarge: (msg = 'Payload Too Large', contentType?: string) =>
136
130
  createErrorResponse(413, msg, contentType),
137
131
 
138
- uriTooLong: (msg = "URI Too Long", contentType?: string) =>
132
+ uriTooLong: (msg = 'URI Too Long', contentType?: string) =>
139
133
  createErrorResponse(414, msg, contentType),
140
134
 
141
- unsupportedMediaType: (
142
- msg = "Unsupported Media Type",
143
- contentType?: string
144
- ) => createErrorResponse(415, msg, contentType),
135
+ unsupportedMediaType: (msg = 'Unsupported Media Type', contentType?: string) =>
136
+ createErrorResponse(415, msg, contentType),
145
137
 
146
- rangeNotSatisfiable: (msg = "Range Not Satisfiable", contentType?: string) =>
138
+ rangeNotSatisfiable: (msg = 'Range Not Satisfiable', contentType?: string) =>
147
139
  createErrorResponse(416, msg, contentType),
148
140
 
149
- expectationFailed: (msg = "Expectation Failed", contentType?: string) =>
141
+ expectationFailed: (msg = 'Expectation Failed', contentType?: string) =>
150
142
  createErrorResponse(417, msg, contentType),
151
143
 
152
144
  imATeapot: (msg = "I'm a teapot", contentType?: string) =>
153
145
  createErrorResponse(418, msg, contentType),
154
146
 
155
- misdirectedRequest: (msg = "Misdirected Request", contentType?: string) =>
147
+ misdirectedRequest: (msg = 'Misdirected Request', contentType?: string) =>
156
148
  createErrorResponse(421, msg, contentType),
157
149
 
158
- unprocessableEntity: (msg = "Unprocessable Entity", contentType?: string) =>
150
+ unprocessableEntity: (msg = 'Unprocessable Entity', contentType?: string) =>
159
151
  createErrorResponse(HTTP_STATUS.UNPROCESSABLE_ENTITY, msg, contentType),
160
152
 
161
- locked: (msg = "Locked", contentType?: string) =>
162
- createErrorResponse(423, msg, contentType),
153
+ locked: (msg = 'Locked', contentType?: string) => createErrorResponse(423, msg, contentType),
163
154
 
164
- failedDependency: (msg = "Failed Dependency", contentType?: string) =>
155
+ failedDependency: (msg = 'Failed Dependency', contentType?: string) =>
165
156
  createErrorResponse(424, msg, contentType),
166
157
 
167
- tooEarly: (msg = "Too Early", contentType?: string) =>
168
- createErrorResponse(425, msg, contentType),
158
+ tooEarly: (msg = 'Too Early', contentType?: string) => createErrorResponse(425, msg, contentType),
169
159
 
170
- upgradeRequired: (msg = "Upgrade Required", contentType?: string) =>
160
+ upgradeRequired: (msg = 'Upgrade Required', contentType?: string) =>
171
161
  createErrorResponse(426, msg, contentType),
172
162
 
173
- preconditionRequired: (msg = "Precondition Required", contentType?: string) =>
163
+ preconditionRequired: (msg = 'Precondition Required', contentType?: string) =>
174
164
  createErrorResponse(428, msg, contentType),
175
165
 
176
- tooManyRequests: (msg = "Too Many Requests", contentType?: string) =>
166
+ tooManyRequests: (msg = 'Too Many Requests', contentType?: string) =>
177
167
  createErrorResponse(429, msg, contentType),
178
168
 
179
- requestHeaderFieldsTooLarge: (
180
- msg = "Request Header Fields Too Large",
181
- contentType?: string
182
- ) => createErrorResponse(431, msg, contentType),
169
+ requestHeaderFieldsTooLarge: (msg = 'Request Header Fields Too Large', contentType?: string) =>
170
+ createErrorResponse(431, msg, contentType),
183
171
 
184
- unavailableForLegalReasons: (
185
- msg = "Unavailable For Legal Reasons",
186
- contentType?: string
187
- ) => createErrorResponse(451, msg, contentType),
172
+ unavailableForLegalReasons: (msg = 'Unavailable For Legal Reasons', contentType?: string) =>
173
+ createErrorResponse(451, msg, contentType),
188
174
 
189
175
  // 5xx Server Errors
190
- internalServerError: (msg = "Internal Server Error", contentType?: string) =>
176
+ internalServerError: (msg = 'Internal Server Error', contentType?: string) =>
191
177
  createErrorResponse(HTTP_STATUS.INTERNAL_SERVER_ERROR, msg, contentType),
192
178
 
193
- notImplemented: (msg = "Not Implemented", contentType?: string) =>
179
+ notImplemented: (msg = 'Not Implemented', contentType?: string) =>
194
180
  createErrorResponse(501, msg, contentType),
195
181
 
196
- badGateway: (msg = "Bad Gateway", contentType?: string) =>
182
+ badGateway: (msg = 'Bad Gateway', contentType?: string) =>
197
183
  createErrorResponse(502, msg, contentType),
198
184
 
199
- serviceUnavailable: (msg = "Service Unavailable", contentType?: string) =>
185
+ serviceUnavailable: (msg = 'Service Unavailable', contentType?: string) =>
200
186
  createErrorResponse(503, msg, contentType),
201
187
 
202
- gatewayTimeout: (msg = "Gateway Timeout", contentType?: string) =>
188
+ gatewayTimeout: (msg = 'Gateway Timeout', contentType?: string) =>
203
189
  createErrorResponse(504, msg, contentType),
204
190
 
205
- httpVersionNotSupported: (
206
- msg = "HTTP Version Not Supported",
207
- contentType?: string
208
- ) => createErrorResponse(505, msg, contentType),
191
+ httpVersionNotSupported: (msg = 'HTTP Version Not Supported', contentType?: string) =>
192
+ createErrorResponse(505, msg, contentType),
209
193
 
210
- variantAlsoNegotiates: (
211
- msg = "Variant Also Negotiates",
212
- contentType?: string
213
- ) => createErrorResponse(506, msg, contentType),
194
+ variantAlsoNegotiates: (msg = 'Variant Also Negotiates', contentType?: string) =>
195
+ createErrorResponse(506, msg, contentType),
214
196
 
215
- insufficientStorage: (msg = "Insufficient Storage", contentType?: string) =>
197
+ insufficientStorage: (msg = 'Insufficient Storage', contentType?: string) =>
216
198
  createErrorResponse(507, msg, contentType),
217
199
 
218
- loopDetected: (msg = "Loop Detected", contentType?: string) =>
200
+ loopDetected: (msg = 'Loop Detected', contentType?: string) =>
219
201
  createErrorResponse(508, msg, contentType),
220
202
 
221
- notExtended: (msg = "Not Extended", contentType?: string) =>
203
+ notExtended: (msg = 'Not Extended', contentType?: string) =>
222
204
  createErrorResponse(510, msg, contentType),
223
205
 
224
- networkAuthenticationRequired: (
225
- msg = "Network Authentication Required",
226
- contentType?: string
227
- ) => createErrorResponse(511, msg, contentType),
206
+ networkAuthenticationRequired: (msg = 'Network Authentication Required', contentType?: string) =>
207
+ createErrorResponse(511, msg, contentType),
228
208
 
229
209
  // Aliases for common use cases
230
- invalidArgument: (msg = "Invalid Argument", contentType?: string) =>
210
+ invalidArgument: (msg = 'Invalid Argument', contentType?: string) =>
231
211
  createErrorResponse(HTTP_STATUS.UNPROCESSABLE_ENTITY, msg, contentType),
232
212
 
233
- rateLimitExceeded: (msg = "Rate Limit Exceeded", contentType?: string) =>
213
+ rateLimitExceeded: (msg = 'Rate Limit Exceeded', contentType?: string) =>
234
214
  createErrorResponse(429, msg, contentType),
235
215
 
236
- maintenance: (msg = "Service Under Maintenance", contentType?: string) =>
216
+ maintenance: (msg = 'Service Under Maintenance', contentType?: string) =>
237
217
  createErrorResponse(503, msg, contentType),
238
218
 
239
219
  // Helper to create custom error with any status code
@@ -250,13 +230,11 @@ export function createResponse(
250
230
 
251
231
  return new Response(body as string, {
252
232
  status: statusCode,
253
- headers: { "content-type": contentType },
233
+ headers: { 'content-type': contentType },
254
234
  });
255
235
  }
256
236
 
257
- export const protectedRoute = async <
258
- TTypes extends VectorTypes = DefaultVectorTypes
259
- >(
237
+ export const protectedRoute = async <TTypes extends VectorTypes = DefaultVectorTypes>(
260
238
  request: VectorRequest<TTypes>,
261
239
  responseContentType?: string
262
240
  ) => {
@@ -265,10 +243,7 @@ export const protectedRoute = async <
265
243
 
266
244
  const protectedHandler = vector.getProtectedHandler();
267
245
  if (!protectedHandler) {
268
- throw APIError.unauthorized(
269
- "Authentication not configured",
270
- responseContentType
271
- );
246
+ throw APIError.unauthorized('Authentication not configured', responseContentType);
272
247
  }
273
248
 
274
249
  try {
@@ -276,7 +251,7 @@ export const protectedRoute = async <
276
251
  request.authUser = authUser as GetAuthType<TTypes>;
277
252
  } catch (error) {
278
253
  throw APIError.unauthorized(
279
- error instanceof Error ? error.message : "Authentication failed",
254
+ error instanceof Error ? error.message : 'Authentication failed',
280
255
  responseContentType
281
256
  );
282
257
  }
@@ -307,15 +282,12 @@ export function api<TTypes extends VectorTypes = DefaultVectorTypes>(
307
282
  // This wrapper is only used when routes are NOT auto-discovered
308
283
  return async (request: IRequest) => {
309
284
  if (!expose) {
310
- return APIError.forbidden("Forbidden");
285
+ return APIError.forbidden('Forbidden');
311
286
  }
312
287
 
313
288
  try {
314
289
  if (auth) {
315
- await protectedRoute(
316
- request as any as VectorRequest<TTypes>,
317
- responseContentType
318
- );
290
+ await protectedRoute(request as any as VectorRequest<TTypes>, responseContentType);
319
291
  }
320
292
 
321
293
  if (!rawRequest) {
@@ -325,9 +297,7 @@ export function api<TTypes extends VectorTypes = DefaultVectorTypes>(
325
297
  // Cache handling is now done in the router
326
298
  const result = await fn(request as any as VectorRequest<TTypes>);
327
299
 
328
- return rawResponse
329
- ? result
330
- : ApiResponse.success(result, responseContentType);
300
+ return rawResponse ? result : ApiResponse.success(result, responseContentType);
331
301
  } catch (err: unknown) {
332
302
  // Ensure we return a Response object
333
303
  if (err instanceof Response) {
package/src/index.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  // Public exports for route definitions only
2
- import { route } from "./http";
2
+ import { route } from './http';
3
3
 
4
4
  // Export route function for defining routes
5
5
  export { route };
6
6
 
7
7
  // Export utilities for route handlers
8
- export { APIError, createResponse } from "./http";
8
+ export { APIError, createResponse } from './http';
9
9
 
10
10
  // Export types for TypeScript users
11
- export * from "./types";
11
+ export * from './types';
12
12
 
13
13
  // Note: Vector framework is now config-driven and runs via CLI
14
14
  // Usage: Create vector.config.ts and run 'vector dev' or 'vector start'
@@ -4,11 +4,9 @@ import type {
4
4
  DefaultVectorTypes,
5
5
  VectorRequest,
6
6
  VectorTypes,
7
- } from "../types";
7
+ } from '../types';
8
8
 
9
- export class MiddlewareManager<
10
- TTypes extends VectorTypes = DefaultVectorTypes
11
- > {
9
+ export class MiddlewareManager<TTypes extends VectorTypes = DefaultVectorTypes> {
12
10
  private beforeHandlers: BeforeMiddlewareHandler<TTypes>[] = [];
13
11
  private finallyHandlers: AfterMiddlewareHandler<TTypes>[] = [];
14
12
 
@@ -20,9 +18,7 @@ export class MiddlewareManager<
20
18
  this.finallyHandlers.push(...handlers);
21
19
  }
22
20
 
23
- async executeBefore(
24
- request: VectorRequest<TTypes>
25
- ): Promise<VectorRequest<TTypes> | Response> {
21
+ async executeBefore(request: VectorRequest<TTypes>): Promise<VectorRequest<TTypes> | Response> {
26
22
  let currentRequest = request;
27
23
 
28
24
  for (const handler of this.beforeHandlers) {
@@ -38,10 +34,7 @@ export class MiddlewareManager<
38
34
  return currentRequest;
39
35
  }
40
36
 
41
- async executeFinally(
42
- response: Response,
43
- request: VectorRequest<TTypes>
44
- ): Promise<Response> {
37
+ async executeFinally(response: Response, request: VectorRequest<TTypes>): Promise<Response> {
45
38
  let currentResponse = response;
46
39
 
47
40
  for (const handler of this.finallyHandlers) {
package/src/utils/path.ts CHANGED
@@ -1,9 +1,18 @@
1
1
  export function toFileUrl(path: string): string {
2
- return process.platform === 'win32'
3
- ? `file:///${path.replace(/\\/g, '/')}`
4
- : path;
2
+ return process.platform === 'win32' ? `file:///${path.replace(/\\/g, '/')}` : path;
5
3
  }
6
4
 
7
5
  export function normalizePath(path: string): string {
8
6
  return path.replace(/\\/g, '/').replace(/\/+/g, '/');
9
- }
7
+ }
8
+
9
+ export function buildRouteRegex(path: string): RegExp {
10
+ return RegExp(
11
+ `^${path
12
+ .replace(/\/+(\/|$)/g, '$1')
13
+ .replace(/(\/?\.?):(\w+)\+/g, '($1(?<$2>[\\s\\S]+))')
14
+ .replace(/(\/?\.?):(\w+)/g, '($1(?<$2>[^$1/]+?))')
15
+ .replace(/\./g, '\\.')
16
+ .replace(/(\/?)\*/g, '($1.*)?')}/*$`
17
+ );
18
+ }