@vistagenic/vista 0.3.2 → 0.3.3
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/bin/vista.js +79 -109
- package/dist/ai/embeddings.d.ts +11 -11
- package/dist/ai/embeddings.js +71 -71
- package/dist/ai/index.d.ts +10 -10
- package/dist/ai/index.js +26 -26
- package/dist/ai/providers/base.js +85 -85
- package/dist/ai/react/react-server.d.ts +1 -1
- package/dist/ai/react/react-server.js +17 -17
- package/dist/ai/react/use-agent.js +138 -138
- package/dist/auth/core.d.ts +140 -140
- package/dist/auth/core.js +165 -165
- package/dist/auth/index.d.ts +20 -20
- package/dist/auth/index.js +369 -369
- package/dist/auth/react-server.d.ts +1 -1
- package/dist/auth/react-server.js +10 -10
- package/dist/auth/react.d.ts +18 -18
- package/dist/auth/react.js +71 -71
- package/dist/bin/build-rsc-flashpack.js +6 -0
- package/dist/bin/build-rsc.js +537 -492
- package/dist/bin/build.js +355 -353
- package/dist/bin/generate.js +513 -510
- package/dist/bin/webpack.config.js +4 -3
- package/dist/build/manifest.d.ts +169 -169
- package/dist/build/manifest.js +423 -423
- package/dist/build/rsc/client-manifest.d.ts +62 -62
- package/dist/build/rsc/client-manifest.js +295 -295
- package/dist/build/rsc/compiler.js +5 -4
- package/dist/build/rsc/native-scanner.d.ts +135 -135
- package/dist/build/rsc/native-scanner.js +203 -203
- package/dist/build/rsc/react-client-reference-manifest.d.ts +28 -28
- package/dist/build/rsc/react-client-reference-manifest.js +240 -240
- package/dist/build/standalone.js +260 -254
- package/dist/client/dynamic.react-server.d.ts +2 -2
- package/dist/client/dynamic.react-server.js +23 -23
- package/dist/client/link.react-server.d.ts +2 -2
- package/dist/client/link.react-server.js +11 -11
- package/dist/client/navigation.react-server.d.ts +1 -1
- package/dist/client/navigation.react-server.js +17 -17
- package/dist/client/router.react-server.d.ts +1 -1
- package/dist/client/router.react-server.js +17 -17
- package/dist/client/rsc-router.react-server.d.ts +1 -1
- package/dist/client/rsc-router.react-server.js +17 -17
- package/dist/client/script.react-server.d.ts +2 -2
- package/dist/client/script.react-server.js +23 -23
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -7
- package/dist/deploy/adapters/cloudflare.js +43 -19
- package/dist/deploy/adapters/docker.d.ts +1 -0
- package/dist/deploy/adapters/docker.js +5 -4
- package/dist/deploy/adapters/netlify.js +62 -17
- package/dist/deploy/adapters/vercel.js +80 -50
- package/dist/deploy/preflight.js +4 -3
- package/dist/deploy/runtime-pack.d.ts +11 -0
- package/dist/deploy/runtime-pack.js +300 -0
- package/dist/deploy/utils.d.ts +1 -1
- package/dist/deploy/utils.js +31 -2
- package/dist/flashpack/command.js +1 -0
- package/dist/flashpack/runtime.js +2 -1
- package/dist/index.d.ts +23 -23
- package/dist/index.js +61 -61
- package/dist/server/app-dir.d.ts +22 -0
- package/dist/server/app-dir.js +77 -0
- package/dist/server/cookie-parse.d.ts +4 -4
- package/dist/server/cookie-parse.js +14 -14
- package/dist/server/engine.js +5 -5
- package/dist/server/image-optimizer.js +2 -1
- package/dist/server/index.d.ts +109 -109
- package/dist/server/index.js +315 -315
- package/dist/server/middleware-runner.d.ts +125 -125
- package/dist/server/middleware-runner.js +607 -615
- package/dist/server/middleware-security.d.ts +36 -36
- package/dist/server/middleware-security.js +183 -183
- package/dist/server/module-compile-hook.js +695 -695
- package/dist/server/root-resolver.js +6 -5
- package/dist/server/rsc-engine-flashpack.d.ts +3 -1
- package/dist/server/rsc-engine-flashpack.js +12 -1
- package/dist/server/rsc-engine.d.ts +6 -1
- package/dist/server/rsc-engine.js +203 -219
- package/dist/server/rsc-upstream.js +893 -892
- package/dist/server/static-generator.js +4 -3
- package/dist/server/structure-validator.js +2 -1
- package/dist/server/structure-watch.js +2 -0
- package/dist/server/typed-api-runtime.js +639 -635
- package/dist/server/vista-import-map.js +134 -134
- package/dist/stack/index.d.ts +34 -34
- package/dist/stack/index.js +49 -49
- package/dist/stack/server/caller.d.ts +13 -13
- package/dist/stack/server/caller.js +42 -42
- package/dist/stack/server/executor.d.ts +40 -40
- package/dist/stack/server/executor.js +222 -222
- package/dist/stack/server/index.d.ts +11 -11
- package/dist/stack/server/index.js +24 -24
- package/dist/stack/server/procedure.d.ts +20 -20
- package/dist/stack/server/procedure.js +66 -66
- package/dist/stack/server/types.d.ts +113 -113
- package/dist/theme/react-server.d.ts +10 -10
- package/dist/theme/react-server.js +16 -16
- package/package.json +1 -1
|
@@ -1,615 +1,607 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Vista Middleware Runner
|
|
4
|
-
*
|
|
5
|
-
* Built-in middleware system for Vista.js.
|
|
6
|
-
* Supports:
|
|
7
|
-
* 1. Global middleware (top-level middleware.ts / middleware.js at cwd or src/)
|
|
8
|
-
* 2. Route-specific middleware (co-located in app/ directory segments, e.g. app/api/auth/middleware.ts)
|
|
9
|
-
* 3. Exported middleware from route files (e.g. export const middleware = ... in page.tsx or route.ts)
|
|
10
|
-
* 4. Middleware signature: middleware({ request, next }) matching Issue #7 Part 5
|
|
11
|
-
* 5. Middleware chaining and clearly defined execution order:
|
|
12
|
-
* Global -> Segment middlewares (shallowest to deepest) -> Route-level middleware -> Handler
|
|
13
|
-
* 6. Request modification (headers, URL rewrites) and Response modification / short-circuiting
|
|
14
|
-
*/
|
|
15
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
16
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
17
|
-
};
|
|
18
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.clearMiddlewareCaches = clearMiddlewareCaches;
|
|
20
|
-
exports.discoverGlobalMiddleware = discoverGlobalMiddleware;
|
|
21
|
-
exports.discoverRouteMiddlewares = discoverRouteMiddlewares;
|
|
22
|
-
exports.patternToRegExp = patternToRegExp;
|
|
23
|
-
exports.shouldRunMiddleware = shouldRunMiddleware;
|
|
24
|
-
exports.buildNextRequest = buildNextRequest;
|
|
25
|
-
exports.runMiddleware = runMiddleware;
|
|
26
|
-
exports.applyMiddlewareResult = applyMiddlewareResult;
|
|
27
|
-
const path_1 = __importDefault(require("path"));
|
|
28
|
-
const fs_1 = __importDefault(require("fs"));
|
|
29
|
-
const cookie_parse_1 = require("./cookie-parse");
|
|
30
|
-
const middleware_security_1 = require("./middleware-security");
|
|
31
|
-
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
if (
|
|
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
|
-
bodyContent
|
|
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
|
-
return true;
|
|
609
|
-
}
|
|
610
|
-
case 'next':
|
|
611
|
-
case 'skip':
|
|
612
|
-
default:
|
|
613
|
-
return false; // continue
|
|
614
|
-
}
|
|
615
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Vista Middleware Runner
|
|
4
|
+
*
|
|
5
|
+
* Built-in middleware system for Vista.js.
|
|
6
|
+
* Supports:
|
|
7
|
+
* 1. Global middleware (top-level middleware.ts / middleware.js at cwd or src/)
|
|
8
|
+
* 2. Route-specific middleware (co-located in app/ directory segments, e.g. app/api/auth/middleware.ts)
|
|
9
|
+
* 3. Exported middleware from route files (e.g. export const middleware = ... in page.tsx or route.ts)
|
|
10
|
+
* 4. Middleware signature: middleware({ request, next }) matching Issue #7 Part 5
|
|
11
|
+
* 5. Middleware chaining and clearly defined execution order:
|
|
12
|
+
* Global -> Segment middlewares (shallowest to deepest) -> Route-level middleware -> Handler
|
|
13
|
+
* 6. Request modification (headers, URL rewrites) and Response modification / short-circuiting
|
|
14
|
+
*/
|
|
15
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
16
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
17
|
+
};
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.clearMiddlewareCaches = clearMiddlewareCaches;
|
|
20
|
+
exports.discoverGlobalMiddleware = discoverGlobalMiddleware;
|
|
21
|
+
exports.discoverRouteMiddlewares = discoverRouteMiddlewares;
|
|
22
|
+
exports.patternToRegExp = patternToRegExp;
|
|
23
|
+
exports.shouldRunMiddleware = shouldRunMiddleware;
|
|
24
|
+
exports.buildNextRequest = buildNextRequest;
|
|
25
|
+
exports.runMiddleware = runMiddleware;
|
|
26
|
+
exports.applyMiddlewareResult = applyMiddlewareResult;
|
|
27
|
+
const path_1 = __importDefault(require("path"));
|
|
28
|
+
const fs_1 = __importDefault(require("fs"));
|
|
29
|
+
const cookie_parse_1 = require("./cookie-parse");
|
|
30
|
+
const middleware_security_1 = require("./middleware-security");
|
|
31
|
+
const app_dir_1 = require("./app-dir");
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Discovery Caches
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
const globalDiscoveryCache = new Map();
|
|
36
|
+
const routeDiscoveryCache = new Map();
|
|
37
|
+
const MIDDLEWARE_FILE_EXTS = ['.ts', '.tsx', '.js', '.jsx'];
|
|
38
|
+
const ROUTE_FILE_STEMS = ['page', 'route', 'index', 'layout'];
|
|
39
|
+
function clearMiddlewareCaches() {
|
|
40
|
+
globalDiscoveryCache.clear();
|
|
41
|
+
routeDiscoveryCache.clear();
|
|
42
|
+
}
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Helpers
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
/**
|
|
47
|
+
* Discover top-level global middleware.
|
|
48
|
+
* Checks `<cwd>/middleware.*` then `<cwd>/src/middleware.*`.
|
|
49
|
+
*/
|
|
50
|
+
function discoverGlobalMiddleware(cwd, bustCache) {
|
|
51
|
+
if (!bustCache && globalDiscoveryCache.has(cwd)) {
|
|
52
|
+
return globalDiscoveryCache.get(cwd);
|
|
53
|
+
}
|
|
54
|
+
const searchDirs = [cwd, path_1.default.join(cwd, 'src')];
|
|
55
|
+
for (const dir of searchDirs) {
|
|
56
|
+
for (const ext of MIDDLEWARE_FILE_EXTS) {
|
|
57
|
+
const candidate = path_1.default.join(dir, `middleware${ext}`);
|
|
58
|
+
if (fs_1.default.existsSync(candidate) && fs_1.default.statSync(candidate).isFile()) {
|
|
59
|
+
globalDiscoveryCache.set(cwd, candidate);
|
|
60
|
+
return candidate;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
globalDiscoveryCache.set(cwd, null);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Discover route-specific middleware files along the path hierarchy.
|
|
69
|
+
* Returns file paths from shallowest segment to deepest segment.
|
|
70
|
+
*/
|
|
71
|
+
function discoverRouteMiddlewares(cwd, pathname, bustCache) {
|
|
72
|
+
const cacheKey = `${cwd}:${pathname}`;
|
|
73
|
+
if (!bustCache && routeDiscoveryCache.has(cacheKey)) {
|
|
74
|
+
return routeDiscoveryCache.get(cacheKey);
|
|
75
|
+
}
|
|
76
|
+
const appDir = (0, app_dir_1.resolveAppDirOrNull)(cwd);
|
|
77
|
+
if (!appDir) {
|
|
78
|
+
routeDiscoveryCache.set(cacheKey, []);
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const globalMiddleware = discoverGlobalMiddleware(cwd, bustCache);
|
|
82
|
+
const results = [];
|
|
83
|
+
// 1. Check app/middleware.* (if different from global middleware)
|
|
84
|
+
for (const ext of MIDDLEWARE_FILE_EXTS) {
|
|
85
|
+
const appLevel = path_1.default.join(appDir, `middleware${ext}`);
|
|
86
|
+
if (fs_1.default.existsSync(appLevel) && fs_1.default.statSync(appLevel).isFile() && appLevel !== globalMiddleware) {
|
|
87
|
+
results.push(appLevel);
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// 2. Traverse route segments from root to leaf
|
|
92
|
+
const segments = pathname.split('/').filter(Boolean);
|
|
93
|
+
let currentDir = appDir;
|
|
94
|
+
for (let i = 0; i < segments.length; i++) {
|
|
95
|
+
const seg = segments[i];
|
|
96
|
+
let nextDir = null;
|
|
97
|
+
// Direct segment match (e.g. app/api)
|
|
98
|
+
const exactDir = path_1.default.join(currentDir, seg);
|
|
99
|
+
if (fs_1.default.existsSync(exactDir) && fs_1.default.statSync(exactDir).isDirectory()) {
|
|
100
|
+
nextDir = exactDir;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
// Dynamic route segment match (e.g. [id], [...slug])
|
|
104
|
+
try {
|
|
105
|
+
const entries = fs_1.default.readdirSync(currentDir, { withFileTypes: true });
|
|
106
|
+
const dynamicEntry = entries.find((e) => e.isDirectory() &&
|
|
107
|
+
e.name.startsWith('[') &&
|
|
108
|
+
e.name.endsWith(']') &&
|
|
109
|
+
e.name !== '[not-found]');
|
|
110
|
+
if (dynamicEntry) {
|
|
111
|
+
nextDir = path_1.default.join(currentDir, dynamicEntry.name);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// Ignore read errors
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!nextDir) {
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
currentDir = nextDir;
|
|
122
|
+
// Check for co-located middleware in this segment directory
|
|
123
|
+
for (const ext of MIDDLEWARE_FILE_EXTS) {
|
|
124
|
+
const segmentMiddleware = path_1.default.join(currentDir, `middleware${ext}`);
|
|
125
|
+
if (fs_1.default.existsSync(segmentMiddleware) &&
|
|
126
|
+
fs_1.default.statSync(segmentMiddleware).isFile() &&
|
|
127
|
+
segmentMiddleware !== globalMiddleware &&
|
|
128
|
+
!results.includes(segmentMiddleware)) {
|
|
129
|
+
results.push(segmentMiddleware);
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// 3. At leaf directory, check for exported middleware in route files (page.tsx, route.ts, etc.)
|
|
135
|
+
for (const stem of ROUTE_FILE_STEMS) {
|
|
136
|
+
for (const ext of MIDDLEWARE_FILE_EXTS) {
|
|
137
|
+
const candidateRouteFile = path_1.default.join(currentDir, `${stem}${ext}`);
|
|
138
|
+
if (fs_1.default.existsSync(candidateRouteFile) &&
|
|
139
|
+
fs_1.default.statSync(candidateRouteFile).isFile() &&
|
|
140
|
+
!results.includes(candidateRouteFile)) {
|
|
141
|
+
// Record candidate route file to inspect for exported middleware
|
|
142
|
+
results.push(candidateRouteFile);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
routeDiscoveryCache.set(cacheKey, results);
|
|
147
|
+
return results;
|
|
148
|
+
}
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Matcher Support
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
function patternToRegExp(pattern) {
|
|
153
|
+
// Convert Next.js-style / path pattern to RegExp:
|
|
154
|
+
// /foo/:path* → /foo(?:/(.*))?
|
|
155
|
+
// /foo/:bar → /foo/[^/]+
|
|
156
|
+
// /foo/* → /foo(?:/(.*))?
|
|
157
|
+
let re = pattern
|
|
158
|
+
.replace(/\/:[^/]+\*/g, '(?:/(.*))?') // /:path* (0 or more sub-paths)
|
|
159
|
+
.replace(/:[^/]+\*/g, '(.*)') // bare :path*
|
|
160
|
+
.replace(/:[^/]+/g, '[^/]+') // :param (single segment)
|
|
161
|
+
.replace(/\/\*/g, '(?:/(.*))?') // /*
|
|
162
|
+
.replace(/\*/g, '(.*)'); // bare *
|
|
163
|
+
return new RegExp(`^${re}/?$`);
|
|
164
|
+
}
|
|
165
|
+
function shouldRunMiddleware(middlewareModule, pathname, request) {
|
|
166
|
+
const config = middlewareModule.config;
|
|
167
|
+
if (!config?.matcher)
|
|
168
|
+
return true;
|
|
169
|
+
const matchers = Array.isArray(config.matcher)
|
|
170
|
+
? config.matcher
|
|
171
|
+
: [config.matcher];
|
|
172
|
+
return matchers.some((matcher) => {
|
|
173
|
+
try {
|
|
174
|
+
const source = typeof matcher === 'string' ? matcher : matcher.source;
|
|
175
|
+
const re = patternToRegExp(source);
|
|
176
|
+
if (!re.test(pathname)) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
if (typeof matcher === 'string' || !request) {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
if (matcher.has && !matcher.has.every((rule) => matchRequestRule(request, rule))) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
if (matcher.missing && !matcher.missing.every((rule) => !matchRequestRule(request, rule))) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
// Fail closed: a broken matcher must not skip auth middleware.
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function matchRequestRule(request, rule) {
|
|
197
|
+
let actual;
|
|
198
|
+
if (rule.type === 'header') {
|
|
199
|
+
actual = request.headers.get(rule.key) || undefined;
|
|
200
|
+
}
|
|
201
|
+
else if (rule.type === 'cookie') {
|
|
202
|
+
actual = request.cookies.get(rule.key)?.value;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
actual = request.nextUrl.searchParams.get(rule.key) || undefined;
|
|
206
|
+
}
|
|
207
|
+
if (rule.value === undefined) {
|
|
208
|
+
return Boolean(actual);
|
|
209
|
+
}
|
|
210
|
+
return actual === rule.value;
|
|
211
|
+
}
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
// Build NextRequest-like Object
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
function buildNextRequest(req) {
|
|
216
|
+
const protocol = req.protocol || 'http';
|
|
217
|
+
const host = (req.get && req.get('host')) || req.headers?.host || 'localhost';
|
|
218
|
+
const rawPath = req.path || (req.url ? req.url.split('?')[0] : '/');
|
|
219
|
+
const fullUrl = `${protocol}://${host}${req.originalUrl || req.url || rawPath}`;
|
|
220
|
+
const headers = new Headers();
|
|
221
|
+
if (req.headers) {
|
|
222
|
+
for (const [key, val] of Object.entries(req.headers)) {
|
|
223
|
+
if (val !== undefined) {
|
|
224
|
+
if (Array.isArray(val)) {
|
|
225
|
+
for (const item of val) {
|
|
226
|
+
headers.append(key, item);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
headers.set(key, String(val));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const queryParams = new URLSearchParams(typeof req.query === 'object' && req.query !== null
|
|
236
|
+
? req.query
|
|
237
|
+
: {});
|
|
238
|
+
// Cookie helpers
|
|
239
|
+
const rawCookies = req.cookies || {};
|
|
240
|
+
if (!Object.keys(rawCookies).length && req.headers?.cookie) {
|
|
241
|
+
for (const pair of String(req.headers.cookie).split(';')) {
|
|
242
|
+
const [k, ...v] = pair.split('=');
|
|
243
|
+
const name = k?.trim();
|
|
244
|
+
if (name) {
|
|
245
|
+
rawCookies[name] = (0, cookie_parse_1.safeDecodeURIComponent)(v.join('=').trim());
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
url: fullUrl,
|
|
251
|
+
method: req.method || 'GET',
|
|
252
|
+
path: rawPath,
|
|
253
|
+
headers,
|
|
254
|
+
nextUrl: {
|
|
255
|
+
pathname: rawPath,
|
|
256
|
+
searchParams: queryParams,
|
|
257
|
+
href: fullUrl,
|
|
258
|
+
origin: `${protocol}://${host}`,
|
|
259
|
+
},
|
|
260
|
+
cookies: {
|
|
261
|
+
get: (name) => rawCookies[name] !== undefined ? { name, value: String(rawCookies[name]) } : undefined,
|
|
262
|
+
getAll: () => Object.entries(rawCookies).map(([n, v]) => ({
|
|
263
|
+
name: n,
|
|
264
|
+
value: v,
|
|
265
|
+
})),
|
|
266
|
+
has: (name) => rawCookies[name] !== undefined,
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function cloneRequestWithHeaders(req, newHeaders) {
|
|
271
|
+
const mergedHeaders = new Headers(req.headers);
|
|
272
|
+
if (newHeaders instanceof Headers) {
|
|
273
|
+
newHeaders.forEach((val, key) => mergedHeaders.set(key, val));
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
for (const [k, v] of Object.entries(newHeaders)) {
|
|
277
|
+
mergedHeaders.set(k, String(v));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
...req,
|
|
282
|
+
headers: mergedHeaders,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
// Execution Pipeline
|
|
287
|
+
// ---------------------------------------------------------------------------
|
|
288
|
+
function loadModule(filePath, isDev) {
|
|
289
|
+
try {
|
|
290
|
+
if (isDev) {
|
|
291
|
+
try {
|
|
292
|
+
delete require.cache[require.resolve(filePath)];
|
|
293
|
+
}
|
|
294
|
+
catch { }
|
|
295
|
+
}
|
|
296
|
+
return require(filePath);
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
console.warn(`[vista:middleware] Failed to load middleware module ${filePath}:`, err?.message);
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Run user-defined middleware chain (global + route-specific) for the given request.
|
|
305
|
+
*
|
|
306
|
+
* Execution Order:
|
|
307
|
+
* 1. Global middleware (cwd/middleware.ts or cwd/src/middleware.ts)
|
|
308
|
+
* 2. Route segment middlewares (from shallowest to deepest in app/)
|
|
309
|
+
* 3. Route file middleware (exported `middleware` from page.tsx/route.ts)
|
|
310
|
+
* 4. Downstream handler (Page component or API route handler)
|
|
311
|
+
*
|
|
312
|
+
* Returns a `MiddlewareResult` indicating whether the request was redirected,
|
|
313
|
+
* short-circuited with a response, rewritten, or continued to the next handler.
|
|
314
|
+
*/
|
|
315
|
+
async function runMiddleware(req, cwd, isDev = false) {
|
|
316
|
+
const pathname = req.path || (req.url ? req.url.split('?')[0] : '/');
|
|
317
|
+
// 1. Discover Global Middleware
|
|
318
|
+
const globalFile = discoverGlobalMiddleware(cwd, isDev);
|
|
319
|
+
const entries = [];
|
|
320
|
+
if (globalFile) {
|
|
321
|
+
entries.push({
|
|
322
|
+
source: 'global',
|
|
323
|
+
filePath: globalFile,
|
|
324
|
+
depth: 0,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
// 2. Discover Route-Specific Middlewares
|
|
328
|
+
const routeFiles = discoverRouteMiddlewares(cwd, pathname, isDev);
|
|
329
|
+
for (let i = 0; i < routeFiles.length; i++) {
|
|
330
|
+
const file = routeFiles[i];
|
|
331
|
+
const isMiddlewareFile = path_1.default.basename(file).startsWith('middleware.');
|
|
332
|
+
entries.push({
|
|
333
|
+
source: isMiddlewareFile ? 'segment' : 'route',
|
|
334
|
+
filePath: file,
|
|
335
|
+
depth: i + 1,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
// If no middleware discovered, skip directly
|
|
339
|
+
if (entries.length === 0) {
|
|
340
|
+
return { kind: 'skip' };
|
|
341
|
+
}
|
|
342
|
+
const activeChain = [];
|
|
343
|
+
for (const entry of entries) {
|
|
344
|
+
const mod = loadModule(entry.filePath, isDev);
|
|
345
|
+
if (!mod) {
|
|
346
|
+
// Dedicated middleware files must fail closed. Skipping them would
|
|
347
|
+
// silently bypass auth. Route files may fail for unrelated reasons.
|
|
348
|
+
if (entry.source !== 'route') {
|
|
349
|
+
return {
|
|
350
|
+
kind: 'short-circuit',
|
|
351
|
+
status: 500,
|
|
352
|
+
body: 'Middleware Error',
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
// In a route file (e.g. page.tsx, route.ts), we ONLY accept an explicit `export const middleware`
|
|
358
|
+
// or `export function middleware` (not `export default`, which is the page component)
|
|
359
|
+
let fn;
|
|
360
|
+
if (entry.source === 'route') {
|
|
361
|
+
if (typeof mod.middleware === 'function') {
|
|
362
|
+
fn = mod.middleware;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
// In a dedicated middleware.* file, accept either `export default` or `export function middleware`
|
|
367
|
+
if (typeof mod.middleware === 'function') {
|
|
368
|
+
fn = mod.middleware;
|
|
369
|
+
}
|
|
370
|
+
else if (typeof mod.default === 'function') {
|
|
371
|
+
fn = mod.default;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (!fn) {
|
|
375
|
+
if (entry.source !== 'route') {
|
|
376
|
+
return {
|
|
377
|
+
kind: 'short-circuit',
|
|
378
|
+
status: 500,
|
|
379
|
+
body: 'Middleware Error',
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
// Check optional matcher config
|
|
385
|
+
if (!shouldRunMiddleware(mod, pathname, buildNextRequest(req))) {
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
activeChain.push({
|
|
389
|
+
filePath: entry.filePath,
|
|
390
|
+
source: entry.source,
|
|
391
|
+
fn,
|
|
392
|
+
allowedRedirectHosts: mod.config?.allowedRedirectHosts,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
if (activeChain.length === 0) {
|
|
396
|
+
return { kind: 'skip' };
|
|
397
|
+
}
|
|
398
|
+
// 4. Execute the chain
|
|
399
|
+
let currentRequest = buildNextRequest(req);
|
|
400
|
+
const aggregatedResponseHeaders = new Map();
|
|
401
|
+
const modifiedRequestHeaders = new Map();
|
|
402
|
+
async function dispatch(index, requestObj) {
|
|
403
|
+
if (index >= activeChain.length) {
|
|
404
|
+
// Terminal node: reached end of middleware chain
|
|
405
|
+
return new Response(null, {
|
|
406
|
+
status: 200,
|
|
407
|
+
headers: {
|
|
408
|
+
'x-middleware-next': '1',
|
|
409
|
+
},
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
const currentItem = activeChain[index];
|
|
413
|
+
let nextCalled = false;
|
|
414
|
+
let nextRequestObj = requestObj;
|
|
415
|
+
const nextFn = async (options) => {
|
|
416
|
+
nextCalled = true;
|
|
417
|
+
if (options?.request?.headers) {
|
|
418
|
+
const sanitized = (0, middleware_security_1.sanitizeRequestHeaderMap)(options.request.headers);
|
|
419
|
+
const headerRecord = {};
|
|
420
|
+
sanitized.forEach((value, key) => {
|
|
421
|
+
headerRecord[key] = value;
|
|
422
|
+
modifiedRequestHeaders.set(key, value);
|
|
423
|
+
});
|
|
424
|
+
nextRequestObj = cloneRequestWithHeaders(requestObj, headerRecord);
|
|
425
|
+
}
|
|
426
|
+
return dispatch(index + 1, nextRequestObj);
|
|
427
|
+
};
|
|
428
|
+
const context = {
|
|
429
|
+
request: requestObj,
|
|
430
|
+
next: nextFn,
|
|
431
|
+
url: requestObj.url,
|
|
432
|
+
method: requestObj.method,
|
|
433
|
+
headers: requestObj.headers,
|
|
434
|
+
nextUrl: requestObj.nextUrl,
|
|
435
|
+
cookies: requestObj.cookies,
|
|
436
|
+
};
|
|
437
|
+
try {
|
|
438
|
+
// Invoke middleware. Supports:
|
|
439
|
+
// - middleware({ request, next }) [Issue #7 Part 5 signature]
|
|
440
|
+
// - middleware(request, next)
|
|
441
|
+
// - middleware(request)
|
|
442
|
+
const output = await currentItem.fn(context, nextFn);
|
|
443
|
+
// Handle direct Response return
|
|
444
|
+
if (output instanceof Response) {
|
|
445
|
+
return output;
|
|
446
|
+
}
|
|
447
|
+
// If next() was explicitly called and returned nothing, propagate standard continuation
|
|
448
|
+
if (nextCalled) {
|
|
449
|
+
return new Response(null, {
|
|
450
|
+
status: 200,
|
|
451
|
+
headers: {
|
|
452
|
+
'x-middleware-next': '1',
|
|
453
|
+
},
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
// If middleware returned void without calling next(), continue to next in chain
|
|
457
|
+
return dispatch(index + 1, requestObj);
|
|
458
|
+
}
|
|
459
|
+
catch (err) {
|
|
460
|
+
console.error(`[vista:middleware] Error in middleware at ${currentItem.filePath}:`, err?.message ?? String(err));
|
|
461
|
+
if (process.env.NODE_ENV === 'production') {
|
|
462
|
+
return new Response('Middleware Error', { status: 500 });
|
|
463
|
+
}
|
|
464
|
+
return dispatch(index + 1, requestObj);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
const finalResponse = await dispatch(0, currentRequest);
|
|
468
|
+
// 5. Convert final Response to MiddlewareResult
|
|
469
|
+
// Collect all response headers
|
|
470
|
+
if (finalResponse.headers && typeof finalResponse.headers.forEach === 'function') {
|
|
471
|
+
finalResponse.headers.forEach((val, key) => {
|
|
472
|
+
const lower = key.toLowerCase();
|
|
473
|
+
if (lower.startsWith('x-middleware-request-')) {
|
|
474
|
+
const name = lower.slice('x-middleware-request-'.length);
|
|
475
|
+
if (!name || name === 'host' || name === 'cookie' || name.startsWith('x-forwarded-') || name.startsWith('x-middleware-')) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
modifiedRequestHeaders.set(name, val);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
aggregatedResponseHeaders.set(key, val);
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
if (process.env.NODE_ENV === 'production') {
|
|
485
|
+
(0, middleware_security_1.securityHeaders)().forEach((value, key) => {
|
|
486
|
+
if (!aggregatedResponseHeaders.has(key) && !aggregatedResponseHeaders.has(key.toLowerCase())) {
|
|
487
|
+
aggregatedResponseHeaders.set(key, value);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
const allowedRedirectHosts = activeChain.flatMap((item) => item.allowedRedirectHosts || []);
|
|
492
|
+
const requestUrl = currentRequest.url;
|
|
493
|
+
// 5a. Redirect
|
|
494
|
+
const location = finalResponse.headers?.get?.('Location') || finalResponse.headers?.get?.('location');
|
|
495
|
+
if (location) {
|
|
496
|
+
if (!(0, middleware_security_1.isSafeRedirectLocation)(location, requestUrl, allowedRedirectHosts)) {
|
|
497
|
+
return {
|
|
498
|
+
kind: 'short-circuit',
|
|
499
|
+
status: 400,
|
|
500
|
+
responseHeaders: aggregatedResponseHeaders,
|
|
501
|
+
requestHeaders: modifiedRequestHeaders,
|
|
502
|
+
body: 'Invalid redirect',
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
return {
|
|
506
|
+
kind: 'redirect',
|
|
507
|
+
status: finalResponse.status || 307,
|
|
508
|
+
location,
|
|
509
|
+
responseHeaders: aggregatedResponseHeaders,
|
|
510
|
+
requestHeaders: modifiedRequestHeaders,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
// 5b. Rewrite
|
|
514
|
+
const rewrite = finalResponse.headers?.get?.('x-middleware-rewrite');
|
|
515
|
+
if (rewrite) {
|
|
516
|
+
if (!(0, middleware_security_1.isSafeRewriteLocation)(rewrite)) {
|
|
517
|
+
return {
|
|
518
|
+
kind: 'short-circuit',
|
|
519
|
+
status: 400,
|
|
520
|
+
responseHeaders: aggregatedResponseHeaders,
|
|
521
|
+
requestHeaders: modifiedRequestHeaders,
|
|
522
|
+
body: 'Invalid rewrite',
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
return {
|
|
526
|
+
kind: 'rewrite',
|
|
527
|
+
location: rewrite,
|
|
528
|
+
responseHeaders: aggregatedResponseHeaders,
|
|
529
|
+
requestHeaders: modifiedRequestHeaders,
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
// 5c. Continue to page/route handler
|
|
533
|
+
const shouldContinue = finalResponse.headers?.get?.('x-middleware-next');
|
|
534
|
+
if (shouldContinue) {
|
|
535
|
+
return {
|
|
536
|
+
kind: 'next',
|
|
537
|
+
responseHeaders: aggregatedResponseHeaders,
|
|
538
|
+
requestHeaders: modifiedRequestHeaders,
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
// 5d. Short-circuit with response body and custom status
|
|
542
|
+
let bodyContent = null;
|
|
543
|
+
try {
|
|
544
|
+
bodyContent = await finalResponse.text();
|
|
545
|
+
}
|
|
546
|
+
catch { }
|
|
547
|
+
return {
|
|
548
|
+
kind: 'short-circuit',
|
|
549
|
+
status: finalResponse.status || 200,
|
|
550
|
+
responseHeaders: aggregatedResponseHeaders,
|
|
551
|
+
requestHeaders: modifiedRequestHeaders,
|
|
552
|
+
body: bodyContent,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
// ---------------------------------------------------------------------------
|
|
556
|
+
// Apply Middleware Result
|
|
557
|
+
// ---------------------------------------------------------------------------
|
|
558
|
+
/**
|
|
559
|
+
* Apply a MiddlewareResult to the Express request/response.
|
|
560
|
+
* Returns `true` if the response was finalized (caller should `return`),
|
|
561
|
+
* `false` if the request should continue to the next handler.
|
|
562
|
+
*/
|
|
563
|
+
function applyMiddlewareResult(result, req, res) {
|
|
564
|
+
// 1. Forward any modified request headers to req.headers
|
|
565
|
+
if (result.requestHeaders && req.headers) {
|
|
566
|
+
const sanitized = (0, middleware_security_1.sanitizeRequestHeaderMap)(result.requestHeaders);
|
|
567
|
+
sanitized.forEach((value, key) => {
|
|
568
|
+
req.headers[key] = value;
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
// 2. Forward any response headers the middleware set
|
|
572
|
+
if (result.responseHeaders) {
|
|
573
|
+
result.responseHeaders.forEach((value, key) => {
|
|
574
|
+
const lower = key.toLowerCase();
|
|
575
|
+
// Skip internal transport headers
|
|
576
|
+
if (lower === 'x-middleware-next' ||
|
|
577
|
+
lower === 'x-middleware-rewrite' ||
|
|
578
|
+
lower === 'location' ||
|
|
579
|
+
lower.startsWith('x-middleware-request-')) {
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
res.setHeader(key, value);
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
switch (result.kind) {
|
|
586
|
+
case 'redirect':
|
|
587
|
+
res.redirect(result.status || 307, result.location);
|
|
588
|
+
return true;
|
|
589
|
+
case 'rewrite':
|
|
590
|
+
req.url = result.location;
|
|
591
|
+
return false; // continue with rewritten URL
|
|
592
|
+
case 'short-circuit': {
|
|
593
|
+
const status = result.status || 403;
|
|
594
|
+
if (result.body !== undefined && result.body !== null && result.body !== '') {
|
|
595
|
+
res.status(status).send(result.body);
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
res.status(status).end();
|
|
599
|
+
}
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
case 'next':
|
|
603
|
+
case 'skip':
|
|
604
|
+
default:
|
|
605
|
+
return false; // continue
|
|
606
|
+
}
|
|
607
|
+
}
|