@mastra/koa 1.4.16 → 1.5.0-alpha.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.
package/dist/index.js CHANGED
@@ -306,6 +306,7 @@ function toWebRequest2(ctx) {
306
306
  });
307
307
  }
308
308
  var MastraServer = class extends MastraServer$1 {
309
+ activeRouteDispatchers = /* @__PURE__ */ new WeakMap();
309
310
  async init() {
310
311
  this.registerErrorMiddleware();
311
312
  await super.init();
@@ -323,11 +324,12 @@ var MastraServer = class extends MastraServer$1 {
323
324
  * should use `server.onError` or register their own middleware between this and the routes.
324
325
  */
325
326
  registerErrorMiddleware() {
326
- this.app.use(async (ctx, next) => {
327
+ const server = this;
328
+ this.app.use(async function mastraErrorBoundary(ctx, next) {
327
329
  try {
328
330
  await next();
329
331
  } catch (err) {
330
- if (await this.handleOnError(err, ctx)) {
332
+ if (await server.handleOnError(err, ctx)) {
331
333
  return;
332
334
  }
333
335
  const error = err instanceof Error ? err : new Error(String(err));
@@ -391,7 +393,8 @@ var MastraServer = class extends MastraServer$1 {
391
393
  }
392
394
  }
393
395
  createContextMiddleware() {
394
- return async (ctx, next) => {
396
+ const server = this;
397
+ return async function mastraRequestContext(ctx, next) {
395
398
  let bodyRequestContext;
396
399
  let paramsRequestContext;
397
400
  if (ctx.method === "POST" || ctx.method === "PUT") {
@@ -421,14 +424,14 @@ var MastraServer = class extends MastraServer$1 {
421
424
  } catch {
422
425
  }
423
426
  }
424
- const requestContext = this.mergeRequestContext({ paramsRequestContext, bodyRequestContext });
427
+ const requestContext = server.mergeRequestContext({ paramsRequestContext, bodyRequestContext });
425
428
  ctx.state.requestContext = requestContext;
426
- ctx.state.mastra = this.mastra;
427
- ctx.state.tools = this.tools || {};
428
- if (this.taskStore) {
429
- ctx.state.taskStore = this.taskStore;
429
+ ctx.state.mastra = server.mastra;
430
+ ctx.state.tools = server.tools || {};
431
+ if (server.taskStore) {
432
+ ctx.state.taskStore = server.taskStore;
430
433
  }
431
- ctx.state.customRouteAuthConfig = this.customRouteAuthConfig;
434
+ ctx.state.customRouteAuthConfig = server.customRouteAuthConfig;
432
435
  const controller = new AbortController();
433
436
  ctx.req.on("close", () => {
434
437
  if (!ctx.res.writableEnded) {
@@ -439,6 +442,193 @@ var MastraServer = class extends MastraServer$1 {
439
442
  await next();
440
443
  };
441
444
  }
445
+ getRouteDispatcherGroup(app) {
446
+ const activeGroup = this.activeRouteDispatchers.get(app);
447
+ if (activeGroup && app.middleware.length === activeGroup.stackLengthAfterRegistration) {
448
+ return activeGroup;
449
+ }
450
+ const group = {
451
+ routes: [],
452
+ stackLengthAfterRegistration: 0
453
+ };
454
+ app.use(this.createRouteDispatcherMiddleware(group));
455
+ group.stackLengthAfterRegistration = app.middleware.length;
456
+ this.activeRouteDispatchers.set(app, group);
457
+ return group;
458
+ }
459
+ createRouteDispatcherMiddleware(group) {
460
+ const server = this;
461
+ return async function mastraRouteDispatcher(ctx, next) {
462
+ const matchedRoute = server.findRegisteredRoute(group.routes, ctx);
463
+ if (!matchedRoute) {
464
+ await next();
465
+ return;
466
+ }
467
+ await server.handleMatchedRoute(matchedRoute, ctx);
468
+ };
469
+ }
470
+ findRegisteredRoute(routes, ctx) {
471
+ const method = ctx.method.toUpperCase();
472
+ for (const registeredRoute of routes) {
473
+ if (registeredRoute.route.method.toUpperCase() !== "ALL" && method !== registeredRoute.route.method.toUpperCase()) {
474
+ continue;
475
+ }
476
+ const match = registeredRoute.pathRegex.exec(ctx.path);
477
+ if (!match) {
478
+ continue;
479
+ }
480
+ ctx.params = {};
481
+ registeredRoute.paramNames.forEach((name, index) => {
482
+ ctx.params[name] = match[index + 1];
483
+ });
484
+ return registeredRoute;
485
+ }
486
+ return void 0;
487
+ }
488
+ async handleMatchedRoute(registeredRoute, ctx) {
489
+ const { route, prefix } = registeredRoute;
490
+ const authError = await this.checkRouteAuth(route, {
491
+ path: String(ctx.path || "/"),
492
+ method: String(ctx.method || "GET"),
493
+ getHeader: (name) => ctx.headers[name.toLowerCase()],
494
+ getQuery: (name) => ctx.query[name],
495
+ requestContext: ctx.state.requestContext,
496
+ request: toWebRequest2(ctx),
497
+ buildAuthorizeContext: () => toWebRequest2(ctx)
498
+ });
499
+ if (authError) {
500
+ if (authError.headers) {
501
+ for (const [key, value] of Object.entries(authError.headers)) {
502
+ ctx.set(key, value);
503
+ }
504
+ }
505
+ if (authError.error) {
506
+ ctx.status = authError.status;
507
+ ctx.body = { error: authError.error };
508
+ return;
509
+ }
510
+ }
511
+ const params = await this.getParams(route, ctx);
512
+ if (params.bodyParseError) {
513
+ ctx.status = 400;
514
+ ctx.body = {
515
+ error: "Invalid request body",
516
+ issues: [{ field: "body", message: params.bodyParseError.message }]
517
+ };
518
+ return;
519
+ }
520
+ if (params.queryParams) {
521
+ try {
522
+ params.queryParams = await this.parseQueryParams(route, params.queryParams);
523
+ } catch (error) {
524
+ this.mastra.getLogger()?.error("Error parsing query params", {
525
+ error: error instanceof Error ? { message: error.message, stack: error.stack } : error
526
+ });
527
+ if (error instanceof ZodError) {
528
+ const resolved = this.resolveValidationError(route, error, "query");
529
+ ctx.status = resolved.status;
530
+ ctx.body = resolved.body;
531
+ return;
532
+ }
533
+ ctx.status = 400;
534
+ ctx.body = {
535
+ error: "Invalid query parameters",
536
+ issues: [{ field: "unknown", message: error instanceof Error ? error.message : "Unknown error" }]
537
+ };
538
+ return;
539
+ }
540
+ }
541
+ if (params.body) {
542
+ try {
543
+ params.body = await this.parseBody(route, params.body);
544
+ } catch (error) {
545
+ this.mastra.getLogger()?.error("Error parsing body", {
546
+ error: error instanceof Error ? { message: error.message, stack: error.stack } : error
547
+ });
548
+ if (error instanceof ZodError) {
549
+ const resolved = this.resolveValidationError(route, error, "body");
550
+ ctx.status = resolved.status;
551
+ ctx.body = resolved.body;
552
+ return;
553
+ }
554
+ ctx.status = 400;
555
+ ctx.body = {
556
+ error: "Invalid request body",
557
+ issues: [{ field: "unknown", message: error instanceof Error ? error.message : "Unknown error" }]
558
+ };
559
+ return;
560
+ }
561
+ }
562
+ if (params.urlParams) {
563
+ try {
564
+ params.urlParams = await this.parsePathParams(route, params.urlParams);
565
+ } catch (error) {
566
+ this.mastra.getLogger()?.error("Error parsing path params", {
567
+ error: error instanceof Error ? { message: error.message, stack: error.stack } : error
568
+ });
569
+ if (error instanceof ZodError) {
570
+ const resolved = this.resolveValidationError(route, error, "path");
571
+ ctx.status = resolved.status;
572
+ ctx.body = resolved.body;
573
+ return;
574
+ }
575
+ ctx.status = 400;
576
+ ctx.body = {
577
+ error: "Invalid path parameters",
578
+ issues: [{ field: "unknown", message: error instanceof Error ? error.message : "Unknown error" }]
579
+ };
580
+ return;
581
+ }
582
+ }
583
+ const handlerParams = {
584
+ ...params.urlParams,
585
+ ...params.queryParams,
586
+ ...typeof params.body === "object" ? params.body : {},
587
+ requestContext: ctx.state.requestContext,
588
+ mastra: this.mastra,
589
+ tools: ctx.state.tools,
590
+ taskStore: ctx.state.taskStore,
591
+ abortSignal: ctx.state.abortSignal,
592
+ routePrefix: prefix
593
+ };
594
+ const authConfig = this.mastra.getServer()?.auth;
595
+ if (authConfig) {
596
+ const hasPermission = await loadHasPermission();
597
+ if (hasPermission) {
598
+ const userPermissions = ctx.state.requestContext.get("userPermissions");
599
+ const permissionError = this.checkRoutePermission(route, userPermissions, hasPermission);
600
+ if (permissionError) {
601
+ ctx.status = permissionError.status;
602
+ ctx.body = {
603
+ error: permissionError.error,
604
+ message: permissionError.message
605
+ };
606
+ return;
607
+ }
608
+ }
609
+ }
610
+ try {
611
+ const result = await route.handler(handlerParams);
612
+ await this.sendResponse(route, ctx, result, prefix);
613
+ } catch (error) {
614
+ this.mastra.getLogger()?.error("Error calling handler", {
615
+ error: error instanceof Error ? { message: error.message, stack: error.stack } : error,
616
+ path: route.path,
617
+ method: route.method
618
+ });
619
+ if (error && typeof error === "object") {
620
+ if (!("status" in error)) {
621
+ if ("details" in error && error.details && typeof error.details === "object" && "status" in error.details) {
622
+ error.status = error.details.status;
623
+ }
624
+ }
625
+ }
626
+ if (await this.handleOnError(error, ctx)) {
627
+ return;
628
+ }
629
+ throw error;
630
+ }
631
+ }
442
632
  async stream(route, ctx, result) {
443
633
  ctx.respond = false;
444
634
  const streamFormat = route.streamFormat || "stream";
@@ -664,165 +854,14 @@ var MastraServer = class extends MastraServer$1 {
664
854
  const prefix = prefixParam ?? this.prefix ?? "";
665
855
  const fullPath = `${prefix}${route.path}`;
666
856
  const koaPath = fullPath;
667
- const handler = async (ctx, next) => {
668
- const pathRegex = this.pathToRegex(koaPath);
669
- const match = pathRegex.exec(ctx.path);
670
- if (!match) {
671
- await next();
672
- return;
673
- }
674
- if (route.method.toUpperCase() !== "ALL" && ctx.method.toUpperCase() !== route.method.toUpperCase()) {
675
- await next();
676
- return;
677
- }
678
- const paramNames = this.extractParamNames(koaPath);
679
- ctx.params = {};
680
- paramNames.forEach((name, index) => {
681
- ctx.params[name] = match[index + 1];
682
- });
683
- const authError = await this.checkRouteAuth(route, {
684
- path: String(ctx.path || "/"),
685
- method: String(ctx.method || "GET"),
686
- getHeader: (name) => ctx.headers[name.toLowerCase()],
687
- getQuery: (name) => ctx.query[name],
688
- requestContext: ctx.state.requestContext,
689
- request: toWebRequest2(ctx),
690
- buildAuthorizeContext: () => toWebRequest2(ctx)
691
- });
692
- if (authError) {
693
- if (authError.headers) {
694
- for (const [key, value] of Object.entries(authError.headers)) {
695
- ctx.set(key, value);
696
- }
697
- }
698
- if (authError.error) {
699
- ctx.status = authError.status;
700
- ctx.body = { error: authError.error };
701
- return;
702
- }
703
- }
704
- const params = await this.getParams(route, ctx);
705
- if (params.bodyParseError) {
706
- ctx.status = 400;
707
- ctx.body = {
708
- error: "Invalid request body",
709
- issues: [{ field: "body", message: params.bodyParseError.message }]
710
- };
711
- return;
712
- }
713
- if (params.queryParams) {
714
- try {
715
- params.queryParams = await this.parseQueryParams(route, params.queryParams);
716
- } catch (error) {
717
- this.mastra.getLogger()?.error("Error parsing query params", {
718
- error: error instanceof Error ? { message: error.message, stack: error.stack } : error
719
- });
720
- if (error instanceof ZodError) {
721
- const resolved = this.resolveValidationError(route, error, "query");
722
- ctx.status = resolved.status;
723
- ctx.body = resolved.body;
724
- return;
725
- }
726
- ctx.status = 400;
727
- ctx.body = {
728
- error: "Invalid query parameters",
729
- issues: [{ field: "unknown", message: error instanceof Error ? error.message : "Unknown error" }]
730
- };
731
- return;
732
- }
733
- }
734
- if (params.body) {
735
- try {
736
- params.body = await this.parseBody(route, params.body);
737
- } catch (error) {
738
- this.mastra.getLogger()?.error("Error parsing body", {
739
- error: error instanceof Error ? { message: error.message, stack: error.stack } : error
740
- });
741
- if (error instanceof ZodError) {
742
- const resolved = this.resolveValidationError(route, error, "body");
743
- ctx.status = resolved.status;
744
- ctx.body = resolved.body;
745
- return;
746
- }
747
- ctx.status = 400;
748
- ctx.body = {
749
- error: "Invalid request body",
750
- issues: [{ field: "unknown", message: error instanceof Error ? error.message : "Unknown error" }]
751
- };
752
- return;
753
- }
754
- }
755
- if (params.urlParams) {
756
- try {
757
- params.urlParams = await this.parsePathParams(route, params.urlParams);
758
- } catch (error) {
759
- this.mastra.getLogger()?.error("Error parsing path params", {
760
- error: error instanceof Error ? { message: error.message, stack: error.stack } : error
761
- });
762
- if (error instanceof ZodError) {
763
- const resolved = this.resolveValidationError(route, error, "path");
764
- ctx.status = resolved.status;
765
- ctx.body = resolved.body;
766
- return;
767
- }
768
- ctx.status = 400;
769
- ctx.body = {
770
- error: "Invalid path parameters",
771
- issues: [{ field: "unknown", message: error instanceof Error ? error.message : "Unknown error" }]
772
- };
773
- return;
774
- }
775
- }
776
- const handlerParams = {
777
- ...params.urlParams,
778
- ...params.queryParams,
779
- ...typeof params.body === "object" ? params.body : {},
780
- requestContext: ctx.state.requestContext,
781
- mastra: this.mastra,
782
- tools: ctx.state.tools,
783
- taskStore: ctx.state.taskStore,
784
- abortSignal: ctx.state.abortSignal,
785
- routePrefix: prefix
786
- };
787
- const authConfig = this.mastra.getServer()?.auth;
788
- if (authConfig) {
789
- const hasPermission = await loadHasPermission();
790
- if (hasPermission) {
791
- const userPermissions = ctx.state.requestContext.get("userPermissions");
792
- const permissionError = this.checkRoutePermission(route, userPermissions, hasPermission);
793
- if (permissionError) {
794
- ctx.status = permissionError.status;
795
- ctx.body = {
796
- error: permissionError.error,
797
- message: permissionError.message
798
- };
799
- return;
800
- }
801
- }
802
- }
803
- try {
804
- const result = await route.handler(handlerParams);
805
- await this.sendResponse(route, ctx, result, prefix);
806
- } catch (error) {
807
- this.mastra.getLogger()?.error("Error calling handler", {
808
- error: error instanceof Error ? { message: error.message, stack: error.stack } : error,
809
- path: route.path,
810
- method: route.method
811
- });
812
- if (error && typeof error === "object") {
813
- if (!("status" in error)) {
814
- if ("details" in error && error.details && typeof error.details === "object" && "status" in error.details) {
815
- error.status = error.details.status;
816
- }
817
- }
818
- }
819
- if (await this.handleOnError(error, ctx)) {
820
- return;
821
- }
822
- throw error;
823
- }
824
- };
825
- app.use(handler);
857
+ const group = this.getRouteDispatcherGroup(app);
858
+ group.routes.push({
859
+ route,
860
+ prefix,
861
+ koaPath,
862
+ pathRegex: this.pathToRegex(koaPath),
863
+ paramNames: this.extractParamNames(koaPath)
864
+ });
826
865
  }
827
866
  /**
828
867
  * Convert Express-style path to regex for matching
@@ -843,10 +882,11 @@ var MastraServer = class extends MastraServer$1 {
843
882
  }
844
883
  async registerCustomApiRoutes() {
845
884
  if (!await this.buildCustomRouteHandler()) return;
846
- this.app.use(async (ctx, next) => {
885
+ const server = this;
886
+ this.app.use(async function mastraCustomRouteDispatcher(ctx, next) {
847
887
  const path = String(ctx.path || "/");
848
888
  const method = String(ctx.method || "GET");
849
- if (isProtectedCustomRoute(path, method, this.customRouteAuthConfig)) {
889
+ if (isProtectedCustomRoute(path, method, server.customRouteAuthConfig)) {
850
890
  const serverRoute = {
851
891
  method,
852
892
  path,
@@ -854,7 +894,7 @@ var MastraServer = class extends MastraServer$1 {
854
894
  handler: async () => {
855
895
  }
856
896
  };
857
- const authError = await this.checkRouteAuth(serverRoute, {
897
+ const authError = await server.checkRouteAuth(serverRoute, {
858
898
  path,
859
899
  method,
860
900
  getHeader: (name) => ctx.headers[name.toLowerCase()],
@@ -875,7 +915,7 @@ var MastraServer = class extends MastraServer$1 {
875
915
  return;
876
916
  }
877
917
  }
878
- const authConfig = this.mastra.getServer()?.auth;
918
+ const authConfig = server.mastra.getServer()?.auth;
879
919
  if (authConfig) {
880
920
  let hasPermission;
881
921
  try {
@@ -887,7 +927,7 @@ var MastraServer = class extends MastraServer$1 {
887
927
  }
888
928
  if (hasPermission) {
889
929
  const userPermissions = ctx.state.requestContext.get("userPermissions");
890
- const permissionError = this.checkRoutePermission(serverRoute, userPermissions, hasPermission);
930
+ const permissionError = server.checkRoutePermission(serverRoute, userPermissions, hasPermission);
891
931
  if (permissionError) {
892
932
  ctx.status = permissionError.status;
893
933
  ctx.body = {
@@ -899,7 +939,7 @@ var MastraServer = class extends MastraServer$1 {
899
939
  }
900
940
  }
901
941
  }
902
- const response = await this.handleCustomRouteRequest(
942
+ const response = await server.handleCustomRouteRequest(
903
943
  `${ctx.protocol}://${ctx.host}${ctx.originalUrl || ctx.url}`,
904
944
  ctx.method,
905
945
  ctx.headers,
@@ -908,7 +948,7 @@ var MastraServer = class extends MastraServer$1 {
908
948
  );
909
949
  if (!response) return next();
910
950
  ctx.respond = false;
911
- await this.writeCustomRouteResponse(response, ctx.res);
951
+ await server.writeCustomRouteResponse(response, ctx.res);
912
952
  });
913
953
  }
914
954
  registerContextMiddleware() {
@@ -920,8 +960,9 @@ var MastraServer = class extends MastraServer$1 {
920
960
  if (!this.httpLoggingConfig?.enabled) {
921
961
  return;
922
962
  }
923
- this.app.use(async (ctx, next) => {
924
- if (!this.shouldLogRequest(ctx.path)) {
963
+ const server = this;
964
+ this.app.use(async function mastraHttpLogger(ctx, next) {
965
+ if (!server.shouldLogRequest(ctx.path)) {
925
966
  return next();
926
967
  }
927
968
  const start = Date.now();
@@ -930,19 +971,19 @@ var MastraServer = class extends MastraServer$1 {
930
971
  await next();
931
972
  const duration2 = Date.now() - start;
932
973
  const status = ctx.status;
933
- const level = this.httpLoggingConfig?.level || "info";
974
+ const level = server.httpLoggingConfig?.level || "info";
934
975
  const logData = {
935
976
  method,
936
977
  path,
937
978
  status,
938
979
  duration: `${duration2}ms`
939
980
  };
940
- if (this.httpLoggingConfig?.includeQueryParams) {
981
+ if (server.httpLoggingConfig?.includeQueryParams) {
941
982
  logData.query = ctx.query;
942
983
  }
943
- if (this.httpLoggingConfig?.includeHeaders) {
984
+ if (server.httpLoggingConfig?.includeHeaders) {
944
985
  const headers = { ...ctx.headers };
945
- const redactHeaders = this.httpLoggingConfig.redactHeaders || [];
986
+ const redactHeaders = server.httpLoggingConfig.redactHeaders || [];
946
987
  redactHeaders.forEach((h) => {
947
988
  const key = h.toLowerCase();
948
989
  if (headers[key] !== void 0) {
@@ -951,7 +992,7 @@ var MastraServer = class extends MastraServer$1 {
951
992
  });
952
993
  logData.headers = headers;
953
994
  }
954
- this.logger[level](`${method} ${path} ${status} ${duration2}ms`, logData);
995
+ server.logger[level](`${method} ${path} ${status} ${duration2}ms`, logData);
955
996
  });
956
997
  }
957
998
  };