@mastra/fastify 1.4.11-alpha.0 → 1.4.11-alpha.2
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/CHANGELOG.md +16 -0
- package/dist/index.cjs +675 -789
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +673 -786
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/dist/index.cjs
CHANGED
|
@@ -1,800 +1,686 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
headers.set(key, value);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
return new globalThis.Request(url, {
|
|
23
|
-
method: request.method,
|
|
24
|
-
headers
|
|
25
|
-
});
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _fastify_busboy = require("@fastify/busboy");
|
|
3
|
+
let _mastra_server_server_adapter = require("@mastra/server/server-adapter");
|
|
4
|
+
let _mastra_core_request_context = require("@mastra/core/request-context");
|
|
5
|
+
let _mastra_server_auth = require("@mastra/server/auth");
|
|
6
|
+
//#region src/auth-middleware.ts
|
|
7
|
+
function toWebRequest$1(request) {
|
|
8
|
+
const url = `${request.protocol || "http"}://${request.headers.host || "localhost"}${request.url}`;
|
|
9
|
+
const headers = new Headers();
|
|
10
|
+
for (const [key, value] of Object.entries(request.headers)) {
|
|
11
|
+
if (!value) continue;
|
|
12
|
+
if (Array.isArray(value)) value.forEach((v) => headers.append(key, v));
|
|
13
|
+
else headers.set(key, value);
|
|
14
|
+
}
|
|
15
|
+
return new globalThis.Request(url, {
|
|
16
|
+
method: request.method,
|
|
17
|
+
headers
|
|
18
|
+
});
|
|
26
19
|
}
|
|
27
|
-
function createAuthMiddleware({
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
authConfig,
|
|
57
|
-
customRouteAuthConfig,
|
|
58
|
-
requestContext: request.requestContext,
|
|
59
|
-
rawRequest: toWebRequest(request),
|
|
60
|
-
token,
|
|
61
|
-
buildAuthorizeContext: () => toWebRequest(request)
|
|
62
|
-
});
|
|
63
|
-
if (result.action === "error") {
|
|
64
|
-
return reply.status(result.status).send(result.body);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
20
|
+
function createAuthMiddleware({ mastra, requiresAuth = true }) {
|
|
21
|
+
return async (request, reply) => {
|
|
22
|
+
if (!requiresAuth) return;
|
|
23
|
+
const authConfig = mastra.getServer()?.auth;
|
|
24
|
+
if (!authConfig) return;
|
|
25
|
+
request.requestContext ??= new _mastra_core_request_context.RequestContext();
|
|
26
|
+
request.mastra ??= mastra;
|
|
27
|
+
const path = String(request.url.split("?")[0] || "/");
|
|
28
|
+
const method = String(request.method || "GET");
|
|
29
|
+
const customRouteAuthConfig = new Map(request.customRouteAuthConfig ?? []);
|
|
30
|
+
customRouteAuthConfig.set(`${method}:${path}`, true);
|
|
31
|
+
const authHeader = request.headers.authorization;
|
|
32
|
+
let token = authHeader ? authHeader.replace("Bearer ", "") : null;
|
|
33
|
+
const query = request.query;
|
|
34
|
+
if (!token && query.apiKey) token = query.apiKey || null;
|
|
35
|
+
const result = await (0, _mastra_server_auth.coreAuthMiddleware)({
|
|
36
|
+
path,
|
|
37
|
+
method,
|
|
38
|
+
getHeader: (name) => request.headers[name.toLowerCase()],
|
|
39
|
+
mastra,
|
|
40
|
+
authConfig,
|
|
41
|
+
customRouteAuthConfig,
|
|
42
|
+
requestContext: request.requestContext,
|
|
43
|
+
rawRequest: toWebRequest$1(request),
|
|
44
|
+
token,
|
|
45
|
+
buildAuthorizeContext: () => toWebRequest$1(request)
|
|
46
|
+
});
|
|
47
|
+
if (result.action === "error") return reply.status(result.status).send(result.body);
|
|
48
|
+
};
|
|
67
49
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/index.ts
|
|
52
|
+
let _hasPermissionPromise;
|
|
71
53
|
function loadHasPermission() {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
);
|
|
77
|
-
return void 0;
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
return _hasPermissionPromise;
|
|
54
|
+
if (!_hasPermissionPromise) _hasPermissionPromise = import("@mastra/core/auth/ee").then((m) => m.hasPermission).catch(() => {
|
|
55
|
+
console.error("[@mastra/fastify] Auth features require @mastra/core >= 1.6.0. Please upgrade: npm install @mastra/core@latest");
|
|
56
|
+
});
|
|
57
|
+
return _hasPermissionPromise;
|
|
81
58
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return new globalThis.Request(url, {
|
|
97
|
-
method: request.method,
|
|
98
|
-
headers
|
|
99
|
-
});
|
|
59
|
+
/**
|
|
60
|
+
* Convert Fastify request to Web API Request for cookie-based auth providers.
|
|
61
|
+
*/
|
|
62
|
+
function toWebRequest(request) {
|
|
63
|
+
const url = `${request.protocol || "http"}://${request.headers.host || "localhost"}${request.url}`;
|
|
64
|
+
const headers = new Headers();
|
|
65
|
+
for (const [key, value] of Object.entries(request.headers)) if (value) if (Array.isArray(value)) value.forEach((v) => headers.append(key, v));
|
|
66
|
+
else headers.set(key, value);
|
|
67
|
+
return new globalThis.Request(url, {
|
|
68
|
+
method: request.method,
|
|
69
|
+
headers
|
|
70
|
+
});
|
|
100
71
|
}
|
|
101
72
|
function isRequestAborted(rawRequest) {
|
|
102
|
-
|
|
73
|
+
return rawRequest.aborted || rawRequest.readableAborted || !rawRequest.complete;
|
|
103
74
|
}
|
|
104
|
-
var MastraServer = class extends
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
for (const item of value) response.headers.append(key, String(item));
|
|
711
|
-
} else if (isSetCookie) {
|
|
712
|
-
response.headers.append(key, String(value));
|
|
713
|
-
} else {
|
|
714
|
-
response.headers.set(key, String(value));
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
reply.hijack();
|
|
718
|
-
await this.writeCustomRouteResponse(response, reply.raw, request.abortSignal);
|
|
719
|
-
};
|
|
720
|
-
if (route.method === "ALL") {
|
|
721
|
-
const methods = ["GET", "POST", "PUT", "DELETE", "PATCH"];
|
|
722
|
-
for (const method of methods) {
|
|
723
|
-
this.app.route({ method, url: route.path, handler: fastifyHandler });
|
|
724
|
-
}
|
|
725
|
-
} else {
|
|
726
|
-
this.app.route({
|
|
727
|
-
method: route.method,
|
|
728
|
-
url: route.path,
|
|
729
|
-
handler: fastifyHandler
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
registerContextMiddleware() {
|
|
735
|
-
this.app.removeContentTypeParser("application/json");
|
|
736
|
-
this.app.addContentTypeParser("application/json", { parseAs: "string" }, (_request, body, done) => {
|
|
737
|
-
try {
|
|
738
|
-
if (!body || typeof body === "string" && body.trim() === "") {
|
|
739
|
-
done(null, void 0);
|
|
740
|
-
return;
|
|
741
|
-
}
|
|
742
|
-
const parsed = JSON.parse(body);
|
|
743
|
-
done(null, parsed);
|
|
744
|
-
} catch (err) {
|
|
745
|
-
done(err, void 0);
|
|
746
|
-
}
|
|
747
|
-
});
|
|
748
|
-
this.app.addContentTypeParser("multipart/form-data", (_request, _payload, done) => {
|
|
749
|
-
done(null, void 0);
|
|
750
|
-
});
|
|
751
|
-
this.app.addHook("preHandler", this.createContextMiddleware());
|
|
752
|
-
}
|
|
753
|
-
registerAuthMiddleware() {
|
|
754
|
-
}
|
|
755
|
-
registerHttpLoggingMiddleware() {
|
|
756
|
-
if (!this.httpLoggingConfig?.enabled) {
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
this.app.addHook("onRequest", async (request, reply) => {
|
|
760
|
-
const urlPath = request.url.split("?")[0];
|
|
761
|
-
if (!this.shouldLogRequest(urlPath)) {
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
const start = Date.now();
|
|
765
|
-
const method = request.method;
|
|
766
|
-
const path = urlPath;
|
|
767
|
-
reply.raw.once("finish", () => {
|
|
768
|
-
const duration = Date.now() - start;
|
|
769
|
-
const status = reply.statusCode;
|
|
770
|
-
const level = this.httpLoggingConfig?.level || "info";
|
|
771
|
-
const logData = {
|
|
772
|
-
method,
|
|
773
|
-
path,
|
|
774
|
-
status,
|
|
775
|
-
duration: `${duration}ms`
|
|
776
|
-
};
|
|
777
|
-
if (this.httpLoggingConfig?.includeQueryParams) {
|
|
778
|
-
logData.query = request.query;
|
|
779
|
-
}
|
|
780
|
-
if (this.httpLoggingConfig?.includeHeaders) {
|
|
781
|
-
const headers = { ...request.headers };
|
|
782
|
-
const redactHeaders = this.httpLoggingConfig.redactHeaders || [];
|
|
783
|
-
redactHeaders.forEach((h) => {
|
|
784
|
-
const key = h.toLowerCase();
|
|
785
|
-
if (headers[key] !== void 0) {
|
|
786
|
-
headers[key] = "[REDACTED]";
|
|
787
|
-
}
|
|
788
|
-
});
|
|
789
|
-
logData.headers = headers;
|
|
790
|
-
}
|
|
791
|
-
this.logger[level](`${method} ${path} ${status} ${duration}ms`, logData);
|
|
792
|
-
});
|
|
793
|
-
});
|
|
794
|
-
}
|
|
75
|
+
var MastraServer = class extends _mastra_server_server_adapter.MastraServer {
|
|
76
|
+
createContextMiddleware() {
|
|
77
|
+
return async (request, reply) => {
|
|
78
|
+
let bodyRequestContext;
|
|
79
|
+
let paramsRequestContext;
|
|
80
|
+
if (request.method === "POST" || request.method === "PUT") {
|
|
81
|
+
if (request.headers["content-type"]?.includes("application/json") && request.body) {
|
|
82
|
+
const body = request.body;
|
|
83
|
+
if (body.requestContext) bodyRequestContext = body.requestContext;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (request.method === "GET") try {
|
|
87
|
+
const encodedRequestContext = request.query.requestContext;
|
|
88
|
+
if (typeof encodedRequestContext === "string") try {
|
|
89
|
+
paramsRequestContext = JSON.parse(encodedRequestContext);
|
|
90
|
+
} catch {
|
|
91
|
+
try {
|
|
92
|
+
const json = Buffer.from(encodedRequestContext, "base64").toString("utf-8");
|
|
93
|
+
paramsRequestContext = JSON.parse(json);
|
|
94
|
+
} catch {}
|
|
95
|
+
}
|
|
96
|
+
} catch {}
|
|
97
|
+
const requestContext = this.mergeRequestContext({
|
|
98
|
+
paramsRequestContext,
|
|
99
|
+
bodyRequestContext
|
|
100
|
+
});
|
|
101
|
+
this.applyRequestMetadataToContext({
|
|
102
|
+
requestContext,
|
|
103
|
+
getHeader: (name) => {
|
|
104
|
+
const value = request.headers[name.toLowerCase()];
|
|
105
|
+
return Array.isArray(value) ? value[0] : value;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
request.requestContext = requestContext;
|
|
109
|
+
request.mastra = this.mastra;
|
|
110
|
+
request.registeredTools = this.tools || {};
|
|
111
|
+
if (this.taskStore) request.taskStore = this.taskStore;
|
|
112
|
+
request.customRouteAuthConfig = this.customRouteAuthConfig;
|
|
113
|
+
const controller = new AbortController();
|
|
114
|
+
request.raw.on("close", () => {
|
|
115
|
+
if (isRequestAborted(request.raw)) controller.abort();
|
|
116
|
+
});
|
|
117
|
+
reply.raw.on("close", () => {
|
|
118
|
+
if (!reply.raw.writableEnded) controller.abort();
|
|
119
|
+
});
|
|
120
|
+
request.abortSignal = controller.signal;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
async stream(route, reply, result, request) {
|
|
124
|
+
const rawHeaders = reply.getHeaders();
|
|
125
|
+
const existingHeaders = {};
|
|
126
|
+
for (const [key, value] of Object.entries(rawHeaders)) {
|
|
127
|
+
if (value === void 0) continue;
|
|
128
|
+
const lowerKey = key.toLowerCase();
|
|
129
|
+
if (lowerKey === "content-length" || lowerKey === "transfer-encoding") continue;
|
|
130
|
+
existingHeaders[key] = value;
|
|
131
|
+
}
|
|
132
|
+
reply.hijack();
|
|
133
|
+
const streamFormat = route.streamFormat || "stream";
|
|
134
|
+
const sseHeaders = streamFormat === "sse" ? {
|
|
135
|
+
"Content-Type": "text/event-stream",
|
|
136
|
+
"Cache-Control": "no-cache",
|
|
137
|
+
Connection: "keep-alive",
|
|
138
|
+
"X-Accel-Buffering": "no"
|
|
139
|
+
} : { "Content-Type": "text/plain" };
|
|
140
|
+
reply.raw.writeHead(200, {
|
|
141
|
+
...existingHeaders,
|
|
142
|
+
...sseHeaders,
|
|
143
|
+
"Transfer-Encoding": "chunked"
|
|
144
|
+
});
|
|
145
|
+
if (streamFormat === "sse" && route.sseFlushOnConnect) reply.raw.write(": connected\n\n");
|
|
146
|
+
const reader = (result instanceof ReadableStream ? result : result.fullStream).getReader();
|
|
147
|
+
let readerCanceled = false;
|
|
148
|
+
const cancelReader = (reason) => {
|
|
149
|
+
if (readerCanceled) return;
|
|
150
|
+
readerCanceled = true;
|
|
151
|
+
reader.cancel(reason);
|
|
152
|
+
};
|
|
153
|
+
const cancelReaderOnResponseClose = () => cancelReader("request aborted");
|
|
154
|
+
const cancelReaderOnRequestClose = () => {
|
|
155
|
+
if (request && isRequestAborted(request.raw)) cancelReader("request aborted");
|
|
156
|
+
};
|
|
157
|
+
reply.raw.on("close", cancelReaderOnResponseClose);
|
|
158
|
+
request?.raw.on("close", cancelReaderOnRequestClose);
|
|
159
|
+
try {
|
|
160
|
+
while (true) {
|
|
161
|
+
const { done, value } = await reader.read();
|
|
162
|
+
if (done) break;
|
|
163
|
+
if (value) {
|
|
164
|
+
if (streamFormat === "sse" && typeof value === "string" && value.startsWith(":")) {
|
|
165
|
+
reply.raw.write(value);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
const outputValue = this.streamOptions?.redact ?? true ? (0, _mastra_server_server_adapter.redactStreamChunk)(value) : value;
|
|
169
|
+
const serialized = (0, _mastra_server_server_adapter.serializeStreamChunk)(outputValue);
|
|
170
|
+
if (!serialized.ok) {
|
|
171
|
+
this.mastra.getLogger()?.error("Failed to serialize stream chunk, skipping", {
|
|
172
|
+
path: route.path,
|
|
173
|
+
chunkType: outputValue?.type,
|
|
174
|
+
error: serialized.error.message
|
|
175
|
+
});
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (streamFormat === "sse") reply.raw.write(`data: ${serialized.json}\n\n`);
|
|
179
|
+
else reply.raw.write(serialized.json + "");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
this.mastra.getLogger()?.error("Error in stream processing", { error: error instanceof Error ? {
|
|
184
|
+
message: error.message,
|
|
185
|
+
stack: error.stack
|
|
186
|
+
} : error });
|
|
187
|
+
} finally {
|
|
188
|
+
reply.raw.off("close", cancelReaderOnResponseClose);
|
|
189
|
+
request?.raw.off("close", cancelReaderOnRequestClose);
|
|
190
|
+
if (!reply.raw.writableEnded && !reply.raw.destroyed) reply.raw.end();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async getParams(route, request) {
|
|
194
|
+
const urlParams = request.params || {};
|
|
195
|
+
const queryParams = (0, _mastra_server_server_adapter.normalizeQueryParams)(request.query || {});
|
|
196
|
+
let body;
|
|
197
|
+
let bodyParseError;
|
|
198
|
+
if (route.method === "POST" || route.method === "PUT" || route.method === "PATCH" || route.method === "DELETE") if ((request.headers["content-type"] || "").includes("multipart/form-data")) try {
|
|
199
|
+
const maxFileSize = route.maxBodySize ?? this.bodyLimitOptions?.maxSize;
|
|
200
|
+
body = await this.parseMultipartFormData(request, maxFileSize);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
this.mastra.getLogger()?.error("Failed to parse multipart form data", { error: error instanceof Error ? {
|
|
203
|
+
message: error.message,
|
|
204
|
+
stack: error.stack
|
|
205
|
+
} : error });
|
|
206
|
+
if (error instanceof Error && error.message.toLowerCase().includes("size")) throw error;
|
|
207
|
+
bodyParseError = { message: error instanceof Error ? error.message : "Failed to parse multipart form data" };
|
|
208
|
+
}
|
|
209
|
+
else body = request.body;
|
|
210
|
+
return {
|
|
211
|
+
urlParams,
|
|
212
|
+
queryParams,
|
|
213
|
+
body,
|
|
214
|
+
bodyParseError
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Parse multipart/form-data using @fastify/busboy.
|
|
219
|
+
* Converts file uploads to Buffers and parses JSON field values.
|
|
220
|
+
*
|
|
221
|
+
* @param request - The Fastify request object
|
|
222
|
+
* @param maxFileSize - Optional maximum file size in bytes
|
|
223
|
+
*/
|
|
224
|
+
parseMultipartFormData(request, maxFileSize) {
|
|
225
|
+
return new Promise((resolve, reject) => {
|
|
226
|
+
const result = {};
|
|
227
|
+
const busboy = new _fastify_busboy.Busboy({
|
|
228
|
+
headers: { "content-type": request.headers["content-type"] },
|
|
229
|
+
limits: maxFileSize ? { fileSize: maxFileSize } : void 0
|
|
230
|
+
});
|
|
231
|
+
busboy.on("file", (fieldname, file, _filename, _encoding, _mimetype) => {
|
|
232
|
+
const chunks = [];
|
|
233
|
+
let limitExceeded = false;
|
|
234
|
+
file.on("data", (chunk) => {
|
|
235
|
+
chunks.push(chunk);
|
|
236
|
+
});
|
|
237
|
+
file.on("limit", () => {
|
|
238
|
+
limitExceeded = true;
|
|
239
|
+
file.resume();
|
|
240
|
+
reject(/* @__PURE__ */ new Error(`File size limit exceeded${maxFileSize ? ` (max: ${maxFileSize} bytes)` : ""}`));
|
|
241
|
+
});
|
|
242
|
+
file.on("end", () => {
|
|
243
|
+
if (!limitExceeded) result[fieldname] = Buffer.concat(chunks);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
busboy.on("field", (fieldname, value) => {
|
|
247
|
+
try {
|
|
248
|
+
result[fieldname] = JSON.parse(value);
|
|
249
|
+
} catch {
|
|
250
|
+
result[fieldname] = value;
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
busboy.on("finish", () => {
|
|
254
|
+
resolve(result);
|
|
255
|
+
});
|
|
256
|
+
busboy.on("error", (error) => {
|
|
257
|
+
reject(error);
|
|
258
|
+
});
|
|
259
|
+
request.raw.pipe(busboy);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
async sendResponse(route, reply, result, request, prefix) {
|
|
263
|
+
const resolvedPrefix = prefix ?? this.prefix ?? "";
|
|
264
|
+
if (result && typeof result === "object" && "__refreshHeaders" in result) {
|
|
265
|
+
const refreshHeaders = result.__refreshHeaders;
|
|
266
|
+
for (const [key, value] of Object.entries(refreshHeaders)) reply.header(key, value);
|
|
267
|
+
delete result.__refreshHeaders;
|
|
268
|
+
}
|
|
269
|
+
if (route.responseType === "json") await reply.send(result);
|
|
270
|
+
else if (route.responseType === "stream") await this.stream(route, reply, result, request);
|
|
271
|
+
else if (route.responseType === "datastream-response") {
|
|
272
|
+
const fetchResponse = result;
|
|
273
|
+
fetchResponse.headers.forEach((value, key) => reply.header(key, value));
|
|
274
|
+
reply.status(fetchResponse.status);
|
|
275
|
+
if (fetchResponse.body) {
|
|
276
|
+
const reader = fetchResponse.body.getReader();
|
|
277
|
+
let readerCanceled = false;
|
|
278
|
+
const cancelReader = (reason) => {
|
|
279
|
+
if (readerCanceled) return;
|
|
280
|
+
readerCanceled = true;
|
|
281
|
+
reader.cancel(reason);
|
|
282
|
+
};
|
|
283
|
+
const cancelReaderOnResponseClose = () => cancelReader("request aborted");
|
|
284
|
+
const cancelReaderOnRequestClose = () => {
|
|
285
|
+
if (request && isRequestAborted(request.raw)) cancelReader("request aborted");
|
|
286
|
+
};
|
|
287
|
+
const onResError = (err) => {
|
|
288
|
+
this.mastra.getLogger()?.error("Error writing datastream response", { error: err instanceof Error ? {
|
|
289
|
+
message: err.message,
|
|
290
|
+
stack: err.stack
|
|
291
|
+
} : err });
|
|
292
|
+
cancelReader("response write error");
|
|
293
|
+
};
|
|
294
|
+
reply.raw.once("error", onResError);
|
|
295
|
+
reply.raw.on("close", cancelReaderOnResponseClose);
|
|
296
|
+
request?.raw.on("close", cancelReaderOnRequestClose);
|
|
297
|
+
try {
|
|
298
|
+
while (true) {
|
|
299
|
+
const { done, value } = await reader.read();
|
|
300
|
+
if (done) break;
|
|
301
|
+
reply.raw.write(value);
|
|
302
|
+
}
|
|
303
|
+
} catch (error) {
|
|
304
|
+
this.mastra.getLogger()?.error("Error in datastream processing", { error: error instanceof Error ? {
|
|
305
|
+
message: error.message,
|
|
306
|
+
stack: error.stack
|
|
307
|
+
} : error });
|
|
308
|
+
} finally {
|
|
309
|
+
reply.raw.off("error", onResError);
|
|
310
|
+
reply.raw.off("close", cancelReaderOnResponseClose);
|
|
311
|
+
request?.raw.off("close", cancelReaderOnRequestClose);
|
|
312
|
+
if (!reply.raw.writableEnded && !reply.raw.destroyed) reply.raw.end();
|
|
313
|
+
}
|
|
314
|
+
} else reply.raw.end();
|
|
315
|
+
} else if (route.responseType === "mcp-http") {
|
|
316
|
+
if (!request) {
|
|
317
|
+
await reply.status(500).send({ error: "Request object required for MCP transport" });
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const { server, httpPath, mcpOptions: routeMcpOptions } = result;
|
|
321
|
+
try {
|
|
322
|
+
reply.hijack();
|
|
323
|
+
const rawReq = request.raw;
|
|
324
|
+
if (request.body !== void 0) rawReq.body = request.body;
|
|
325
|
+
const options = {
|
|
326
|
+
...this.mcpOptions,
|
|
327
|
+
...routeMcpOptions
|
|
328
|
+
};
|
|
329
|
+
await server.startHTTP({
|
|
330
|
+
url: new URL(request.url, `http://${request.headers.host}`),
|
|
331
|
+
httpPath: `${resolvedPrefix}${httpPath}`,
|
|
332
|
+
req: rawReq,
|
|
333
|
+
res: reply.raw,
|
|
334
|
+
options: Object.keys(options).length > 0 ? options : void 0
|
|
335
|
+
});
|
|
336
|
+
} catch {
|
|
337
|
+
if (!reply.raw.headersSent) {
|
|
338
|
+
reply.raw.writeHead(500, { "Content-Type": "application/json" });
|
|
339
|
+
reply.raw.end(JSON.stringify({
|
|
340
|
+
jsonrpc: "2.0",
|
|
341
|
+
error: {
|
|
342
|
+
code: -32603,
|
|
343
|
+
message: "Internal server error"
|
|
344
|
+
},
|
|
345
|
+
id: null
|
|
346
|
+
}));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} else if (route.responseType === "mcp-sse") {
|
|
350
|
+
if (!request) {
|
|
351
|
+
await reply.status(500).send({ error: "Request object required for MCP transport" });
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const { server, ssePath, messagePath } = result;
|
|
355
|
+
try {
|
|
356
|
+
reply.hijack();
|
|
357
|
+
const rawReq = request.raw;
|
|
358
|
+
if (request.body !== void 0) rawReq.body = request.body;
|
|
359
|
+
await server.startSSE({
|
|
360
|
+
url: new URL(request.url, `http://${request.headers.host}`),
|
|
361
|
+
ssePath: `${resolvedPrefix}${ssePath}`,
|
|
362
|
+
messagePath: `${resolvedPrefix}${messagePath}`,
|
|
363
|
+
req: rawReq,
|
|
364
|
+
res: reply.raw
|
|
365
|
+
});
|
|
366
|
+
} catch {
|
|
367
|
+
if (!reply.raw.headersSent) {
|
|
368
|
+
reply.raw.writeHead(500, { "Content-Type": "application/json" });
|
|
369
|
+
reply.raw.end(JSON.stringify({ error: "Error handling MCP SSE request" }));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
} else reply.status(500);
|
|
373
|
+
}
|
|
374
|
+
async registerRoute(app, route, { prefix: prefixParam } = {}) {
|
|
375
|
+
const prefix = prefixParam ?? this.prefix ?? "";
|
|
376
|
+
const fastifyPath = `${prefix}${route.path}`;
|
|
377
|
+
const handler = async (request, reply) => {
|
|
378
|
+
const authError = await this.checkRouteAuth(route, {
|
|
379
|
+
path: String(request.url.split("?")[0] || "/"),
|
|
380
|
+
method: String(request.method || "GET"),
|
|
381
|
+
getHeader: (name) => request.headers[name.toLowerCase()],
|
|
382
|
+
getQuery: (name) => request.query[name],
|
|
383
|
+
requestContext: request.requestContext,
|
|
384
|
+
request: toWebRequest(request),
|
|
385
|
+
buildAuthorizeContext: () => toWebRequest(request)
|
|
386
|
+
});
|
|
387
|
+
if (authError) {
|
|
388
|
+
if (authError.headers) for (const [key, value] of Object.entries(authError.headers)) reply.header(key, value);
|
|
389
|
+
if (authError.error) return reply.status(authError.status).send({ error: authError.error });
|
|
390
|
+
}
|
|
391
|
+
const params = await this.getParams(route, request);
|
|
392
|
+
if (params.bodyParseError) return reply.status(400).send({
|
|
393
|
+
error: "Invalid request body",
|
|
394
|
+
issues: [{
|
|
395
|
+
field: "body",
|
|
396
|
+
message: params.bodyParseError.message
|
|
397
|
+
}]
|
|
398
|
+
});
|
|
399
|
+
if (params.queryParams) try {
|
|
400
|
+
params.queryParams = await this.parseQueryParams(route, params.queryParams);
|
|
401
|
+
} catch (error) {
|
|
402
|
+
this.mastra.getLogger()?.error("Error parsing query params", { error: error instanceof Error ? {
|
|
403
|
+
message: error.message,
|
|
404
|
+
stack: error.stack
|
|
405
|
+
} : error });
|
|
406
|
+
if ((0, _mastra_server_server_adapter.isZodError)(error)) {
|
|
407
|
+
const { status, body } = this.resolveValidationError(route, error, "query");
|
|
408
|
+
return reply.status(status).send(body);
|
|
409
|
+
}
|
|
410
|
+
return reply.status(400).send({
|
|
411
|
+
error: "Invalid query parameters",
|
|
412
|
+
issues: [{
|
|
413
|
+
field: "unknown",
|
|
414
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
415
|
+
}]
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
if (params.body) try {
|
|
419
|
+
params.body = await this.parseBody(route, params.body);
|
|
420
|
+
} catch (error) {
|
|
421
|
+
this.mastra.getLogger()?.error("Error parsing body", { error: error instanceof Error ? {
|
|
422
|
+
message: error.message,
|
|
423
|
+
stack: error.stack
|
|
424
|
+
} : error });
|
|
425
|
+
if ((0, _mastra_server_server_adapter.isZodError)(error)) {
|
|
426
|
+
const { status, body } = this.resolveValidationError(route, error, "body");
|
|
427
|
+
return reply.status(status).send(body);
|
|
428
|
+
}
|
|
429
|
+
return reply.status(400).send({
|
|
430
|
+
error: "Invalid request body",
|
|
431
|
+
issues: [{
|
|
432
|
+
field: "unknown",
|
|
433
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
434
|
+
}]
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
if (params.urlParams) try {
|
|
438
|
+
params.urlParams = await this.parsePathParams(route, params.urlParams);
|
|
439
|
+
} catch (error) {
|
|
440
|
+
this.mastra.getLogger()?.error("Error parsing path params", { error: error instanceof Error ? {
|
|
441
|
+
message: error.message,
|
|
442
|
+
stack: error.stack
|
|
443
|
+
} : error });
|
|
444
|
+
if ((0, _mastra_server_server_adapter.isZodError)(error)) {
|
|
445
|
+
const { status, body } = this.resolveValidationError(route, error, "path");
|
|
446
|
+
return reply.status(status).send(body);
|
|
447
|
+
}
|
|
448
|
+
return reply.status(400).send({
|
|
449
|
+
error: "Invalid path parameters",
|
|
450
|
+
issues: [{
|
|
451
|
+
field: "unknown",
|
|
452
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
453
|
+
}]
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
const handlerParams = {
|
|
457
|
+
...params.urlParams,
|
|
458
|
+
...params.queryParams,
|
|
459
|
+
...typeof params.body === "object" ? params.body : {},
|
|
460
|
+
requestContext: request.requestContext,
|
|
461
|
+
mastra: this.mastra,
|
|
462
|
+
registeredTools: request.registeredTools,
|
|
463
|
+
taskStore: request.taskStore,
|
|
464
|
+
abortSignal: request.abortSignal,
|
|
465
|
+
routePrefix: prefix,
|
|
466
|
+
request: toWebRequest(request)
|
|
467
|
+
};
|
|
468
|
+
const requestContext = request.requestContext;
|
|
469
|
+
if (this.mastra.getStudio?.()?.auth || this.mastra.getServer()?.auth) {
|
|
470
|
+
const hasPermission = await loadHasPermission();
|
|
471
|
+
if (hasPermission) {
|
|
472
|
+
const userPermissions = requestContext.get("mastra__userPermissions");
|
|
473
|
+
const permissionError = this.checkRoutePermission(route, userPermissions, hasPermission, requestContext);
|
|
474
|
+
if (permissionError) return reply.status(permissionError.status).send({
|
|
475
|
+
error: permissionError.error,
|
|
476
|
+
message: permissionError.message
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
const fgaError = await (0, _mastra_server_server_adapter.checkRouteFGA)(this.mastra, route, requestContext, {
|
|
481
|
+
...params.urlParams,
|
|
482
|
+
...params.queryParams,
|
|
483
|
+
...typeof params.body === "object" ? params.body : {}
|
|
484
|
+
});
|
|
485
|
+
if (fgaError) return reply.status(fgaError.status).send({
|
|
486
|
+
error: fgaError.error,
|
|
487
|
+
message: fgaError.message
|
|
488
|
+
});
|
|
489
|
+
try {
|
|
490
|
+
const result = await route.handler(handlerParams);
|
|
491
|
+
await this.sendResponse(route, reply, result, request, prefix);
|
|
492
|
+
} catch (error) {
|
|
493
|
+
const httpStatus = error && typeof error === "object" && "status" in error ? error.status : void 0;
|
|
494
|
+
if (!(typeof httpStatus === "number" && httpStatus >= 400 && httpStatus < 500)) this.mastra.getLogger()?.error("Error calling handler", {
|
|
495
|
+
error: error instanceof Error ? {
|
|
496
|
+
message: error.message,
|
|
497
|
+
stack: error.stack
|
|
498
|
+
} : error,
|
|
499
|
+
path: route.path,
|
|
500
|
+
method: route.method
|
|
501
|
+
});
|
|
502
|
+
let status = 500;
|
|
503
|
+
if (error && typeof error === "object") {
|
|
504
|
+
if ("status" in error) status = error.status;
|
|
505
|
+
else if ("details" in error && error.details && typeof error.details === "object" && "status" in error.details) status = error.details.status;
|
|
506
|
+
}
|
|
507
|
+
await reply.status(status).send({ error: error instanceof Error ? error.message : "Unknown error" });
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
const shouldApplyBodyLimit = this.bodyLimitOptions && [
|
|
511
|
+
"POST",
|
|
512
|
+
"PUT",
|
|
513
|
+
"PATCH"
|
|
514
|
+
].includes(route.method.toUpperCase());
|
|
515
|
+
const maxSize = route.maxBodySize ?? this.bodyLimitOptions?.maxSize;
|
|
516
|
+
const config = shouldApplyBodyLimit && maxSize ? { bodyLimit: maxSize } : void 0;
|
|
517
|
+
if (route.method.toUpperCase() === "ALL") for (const method of [
|
|
518
|
+
"GET",
|
|
519
|
+
"POST",
|
|
520
|
+
"PUT",
|
|
521
|
+
"DELETE",
|
|
522
|
+
"PATCH"
|
|
523
|
+
]) try {
|
|
524
|
+
app.route({
|
|
525
|
+
method,
|
|
526
|
+
url: fastifyPath,
|
|
527
|
+
handler,
|
|
528
|
+
config
|
|
529
|
+
});
|
|
530
|
+
} catch (err) {
|
|
531
|
+
if (err instanceof Error && err.message.includes("already declared")) continue;
|
|
532
|
+
throw err;
|
|
533
|
+
}
|
|
534
|
+
else app.route({
|
|
535
|
+
method: route.method,
|
|
536
|
+
url: fastifyPath,
|
|
537
|
+
handler,
|
|
538
|
+
config
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
async registerCustomApiRoutes() {
|
|
542
|
+
if (!await this.buildCustomRouteHandler()) return;
|
|
543
|
+
const routes = this.customApiRoutes ?? this.mastra.getServer()?.apiRoutes ?? [];
|
|
544
|
+
for (const route of routes) {
|
|
545
|
+
const serverRoute = {
|
|
546
|
+
method: route.method,
|
|
547
|
+
path: route.path,
|
|
548
|
+
responseType: "json",
|
|
549
|
+
handler: async () => {},
|
|
550
|
+
requiresAuth: route.requiresAuth,
|
|
551
|
+
requiresPermission: route.requiresPermission,
|
|
552
|
+
fga: route.fga
|
|
553
|
+
};
|
|
554
|
+
const fastifyHandler = async (request, reply) => {
|
|
555
|
+
const authError = await this.checkRouteAuth(serverRoute, {
|
|
556
|
+
path: String(request.url.split("?")[0] || "/"),
|
|
557
|
+
method: String(request.method || "GET"),
|
|
558
|
+
getHeader: (name) => request.headers[name.toLowerCase()],
|
|
559
|
+
getQuery: (name) => request.query[name],
|
|
560
|
+
requestContext: request.requestContext,
|
|
561
|
+
request: toWebRequest(request),
|
|
562
|
+
buildAuthorizeContext: () => toWebRequest(request)
|
|
563
|
+
});
|
|
564
|
+
if (authError) {
|
|
565
|
+
if (authError.headers) for (const [key, value] of Object.entries(authError.headers)) reply.header(key, value);
|
|
566
|
+
if (authError.error) return reply.status(authError.status).send({ error: authError.error });
|
|
567
|
+
}
|
|
568
|
+
const requestContext = request.requestContext;
|
|
569
|
+
if (this.mastra.getStudio?.()?.auth || this.mastra.getServer()?.auth) {
|
|
570
|
+
let hasPermission;
|
|
571
|
+
try {
|
|
572
|
+
({hasPermission} = await import("@mastra/core/auth/ee"));
|
|
573
|
+
} catch {
|
|
574
|
+
console.error("[@mastra/fastify] Auth features require @mastra/core >= 1.6.0. Please upgrade: npm install @mastra/core@latest");
|
|
575
|
+
}
|
|
576
|
+
if (hasPermission) {
|
|
577
|
+
const userPermissions = requestContext.get("mastra__userPermissions");
|
|
578
|
+
const permissionError = this.checkRoutePermission(serverRoute, userPermissions, hasPermission, requestContext);
|
|
579
|
+
if (permissionError) return reply.status(permissionError.status).send({
|
|
580
|
+
error: permissionError.error,
|
|
581
|
+
message: permissionError.message
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
const fgaError = await (0, _mastra_server_server_adapter.checkRouteFGA)(this.mastra, serverRoute, requestContext, {
|
|
586
|
+
...request.params,
|
|
587
|
+
...request.query,
|
|
588
|
+
...typeof request.body === "object" && request.body !== null ? request.body : {}
|
|
589
|
+
});
|
|
590
|
+
if (fgaError) return reply.status(fgaError.status).send({
|
|
591
|
+
error: fgaError.error,
|
|
592
|
+
message: fgaError.message
|
|
593
|
+
});
|
|
594
|
+
const response = await this.handleCustomRouteRequest(`http://${request.headers.host}${request.url}`, request.method, request.headers, request.body, request.requestContext, request.abortSignal);
|
|
595
|
+
if (!response) {
|
|
596
|
+
reply.status(404).send({ error: "Not Found" });
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
const existingHeaders = reply.getHeaders();
|
|
600
|
+
for (const [key, value] of Object.entries(existingHeaders)) {
|
|
601
|
+
if (value === void 0) continue;
|
|
602
|
+
const lowerKey = key.toLowerCase();
|
|
603
|
+
if (lowerKey === "content-length" || lowerKey === "transfer-encoding") continue;
|
|
604
|
+
const isSetCookie = lowerKey === "set-cookie";
|
|
605
|
+
if (!isSetCookie && response.headers.has(key)) continue;
|
|
606
|
+
if (Array.isArray(value)) for (const item of value) response.headers.append(key, String(item));
|
|
607
|
+
else if (isSetCookie) response.headers.append(key, String(value));
|
|
608
|
+
else response.headers.set(key, String(value));
|
|
609
|
+
}
|
|
610
|
+
reply.hijack();
|
|
611
|
+
await this.writeCustomRouteResponse(response, reply.raw, request.abortSignal);
|
|
612
|
+
};
|
|
613
|
+
if (route.method === "ALL") for (const method of [
|
|
614
|
+
"GET",
|
|
615
|
+
"POST",
|
|
616
|
+
"PUT",
|
|
617
|
+
"DELETE",
|
|
618
|
+
"PATCH"
|
|
619
|
+
]) this.app.route({
|
|
620
|
+
method,
|
|
621
|
+
url: route.path,
|
|
622
|
+
handler: fastifyHandler
|
|
623
|
+
});
|
|
624
|
+
else this.app.route({
|
|
625
|
+
method: route.method,
|
|
626
|
+
url: route.path,
|
|
627
|
+
handler: fastifyHandler
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
registerContextMiddleware() {
|
|
632
|
+
this.app.removeContentTypeParser("application/json");
|
|
633
|
+
this.app.addContentTypeParser("application/json", { parseAs: "string" }, (_request, body, done) => {
|
|
634
|
+
try {
|
|
635
|
+
if (!body || typeof body === "string" && body.trim() === "") {
|
|
636
|
+
done(null, void 0);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
done(null, JSON.parse(body));
|
|
640
|
+
} catch (err) {
|
|
641
|
+
done(err, void 0);
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
this.app.addContentTypeParser("multipart/form-data", (_request, _payload, done) => {
|
|
645
|
+
done(null, void 0);
|
|
646
|
+
});
|
|
647
|
+
this.app.addHook("preHandler", this.createContextMiddleware());
|
|
648
|
+
}
|
|
649
|
+
registerAuthMiddleware() {}
|
|
650
|
+
registerHttpLoggingMiddleware() {
|
|
651
|
+
if (!this.httpLoggingConfig?.enabled) return;
|
|
652
|
+
this.app.addHook("onRequest", async (request, reply) => {
|
|
653
|
+
const urlPath = request.url.split("?")[0];
|
|
654
|
+
if (!this.shouldLogRequest(urlPath)) return;
|
|
655
|
+
const start = Date.now();
|
|
656
|
+
const method = request.method;
|
|
657
|
+
const path = urlPath;
|
|
658
|
+
reply.raw.once("finish", () => {
|
|
659
|
+
const duration = Date.now() - start;
|
|
660
|
+
const status = reply.statusCode;
|
|
661
|
+
const level = this.httpLoggingConfig?.level || "info";
|
|
662
|
+
const logData = {
|
|
663
|
+
method,
|
|
664
|
+
path,
|
|
665
|
+
status,
|
|
666
|
+
duration: `${duration}ms`
|
|
667
|
+
};
|
|
668
|
+
if (this.httpLoggingConfig?.includeQueryParams) logData.query = request.query;
|
|
669
|
+
if (this.httpLoggingConfig?.includeHeaders) {
|
|
670
|
+
const headers = { ...request.headers };
|
|
671
|
+
(this.httpLoggingConfig.redactHeaders || []).forEach((h) => {
|
|
672
|
+
const key = h.toLowerCase();
|
|
673
|
+
if (headers[key] !== void 0) headers[key] = "[REDACTED]";
|
|
674
|
+
});
|
|
675
|
+
logData.headers = headers;
|
|
676
|
+
}
|
|
677
|
+
this.logger[level](`${method} ${path} ${status} ${duration}ms`, logData);
|
|
678
|
+
});
|
|
679
|
+
});
|
|
680
|
+
}
|
|
795
681
|
};
|
|
796
|
-
|
|
682
|
+
//#endregion
|
|
797
683
|
exports.MastraServer = MastraServer;
|
|
798
684
|
exports.createAuthMiddleware = createAuthMiddleware;
|
|
799
|
-
|
|
685
|
+
|
|
800
686
|
//# sourceMappingURL=index.cjs.map
|