@mastra/nestjs 0.2.38 → 0.2.39-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.
@@ -1 +1 @@
1
- {"version":3,"file":"mastra.controller.d.ts","sourceRoot":"","sources":["../../src/controllers/mastra.controller.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAKxE;;;;;;GAMG;AACH,qBAIa,gBAAgB;IAED,OAAO,CAAC,QAAQ,CAAC,OAAO;IACnB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC3B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW;IALnD,YAC2C,OAAO,EAAE,mBAAmB,EACvB,YAAY,EAAE,mBAAmB,EAC/B,cAAc,EAAE,qBAAqB,EACxC,YAAY,EAAE,kBAAkB,EACvC,WAAW,EAAE,WAAW,EAC5D;IAEJ;;OAEG;IAEG,aAAa,CAAQ,GAAG,EAAE,OAAO,EAA8B,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAmDpG;IAED;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IAoBxB;;OAEG;YACW,SAAS;CAsBxB"}
1
+ {"version":3,"file":"mastra.controller.d.ts","sourceRoot":"","sources":["../../src/controllers/mastra.controller.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAEtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAKxE;;;;;;GAMG;AACH,qBAIa,gBAAgB;IAED,OAAO,CAAC,QAAQ,CAAC,OAAO;IACnB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC3B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW;IALnD,YAC2C,OAAO,EAAE,mBAAmB,EACvB,YAAY,EAAE,mBAAmB,EAC/B,cAAc,EAAE,qBAAqB,EACxC,YAAY,EAAE,kBAAkB,EACvC,WAAW,EAAE,WAAW,EAC5D;IAEJ;;OAEG;IAEG,aAAa,CAAQ,GAAG,EAAE,OAAO,EAA8B,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CA2EpG;IAED;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IAoBxB;;OAEG;YACW,SAAS;CAsBxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"mastra-exception.filter.d.ts","sourceRoot":"","sources":["../../src/filters/mastra-exception.filter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAYrE;;;;;;;;;;;;;GAaG;AACH,qBACa,qBAAsB,YAAW,eAAe;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;IAE3D,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDlE;IAED;;OAEG;IACH,OAAO,CAAC,cAAc;IAgHtB;;OAEG;IACH,OAAO,CAAC,YAAY;CAkCrB"}
1
+ {"version":3,"file":"mastra-exception.filter.d.ts","sourceRoot":"","sources":["../../src/filters/mastra-exception.filter.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAYrE;;;;;;;;;;;;;GAaG;AACH,qBACa,qBAAsB,YAAW,eAAe;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;IAE3D,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA0DlE;IAED;;OAEG;IACH,OAAO,CAAC,cAAc;IAgHtB;;OAEG;IACH,OAAO,CAAC,YAAY;CAkCrB"}
package/dist/index.cjs CHANGED
@@ -25,6 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
  let _nestjs_common = require("@nestjs/common");
26
26
  let _mastra_server_server_adapter = require("@mastra/server/server-adapter");
27
27
  let _mastra_server_handlers_error = require("@mastra/server/handlers/error");
28
+ let _mastra_core_auth_ee = require("@mastra/core/auth/ee");
28
29
  let _mastra_core_request_context = require("@mastra/core/request-context");
29
30
  let _mastra_server_auth = require("@mastra/server/auth");
30
31
  let stream = require("stream");
@@ -225,6 +226,11 @@ let RouteHandlerService = _RouteHandlerService = class RouteHandlerService {
225
226
  if ((0, _mastra_server_server_adapter.isZodError)(error)) throw this.createValidationError(route, error, "body");
226
227
  throw error;
227
228
  }
229
+ await this.authorizeRoute(route, params.requestContext, {
230
+ ...validatedPathParams,
231
+ ...validatedQueryParams,
232
+ ...typeof validatedBody === "object" && validatedBody !== null ? validatedBody : {}
233
+ });
228
234
  const context = {
229
235
  mastra: this.mastra,
230
236
  requestContext: params.requestContext,
@@ -247,6 +253,39 @@ let RouteHandlerService = _RouteHandlerService = class RouteHandlerService {
247
253
  sseFlushOnConnect: route.sseFlushOnConnect
248
254
  };
249
255
  }
256
+ /**
257
+ * Enforce the route's RBAC permission and FGA check, matching the other server adapters.
258
+ * Relies on the auth guard having stored the user, permissions, and auth mode in the request context.
259
+ */
260
+ async authorizeRoute(route, requestContext, params) {
261
+ await this.enforceRoutePermission(route, requestContext);
262
+ await this.enforceRouteFGA(route, requestContext, params);
263
+ }
264
+ /**
265
+ * Throws 403 when the user lacks the route's effective RBAC permission.
266
+ * Skipped when Mastra auth is explicitly disabled (`auth.enabled: false`), since no Mastra user is resolved.
267
+ */
268
+ async enforceRoutePermission(route, requestContext) {
269
+ if (this.options.auth?.enabled === false) return;
270
+ const studio = this.mastra.getStudio?.();
271
+ const server = this.mastra.getServer();
272
+ if (!studio?.auth && !server?.auth) return;
273
+ const requiredPermission = (requestContext.get(_mastra_server_server_adapter.MASTRA_AUTH_MODE_KEY) === "studio" ? studio?.rbac ?? server?.rbac : server?.rbac) ? (0, _mastra_server_server_adapter.getEffectivePermission)(route) : null;
274
+ if (requiredPermission) {
275
+ const required = Array.isArray(requiredPermission) ? requiredPermission : [requiredPermission];
276
+ const userPermissions = requestContext.get("mastra__userPermissions");
277
+ if (!userPermissions || !required.some((permission) => (0, _mastra_core_auth_ee.hasPermission)(userPermissions, permission))) throw new _nestjs_common.ForbiddenException(`Missing required permission: ${required.join(" or ")}`);
278
+ }
279
+ }
280
+ /**
281
+ * Throws when the FGA provider denies the route's resource.
282
+ * Skipped when Mastra auth is explicitly disabled (`auth.enabled: false`), since no Mastra user is resolved.
283
+ */
284
+ async enforceRouteFGA(route, requestContext, params) {
285
+ if (this.options.auth?.enabled === false) return;
286
+ const fgaError = await (0, _mastra_server_server_adapter.checkRouteFGA)(this.mastra, route, requestContext, params);
287
+ if (fgaError) throw new _nestjs_common.HttpException(fgaError.message, fgaError.status);
288
+ }
250
289
  async parseBody(route, body) {
251
290
  const bodySchema = route.bodySchema;
252
291
  if (!bodySchema) return body;
@@ -340,7 +379,8 @@ let MastraExceptionFilter = _MastraExceptionFilter = class MastraExceptionFilter
340
379
  return;
341
380
  }
342
381
  if (exception instanceof _mastra_server_server_adapter.HTTPException) {
343
- response.status(exception.status).json({ error: exception.message });
382
+ const { status, body } = (0, _mastra_server_server_adapter.getHTTPErrorResponse)(exception);
383
+ response.status(status).json(body);
344
384
  return;
345
385
  }
346
386
  const normalized = this.normalizeError(exception);
@@ -500,7 +540,7 @@ let CustomRouteService = class CustomRouteService {
500
540
  * Runs a matched custom route and writes its response.
501
541
  * Returns false if no custom route handled the request.
502
542
  *
503
- * `authenticate` runs after Hono's router selects the route, so auth is enforced
543
+ * `authenticate` runs after Hono's router selects the route, so auth, RBAC, and FGA are enforced
504
544
  * for exactly the routes Hono dispatches (including regex-constrained params).
505
545
  */
506
546
  async handle(req, res, requestContext, authenticate) {
@@ -561,6 +601,8 @@ let CustomRouteService = class CustomRouteService {
561
601
  const serverOnError = mastra.getServer()?.onError;
562
602
  app.onError((err, c) => {
563
603
  if (serverOnError) return serverOnError(err, c);
604
+ const httpExceptionResponse = (0, _mastra_server_server_adapter.getCustomRouteHTTPExceptionResponse)(err);
605
+ if (httpExceptionResponse) return httpExceptionResponse;
564
606
  mastra.getLogger()?.error(`Custom route handler failed: ${c.req.method} ${c.req.path}`, { error: err instanceof Error ? err.stack ?? err.message : String(err) });
565
607
  return c.json({ error: "Internal Server Error" }, 500);
566
608
  });
@@ -571,7 +613,7 @@ let CustomRouteService = class CustomRouteService {
571
613
  const requiresAuth = this.authConfig.get(`${route.method}:${route.path}`) ?? route.requiresAuth !== false;
572
614
  const authMiddleware = async (c, next) => {
573
615
  try {
574
- await c.env.authenticate(requiresAuth);
616
+ await c.env.authenticate(route, requiresAuth, c.req.param());
575
617
  } catch (err) {
576
618
  c.env.authError = err;
577
619
  return c.body(null, 401);
@@ -638,11 +680,29 @@ let AuthService = class AuthService {
638
680
  }
639
681
  /**
640
682
  * Whether Mastra auth should run: module options enable it, or the Mastra
641
- * server has auth configured (unless module options explicitly disable it).
683
+ * server or Studio has auth configured (unless module options explicitly disable it).
642
684
  */
643
685
  isEnabled() {
644
686
  if (this.options.auth?.enabled === false) return false;
645
- return Boolean(this.options.auth?.enabled || this.mastra.getServer()?.auth);
687
+ return Boolean(this.options.auth?.enabled || this.mastra.getServer()?.auth || this.mastra.getStudio?.()?.auth);
688
+ }
689
+ /**
690
+ * Studio requests (`x-mastra-client-type: studio`) use `studio.auth` exclusively when it is
691
+ * configured, without falling back to `server.auth`; all other requests use `server.auth`.
692
+ */
693
+ getEffectiveAuthConfig(request) {
694
+ const clientType = request.headers[_mastra_server_server_adapter.MASTRA_CLIENT_TYPE_HEADER];
695
+ const isStudioRequest = (0, _mastra_server_server_adapter.isStudioClientTypeHeader)(Array.isArray(clientType) ? clientType[0] : clientType);
696
+ const studioAuth = this.mastra.getStudio?.()?.auth;
697
+ if (isStudioRequest && studioAuth) return {
698
+ authConfig: studioAuth,
699
+ authMode: "studio"
700
+ };
701
+ const serverAuth = this.mastra.getServer()?.auth;
702
+ return serverAuth ? {
703
+ authConfig: serverAuth,
704
+ authMode: "server"
705
+ } : null;
646
706
  }
647
707
  /**
648
708
  * Check authentication for a request.
@@ -650,9 +710,11 @@ let AuthService = class AuthService {
650
710
  * Throws UnauthorizedException or ForbiddenException if auth fails.
651
711
  */
652
712
  async authenticate(request, options = {}) {
653
- const authConfig = this.mastra.getServer()?.auth;
654
- if (!authConfig) return;
713
+ const effectiveAuth = this.getEffectiveAuthConfig(request);
714
+ if (!effectiveAuth) return;
715
+ const { authConfig, authMode } = effectiveAuth;
655
716
  const requestContext = options.requestContext ?? new _mastra_core_request_context.RequestContext();
717
+ requestContext.set(_mastra_server_server_adapter.MASTRA_AUTH_MODE_KEY, authMode);
656
718
  const path = request.path;
657
719
  const method = request.method;
658
720
  let customRouteAuthConfig = this.customRoutes.customRouteAuthConfig;
@@ -11030,15 +11092,31 @@ let MastraController = class MastraController {
11030
11092
  const routePath = getMastraRoutePath(path, this.options.prefix);
11031
11093
  if (!routePath || !this.routeHandler.matchRoute(method, routePath)) {
11032
11094
  const requestContext = this.requestContext.requestContext;
11033
- const authenticate = async (requiresAuth) => {
11034
- if (!this.authService.isEnabled()) return;
11035
- const user = await this.authService.authenticate(req, {
11036
- requestContext,
11037
- response: res,
11095
+ const authenticate = async (customRoute, requiresAuth, pathParams) => {
11096
+ if (this.authService.isEnabled()) {
11097
+ const user = await this.authService.authenticate(req, {
11098
+ requestContext,
11099
+ response: res,
11100
+ requiresAuth,
11101
+ customRoute: true
11102
+ });
11103
+ if (user !== void 0) this.requestContext.setUser(user);
11104
+ }
11105
+ const serverRoute = {
11106
+ method: customRoute.method,
11107
+ path: customRoute.path,
11108
+ responseType: "json",
11109
+ handler: async () => {},
11038
11110
  requiresAuth,
11039
- customRoute: true
11111
+ requiresPermission: customRoute.requiresPermission,
11112
+ fga: customRoute.fga
11113
+ };
11114
+ if (requiresAuth) await this.routeHandler.enforceRoutePermission(serverRoute, requestContext);
11115
+ if (customRoute.fga) await this.routeHandler.enforceRouteFGA(serverRoute, requestContext, {
11116
+ ...typeof req.body === "object" && req.body !== null ? req.body : {},
11117
+ ...req.query,
11118
+ ...pathParams
11040
11119
  });
11041
- if (user !== void 0) this.requestContext.setUser(user);
11042
11120
  };
11043
11121
  if (await this.customRoutes.handle(req, res, requestContext, authenticate)) return;
11044
11122
  }
@@ -11359,6 +11437,15 @@ let MastraService = _MastraService = class MastraService {
11359
11437
  }
11360
11438
  this.serverAdapter = new NestMastraServer(app, this.routeHandler);
11361
11439
  this.mastra.setMastraServer(this.serverAdapter);
11440
+ this.warnIfServerMiddlewareConfigured();
11441
+ }
11442
+ /** Mastra server middleware uses Hono's `(c, next)` signature, so NestJS cannot run it. */
11443
+ warnIfServerMiddlewareConfigured() {
11444
+ const instanceMiddleware = this.mastra.getServerMiddleware?.() ?? [];
11445
+ const configMiddleware = this.mastra.getServer()?.middleware;
11446
+ const hasConfigMiddleware = Array.isArray(configMiddleware) ? configMiddleware.length > 0 : !!configMiddleware;
11447
+ if (instanceMiddleware.length === 0 && !hasConfigMiddleware) return;
11448
+ this.mastra.getLogger()?.warn("Mastra server middleware (`server.middleware` or `setServerMiddleware()`) is configured, but this server adapter cannot run Hono middleware handlers. The configured middleware will not run — register middleware through your server framework instead.");
11362
11449
  }
11363
11450
  /**
11364
11451
  * Get the Mastra instance.