@mastra/nestjs 0.2.26 → 0.2.27-alpha.5
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 +3 -1
- package/dist/_types/rxjs/package.json +1 -0
- package/dist/filters/mastra-exception.filter.d.ts +1 -1
- package/dist/filters/mastra-exception.filter.d.ts.map +1 -1
- package/dist/index.cjs +117 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +118 -26
- package/dist/index.js.map +1 -1
- package/dist/interceptors/request-tracking.interceptor.d.ts +1 -1
- package/dist/interceptors/streaming.interceptor.d.ts +1 -1
- package/dist/interceptors/tracing.interceptor.d.ts +1 -1
- package/dist/mastra-server.adapter.d.ts +9 -3
- package/dist/mastra-server.adapter.d.ts.map +1 -1
- package/dist/mastra.service.d.ts +7 -2
- package/dist/mastra.service.d.ts.map +1 -1
- package/dist/middleware/json-body.middleware.d.ts +1 -1
- package/dist/middleware/json-body.middleware.d.ts.map +1 -1
- package/dist/services/route-handler.service.d.ts +9 -1
- package/dist/services/route-handler.service.d.ts.map +1 -1
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ import { NestFactory } from '@nestjs/core';
|
|
|
35
35
|
import { AppModule } from './app.module';
|
|
36
36
|
|
|
37
37
|
async function bootstrap() {
|
|
38
|
-
const app = await NestFactory.create(AppModule);
|
|
38
|
+
const app = await NestFactory.create(AppModule, { bodyParser: false });
|
|
39
39
|
await app.listen(3000);
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -48,6 +48,8 @@ bootstrap();
|
|
|
48
48
|
|
|
49
49
|
The module registers Mastra routes under `/api` by default. Because it uses a catch-all NestJS controller, either import `MastraModule` last or assign a dedicated prefix such as `/api/mastra`.
|
|
50
50
|
|
|
51
|
+
Disable NestJS's default body parser when creating the application. `MastraModule` installs its own JSON parser so body limits, DELETE request bodies, scalar JSON, and route schema validation use the same adapter behavior.
|
|
52
|
+
|
|
51
53
|
- [NestJS adapter reference](https://mastra.ai/reference/server/nestjs-adapter)
|
|
52
54
|
|
|
53
55
|
## Changelog
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -15,7 +15,7 @@ import type { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
|
|
|
15
15
|
*/
|
|
16
16
|
export declare class MastraExceptionFilter implements ExceptionFilter {
|
|
17
17
|
private readonly logger;
|
|
18
|
-
catch(exception: unknown, host: ArgumentsHost): void
|
|
18
|
+
catch(exception: unknown, host: ArgumentsHost): Promise<void>;
|
|
19
19
|
/**
|
|
20
20
|
* Normalize any error type to a consistent format.
|
|
21
21
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mastra-exception.filter.d.ts","sourceRoot":"","sources":["../../src/filters/mastra-exception.filter.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/index.cjs
CHANGED
|
@@ -86,6 +86,27 @@ function __decorate(decorators, target, key, desc) {
|
|
|
86
86
|
//#endregion
|
|
87
87
|
//#region src/services/route-handler.service.ts
|
|
88
88
|
var _RouteHandlerService;
|
|
89
|
+
const VALIDATION_CONTEXT_LABELS = {
|
|
90
|
+
query: "query parameters",
|
|
91
|
+
body: "request body",
|
|
92
|
+
path: "path parameters"
|
|
93
|
+
};
|
|
94
|
+
function getSchemaTypeName(schema) {
|
|
95
|
+
if (!schema || typeof schema !== "object") return;
|
|
96
|
+
const definition = schema._def ?? schema.def;
|
|
97
|
+
return definition?.typeName ?? definition?.type;
|
|
98
|
+
}
|
|
99
|
+
function unwrapOptionalNullable(schema) {
|
|
100
|
+
let inner = schema;
|
|
101
|
+
let typeName = getSchemaTypeName(inner);
|
|
102
|
+
while (typeName === "ZodOptional" || typeName === "optional" || typeName === "ZodNullable" || typeName === "nullable") {
|
|
103
|
+
const definition = inner?._def ?? inner?.def;
|
|
104
|
+
if (!definition?.innerType) return inner;
|
|
105
|
+
inner = definition.innerType;
|
|
106
|
+
typeName = getSchemaTypeName(inner);
|
|
107
|
+
}
|
|
108
|
+
return inner;
|
|
109
|
+
}
|
|
89
110
|
let RouteHandlerService = _RouteHandlerService = class RouteHandlerService {
|
|
90
111
|
mastra;
|
|
91
112
|
options;
|
|
@@ -103,10 +124,14 @@ let RouteHandlerService = _RouteHandlerService = class RouteHandlerService {
|
|
|
103
124
|
this.mastra = mastra;
|
|
104
125
|
this.options = options;
|
|
105
126
|
this.routeMap = /* @__PURE__ */ new Map();
|
|
106
|
-
for (const route of _mastra_server_server_adapter.SERVER_ROUTES)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
127
|
+
for (const route of _mastra_server_server_adapter.SERVER_ROUTES) this.registerRoute(route);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Register a route with the catch-all NestJS controller.
|
|
131
|
+
*/
|
|
132
|
+
registerRoute(route) {
|
|
133
|
+
const key = this.getRouteKey(route.method, route.path);
|
|
134
|
+
this.routeMap.set(key, route);
|
|
110
135
|
}
|
|
111
136
|
/**
|
|
112
137
|
* Find a route by exact method and path pattern.
|
|
@@ -133,7 +158,7 @@ let RouteHandlerService = _RouteHandlerService = class RouteHandlerService {
|
|
|
133
158
|
route: exactRoute,
|
|
134
159
|
pathParams: {}
|
|
135
160
|
};
|
|
136
|
-
for (const route of
|
|
161
|
+
for (const route of this.routeMap.values()) {
|
|
137
162
|
if (route.method.toUpperCase() !== checkMethod) continue;
|
|
138
163
|
const pathParams = this.matchPath(route.path, path);
|
|
139
164
|
if (pathParams) return {
|
|
@@ -148,7 +173,7 @@ let RouteHandlerService = _RouteHandlerService = class RouteHandlerService {
|
|
|
148
173
|
* Get all routes (for dynamic controller generation).
|
|
149
174
|
*/
|
|
150
175
|
getAllRoutes() {
|
|
151
|
-
return
|
|
176
|
+
return Array.from(this.routeMap.values());
|
|
152
177
|
}
|
|
153
178
|
/**
|
|
154
179
|
* Match a path against a route pattern.
|
|
@@ -182,21 +207,21 @@ let RouteHandlerService = _RouteHandlerService = class RouteHandlerService {
|
|
|
182
207
|
if (route.pathParamSchema) try {
|
|
183
208
|
validatedPathParams = await route.pathParamSchema.parseAsync(params.pathParams);
|
|
184
209
|
} catch (error) {
|
|
185
|
-
if ((0, _mastra_server_server_adapter.isZodError)(error)) throw
|
|
210
|
+
if ((0, _mastra_server_server_adapter.isZodError)(error)) throw this.createValidationError(route, error, "path");
|
|
186
211
|
throw error;
|
|
187
212
|
}
|
|
188
213
|
let validatedQueryParams = params.queryParams;
|
|
189
214
|
if (route.queryParamSchema) try {
|
|
190
215
|
validatedQueryParams = await route.queryParamSchema.parseAsync(params.queryParams);
|
|
191
216
|
} catch (error) {
|
|
192
|
-
if ((0, _mastra_server_server_adapter.isZodError)(error)) throw
|
|
217
|
+
if ((0, _mastra_server_server_adapter.isZodError)(error)) throw this.createValidationError(route, error, "query");
|
|
193
218
|
throw error;
|
|
194
219
|
}
|
|
195
220
|
let validatedBody = params.body;
|
|
196
|
-
if (route.bodySchema
|
|
197
|
-
validatedBody = await
|
|
221
|
+
if (route.bodySchema) try {
|
|
222
|
+
validatedBody = await this.parseBody(route, params.body);
|
|
198
223
|
} catch (error) {
|
|
199
|
-
if ((0, _mastra_server_server_adapter.isZodError)(error)) throw
|
|
224
|
+
if ((0, _mastra_server_server_adapter.isZodError)(error)) throw this.createValidationError(route, error, "body");
|
|
200
225
|
throw error;
|
|
201
226
|
}
|
|
202
227
|
const context = {
|
|
@@ -221,6 +246,31 @@ let RouteHandlerService = _RouteHandlerService = class RouteHandlerService {
|
|
|
221
246
|
sseFlushOnConnect: route.sseFlushOnConnect
|
|
222
247
|
};
|
|
223
248
|
}
|
|
249
|
+
async parseBody(route, body) {
|
|
250
|
+
const bodySchema = route.bodySchema;
|
|
251
|
+
if (!bodySchema) return body;
|
|
252
|
+
if (body === void 0) {
|
|
253
|
+
const omitted = await bodySchema.safeParseAsync(void 0);
|
|
254
|
+
if (omitted.success) return omitted.data;
|
|
255
|
+
const schemaType = getSchemaTypeName(unwrapOptionalNullable(bodySchema));
|
|
256
|
+
if (schemaType === "object" || schemaType === "ZodObject") {
|
|
257
|
+
const emptyObject = await bodySchema.safeParseAsync({});
|
|
258
|
+
if (emptyObject.success) return emptyObject.data;
|
|
259
|
+
}
|
|
260
|
+
throw omitted.error;
|
|
261
|
+
}
|
|
262
|
+
return bodySchema.parseAsync(body);
|
|
263
|
+
}
|
|
264
|
+
createValidationError(route, error, context) {
|
|
265
|
+
const hook = route.onValidationError ?? this.mastra.getServer()?.onValidationError;
|
|
266
|
+
if (hook) try {
|
|
267
|
+
const result = hook(error, context);
|
|
268
|
+
if (result) return new ValidationError(`Invalid ${VALIDATION_CONTEXT_LABELS[context]}`, error, result.status, result.body);
|
|
269
|
+
} catch (hookError) {
|
|
270
|
+
this.logger.error("Error in custom onValidationError hook", hookError);
|
|
271
|
+
}
|
|
272
|
+
return new ValidationError(`Invalid ${VALIDATION_CONTEXT_LABELS[context]}`, error, 400, (0, _mastra_server_handlers_error.formatZodError)(error, VALIDATION_CONTEXT_LABELS[context]));
|
|
273
|
+
}
|
|
224
274
|
getRouteKey(method, path) {
|
|
225
275
|
return `${method.toUpperCase()}:${path}`;
|
|
226
276
|
}
|
|
@@ -254,9 +304,13 @@ RouteHandlerService = _RouteHandlerService = __decorate([
|
|
|
254
304
|
*/
|
|
255
305
|
var ValidationError = class extends Error {
|
|
256
306
|
zodError;
|
|
257
|
-
|
|
307
|
+
status;
|
|
308
|
+
body;
|
|
309
|
+
constructor(message, zodError, status = 400, body = (0, _mastra_server_handlers_error.formatZodError)(zodError, "request")) {
|
|
258
310
|
super(message);
|
|
259
311
|
this.zodError = zodError;
|
|
312
|
+
this.status = status;
|
|
313
|
+
this.body = body;
|
|
260
314
|
this.name = "ValidationError";
|
|
261
315
|
}
|
|
262
316
|
};
|
|
@@ -265,11 +319,29 @@ var ValidationError = class extends Error {
|
|
|
265
319
|
var _MastraExceptionFilter;
|
|
266
320
|
let MastraExceptionFilter = _MastraExceptionFilter = class MastraExceptionFilter {
|
|
267
321
|
logger = new _nestjs_common.Logger(_MastraExceptionFilter.name);
|
|
268
|
-
catch(exception, host) {
|
|
322
|
+
async catch(exception, host) {
|
|
269
323
|
const ctx = host.switchToHttp();
|
|
270
324
|
const response = ctx.getResponse();
|
|
271
325
|
const request = ctx.getRequest();
|
|
272
326
|
if (response.headersSent) return;
|
|
327
|
+
if (exception instanceof ValidationError) {
|
|
328
|
+
response.status(exception.status).json(exception.body);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const customResponse = (0, _mastra_server_server_adapter.getCustomHTTPExceptionResponse)(exception);
|
|
332
|
+
if (customResponse) {
|
|
333
|
+
customResponse.headers.forEach((value, name) => {
|
|
334
|
+
if (name.toLowerCase() !== "set-cookie") response.setHeader(name, value);
|
|
335
|
+
});
|
|
336
|
+
const setCookies = customResponse.headers.getSetCookie();
|
|
337
|
+
if (setCookies.length > 0) response.setHeader("set-cookie", setCookies);
|
|
338
|
+
response.status(customResponse.status).send(Buffer.from(await customResponse.arrayBuffer()));
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (exception instanceof _mastra_server_server_adapter.HTTPException) {
|
|
342
|
+
response.status(exception.status).json({ error: exception.message });
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
273
345
|
const normalized = this.normalizeError(exception);
|
|
274
346
|
const rawRequestId = request.headers["x-request-id"];
|
|
275
347
|
const requestId = (Array.isArray(rawRequestId) ? rawRequestId[0] : rawRequestId) || request.mastraRequestId;
|
|
@@ -10841,7 +10913,8 @@ let MastraController = class MastraController {
|
|
|
10841
10913
|
if (![
|
|
10842
10914
|
"POST",
|
|
10843
10915
|
"PUT",
|
|
10844
|
-
"PATCH"
|
|
10916
|
+
"PATCH",
|
|
10917
|
+
"DELETE"
|
|
10845
10918
|
].includes(req.method)) return;
|
|
10846
10919
|
if ((req.headers["content-type"] || "").includes("multipart/form-data")) {
|
|
10847
10920
|
const maxFileSize = route.maxBodySize ?? this.options.bodyLimitOptions?.maxFileSize;
|
|
@@ -10851,7 +10924,7 @@ let MastraController = class MastraController {
|
|
|
10851
10924
|
allowedMimeTypes
|
|
10852
10925
|
});
|
|
10853
10926
|
}
|
|
10854
|
-
return req.body;
|
|
10927
|
+
return req.body === void 0 ? {} : req.body;
|
|
10855
10928
|
}
|
|
10856
10929
|
};
|
|
10857
10930
|
__decorate([
|
|
@@ -11051,15 +11124,20 @@ MastraAuthGuard = _MastraAuthGuard = __decorate([
|
|
|
11051
11124
|
//#endregion
|
|
11052
11125
|
//#region src/mastra-server.adapter.ts
|
|
11053
11126
|
/**
|
|
11054
|
-
*
|
|
11055
|
-
* Provides
|
|
11127
|
+
* Mastra server adapter wrapper for NestJS.
|
|
11128
|
+
* Provides app access and delegates dynamic route registration to the catch-all controller.
|
|
11056
11129
|
*/
|
|
11057
11130
|
var NestMastraServer = class extends _mastra_core_server.MastraServerBase {
|
|
11058
|
-
|
|
11131
|
+
routeHandler;
|
|
11132
|
+
constructor(app, routeHandler) {
|
|
11059
11133
|
super({
|
|
11060
11134
|
app,
|
|
11061
11135
|
name: "NestMastraServer"
|
|
11062
11136
|
});
|
|
11137
|
+
this.routeHandler = routeHandler;
|
|
11138
|
+
}
|
|
11139
|
+
async registerRoute(_app, route, _options) {
|
|
11140
|
+
this.routeHandler.registerRoute(route);
|
|
11063
11141
|
}
|
|
11064
11142
|
};
|
|
11065
11143
|
//#endregion
|
|
@@ -11070,20 +11148,31 @@ let MastraService = _MastraService = class MastraService {
|
|
|
11070
11148
|
options;
|
|
11071
11149
|
shutdownService;
|
|
11072
11150
|
httpAdapterHost;
|
|
11151
|
+
routeHandler;
|
|
11073
11152
|
logger = new _nestjs_common.Logger(_MastraService.name);
|
|
11074
11153
|
serverAdapter;
|
|
11075
|
-
constructor(mastra, options, shutdownService, httpAdapterHost) {
|
|
11154
|
+
constructor(mastra, options, shutdownService, httpAdapterHost, routeHandler) {
|
|
11076
11155
|
this.mastra = mastra;
|
|
11077
11156
|
this.options = options;
|
|
11078
11157
|
this.shutdownService = shutdownService;
|
|
11079
11158
|
this.httpAdapterHost = httpAdapterHost;
|
|
11159
|
+
this.routeHandler = routeHandler;
|
|
11160
|
+
this.initializeServerAdapter();
|
|
11161
|
+
}
|
|
11162
|
+
onModuleInit() {
|
|
11163
|
+
this.initializeServerAdapter();
|
|
11164
|
+
}
|
|
11165
|
+
initializeServerAdapter() {
|
|
11166
|
+
if (this.serverAdapter) return;
|
|
11080
11167
|
const adapterType = this.httpAdapterHost?.httpAdapter?.getType?.();
|
|
11081
11168
|
if (adapterType && adapterType !== "express") throw new Error(`MastraModule requires NestJS to use the Express HTTP adapter. Received "${adapterType}". Install @nestjs/platform-express and bootstrap with the Express platform.`);
|
|
11082
11169
|
const app = this.httpAdapterHost?.httpAdapter?.getInstance?.();
|
|
11083
|
-
if (app) {
|
|
11084
|
-
this.
|
|
11085
|
-
|
|
11086
|
-
}
|
|
11170
|
+
if (!app) {
|
|
11171
|
+
this.logger.warn("Unable to register Mastra server adapter: HTTP adapter instance not available");
|
|
11172
|
+
return;
|
|
11173
|
+
}
|
|
11174
|
+
this.serverAdapter = new NestMastraServer(app, this.routeHandler);
|
|
11175
|
+
this.mastra.setMastraServer(this.serverAdapter);
|
|
11087
11176
|
}
|
|
11088
11177
|
/**
|
|
11089
11178
|
* Get the Mastra instance.
|
|
@@ -11122,11 +11211,13 @@ MastraService = _MastraService = __decorate([
|
|
|
11122
11211
|
__decorateParam(1, (0, _nestjs_common.Inject)(MASTRA_OPTIONS)),
|
|
11123
11212
|
__decorateParam(2, (0, _nestjs_common.Inject)(ShutdownService)),
|
|
11124
11213
|
__decorateParam(3, (0, _nestjs_common.Inject)(_nestjs_core.HttpAdapterHost)),
|
|
11214
|
+
__decorateParam(4, (0, _nestjs_common.Inject)(RouteHandlerService)),
|
|
11125
11215
|
__decorateMetadata("design:paramtypes", [
|
|
11126
11216
|
Object,
|
|
11127
11217
|
Object,
|
|
11128
11218
|
typeof ShutdownService === "undefined" ? Object : ShutdownService,
|
|
11129
|
-
typeof _nestjs_core.HttpAdapterHost === "undefined" ? Object : _nestjs_core.HttpAdapterHost
|
|
11219
|
+
typeof _nestjs_core.HttpAdapterHost === "undefined" ? Object : _nestjs_core.HttpAdapterHost,
|
|
11220
|
+
typeof RouteHandlerService === "undefined" ? Object : RouteHandlerService
|
|
11130
11221
|
])
|
|
11131
11222
|
], MastraService);
|
|
11132
11223
|
//#endregion
|
|
@@ -11175,11 +11266,12 @@ let JsonBodyMiddleware = class JsonBodyMiddleware {
|
|
|
11175
11266
|
const maxSize = options.bodyLimitOptions?.maxSize ?? 10485760;
|
|
11176
11267
|
this.jsonParser = express.default.json({
|
|
11177
11268
|
limit: maxSize,
|
|
11269
|
+
strict: false,
|
|
11178
11270
|
type: ["application/json", "application/*+json"]
|
|
11179
11271
|
});
|
|
11180
11272
|
}
|
|
11181
11273
|
use(req, res, next) {
|
|
11182
|
-
if (req.body !== void 0
|
|
11274
|
+
if (req.body !== void 0) {
|
|
11183
11275
|
next();
|
|
11184
11276
|
return;
|
|
11185
11277
|
}
|