vinext 0.0.21 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +6 -3
- package/dist/deploy.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +179 -10
- package/dist/index.js.map +1 -1
- package/dist/routing/app-router.d.ts.map +1 -1
- package/dist/routing/app-router.js +1 -41
- package/dist/routing/app-router.js.map +1 -1
- package/dist/routing/pages-router.d.ts.map +1 -1
- package/dist/routing/pages-router.js +1 -27
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/routing/utils.d.ts +25 -0
- package/dist/routing/utils.d.ts.map +1 -0
- package/dist/routing/utils.js +70 -0
- package/dist/routing/utils.js.map +1 -0
- package/dist/server/app-dev-server.d.ts.map +1 -1
- package/dist/server/app-dev-server.js +70 -3
- package/dist/server/app-dev-server.js.map +1 -1
- package/dist/server/dev-server.d.ts.map +1 -1
- package/dist/server/dev-server.js +77 -4
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/prod-server.d.ts.map +1 -1
- package/dist/server/prod-server.js +7 -0
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/request-log.d.ts +34 -0
- package/dist/server/request-log.d.ts.map +1 -0
- package/dist/server/request-log.js +65 -0
- package/dist/server/request-log.js.map +1 -0
- package/dist/shims/cache-runtime.d.ts.map +1 -1
- package/dist/shims/cache-runtime.js +5 -1
- package/dist/shims/cache-runtime.js.map +1 -1
- package/dist/shims/cache.d.ts +6 -0
- package/dist/shims/cache.d.ts.map +1 -1
- package/dist/shims/cache.js +22 -2
- package/dist/shims/cache.js.map +1 -1
- package/dist/shims/head.d.ts +11 -0
- package/dist/shims/head.d.ts.map +1 -1
- package/dist/shims/head.js +21 -0
- package/dist/shims/head.js.map +1 -1
- package/dist/shims/headers.d.ts +8 -0
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/headers.js +41 -0
- package/dist/shims/headers.js.map +1 -1
- package/dist/shims/script.d.ts.map +1 -1
- package/dist/shims/script.js +7 -1
- package/dist/shims/script.js.map +1 -1
- package/dist/shims/server.d.ts.map +1 -1
- package/dist/shims/server.js +2 -1
- package/dist/shims/server.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { handleApiRoute } from "./server/api-handler.js";
|
|
|
6
6
|
import { generateRscEntry, generateSsrEntry, generateBrowserEntry, } from "./server/app-dev-server.js";
|
|
7
7
|
import { loadNextConfig, resolveNextConfig, } from "./config/next-config.js";
|
|
8
8
|
import { findMiddlewareFile, isProxyFile, runMiddleware } from "./server/middleware.js";
|
|
9
|
+
import { logRequest, now } from "./server/request-log.js";
|
|
9
10
|
import { generateSafeRegExpCode, generateMiddlewareMatcherCode, generateNormalizePathCode } from "./server/middleware-codegen.js";
|
|
10
11
|
import { normalizePath } from "./server/normalize-path.js";
|
|
11
12
|
import { findInstrumentationFile, runInstrumentation } from "./server/instrumentation.js";
|
|
@@ -1026,6 +1027,12 @@ function createReqRes(request, url, query, body) {
|
|
|
1026
1027
|
else { res.writeHead(statusOrUrl, { Location: url2 }); }
|
|
1027
1028
|
res.end();
|
|
1028
1029
|
},
|
|
1030
|
+
getHeaders: function() {
|
|
1031
|
+
var h = Object.assign({}, resHeaders);
|
|
1032
|
+
if (setCookieHeaders.length > 0) h["set-cookie"] = setCookieHeaders;
|
|
1033
|
+
return h;
|
|
1034
|
+
},
|
|
1035
|
+
get headersSent() { return ended; },
|
|
1029
1036
|
};
|
|
1030
1037
|
|
|
1031
1038
|
return { req, res, responsePromise };
|
|
@@ -1140,8 +1147,9 @@ export async function renderPage(request, url, manifest) {
|
|
|
1140
1147
|
}
|
|
1141
1148
|
|
|
1142
1149
|
let pageProps = {};
|
|
1150
|
+
var gsspRes = null;
|
|
1143
1151
|
if (typeof pageModule.getServerSideProps === "function") {
|
|
1144
|
-
const { req, res } = createReqRes(request, routeUrl, parseQuery(routeUrl), undefined);
|
|
1152
|
+
const { req, res, responsePromise } = createReqRes(request, routeUrl, parseQuery(routeUrl), undefined);
|
|
1145
1153
|
const ctx = {
|
|
1146
1154
|
params, req, res,
|
|
1147
1155
|
query: parseQuery(routeUrl),
|
|
@@ -1151,6 +1159,10 @@ export async function renderPage(request, url, manifest) {
|
|
|
1151
1159
|
defaultLocale: i18nConfig ? i18nConfig.defaultLocale : undefined,
|
|
1152
1160
|
};
|
|
1153
1161
|
const result = await pageModule.getServerSideProps(ctx);
|
|
1162
|
+
// If gSSP called res.end() directly (short-circuit), return that response.
|
|
1163
|
+
if (res.headersSent) {
|
|
1164
|
+
return await responsePromise;
|
|
1165
|
+
}
|
|
1154
1166
|
if (result && result.props) pageProps = result.props;
|
|
1155
1167
|
if (result && result.redirect) {
|
|
1156
1168
|
var gsspStatus = result.redirect.statusCode != null ? result.redirect.statusCode : (result.redirect.permanent ? 308 : 307);
|
|
@@ -1159,6 +1171,9 @@ export async function renderPage(request, url, manifest) {
|
|
|
1159
1171
|
if (result && result.notFound) {
|
|
1160
1172
|
return new Response("404", { status: 404 });
|
|
1161
1173
|
}
|
|
1174
|
+
// Preserve the res object so headers/status/cookies set by gSSP
|
|
1175
|
+
// can be merged into the final HTML response.
|
|
1176
|
+
gsspRes = res;
|
|
1162
1177
|
}
|
|
1163
1178
|
// Build font Link header early so it's available for ISR cached responses too.
|
|
1164
1179
|
// Font preloads are module-level state populated at import time and persist across requests.
|
|
@@ -1335,16 +1350,33 @@ export async function renderPage(request, url, manifest) {
|
|
|
1335
1350
|
await isrSet(isrCacheKey, { kind: "PAGES", html: fullHtml, pageData: pageProps, headers: undefined, status: undefined }, isrRevalidateSeconds);
|
|
1336
1351
|
}
|
|
1337
1352
|
|
|
1338
|
-
|
|
1353
|
+
// Merge headers/status/cookies set by getServerSideProps on the res object.
|
|
1354
|
+
// gSSP commonly uses res.setHeader("Set-Cookie", ...) or res.status(304).
|
|
1355
|
+
var finalStatus = 200;
|
|
1356
|
+
const responseHeaders = new Headers({ "Content-Type": "text/html" });
|
|
1357
|
+
if (gsspRes) {
|
|
1358
|
+
finalStatus = gsspRes.statusCode;
|
|
1359
|
+
var gsspHeaders = gsspRes.getHeaders();
|
|
1360
|
+
for (var hk of Object.keys(gsspHeaders)) {
|
|
1361
|
+
var hv = gsspHeaders[hk];
|
|
1362
|
+
if (hk === "set-cookie" && Array.isArray(hv)) {
|
|
1363
|
+
for (var sc of hv) responseHeaders.append("set-cookie", sc);
|
|
1364
|
+
} else if (hv != null) {
|
|
1365
|
+
responseHeaders.set(hk, String(hv));
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
// Ensure Content-Type stays text/html (gSSP shouldn't override it for page renders)
|
|
1369
|
+
responseHeaders.set("Content-Type", "text/html");
|
|
1370
|
+
}
|
|
1339
1371
|
if (isrRevalidateSeconds) {
|
|
1340
|
-
responseHeaders
|
|
1341
|
-
responseHeaders
|
|
1372
|
+
responseHeaders.set("Cache-Control", "s-maxage=" + isrRevalidateSeconds + ", stale-while-revalidate");
|
|
1373
|
+
responseHeaders.set("X-Vinext-Cache", "MISS");
|
|
1342
1374
|
}
|
|
1343
1375
|
// Set HTTP Link header for font preloading
|
|
1344
1376
|
if (_fontLinkHeader) {
|
|
1345
|
-
responseHeaders
|
|
1377
|
+
responseHeaders.set("Link", _fontLinkHeader);
|
|
1346
1378
|
}
|
|
1347
|
-
return new Response(compositeStream, { status:
|
|
1379
|
+
return new Response(compositeStream, { status: finalStatus, headers: responseHeaders });
|
|
1348
1380
|
} catch (e) {
|
|
1349
1381
|
console.error("[vinext] SSR error:", e);
|
|
1350
1382
|
return new Response("Internal Server Error", { status: 500 });
|
|
@@ -2236,8 +2268,135 @@ hydrate();
|
|
|
2236
2268
|
console.error("[vinext] Instrumentation error:", err);
|
|
2237
2269
|
});
|
|
2238
2270
|
}
|
|
2271
|
+
// ── Dev request origin check ─────────────────────────────────────
|
|
2272
|
+
// Registered directly (not in the returned function) so it runs
|
|
2273
|
+
// BEFORE Vite's built-in middleware. This ensures all requests
|
|
2274
|
+
// (including /@*, /__vite*, /node_modules* paths) are validated
|
|
2275
|
+
// before Vite serves any content.
|
|
2276
|
+
server.middlewares.use((req, res, next) => {
|
|
2277
|
+
const blockReason = validateDevRequest({
|
|
2278
|
+
origin: req.headers.origin,
|
|
2279
|
+
host: req.headers.host,
|
|
2280
|
+
"x-forwarded-host": req.headers["x-forwarded-host"],
|
|
2281
|
+
"sec-fetch-site": req.headers["sec-fetch-site"],
|
|
2282
|
+
"sec-fetch-mode": req.headers["sec-fetch-mode"],
|
|
2283
|
+
}, nextConfig?.serverActionsAllowedOrigins);
|
|
2284
|
+
if (blockReason) {
|
|
2285
|
+
console.warn(`[vinext] Blocked dev request: ${blockReason} (${req.url})`);
|
|
2286
|
+
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
2287
|
+
res.end("Forbidden");
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
next();
|
|
2291
|
+
});
|
|
2239
2292
|
// Return a function to register middleware AFTER Vite's built-in middleware
|
|
2240
2293
|
return () => {
|
|
2294
|
+
// App Router request logging in dev server
|
|
2295
|
+
//
|
|
2296
|
+
// For App Router, the RSC plugin handles requests internally.
|
|
2297
|
+
// We install a timing middleware here that:
|
|
2298
|
+
// 1. Intercepts writeHead() to pluck the X-Vinext-Timing header
|
|
2299
|
+
// (compileMs,renderMs) that the RSC entry attaches before
|
|
2300
|
+
// it is flushed to the client.
|
|
2301
|
+
// 2. Logs the full request after res finishes, using those timings.
|
|
2302
|
+
if (hasAppDir) {
|
|
2303
|
+
server.middlewares.use((req, res, next) => {
|
|
2304
|
+
const url = req.url ?? "/";
|
|
2305
|
+
// Skip Vite internals, HMR, and static assets.
|
|
2306
|
+
// Do NOT skip .rsc-suffixed URLs or RSC wire requests (Accept: text/x-component)
|
|
2307
|
+
// — those are soft navigations and should be logged like any other page request.
|
|
2308
|
+
const [pathname] = url.split("?");
|
|
2309
|
+
if (url.startsWith("/@") ||
|
|
2310
|
+
url.startsWith("/__vite") ||
|
|
2311
|
+
url.startsWith("/node_modules") ||
|
|
2312
|
+
(url.includes(".") && !pathname.endsWith(".html") && !pathname.endsWith(".rsc"))) {
|
|
2313
|
+
return next();
|
|
2314
|
+
}
|
|
2315
|
+
const _reqStart = now();
|
|
2316
|
+
let _compileMs;
|
|
2317
|
+
let _renderMs;
|
|
2318
|
+
// Intercept setHeader and writeHead so we can strip X-Vinext-Timing
|
|
2319
|
+
// before it reaches the client and capture the compile/render split.
|
|
2320
|
+
// The RSC plugin may set headers either way depending on its version.
|
|
2321
|
+
// Parse the three-part X-Vinext-Timing header:
|
|
2322
|
+
// "handlerStart,inHandlerCompileMs,renderMs"
|
|
2323
|
+
//
|
|
2324
|
+
// True compile time = time the RSC plugin spent loading/transforming
|
|
2325
|
+
// modules before our handler code ran, plus any in-handler work before
|
|
2326
|
+
// renderToReadableStream. Concretely:
|
|
2327
|
+
// compileMs = (handlerStart - _reqStart) + inHandlerCompileMs
|
|
2328
|
+
// renderMs = renderMs from header, or -1 for RSC-only (soft-nav)
|
|
2329
|
+
// responses where rendering is not measured in the handler.
|
|
2330
|
+
// In that case the middleware computes render time as
|
|
2331
|
+
// totalMs - compileMs.
|
|
2332
|
+
//
|
|
2333
|
+
// handlerStart is performance.now() recorded at the very top of
|
|
2334
|
+
// _handleRequest in the generated RSC entry. _reqStart is recorded
|
|
2335
|
+
// here in the Node middleware, one stack frame before the RSC plugin
|
|
2336
|
+
// loads the module. The gap between them is exactly the Vite
|
|
2337
|
+
// compile/transform cost.
|
|
2338
|
+
function _parseTiming(raw) {
|
|
2339
|
+
const [handlerStart, inHandlerCompileMs, renderMs] = String(raw).split(",").map((v) => Number(v));
|
|
2340
|
+
if (!Number.isNaN(handlerStart) && !Number.isNaN(inHandlerCompileMs) && inHandlerCompileMs !== -1) {
|
|
2341
|
+
_compileMs = Math.max(0, Math.round(handlerStart - _reqStart)) + inHandlerCompileMs;
|
|
2342
|
+
}
|
|
2343
|
+
if (!Number.isNaN(renderMs) && renderMs !== -1) {
|
|
2344
|
+
_renderMs = renderMs;
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
const _origSetHeader = res.setHeader.bind(res);
|
|
2348
|
+
res.setHeader = function (name, value) {
|
|
2349
|
+
if (name.toLowerCase() === "x-vinext-timing") {
|
|
2350
|
+
_parseTiming(value);
|
|
2351
|
+
return res; // drop the header — don't forward to client
|
|
2352
|
+
}
|
|
2353
|
+
return _origSetHeader(name, value);
|
|
2354
|
+
};
|
|
2355
|
+
const _origWriteHead = res.writeHead.bind(res);
|
|
2356
|
+
res.writeHead = function (statusCode, ...args) {
|
|
2357
|
+
// Normalise the optional headers argument (may be reason, headers object, or both).
|
|
2358
|
+
let headers;
|
|
2359
|
+
const [reasonOrHeaders, maybeHeaders] = args;
|
|
2360
|
+
if (typeof reasonOrHeaders === "string") {
|
|
2361
|
+
headers = maybeHeaders;
|
|
2362
|
+
}
|
|
2363
|
+
else {
|
|
2364
|
+
headers = reasonOrHeaders;
|
|
2365
|
+
}
|
|
2366
|
+
// Pull timing out of the headers object when present.
|
|
2367
|
+
if (headers && typeof headers === "object" && !Array.isArray(headers)) {
|
|
2368
|
+
const timingKey = Object.keys(headers).find((k) => k.toLowerCase() === "x-vinext-timing");
|
|
2369
|
+
if (timingKey) {
|
|
2370
|
+
_parseTiming(headers[timingKey]);
|
|
2371
|
+
delete headers[timingKey];
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
return _origWriteHead(statusCode, ...args);
|
|
2375
|
+
};
|
|
2376
|
+
res.on("finish", () => {
|
|
2377
|
+
// Strip .rsc suffix — it's an internal RSC protocol detail,
|
|
2378
|
+
// not part of the actual page path the user navigated to.
|
|
2379
|
+
const logUrl = url.replace(/\.rsc(\?|$)/, "$1");
|
|
2380
|
+
const totalMs = now() - _reqStart;
|
|
2381
|
+
// For RSC-only responses (soft nav), renderMs is -1 (sentinel meaning
|
|
2382
|
+
// "not measured in the handler"). Compute it as totalMs - compileMs,
|
|
2383
|
+
// which is how long the RSC stream took to fully flush to the client —
|
|
2384
|
+
// matching what Next.js shows for soft navigations.
|
|
2385
|
+
const resolvedRenderMs = _renderMs !== undefined
|
|
2386
|
+
? _renderMs
|
|
2387
|
+
: (_compileMs !== undefined ? Math.max(0, Math.round(totalMs - _compileMs)) : undefined);
|
|
2388
|
+
logRequest({
|
|
2389
|
+
method: req.method ?? "GET",
|
|
2390
|
+
url: logUrl,
|
|
2391
|
+
status: res.statusCode,
|
|
2392
|
+
totalMs,
|
|
2393
|
+
compileMs: _compileMs,
|
|
2394
|
+
renderMs: resolvedRenderMs,
|
|
2395
|
+
});
|
|
2396
|
+
});
|
|
2397
|
+
next();
|
|
2398
|
+
});
|
|
2399
|
+
}
|
|
2241
2400
|
server.middlewares.use(async (req, res, next) => {
|
|
2242
2401
|
try {
|
|
2243
2402
|
let url = req.url ?? "/";
|
|
@@ -2255,9 +2414,12 @@ hydrate();
|
|
|
2255
2414
|
if (url.split("?")[0].endsWith(".rsc")) {
|
|
2256
2415
|
return next();
|
|
2257
2416
|
}
|
|
2258
|
-
// ── Cross-origin request protection
|
|
2259
|
-
//
|
|
2260
|
-
//
|
|
2417
|
+
// ── Cross-origin request protection (defense-in-depth) ──────
|
|
2418
|
+
// The pre-Vite middleware above already blocks cross-origin
|
|
2419
|
+
// requests before Vite serves any content. This second check
|
|
2420
|
+
// guards the Pages Router handler specifically, in case the
|
|
2421
|
+
// middleware ordering changes or new middleware is added between
|
|
2422
|
+
// the two. Both calls use the same validateDevRequest() function.
|
|
2261
2423
|
const blockReason = validateDevRequest({
|
|
2262
2424
|
origin: req.headers.origin,
|
|
2263
2425
|
host: req.headers.host,
|
|
@@ -2281,7 +2443,14 @@ hydrate();
|
|
|
2281
2443
|
const imgUrl = rawImgUrl?.replaceAll("\\", "/") ?? null;
|
|
2282
2444
|
// Allowlist: must start with "/" but not "//" — blocks absolute
|
|
2283
2445
|
// URLs, protocol-relative, backslash variants, and exotic schemes.
|
|
2284
|
-
|
|
2446
|
+
// Also block internal Vite paths (/@*, /__vite*, /node_modules*)
|
|
2447
|
+
// to prevent redirecting to dev server endpoints.
|
|
2448
|
+
if (!imgUrl ||
|
|
2449
|
+
!imgUrl.startsWith("/") ||
|
|
2450
|
+
imgUrl.startsWith("//") ||
|
|
2451
|
+
imgUrl.startsWith("/@") ||
|
|
2452
|
+
imgUrl.startsWith("/__vite") ||
|
|
2453
|
+
imgUrl.startsWith("/node_modules")) {
|
|
2285
2454
|
res.writeHead(400);
|
|
2286
2455
|
res.end(!rawImgUrl ? "Missing url parameter" : "Only relative URLs allowed");
|
|
2287
2456
|
return;
|