@webtypen/webframez-core 0.3.76 → 0.3.77

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/README.md CHANGED
@@ -925,3 +925,16 @@ the text representation; their separate `icon` property controls navigation icon
925
925
  ## DataBuilder model forms
926
926
 
927
927
  DataBuilder supports `@Field()`, parameterized `@Validates()`, server-side `ModelForm` classes and `Route.databuilder()` while retaining the v1 request protocol and legacy controller registrations. See [DataBuilder documentation](docs/databuilder.md).
928
+
929
+ ## Routing and lifecycle performance
930
+
931
+ Route and domain patterns are compiled at registration and reused while matching.
932
+ `Router.init()` resets their caches; runtime registration and legacy direct route
933
+ stores still work. Exact-route precedence, domain ordering, parameters and wildcards
934
+ retain their existing behavior. Public regex builders still produce fresh regexes.
935
+
936
+ Lifecycle hooks skip event normalization when no listeners are registered. Use
937
+ `WebframezHooks.hasListeners(eventName)` before constructing an expensive payload.
938
+ Registered handlers retain their ordered, awaited execution and snapshot behavior.
939
+ The router applies this check before building telemetry payloads. Middleware keeps
940
+ its existing `next`/`reject` callback contract.
@@ -32,6 +32,7 @@ declare class WebframezHooksFacade {
32
32
  on<TEventName extends WebframezHookEventName>(eventName: TEventName, handler: WebframezHookHandler<TEventName>): () => this;
33
33
  off<TEventName extends WebframezHookEventName>(eventName: TEventName, handler: WebframezHookHandler<TEventName>): this;
34
34
  clear(eventName?: WebframezHookEventName): this;
35
+ hasListeners(eventName: WebframezHookEventName): boolean;
35
36
  emit<TEventName extends WebframezHookEventName>(eventName: TEventName, context: Omit<Partial<WebframezOperationContext>, "operation" | "phase"> & {
36
37
  operationId: string;
37
38
  }): Promise<void>;
@@ -56,8 +56,16 @@ class WebframezHooksFacade {
56
56
  this.handlers = {};
57
57
  return this;
58
58
  }
59
+ hasListeners(eventName) {
60
+ var _a;
61
+ return (((_a = this.handlers[eventName]) === null || _a === void 0 ? void 0 : _a.size) || 0) > 0;
62
+ }
59
63
  emit(eventName, context) {
60
64
  return __awaiter(this, void 0, void 0, function* () {
65
+ const handlers = this.handlers[eventName];
66
+ if (!handlers || handlers.size < 1) {
67
+ return;
68
+ }
61
69
  const parsed = parseEventName(eventName);
62
70
  const attributes = Object.assign({}, (context.attributes || {}));
63
71
  if (context.error && attributes["error.type"] === undefined) {
@@ -67,10 +75,6 @@ class WebframezHooksFacade {
67
75
  type: eventName,
68
76
  context: Object.assign(Object.assign({}, context), { operation: parsed.operation, phase: parsed.phase, timestamp: context.timestamp || new Date(), attributes }),
69
77
  };
70
- const handlers = this.handlers[eventName];
71
- if (!handlers || handlers.size < 1) {
72
- return;
73
- }
74
78
  for (const handler of Array.from(handlers)) {
75
79
  try {
76
80
  yield handler(event);
@@ -65,6 +65,8 @@ declare class RouterFacade {
65
65
  routesDELETE: {
66
66
  [key: string]: RouteObject[];
67
67
  };
68
+ private routeRegexCache;
69
+ private domainRegexCache;
68
70
  /**
69
71
  * Load the application-routes
70
72
  */
@@ -103,6 +105,8 @@ declare class RouterFacade {
103
105
  extractParams(path: string, match: any): any;
104
106
  escapeRegexPart(value: string): string;
105
107
  buildRouteRegex(path: string): RegExp;
108
+ private getRouteRegex;
109
+ private getDomainRegex;
106
110
  normalizeDomainString(value: string): string | null;
107
111
  getRequestHostCandidates(request?: Request): string[];
108
112
  getRouteDomainFilters(routeObj: RouteObject): string[];
@@ -69,6 +69,8 @@ class RouterFacade {
69
69
  * DELETE Route Store
70
70
  */
71
71
  this.routesDELETE = {};
72
+ this.routeRegexCache = new Map();
73
+ this.domainRegexCache = new Map();
72
74
  }
73
75
  /**
74
76
  * Load the application-routes
@@ -88,6 +90,8 @@ class RouterFacade {
88
90
  this.routesPUT = {};
89
91
  this.routesPATCH = {};
90
92
  this.routesDELETE = {};
93
+ this.routeRegexCache.clear();
94
+ this.domainRegexCache.clear();
91
95
  // Load routes
92
96
  if (options.routesFunction) {
93
97
  options.routesFunction();
@@ -139,6 +143,10 @@ class RouterFacade {
139
143
  else {
140
144
  routeConf.component = component;
141
145
  }
146
+ this.getRouteRegex(path);
147
+ for (const domain of this.getRouteDomainFilters(routeConf)) {
148
+ this.getDomainRegex(domain);
149
+ }
142
150
  if (type === "GET") {
143
151
  if (!this.routesGET[path]) {
144
152
  this.routesGET[path] = [];
@@ -273,6 +281,22 @@ class RouterFacade {
273
281
  const regexString = "^" + escaped.replace(/__WFZ_TOKEN_(\d+)__/g, (all, tokenIndex) => placeholders[tokenIndex]) + "$";
274
282
  return new RegExp(regexString);
275
283
  }
284
+ getRouteRegex(path) {
285
+ let regex = this.routeRegexCache.get(path);
286
+ if (!regex) {
287
+ regex = this.buildRouteRegex(path);
288
+ this.routeRegexCache.set(path, regex);
289
+ }
290
+ return regex;
291
+ }
292
+ getDomainRegex(domain) {
293
+ let regex = this.domainRegexCache.get(domain);
294
+ if (!regex) {
295
+ regex = this.buildDomainRegex(domain);
296
+ this.domainRegexCache.set(domain, regex);
297
+ }
298
+ return regex;
299
+ }
276
300
  normalizeDomainString(value) {
277
301
  if (!value || value.trim() === "") {
278
302
  return null;
@@ -342,7 +366,7 @@ class RouterFacade {
342
366
  return new RegExp("^" + regexBody + "$", "i");
343
367
  }
344
368
  matchDomainFilter(hostname, domainFilter) {
345
- const regex = this.buildDomainRegex(domainFilter);
369
+ const regex = this.getDomainRegex(domainFilter);
346
370
  const match = hostname.match(regex);
347
371
  if (!match) {
348
372
  return { matches: false, matchedDomain: null, wildcard: null };
@@ -411,7 +435,7 @@ class RouterFacade {
411
435
  continue;
412
436
  }
413
437
  for (const routeObj of routeCandidates) {
414
- const match = url.match(this.buildRouteRegex(routeObj.path));
438
+ const match = url.match(this.getRouteRegex(routeObj.path));
415
439
  if (!match) {
416
440
  continue;
417
441
  }
@@ -442,14 +466,16 @@ class RouterFacade {
442
466
  : this.mode === "aws-lambda" && options && options.event && options.event.requestContext && options.event.requestContext.http
443
467
  ? options.event.requestContext.http.method
444
468
  : null;
445
- yield WebframezHooks_1.WebframezHooks.emit("http.request.start", {
446
- operationId: httpOperationId,
447
- name: initialMethod || (this.mode === "aws-lambda" ? "lambda" : "request"),
448
- attributes: {
449
- "http.request.method": initialMethod,
450
- "webframez.mode": this.mode,
451
- },
452
- });
469
+ if (WebframezHooks_1.WebframezHooks.hasListeners("http.request.start")) {
470
+ yield WebframezHooks_1.WebframezHooks.emit("http.request.start", {
471
+ operationId: httpOperationId,
472
+ name: initialMethod || (this.mode === "aws-lambda" ? "lambda" : "request"),
473
+ attributes: {
474
+ "http.request.method": initialMethod,
475
+ "webframez.mode": this.mode,
476
+ },
477
+ });
478
+ }
453
479
  let request;
454
480
  let routeOperationId = null;
455
481
  try {
@@ -478,15 +504,17 @@ class RouterFacade {
478
504
  },
479
505
  });
480
506
  const errorResult = this.handleError(fallbackRequest, response, statusCode, e);
481
- yield WebframezHooks_1.WebframezHooks.emit("http.request.error", {
482
- operationId: httpOperationId,
483
- name: `${fallbackRequest.method || "HTTP"} unmatched`,
484
- status: "error",
485
- error: e,
486
- request: fallbackRequest,
487
- response,
488
- attributes: Object.assign(Object.assign({ "http.request.method": fallbackRequest.method || null, "http.response.status_code": statusCode }, requestTelemetryAttributes(fallbackRequest)), { "webframez.mode": this.mode }),
489
- });
507
+ if (WebframezHooks_1.WebframezHooks.hasListeners("http.request.error")) {
508
+ yield WebframezHooks_1.WebframezHooks.emit("http.request.error", {
509
+ operationId: httpOperationId,
510
+ name: `${fallbackRequest.method || "HTTP"} unmatched`,
511
+ status: "error",
512
+ error: e,
513
+ request: fallbackRequest,
514
+ response,
515
+ attributes: Object.assign(Object.assign({ "http.request.method": fallbackRequest.method || null, "http.response.status_code": statusCode }, requestTelemetryAttributes(fallbackRequest)), { "webframez.mode": this.mode }),
516
+ });
517
+ }
490
518
  return errorResult;
491
519
  }
492
520
  const route = this.dissolve(request);
@@ -496,83 +524,95 @@ class RouterFacade {
496
524
  }
497
525
  if (!route) {
498
526
  const notFoundResult = this.handleError(request, response, 404, "Not found '" + request.url + "' ...");
499
- yield WebframezHooks_1.WebframezHooks.emit("http.request.end", {
500
- operationId: httpOperationId,
501
- name: `${request.method} unmatched`,
502
- status: "ok",
503
- request,
504
- response,
505
- attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": 404 }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
506
- });
527
+ if (WebframezHooks_1.WebframezHooks.hasListeners("http.request.end")) {
528
+ yield WebframezHooks_1.WebframezHooks.emit("http.request.end", {
529
+ operationId: httpOperationId,
530
+ name: `${request.method} unmatched`,
531
+ status: "ok",
532
+ request,
533
+ response,
534
+ attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": 404 }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
535
+ });
536
+ }
507
537
  return notFoundResult;
508
538
  }
509
539
  if (!(route.component && typeof route.component === "function") && !(route.controller && route.method_name)) {
510
540
  const missingRouteResult = this.handleError(request, response, 404, "Missing route function ...");
511
- yield WebframezHooks_1.WebframezHooks.emit("http.request.end", {
512
- operationId: httpOperationId,
513
- name: `${request.method} ${route.path}`,
514
- status: "ok",
515
- request,
516
- response,
517
- attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": 404, "url.template": route.path, "webframez.route": route.path }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
518
- });
519
- return missingRouteResult;
520
- }
521
- try {
522
- yield this.handleMiddleware(route, request, response);
523
- if (request.method === "OPTIONS" && !request.skipOptionsForward) {
524
- const optionsResult = this.handleReturn(request, response, "");
541
+ if (WebframezHooks_1.WebframezHooks.hasListeners("http.request.end")) {
525
542
  yield WebframezHooks_1.WebframezHooks.emit("http.request.end", {
526
543
  operationId: httpOperationId,
527
544
  name: `${request.method} ${route.path}`,
528
545
  status: "ok",
529
546
  request,
530
547
  response,
531
- attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": response.statusCode || 200, "url.template": route.path, "webframez.route": route.path }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
548
+ attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": 404, "url.template": route.path, "webframez.route": route.path }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
532
549
  });
533
- return optionsResult;
534
550
  }
535
- let result = null;
536
- routeOperationId = WebframezHooks_1.WebframezHooks.createOperationId("route");
537
- yield WebframezHooks_1.WebframezHooks.emit("route.handler.start", {
538
- operationId: routeOperationId,
539
- parentOperationId: httpOperationId,
540
- name: `${request.method} ${route.path}`,
541
- attributes: {
542
- "http.request.method": request.method,
543
- "url.template": route.path,
544
- "webframez.route": route.path,
545
- "webframez.route.handler": route.controller && route.method_name
546
- ? `${route.controller.name || "Controller"}@${route.method_name}`
547
- : "component",
548
- },
549
- });
550
- if (route.controller && route.method_name) {
551
- const controllerInstance = new route.controller();
552
- if (!controllerInstance[route.method_name]) {
553
- const unknownMethodResult = this.handleError(request, response, 404, "Unknown method '" + route.method_name + "'.");
554
- yield WebframezHooks_1.WebframezHooks.emit("route.handler.error", {
555
- operationId: routeOperationId,
556
- parentOperationId: httpOperationId,
557
- name: `${request.method} ${route.path}`,
558
- status: "error",
559
- error: new Error("Unknown route method"),
560
- attributes: {
561
- "http.request.method": request.method,
562
- "http.response.status_code": 404,
563
- "url.template": route.path,
564
- "webframez.route": route.path,
565
- "webframez.route.handler": `${route.controller.name || "Controller"}@${route.method_name}`,
566
- },
567
- });
551
+ return missingRouteResult;
552
+ }
553
+ try {
554
+ yield this.handleMiddleware(route, request, response);
555
+ if (request.method === "OPTIONS" && !request.skipOptionsForward) {
556
+ const optionsResult = this.handleReturn(request, response, "");
557
+ if (WebframezHooks_1.WebframezHooks.hasListeners("http.request.end")) {
568
558
  yield WebframezHooks_1.WebframezHooks.emit("http.request.end", {
569
559
  operationId: httpOperationId,
570
560
  name: `${request.method} ${route.path}`,
571
561
  status: "ok",
572
562
  request,
573
563
  response,
574
- attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": 404, "url.template": route.path, "webframez.route": route.path }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
564
+ attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": response.statusCode || 200, "url.template": route.path, "webframez.route": route.path }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
575
565
  });
566
+ }
567
+ return optionsResult;
568
+ }
569
+ let result = null;
570
+ routeOperationId = WebframezHooks_1.WebframezHooks.createOperationId("route");
571
+ if (WebframezHooks_1.WebframezHooks.hasListeners("route.handler.start")) {
572
+ yield WebframezHooks_1.WebframezHooks.emit("route.handler.start", {
573
+ operationId: routeOperationId,
574
+ parentOperationId: httpOperationId,
575
+ name: `${request.method} ${route.path}`,
576
+ attributes: {
577
+ "http.request.method": request.method,
578
+ "url.template": route.path,
579
+ "webframez.route": route.path,
580
+ "webframez.route.handler": route.controller && route.method_name
581
+ ? `${route.controller.name || "Controller"}@${route.method_name}`
582
+ : "component",
583
+ },
584
+ });
585
+ }
586
+ if (route.controller && route.method_name) {
587
+ const controllerInstance = new route.controller();
588
+ if (!controllerInstance[route.method_name]) {
589
+ const unknownMethodResult = this.handleError(request, response, 404, "Unknown method '" + route.method_name + "'.");
590
+ if (WebframezHooks_1.WebframezHooks.hasListeners("route.handler.error")) {
591
+ yield WebframezHooks_1.WebframezHooks.emit("route.handler.error", {
592
+ operationId: routeOperationId,
593
+ parentOperationId: httpOperationId,
594
+ name: `${request.method} ${route.path}`,
595
+ status: "error",
596
+ error: new Error("Unknown route method"),
597
+ attributes: {
598
+ "http.request.method": request.method,
599
+ "http.response.status_code": 404,
600
+ "url.template": route.path,
601
+ "webframez.route": route.path,
602
+ "webframez.route.handler": `${route.controller.name || "Controller"}@${route.method_name}`,
603
+ },
604
+ });
605
+ }
606
+ if (WebframezHooks_1.WebframezHooks.hasListeners("http.request.end")) {
607
+ yield WebframezHooks_1.WebframezHooks.emit("http.request.end", {
608
+ operationId: httpOperationId,
609
+ name: `${request.method} ${route.path}`,
610
+ status: "ok",
611
+ request,
612
+ response,
613
+ attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": 404, "url.template": route.path, "webframez.route": route.path }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
614
+ });
615
+ }
576
616
  return unknownMethodResult;
577
617
  }
578
618
  result = yield controllerInstance[route.method_name].bind(controllerInstance)(request, response);
@@ -581,40 +621,46 @@ class RouterFacade {
581
621
  result = yield route.component(request, response);
582
622
  }
583
623
  const returnResult = this.handleReturn(request, response, result);
584
- yield WebframezHooks_1.WebframezHooks.emit("route.handler.end", {
585
- operationId: routeOperationId,
586
- parentOperationId: httpOperationId,
587
- name: `${request.method} ${route.path}`,
588
- status: "ok",
589
- attributes: {
590
- "http.request.method": request.method,
591
- "http.response.status_code": response.statusCode || 200,
592
- "url.template": route.path,
593
- "webframez.route": route.path,
594
- },
595
- });
596
- yield WebframezHooks_1.WebframezHooks.emit("http.request.end", {
597
- operationId: httpOperationId,
598
- name: `${request.method} ${route.path}`,
599
- status: "ok",
600
- request,
601
- response,
602
- attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": response.statusCode || 200, "url.template": route.path, "webframez.route": route.path }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
603
- });
604
- return returnResult;
605
- }
606
- catch (e) {
607
- if (this.isMiddlewareRejectSignal(e)) {
608
- const middlewareErrorResult = this.handleError(request, response, 500, e.reason);
609
- yield WebframezHooks_1.WebframezHooks.emit("http.request.error", {
624
+ if (WebframezHooks_1.WebframezHooks.hasListeners("route.handler.end")) {
625
+ yield WebframezHooks_1.WebframezHooks.emit("route.handler.end", {
626
+ operationId: routeOperationId,
627
+ parentOperationId: httpOperationId,
628
+ name: `${request.method} ${route.path}`,
629
+ status: "ok",
630
+ attributes: {
631
+ "http.request.method": request.method,
632
+ "http.response.status_code": response.statusCode || 200,
633
+ "url.template": route.path,
634
+ "webframez.route": route.path,
635
+ },
636
+ });
637
+ }
638
+ if (WebframezHooks_1.WebframezHooks.hasListeners("http.request.end")) {
639
+ yield WebframezHooks_1.WebframezHooks.emit("http.request.end", {
610
640
  operationId: httpOperationId,
611
641
  name: `${request.method} ${route.path}`,
612
- status: "error",
613
- error: e.reason || e,
642
+ status: "ok",
614
643
  request,
615
644
  response,
616
- attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": 500, "url.template": route.path, "webframez.route": route.path }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
645
+ attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": response.statusCode || 200, "url.template": route.path, "webframez.route": route.path }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
617
646
  });
647
+ }
648
+ return returnResult;
649
+ }
650
+ catch (e) {
651
+ if (this.isMiddlewareRejectSignal(e)) {
652
+ const middlewareErrorResult = this.handleError(request, response, 500, e.reason);
653
+ if (WebframezHooks_1.WebframezHooks.hasListeners("http.request.error")) {
654
+ yield WebframezHooks_1.WebframezHooks.emit("http.request.error", {
655
+ operationId: httpOperationId,
656
+ name: `${request.method} ${route.path}`,
657
+ status: "error",
658
+ error: e.reason || e,
659
+ request,
660
+ response,
661
+ attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": 500, "url.template": route.path, "webframez.route": route.path }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
662
+ });
663
+ }
618
664
  return middlewareErrorResult;
619
665
  }
620
666
  yield ErrorHandler_1.ErrorHandler.report(e, {
@@ -632,29 +678,33 @@ class RouterFacade {
632
678
  });
633
679
  const errorResult = this.handleError(request, response, 500, e);
634
680
  if (routeOperationId) {
635
- yield WebframezHooks_1.WebframezHooks.emit("route.handler.error", {
636
- operationId: routeOperationId,
637
- parentOperationId: httpOperationId,
681
+ if (WebframezHooks_1.WebframezHooks.hasListeners("route.handler.error")) {
682
+ yield WebframezHooks_1.WebframezHooks.emit("route.handler.error", {
683
+ operationId: routeOperationId,
684
+ parentOperationId: httpOperationId,
685
+ name: `${request.method} ${route.path}`,
686
+ status: "error",
687
+ error: e,
688
+ attributes: {
689
+ "http.request.method": request.method,
690
+ "http.response.status_code": 500,
691
+ "url.template": route.path,
692
+ "webframez.route": route.path,
693
+ },
694
+ });
695
+ }
696
+ }
697
+ if (WebframezHooks_1.WebframezHooks.hasListeners("http.request.error")) {
698
+ yield WebframezHooks_1.WebframezHooks.emit("http.request.error", {
699
+ operationId: httpOperationId,
638
700
  name: `${request.method} ${route.path}`,
639
701
  status: "error",
640
702
  error: e,
641
- attributes: {
642
- "http.request.method": request.method,
643
- "http.response.status_code": 500,
644
- "url.template": route.path,
645
- "webframez.route": route.path,
646
- },
703
+ request,
704
+ response,
705
+ attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": 500, "url.template": route.path, "webframez.route": route.path }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
647
706
  });
648
707
  }
649
- yield WebframezHooks_1.WebframezHooks.emit("http.request.error", {
650
- operationId: httpOperationId,
651
- name: `${request.method} ${route.path}`,
652
- status: "error",
653
- error: e,
654
- request,
655
- response,
656
- attributes: Object.assign(Object.assign({ "http.request.method": request.method, "http.response.status_code": 500, "url.template": route.path, "webframez.route": route.path }, requestTelemetryAttributes(request)), { "webframez.mode": this.mode }),
657
- });
658
708
  return errorResult;
659
709
  }
660
710
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webtypen/webframez-core",
3
- "version": "0.3.76",
3
+ "version": "0.3.77",
4
4
  "description": "The core package of the typescript based webframez (web-framework) by webtypen.de",
5
5
  "homepage": "https://webtypen.de/",
6
6
  "license": "MIT",