@carno.js/core 0.2.9 → 0.2.11

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.
@@ -2,137 +2,244 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.compileRouteHandler = compileRouteHandler;
4
4
  exports.compileValidatedHandler = compileValidatedHandler;
5
+ const TEXT_HEADERS = Object.freeze({
6
+ 'Content-Type': 'text/html'
7
+ });
8
+ const JSON_HEADERS = Object.freeze({
9
+ 'Content-Type': 'application/json'
10
+ });
11
+ function isBodyInitResult(result) {
12
+ if (!result) {
13
+ return false;
14
+ }
15
+ if (result instanceof ReadableStream) {
16
+ return true;
17
+ }
18
+ if (result instanceof Blob || result instanceof ArrayBuffer) {
19
+ return true;
20
+ }
21
+ if (ArrayBuffer.isView(result)) {
22
+ return true;
23
+ }
24
+ return result instanceof FormData || result instanceof URLSearchParams;
25
+ }
26
+ /**
27
+ * Cria handler inline com response building integrado.
28
+ * Usado quando não há parâmetros.
29
+ */
30
+ function createInlineResponseHandler(handler, isAsync) {
31
+ if (isAsync) {
32
+ return async (c) => {
33
+ const r = await handler();
34
+ const s = c.getResponseStatus() || 200;
35
+ if (typeof r === 'string')
36
+ return new Response(r, { status: s, headers: TEXT_HEADERS });
37
+ if (r instanceof Response)
38
+ return r;
39
+ if (r == null)
40
+ return new Response('', { status: s, headers: TEXT_HEADERS });
41
+ if (typeof r === 'number' || typeof r === 'boolean') {
42
+ return new Response(String(r), { status: s, headers: TEXT_HEADERS });
43
+ }
44
+ if (isBodyInitResult(r)) {
45
+ return new Response(r, { status: s });
46
+ }
47
+ return new Response(JSON.stringify(r), { status: s, headers: JSON_HEADERS });
48
+ };
49
+ }
50
+ return (c) => {
51
+ const r = handler();
52
+ const s = c.getResponseStatus() || 200;
53
+ if (typeof r === 'string')
54
+ return new Response(r, { status: s, headers: TEXT_HEADERS });
55
+ if (r instanceof Response)
56
+ return r;
57
+ if (r == null)
58
+ return new Response('', { status: s, headers: TEXT_HEADERS });
59
+ if (typeof r === 'number' || typeof r === 'boolean') {
60
+ return new Response(String(r), { status: s, headers: TEXT_HEADERS });
61
+ }
62
+ if (isBodyInitResult(r)) {
63
+ return new Response(r, { status: s });
64
+ }
65
+ return new Response(JSON.stringify(r), { status: s, headers: JSON_HEADERS });
66
+ };
67
+ }
5
68
  function escapeKey(key) {
6
- return key.replace(/['"\\]/g, '\\$&');
69
+ return key.replace(/['\"\\]/g, '\\$&');
7
70
  }
8
- function buildArgExpression(param, index) {
71
+ /**
72
+ * Gera expressão de acesso a parâmetro inline.
73
+ * Otimizado para evitar chamadas de função quando possível.
74
+ */
75
+ function buildArgExpression(param) {
9
76
  const key = param.key ? escapeKey(param.key) : undefined;
10
77
  switch (param.type) {
11
78
  case 'param':
12
- return key ? `ctx.param['${key}']` : 'ctx.param';
79
+ return key ? `c.param['${key}']` : 'c.param';
13
80
  case 'query':
14
- return key ? `ctx.query['${key}']` : 'ctx.query';
81
+ return key ? `c.query['${key}']` : 'c.query';
15
82
  case 'body':
16
- return key ? `ctx.body['${key}']` : 'ctx.body';
83
+ return key ? `c.body['${key}']` : 'c.body';
17
84
  case 'headers':
18
- return key
19
- ? `ctx.headers.get('${key}')`
20
- : 'ctx.headers';
85
+ return key ? `c.headers.get('${key}')` : 'c.headers';
21
86
  case 'req':
22
- return 'ctx.req';
87
+ return 'c.req';
23
88
  case 'locals':
24
- return 'ctx.locals';
25
- case 'di':
26
- return `resolvers[${index}](ctx)`;
89
+ return 'c.locals';
27
90
  default:
28
- return `resolvers[${index}](ctx)`;
91
+ return 'undefined';
29
92
  }
30
93
  }
94
+ /**
95
+ * Compila route handler em função otimizada.
96
+ *
97
+ * Estratégias de otimização:
98
+ * - Inline de acesso a parâmetros
99
+ * - Bind do handler no compile time
100
+ * - Código gerado monomórfico
101
+ * - Sem overhead de resolvers array
102
+ */
103
+ /**
104
+ * Compila route handler em função otimizada que retorna Response inline.
105
+ *
106
+ * Estratégias de otimização:
107
+ * - Inline de acesso a parâmetros
108
+ * - Inline de response building (elimina executeFastPath)
109
+ * - Bind do handler no compile time
110
+ * - Código gerado monomórfico
111
+ * - Headers pré-frozen para otimização V8
112
+ */
31
113
  function compileRouteHandler(instance, methodName, paramInfos) {
32
114
  const handler = instance[methodName].bind(instance);
33
115
  if (paramInfos.length === 0) {
34
- return () => handler();
116
+ return createInlineResponseHandler(handler, false);
35
117
  }
36
- const hasBodyParam = paramInfos.some((p) => p.type === 'body');
37
118
  const hasDIParam = paramInfos.some((p) => p.type === 'di');
38
119
  if (hasDIParam) {
39
120
  return createFallbackHandler(handler, paramInfos);
40
121
  }
122
+ const hasBodyParam = paramInfos.some((p) => p.type === 'body');
41
123
  const argExpressions = paramInfos.map(buildArgExpression);
124
+ const argsCode = argExpressions.join(',');
42
125
  if (hasBodyParam) {
43
- return createAsyncHandler(handler, argExpressions);
126
+ const code = `return async function(c){
127
+ await c.getBody();
128
+ const r=await h(${argsCode});
129
+ const s=c.getResponseStatus()||200;
130
+ if(typeof r==='string')return new Response(r,{status:s,headers:TH});
131
+ if(r instanceof Response)return r;
132
+ if(r==null)return new Response('',{status:s,headers:TH});
133
+ if(typeof r==='number'||typeof r==='boolean')return new Response(String(r),{status:s,headers:TH});
134
+ if(isBI(r))return new Response(r,{status:s});
135
+ return new Response(JSON.stringify(r),{status:s,headers:JH});
136
+ }`;
137
+ return new Function('h', 'TH', 'JH', 'isBI', code)(handler, TEXT_HEADERS, JSON_HEADERS, isBodyInitResult);
44
138
  }
45
- return createSyncHandler(handler, argExpressions);
46
- }
47
- function createSyncHandler(handler, argExpressions) {
48
- const code = `
49
- return function compiledHandler(ctx) {
50
- return handler(${argExpressions.join(', ')});
51
- }
52
- `;
53
- return new Function('handler', code)(handler);
54
- }
55
- function createAsyncHandler(handler, argExpressions) {
56
- const code = `
57
- return async function compiledHandler(ctx) {
58
- await ctx.getBody();
59
- return handler(${argExpressions.join(', ')});
60
- }
61
- `;
62
- return new Function('handler', code)(handler);
139
+ const code = `return function(c){
140
+ const r=h(${argsCode});
141
+ const s=c.getResponseStatus()||200;
142
+ if(typeof r==='string')return new Response(r,{status:s,headers:TH});
143
+ if(r instanceof Response)return r;
144
+ if(r==null)return new Response('',{status:s,headers:TH});
145
+ if(typeof r==='number'||typeof r==='boolean')return new Response(String(r),{status:s,headers:TH});
146
+ if(isBI(r))return new Response(r,{status:s});
147
+ return new Response(JSON.stringify(r),{status:s,headers:JH});
148
+ }`;
149
+ return new Function('h', 'TH', 'JH', 'isBI', code)(handler, TEXT_HEADERS, JSON_HEADERS, isBodyInitResult);
63
150
  }
151
+ /**
152
+ * Handler fallback para casos com DI.
153
+ */
64
154
  function createFallbackHandler(handler, paramInfos) {
65
155
  return (ctx) => {
66
- const args = [];
67
- for (const param of paramInfos) {
68
- switch (param.type) {
69
- case 'param':
70
- args.push(param.key ? ctx.param[param.key] : ctx.param);
71
- break;
72
- case 'query':
73
- args.push(param.key ? ctx.query[param.key] : ctx.query);
74
- break;
75
- case 'body':
76
- args.push(param.key ? ctx.body[param.key] : ctx.body);
77
- break;
78
- case 'headers':
79
- args.push(param.key ? ctx.headers.get(param.key) : ctx.headers);
80
- break;
81
- case 'req':
82
- args.push(ctx.req);
83
- break;
84
- case 'locals':
85
- args.push(ctx.locals);
86
- break;
87
- default:
88
- args.push(undefined);
89
- break;
90
- }
91
- }
156
+ const args = resolveArgs(paramInfos, ctx);
92
157
  return handler(...args);
93
158
  };
94
159
  }
95
- function compileValidatedHandler(instance, methodName, paramInfos, validators) {
160
+ /**
161
+ * Resolve argumentos para fallback handler.
162
+ */
163
+ function resolveArgs(paramInfos, ctx) {
164
+ const args = new Array(paramInfos.length);
165
+ let i = 0;
166
+ for (const param of paramInfos) {
167
+ args[i++] = resolveArg(param, ctx);
168
+ }
169
+ return args;
170
+ }
171
+ /**
172
+ * Resolve um argumento individual.
173
+ */
174
+ function resolveArg(param, ctx) {
175
+ switch (param.type) {
176
+ case 'param':
177
+ return param.key ? ctx.param[param.key] : ctx.param;
178
+ case 'query':
179
+ return param.key ? ctx.query[param.key] : ctx.query;
180
+ case 'body':
181
+ return param.key ? ctx.body[param.key] : ctx.body;
182
+ case 'headers':
183
+ return param.key ? ctx.headers.get(param.key) : ctx.headers;
184
+ case 'req':
185
+ return ctx.req;
186
+ case 'locals':
187
+ return ctx.locals;
188
+ default:
189
+ return undefined;
190
+ }
191
+ }
192
+ /**
193
+ * Compila handler com validação inline.
194
+ */
195
+ function compileValidatedHandler(instance, methodName, paramInfos, validatorAdapter) {
96
196
  const handler = instance[methodName].bind(instance);
97
197
  const hasBodyParam = paramInfos.some((p) => p.type === 'body');
98
- const resolveArg = (ctx, param, index) => {
99
- let value;
100
- switch (param.type) {
101
- case 'param':
102
- value = param.key ? ctx.param[param.key] : ctx.param;
103
- break;
104
- case 'query':
105
- value = param.key ? ctx.query[param.key] : ctx.query;
106
- break;
107
- case 'body':
108
- value = param.key ? ctx.body[param.key] : ctx.body;
109
- break;
110
- case 'headers':
111
- value = param.key ? ctx.headers.get(param.key) : ctx.headers;
112
- break;
113
- case 'req':
114
- value = ctx.req;
115
- break;
116
- case 'locals':
117
- value = ctx.locals;
118
- break;
119
- default:
120
- value = undefined;
121
- }
122
- if (param.needsValidation && validators[index]) {
123
- return validators[index](value);
124
- }
125
- return value;
126
- };
198
+ if (paramInfos.length === 0) {
199
+ return createInlineResponseHandler(handler, false);
200
+ }
201
+ const { argAssignments, argList, tokenParams, tokenValues, } = buildValidatedArgs(paramInfos);
127
202
  if (hasBodyParam) {
128
- return async (ctx) => {
129
- await ctx.getBody();
130
- const args = paramInfos.map((p, i) => resolveArg(ctx, p, i));
131
- return handler(...args);
132
- };
203
+ const code = `return async function(c){\nawait c.getBody();\n${argAssignments}\nconst r=await h(${argList});\nconst s=c.getResponseStatus()||200;\nif(typeof r==='string')return new Response(r,{status:s,headers:TH});\nif(r instanceof Response)return r;\nif(r==null)return new Response('',{status:s,headers:TH});\nif(typeof r==='number'||typeof r==='boolean')return new Response(String(r),{status:s,headers:TH});\nif(isBI(r))return new Response(r,{status:s});\nreturn new Response(JSON.stringify(r),{status:s,headers:JH});\n}`;
204
+ return new Function('h', 'va', ...tokenParams, 'TH', 'JH', 'isBI', code)(handler, validatorAdapter, ...tokenValues, TEXT_HEADERS, JSON_HEADERS, isBodyInitResult);
133
205
  }
134
- return (ctx) => {
135
- const args = paramInfos.map((p, i) => resolveArg(ctx, p, i));
136
- return handler(...args);
206
+ const code = `return function(c){\n${argAssignments}\nconst r=h(${argList});\nconst s=c.getResponseStatus()||200;\nif(typeof r==='string')return new Response(r,{status:s,headers:TH});\nif(r instanceof Response)return r;\nif(r==null)return new Response('',{status:s,headers:TH});\nif(typeof r==='number'||typeof r==='boolean')return new Response(String(r),{status:s,headers:TH});\nif(isBI(r))return new Response(r,{status:s});\nreturn new Response(JSON.stringify(r),{status:s,headers:JH});\n}`;
207
+ return new Function('h', 'va', ...tokenParams, 'TH', 'JH', 'isBI', code)(handler, validatorAdapter, ...tokenValues, TEXT_HEADERS, JSON_HEADERS, isBodyInitResult);
208
+ }
209
+ function buildValidatedArgs(paramInfos) {
210
+ const assignments = [];
211
+ const args = [];
212
+ const tokenParams = [];
213
+ const tokenValues = [];
214
+ let index = 0;
215
+ for (const param of paramInfos) {
216
+ const argName = `a${index}`;
217
+ const argExpr = buildArgExpression(param);
218
+ const tokenName = getTokenParamName(param, index, tokenParams, tokenValues);
219
+ const valueExpr = buildValidatedExpression(argExpr, tokenName);
220
+ assignments.push(`const ${argName}=${valueExpr};`);
221
+ args.push(argName);
222
+ index += 1;
223
+ }
224
+ return {
225
+ argAssignments: assignments.join('\n'),
226
+ argList: args.join(','),
227
+ tokenParams,
228
+ tokenValues,
137
229
  };
138
230
  }
231
+ function buildValidatedExpression(argExpr, tokenName) {
232
+ if (!tokenName) {
233
+ return argExpr;
234
+ }
235
+ return `va.validateAndTransform(${tokenName}, ${argExpr})`;
236
+ }
237
+ function getTokenParamName(param, index, tokenParams, tokenValues) {
238
+ if (!param.needsValidation || !param.token) {
239
+ return null;
240
+ }
241
+ const tokenName = `t${index}`;
242
+ tokenParams.push(tokenName);
243
+ tokenValues.push(param.token);
244
+ return tokenName;
245
+ }
@@ -1,16 +1,11 @@
1
1
  import { TokenRouteWithProvider, TokenRouteWithProviderMap } from "../container";
2
2
  import { Context } from "../domain";
3
+ interface ParsedUrl {
4
+ pathname: string;
5
+ }
3
6
  declare class Matcher {
4
7
  match(request: Request, routes: TokenRouteWithProviderMap, context: Context): TokenRouteWithProvider;
5
- /**
6
- * Identify route by url path.
7
- * The route can have params (:param) and wildcards (*).
8
- *
9
- * @param route
10
- * @param url
11
- * @param context
12
- */
13
- identifyRoute(route: TokenRouteWithProvider, url: URL, context: Context): boolean;
8
+ identifyRoute(route: TokenRouteWithProvider, url: ParsedUrl, context: Context): boolean;
14
9
  }
15
10
  export declare const RouteResolver: Matcher;
16
11
  export {};
@@ -1,7 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RouteResolver = void 0;
4
- const parseUrl = require('parseurl-fast');
4
+ function parseUrl(request) {
5
+ const url = request.url;
6
+ const startIndex = url.indexOf('/', 12);
7
+ const queryIndex = url.indexOf('?', startIndex);
8
+ if (queryIndex === -1) {
9
+ return { pathname: startIndex === -1 ? '/' : url.slice(startIndex) };
10
+ }
11
+ return { pathname: url.slice(startIndex, queryIndex) };
12
+ }
5
13
  class Matcher {
6
14
  match(request, routes, context) {
7
15
  const method = request.method.toLowerCase();
@@ -10,20 +18,12 @@ class Matcher {
10
18
  }
11
19
  const routeMethod = routes.get(method);
12
20
  const url = parseUrl(request);
13
- const route = routeMethod?.find(route => this.identifyRoute(route, url, context));
21
+ const route = routeMethod?.find((route) => this.identifyRoute(route, url, context));
14
22
  if (!route) {
15
23
  throw new Error('Method not allowed');
16
24
  }
17
25
  return route;
18
26
  }
19
- /**
20
- * Identify route by url path.
21
- * The route can have params (:param) and wildcards (*).
22
- *
23
- * @param route
24
- * @param url
25
- * @param context
26
- */
27
27
  identifyRoute(route, url, context) {
28
28
  const urlPath = url.pathname.split('/');
29
29
  const routePathSegments = route.path.split('/');
@@ -25,5 +25,4 @@ export declare class RouteCompiler {
25
25
  private createStandardRoute;
26
26
  private createComplexRoute;
27
27
  private createFallbackRoute;
28
- private createValidatedHandler;
29
28
  }
@@ -67,7 +67,7 @@ class RouteCompiler {
67
67
  const hasBodyParam = paramInfos.some((p) => p.type === 'body');
68
68
  let boundHandler;
69
69
  if (hasValidation) {
70
- boundHandler = this.createValidatedHandler(instance, route.methodName, paramInfos, hasBodyParam);
70
+ boundHandler = (0, JITCompiler_1.compileValidatedHandler)(instance, route.methodName, paramInfos, this.validatorAdapter);
71
71
  }
72
72
  else {
73
73
  boundHandler = (0, JITCompiler_1.compileRouteHandler)(instance, route.methodName, paramInfos);
@@ -153,48 +153,5 @@ class RouteCompiler {
153
153
  original: route,
154
154
  };
155
155
  }
156
- createValidatedHandler(instance, methodName, paramInfos, hasBodyParam) {
157
- const handler = instance[methodName].bind(instance);
158
- const resolveArg = (ctx, param) => {
159
- let value;
160
- switch (param.type) {
161
- case 'param':
162
- value = param.key ? ctx.param[param.key] : ctx.param;
163
- break;
164
- case 'query':
165
- value = param.key ? ctx.query[param.key] : ctx.query;
166
- break;
167
- case 'body':
168
- value = param.key ? ctx.body[param.key] : ctx.body;
169
- break;
170
- case 'headers':
171
- value = param.key ? ctx.headers.get(param.key) : ctx.headers;
172
- break;
173
- case 'req':
174
- value = ctx.req;
175
- break;
176
- case 'locals':
177
- value = ctx.locals;
178
- break;
179
- default:
180
- value = undefined;
181
- }
182
- if (param.needsValidation && param.token) {
183
- return this.validatorAdapter.validateAndTransform(param.token, value);
184
- }
185
- return value;
186
- };
187
- if (hasBodyParam) {
188
- return async (ctx) => {
189
- await ctx.getBody();
190
- const args = paramInfos.map((p) => resolveArg(ctx, p));
191
- return handler(...args);
192
- };
193
- }
194
- return (ctx) => {
195
- const args = paramInfos.map((p) => resolveArg(ctx, p));
196
- return handler(...args);
197
- };
198
- }
199
156
  }
200
157
  exports.RouteCompiler = RouteCompiler;
@@ -2,13 +2,11 @@ import { InjectorService, TokenRouteWithProvider } from "../container";
2
2
  import { Context, LocalsContainer } from "../domain";
3
3
  import type { CompiledRoute } from "./CompiledRoute";
4
4
  declare class Router {
5
- private readonly jsonHeaders;
6
5
  private readonly textHeaders;
7
6
  executeRoute(routeStore: CompiledRoute | TokenRouteWithProvider, injector: InjectorService, context: Context, locals: LocalsContainer): Promise<Response>;
8
7
  mountResponse(result: unknown, context: Context): Response;
9
8
  private isNativeResponse;
10
9
  private isBodyInit;
11
- private createJsonResponse;
12
10
  }
13
11
  export declare const RouteExecutor: Router;
14
12
  export {};
@@ -4,7 +4,6 @@ exports.RouteExecutor = void 0;
4
4
  const events_1 = require("../events");
5
5
  class Router {
6
6
  constructor() {
7
- this.jsonHeaders = { "Content-Type": "application/json" };
8
7
  this.textHeaders = { "Content-Type": "text/html" };
9
8
  }
10
9
  async executeRoute(routeStore, injector, context, locals) {
@@ -24,7 +23,24 @@ class Router {
24
23
  if (injector.hasOnResponseHook()) {
25
24
  await injector.callHook(events_1.EventType.OnResponse, { context, result });
26
25
  }
27
- return this.mountResponse(result, context);
26
+ const status = context.getResponseStatus() || 200;
27
+ if (result instanceof Response) {
28
+ return result;
29
+ }
30
+ if (result === null || result === undefined) {
31
+ return new Response("", { status, headers: this.textHeaders });
32
+ }
33
+ const resultType = typeof result;
34
+ if (resultType === "string") {
35
+ return new Response(result, { status, headers: this.textHeaders });
36
+ }
37
+ if (resultType === "number" || resultType === "boolean") {
38
+ return new Response(String(result), { status, headers: this.textHeaders });
39
+ }
40
+ if (this.isBodyInit(result)) {
41
+ return new Response(result, { status });
42
+ }
43
+ return Response.json(result, { status });
28
44
  }
29
45
  mountResponse(result, context) {
30
46
  const status = context.getResponseStatus() || 200;
@@ -44,7 +60,7 @@ class Router {
44
60
  if (this.isBodyInit(result)) {
45
61
  return new Response(result, { status });
46
62
  }
47
- return this.createJsonResponse(result, status);
63
+ return Response.json(result, { status });
48
64
  }
49
65
  isNativeResponse(result) {
50
66
  return result instanceof Response;
@@ -64,15 +80,5 @@ class Router {
64
80
  }
65
81
  return result instanceof FormData || result instanceof URLSearchParams;
66
82
  }
67
- createJsonResponse(body, status) {
68
- try {
69
- const json = JSON.stringify(body);
70
- return new Response(json, { status, headers: this.jsonHeaders });
71
- }
72
- catch (error) {
73
- const fallback = JSON.stringify({ error: "Serialization failed" });
74
- return new Response(fallback, { status: 500, headers: this.jsonHeaders });
75
- }
76
- }
77
83
  }
78
84
  exports.RouteExecutor = new Router();
@@ -18,10 +18,12 @@ export interface Node<T> {
18
18
  export declare class Memoirist<T> {
19
19
  root: Record<string, Node<T>>;
20
20
  history: [string, string, T][];
21
+ private routeCache;
21
22
  private static regex;
22
23
  add(method: string, path: string, store: T): FindResult<T>['store'];
23
24
  find(method: string, url: string): FindResult<T> | null;
24
25
  updateStore(method: string, path: string, oldStore: T, newStore: T): boolean;
26
+ private invalidateCache;
25
27
  private updateHistoryStore;
26
28
  private normalizePath;
27
29
  private findNode;
@@ -1,6 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Memoirist = void 0;
4
+ /**
5
+ * * Empty object shared for static routes without parameters.
6
+ * * Frozen for V8 optimization and immutability.
7
+ * * Avoid allocating an empty object on every request in static routes.
8
+ * */
9
+ const EMPTY_PARAMS = Object.freeze({});
4
10
  const createNode = (part, inert) => ({
5
11
  part,
6
12
  store: null,
@@ -54,6 +60,7 @@ class Memoirist {
54
60
  constructor() {
55
61
  this.root = {};
56
62
  this.history = [];
63
+ this.routeCache = new Map();
57
64
  }
58
65
  add(method, path, store) {
59
66
  if (typeof path !== 'string')
@@ -62,6 +69,7 @@ class Memoirist {
62
69
  path = '/';
63
70
  else if (path[0] !== '/')
64
71
  path = `/${path}`;
72
+ this.invalidateCache();
65
73
  this.history.push([method, path, store]);
66
74
  const isWildcard = path[path.length - 1] === '*';
67
75
  if (isWildcard) {
@@ -162,10 +170,20 @@ class Memoirist {
162
170
  return node.store;
163
171
  }
164
172
  find(method, url) {
173
+ let methodCache = this.routeCache.get(url);
174
+ if (methodCache && methodCache[method] !== undefined) {
175
+ return methodCache[method];
176
+ }
165
177
  const root = this.root[method];
166
178
  if (!root)
167
179
  return null;
168
- return matchRoute(url, url.length, root, 0);
180
+ const result = matchRoute(url, url.length, root, 0);
181
+ if (!methodCache) {
182
+ methodCache = {};
183
+ this.routeCache.set(url, methodCache);
184
+ }
185
+ methodCache[method] = result;
186
+ return result;
169
187
  }
170
188
  updateStore(method, path, oldStore, newStore) {
171
189
  const node = this.findNode(method, path);
@@ -175,6 +193,7 @@ class Memoirist {
175
193
  if (node.store === oldStore) {
176
194
  node.store = newStore;
177
195
  this.updateHistoryStore(method, path, newStore);
196
+ this.invalidateCache();
178
197
  return true;
179
198
  }
180
199
  if (node.params?.store === oldStore) {
@@ -184,15 +203,20 @@ class Memoirist {
184
203
  node.params.names.set(newStore, paramName);
185
204
  }
186
205
  this.updateHistoryStore(method, path, newStore);
206
+ this.invalidateCache();
187
207
  return true;
188
208
  }
189
209
  if (node.wildcardStore === oldStore) {
190
210
  node.wildcardStore = newStore;
191
211
  this.updateHistoryStore(method, path, newStore);
212
+ this.invalidateCache();
192
213
  return true;
193
214
  }
194
215
  return false;
195
216
  }
217
+ invalidateCache() {
218
+ this.routeCache.clear();
219
+ }
196
220
  updateHistoryStore(method, path, newStore) {
197
221
  const normalizedPath = this.normalizePath(path);
198
222
  for (let i = 0; i < this.history.length; i++) {
@@ -288,7 +312,7 @@ const matchRoute = (url, urlLength, node, startIndex) => {
288
312
  if (node.store !== null)
289
313
  return {
290
314
  store: node.store,
291
- params: {}
315
+ params: EMPTY_PARAMS
292
316
  };
293
317
  if (node.wildcardStore !== null)
294
318
  return {
@@ -325,7 +349,13 @@ const matchRoute = (url, urlLength, node, startIndex) => {
325
349
  const route = matchRoute(url, urlLength, param.inert, slashIndex);
326
350
  if (route !== null) {
327
351
  const paramName = resolveParamName(param, route.store);
328
- route.params[paramName] = url.substring(endIndex, slashIndex);
352
+ const paramValue = url.substring(endIndex, slashIndex);
353
+ if (route.params === EMPTY_PARAMS) {
354
+ route.params = { [paramName]: paramValue };
355
+ }
356
+ else {
357
+ route.params[paramName] = paramValue;
358
+ }
329
359
  return route;
330
360
  }
331
361
  }
@@ -9,13 +9,6 @@ class LoggerService {
9
9
  constructor(injector) {
10
10
  this.injector = injector;
11
11
  const pinoConfig = this.injector.applicationConfig.logger || {};
12
- pinoConfig['transport'] = pinoConfig.transport || {
13
- target: 'pino-pretty',
14
- options: {
15
- colorize: true,
16
- ignore: 'pid,hostname',
17
- },
18
- };
19
12
  this.logger = (0, pino_1.default)(pinoConfig);
20
13
  }
21
14
  child(bindings) {