feat(blog): add file-based blog with dynamic slugs, MDX content and layout shell

- Introduced blog routing using Next.js App Router
- Implemented dynamic [slug] pages for blog posts
- Added MDX-based content loading via lib/posts
- Integrated shared TopBar layout with navigation
- Established clear content, lib and component separation
This commit is contained in:
PascalSchattenburg
2026-01-22 14:14:15 +01:00
parent b717952234
commit d147843c76
10412 changed files with 2475583 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
import { middlewareResponse } from './middleware-response';
import * as inspector from 'inspector';
export function getAttachNodejsDebuggerMiddleware() {
return async function(req, res, next) {
const { pathname } = new URL(`http://n${req.url}`);
if (pathname !== '/__nextjs_attach-nodejs-inspector') {
return next();
}
try {
const isInspecting = inspector.url() !== undefined;
const debugPort = process.debugPort;
if (!isInspecting) {
// Node.js will already log that the inspector is listening.
inspector.open(debugPort);
}
const inspectorURLRaw = inspector.url();
if (inspectorURLRaw === undefined) {
// could not open, possibly because already in use.
return middlewareResponse.badRequest(res, `Failed to open port "${debugPort}". Address may be already in use.`);
}
const inspectorURL = new URL(inspectorURLRaw);
const debugInfoListResponse = await fetch(`http://${inspectorURL.host}/json/list`);
const debugInfoList = await debugInfoListResponse.json();
if (!Array.isArray(debugInfoList) || debugInfoList.length === 0) {
throw Object.defineProperty(new Error('No debug targets found'), "__NEXT_ERROR_CODE", {
value: "E927",
enumerable: false,
configurable: true
});
}
return middlewareResponse.json(res, debugInfoList[0].devtoolsFrontendUrl);
} catch (error) {
return middlewareResponse.internalServerError(res);
}
};
}
//# sourceMappingURL=attach-nodejs-debugger-middleware.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/next-devtools/server/attach-nodejs-debugger-middleware.ts"],"sourcesContent":["import { middlewareResponse } from './middleware-response'\nimport type { ServerResponse, IncomingMessage } from 'http'\nimport * as inspector from 'inspector'\n\nexport function getAttachNodejsDebuggerMiddleware() {\n return async function (\n req: IncomingMessage,\n res: ServerResponse,\n next: () => void\n ): Promise<void> {\n const { pathname } = new URL(`http://n${req.url}`)\n\n if (pathname !== '/__nextjs_attach-nodejs-inspector') {\n return next()\n }\n\n try {\n const isInspecting = inspector.url() !== undefined\n const debugPort = process.debugPort\n if (!isInspecting) {\n // Node.js will already log that the inspector is listening.\n inspector.open(debugPort)\n }\n\n const inspectorURLRaw = inspector.url()\n if (inspectorURLRaw === undefined) {\n // could not open, possibly because already in use.\n return middlewareResponse.badRequest(\n res,\n `Failed to open port \"${debugPort}\". Address may be already in use.`\n )\n }\n const inspectorURL = new URL(inspectorURLRaw)\n\n const debugInfoListResponse = await fetch(\n `http://${inspectorURL.host}/json/list`\n )\n const debugInfoList = await debugInfoListResponse.json()\n if (!Array.isArray(debugInfoList) || debugInfoList.length === 0) {\n throw new Error('No debug targets found')\n }\n\n return middlewareResponse.json(res, debugInfoList[0].devtoolsFrontendUrl)\n } catch (error) {\n return middlewareResponse.internalServerError(res)\n }\n }\n}\n"],"names":["middlewareResponse","inspector","getAttachNodejsDebuggerMiddleware","req","res","next","pathname","URL","url","isInspecting","undefined","debugPort","process","open","inspectorURLRaw","badRequest","inspectorURL","debugInfoListResponse","fetch","host","debugInfoList","json","Array","isArray","length","Error","devtoolsFrontendUrl","error","internalServerError"],"mappings":"AAAA,SAASA,kBAAkB,QAAQ,wBAAuB;AAE1D,YAAYC,eAAe,YAAW;AAEtC,OAAO,SAASC;IACd,OAAO,eACLC,GAAoB,EACpBC,GAAmB,EACnBC,IAAgB;QAEhB,MAAM,EAAEC,QAAQ,EAAE,GAAG,IAAIC,IAAI,CAAC,QAAQ,EAAEJ,IAAIK,GAAG,EAAE;QAEjD,IAAIF,aAAa,qCAAqC;YACpD,OAAOD;QACT;QAEA,IAAI;YACF,MAAMI,eAAeR,UAAUO,GAAG,OAAOE;YACzC,MAAMC,YAAYC,QAAQD,SAAS;YACnC,IAAI,CAACF,cAAc;gBACjB,4DAA4D;gBAC5DR,UAAUY,IAAI,CAACF;YACjB;YAEA,MAAMG,kBAAkBb,UAAUO,GAAG;YACrC,IAAIM,oBAAoBJ,WAAW;gBACjC,mDAAmD;gBACnD,OAAOV,mBAAmBe,UAAU,CAClCX,KACA,CAAC,qBAAqB,EAAEO,UAAU,iCAAiC,CAAC;YAExE;YACA,MAAMK,eAAe,IAAIT,IAAIO;YAE7B,MAAMG,wBAAwB,MAAMC,MAClC,CAAC,OAAO,EAAEF,aAAaG,IAAI,CAAC,UAAU,CAAC;YAEzC,MAAMC,gBAAgB,MAAMH,sBAAsBI,IAAI;YACtD,IAAI,CAACC,MAAMC,OAAO,CAACH,kBAAkBA,cAAcI,MAAM,KAAK,GAAG;gBAC/D,MAAM,qBAAmC,CAAnC,IAAIC,MAAM,2BAAV,qBAAA;2BAAA;gCAAA;kCAAA;gBAAkC;YAC1C;YAEA,OAAOzB,mBAAmBqB,IAAI,CAACjB,KAAKgB,aAAa,CAAC,EAAE,CAACM,mBAAmB;QAC1E,EAAE,OAAOC,OAAO;YACd,OAAO3B,mBAAmB4B,mBAAmB,CAACxB;QAChD;IACF;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,25 @@
import { middlewareResponse } from './middleware-response';
import * as Log from '../../build/output/log';
import { devIndicatorServerState } from '../../server/dev/dev-indicator-server-state';
const DISABLE_DEV_INDICATOR_PREFIX = '/__nextjs_disable_dev_indicator';
const COOLDOWN_TIME_MS = process.env.__NEXT_DEV_INDICATOR_COOLDOWN_MS ? parseInt(process.env.__NEXT_DEV_INDICATOR_COOLDOWN_MS) : 1000 * 60 * 60 * 24;
export function getDisableDevIndicatorMiddleware() {
return async function disableDevIndicatorMiddleware(req, res, next) {
try {
const { pathname } = new URL(`http://n${req.url}`);
if (!pathname.startsWith(DISABLE_DEV_INDICATOR_PREFIX)) {
return next();
}
if (req.method !== 'POST') {
return middlewareResponse.methodNotAllowed(res);
}
devIndicatorServerState.disabledUntil = Date.now() + COOLDOWN_TIME_MS;
return middlewareResponse.noContent(res);
} catch (err) {
Log.error('Failed to disable the dev indicator:', err instanceof Error ? err.message : err);
return middlewareResponse.internalServerError(res);
}
};
}
//# sourceMappingURL=dev-indicator-middleware.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/next-devtools/server/dev-indicator-middleware.ts"],"sourcesContent":["import type { ServerResponse, IncomingMessage } from 'http'\nimport { middlewareResponse } from './middleware-response'\nimport * as Log from '../../build/output/log'\nimport { devIndicatorServerState } from '../../server/dev/dev-indicator-server-state'\n\nconst DISABLE_DEV_INDICATOR_PREFIX = '/__nextjs_disable_dev_indicator'\n\nconst COOLDOWN_TIME_MS = process.env.__NEXT_DEV_INDICATOR_COOLDOWN_MS\n ? parseInt(process.env.__NEXT_DEV_INDICATOR_COOLDOWN_MS)\n : // 1 day from now\n 1000 * 60 * 60 * 24\n\nexport function getDisableDevIndicatorMiddleware() {\n return async function disableDevIndicatorMiddleware(\n req: IncomingMessage,\n res: ServerResponse,\n next: () => void\n ): Promise<void> {\n try {\n const { pathname } = new URL(`http://n${req.url}`)\n\n if (!pathname.startsWith(DISABLE_DEV_INDICATOR_PREFIX)) {\n return next()\n }\n\n if (req.method !== 'POST') {\n return middlewareResponse.methodNotAllowed(res)\n }\n\n devIndicatorServerState.disabledUntil = Date.now() + COOLDOWN_TIME_MS\n\n return middlewareResponse.noContent(res)\n } catch (err) {\n Log.error(\n 'Failed to disable the dev indicator:',\n err instanceof Error ? err.message : err\n )\n return middlewareResponse.internalServerError(res)\n }\n }\n}\n"],"names":["middlewareResponse","Log","devIndicatorServerState","DISABLE_DEV_INDICATOR_PREFIX","COOLDOWN_TIME_MS","process","env","__NEXT_DEV_INDICATOR_COOLDOWN_MS","parseInt","getDisableDevIndicatorMiddleware","disableDevIndicatorMiddleware","req","res","next","pathname","URL","url","startsWith","method","methodNotAllowed","disabledUntil","Date","now","noContent","err","error","Error","message","internalServerError"],"mappings":"AACA,SAASA,kBAAkB,QAAQ,wBAAuB;AAC1D,YAAYC,SAAS,yBAAwB;AAC7C,SAASC,uBAAuB,QAAQ,8CAA6C;AAErF,MAAMC,+BAA+B;AAErC,MAAMC,mBAAmBC,QAAQC,GAAG,CAACC,gCAAgC,GACjEC,SAASH,QAAQC,GAAG,CAACC,gCAAgC,IAErD,OAAO,KAAK,KAAK;AAErB,OAAO,SAASE;IACd,OAAO,eAAeC,8BACpBC,GAAoB,EACpBC,GAAmB,EACnBC,IAAgB;QAEhB,IAAI;YACF,MAAM,EAAEC,QAAQ,EAAE,GAAG,IAAIC,IAAI,CAAC,QAAQ,EAAEJ,IAAIK,GAAG,EAAE;YAEjD,IAAI,CAACF,SAASG,UAAU,CAACd,+BAA+B;gBACtD,OAAOU;YACT;YAEA,IAAIF,IAAIO,MAAM,KAAK,QAAQ;gBACzB,OAAOlB,mBAAmBmB,gBAAgB,CAACP;YAC7C;YAEAV,wBAAwBkB,aAAa,GAAGC,KAAKC,GAAG,KAAKlB;YAErD,OAAOJ,mBAAmBuB,SAAS,CAACX;QACtC,EAAE,OAAOY,KAAK;YACZvB,IAAIwB,KAAK,CACP,wCACAD,eAAeE,QAAQF,IAAIG,OAAO,GAAGH;YAEvC,OAAOxB,mBAAmB4B,mBAAmB,CAAChB;QAChD;IACF;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,54 @@
import { existsSync } from 'fs';
import { readFile, writeFile, mkdir } from 'fs/promises';
import { dirname, join } from 'path';
import { middlewareResponse } from './middleware-response';
import { devToolsConfigSchema } from '../shared/devtools-config-schema';
import { deepMerge } from '../shared/deepmerge';
const DEVTOOLS_CONFIG_FILENAME = 'next-devtools-config.json';
const DEVTOOLS_CONFIG_MIDDLEWARE_ENDPOINT = '/__nextjs_devtools_config';
export function devToolsConfigMiddleware({ distDir, sendUpdateSignal }) {
const configPath = join(distDir, 'cache', DEVTOOLS_CONFIG_FILENAME);
return async function devToolsConfigMiddlewareHandler(req, res, next) {
const { pathname } = new URL(`http://n${req.url}`);
if (pathname !== DEVTOOLS_CONFIG_MIDDLEWARE_ENDPOINT) {
return next();
}
if (req.method !== 'POST') {
return middlewareResponse.methodNotAllowed(res);
}
const currentConfig = await getDevToolsConfig(distDir);
const chunks = [];
for await (const chunk of req){
chunks.push(Buffer.from(chunk));
}
let body = Buffer.concat(chunks).toString('utf8');
try {
body = JSON.parse(body);
} catch (error) {
console.error('[Next.js DevTools] Invalid config body passed:', error);
return middlewareResponse.badRequest(res);
}
const validation = devToolsConfigSchema.safeParse(body);
if (!validation.success) {
console.error('[Next.js DevTools] Invalid config passed:', validation.error.message);
return middlewareResponse.badRequest(res);
}
const newConfig = deepMerge(currentConfig, validation.data);
await writeFile(configPath, JSON.stringify(newConfig, null, 2));
sendUpdateSignal(newConfig);
return middlewareResponse.noContent(res);
};
}
export async function getDevToolsConfig(distDir) {
const configPath = join(distDir, 'cache', DEVTOOLS_CONFIG_FILENAME);
if (!existsSync(configPath)) {
await mkdir(dirname(configPath), {
recursive: true
});
await writeFile(configPath, JSON.stringify({}));
return {};
}
return JSON.parse(await readFile(configPath, 'utf8'));
}
//# sourceMappingURL=devtools-config-middleware.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/next-devtools/server/devtools-config-middleware.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'http'\nimport type { DevToolsConfig } from '../dev-overlay/shared'\n\nimport { existsSync } from 'fs'\nimport { readFile, writeFile, mkdir } from 'fs/promises'\nimport { dirname, join } from 'path'\n\nimport { middlewareResponse } from './middleware-response'\nimport { devToolsConfigSchema } from '../shared/devtools-config-schema'\nimport { deepMerge } from '../shared/deepmerge'\n\nconst DEVTOOLS_CONFIG_FILENAME = 'next-devtools-config.json'\nconst DEVTOOLS_CONFIG_MIDDLEWARE_ENDPOINT = '/__nextjs_devtools_config'\n\nexport function devToolsConfigMiddleware({\n distDir,\n sendUpdateSignal,\n}: {\n distDir: string\n sendUpdateSignal: (data: DevToolsConfig) => void\n}) {\n const configPath = join(distDir, 'cache', DEVTOOLS_CONFIG_FILENAME)\n\n return async function devToolsConfigMiddlewareHandler(\n req: IncomingMessage,\n res: ServerResponse,\n next: () => void\n ): Promise<void> {\n const { pathname } = new URL(`http://n${req.url}`)\n\n if (pathname !== DEVTOOLS_CONFIG_MIDDLEWARE_ENDPOINT) {\n return next()\n }\n\n if (req.method !== 'POST') {\n return middlewareResponse.methodNotAllowed(res)\n }\n\n const currentConfig = await getDevToolsConfig(distDir)\n\n const chunks: Buffer[] = []\n for await (const chunk of req) {\n chunks.push(Buffer.from(chunk))\n }\n\n let body = Buffer.concat(chunks).toString('utf8')\n try {\n body = JSON.parse(body)\n } catch (error) {\n console.error('[Next.js DevTools] Invalid config body passed:', error)\n return middlewareResponse.badRequest(res)\n }\n\n const validation = devToolsConfigSchema.safeParse(body)\n if (!validation.success) {\n console.error(\n '[Next.js DevTools] Invalid config passed:',\n validation.error.message\n )\n return middlewareResponse.badRequest(res)\n }\n\n const newConfig = deepMerge(currentConfig, validation.data)\n await writeFile(configPath, JSON.stringify(newConfig, null, 2))\n\n sendUpdateSignal(newConfig)\n\n return middlewareResponse.noContent(res)\n }\n}\n\nexport async function getDevToolsConfig(\n distDir: string\n): Promise<DevToolsConfig> {\n const configPath = join(distDir, 'cache', DEVTOOLS_CONFIG_FILENAME)\n\n if (!existsSync(configPath)) {\n await mkdir(dirname(configPath), { recursive: true })\n await writeFile(configPath, JSON.stringify({}))\n return {}\n }\n\n return JSON.parse(await readFile(configPath, 'utf8'))\n}\n"],"names":["existsSync","readFile","writeFile","mkdir","dirname","join","middlewareResponse","devToolsConfigSchema","deepMerge","DEVTOOLS_CONFIG_FILENAME","DEVTOOLS_CONFIG_MIDDLEWARE_ENDPOINT","devToolsConfigMiddleware","distDir","sendUpdateSignal","configPath","devToolsConfigMiddlewareHandler","req","res","next","pathname","URL","url","method","methodNotAllowed","currentConfig","getDevToolsConfig","chunks","chunk","push","Buffer","from","body","concat","toString","JSON","parse","error","console","badRequest","validation","safeParse","success","message","newConfig","data","stringify","noContent","recursive"],"mappings":"AAGA,SAASA,UAAU,QAAQ,KAAI;AAC/B,SAASC,QAAQ,EAAEC,SAAS,EAAEC,KAAK,QAAQ,cAAa;AACxD,SAASC,OAAO,EAAEC,IAAI,QAAQ,OAAM;AAEpC,SAASC,kBAAkB,QAAQ,wBAAuB;AAC1D,SAASC,oBAAoB,QAAQ,mCAAkC;AACvE,SAASC,SAAS,QAAQ,sBAAqB;AAE/C,MAAMC,2BAA2B;AACjC,MAAMC,sCAAsC;AAE5C,OAAO,SAASC,yBAAyB,EACvCC,OAAO,EACPC,gBAAgB,EAIjB;IACC,MAAMC,aAAaT,KAAKO,SAAS,SAASH;IAE1C,OAAO,eAAeM,gCACpBC,GAAoB,EACpBC,GAAmB,EACnBC,IAAgB;QAEhB,MAAM,EAAEC,QAAQ,EAAE,GAAG,IAAIC,IAAI,CAAC,QAAQ,EAAEJ,IAAIK,GAAG,EAAE;QAEjD,IAAIF,aAAaT,qCAAqC;YACpD,OAAOQ;QACT;QAEA,IAAIF,IAAIM,MAAM,KAAK,QAAQ;YACzB,OAAOhB,mBAAmBiB,gBAAgB,CAACN;QAC7C;QAEA,MAAMO,gBAAgB,MAAMC,kBAAkBb;QAE9C,MAAMc,SAAmB,EAAE;QAC3B,WAAW,MAAMC,SAASX,IAAK;YAC7BU,OAAOE,IAAI,CAACC,OAAOC,IAAI,CAACH;QAC1B;QAEA,IAAII,OAAOF,OAAOG,MAAM,CAACN,QAAQO,QAAQ,CAAC;QAC1C,IAAI;YACFF,OAAOG,KAAKC,KAAK,CAACJ;QACpB,EAAE,OAAOK,OAAO;YACdC,QAAQD,KAAK,CAAC,kDAAkDA;YAChE,OAAO9B,mBAAmBgC,UAAU,CAACrB;QACvC;QAEA,MAAMsB,aAAahC,qBAAqBiC,SAAS,CAACT;QAClD,IAAI,CAACQ,WAAWE,OAAO,EAAE;YACvBJ,QAAQD,KAAK,CACX,6CACAG,WAAWH,KAAK,CAACM,OAAO;YAE1B,OAAOpC,mBAAmBgC,UAAU,CAACrB;QACvC;QAEA,MAAM0B,YAAYnC,UAAUgB,eAAee,WAAWK,IAAI;QAC1D,MAAM1C,UAAUY,YAAYoB,KAAKW,SAAS,CAACF,WAAW,MAAM;QAE5D9B,iBAAiB8B;QAEjB,OAAOrC,mBAAmBwC,SAAS,CAAC7B;IACtC;AACF;AAEA,OAAO,eAAeQ,kBACpBb,OAAe;IAEf,MAAME,aAAaT,KAAKO,SAAS,SAASH;IAE1C,IAAI,CAACT,WAAWc,aAAa;QAC3B,MAAMX,MAAMC,QAAQU,aAAa;YAAEiC,WAAW;QAAK;QACnD,MAAM7C,UAAUY,YAAYoB,KAAKW,SAAS,CAAC,CAAC;QAC5C,OAAO,CAAC;IACV;IAEA,OAAOX,KAAKC,KAAK,CAAC,MAAMlC,SAASa,YAAY;AAC/C","ignoreList":[0]}

View File

@@ -0,0 +1,53 @@
import path from 'path';
import * as fs from 'fs/promises';
import { constants } from 'fs';
import * as Log from '../../../build/output/log';
import { middlewareResponse } from '../middleware-response';
const FONT_PREFIX = '/__nextjs_font/';
const VALID_FONTS = [
'geist-latin-ext.woff2',
'geist-mono-latin-ext.woff2',
'geist-latin.woff2',
'geist-mono-latin.woff2'
];
const FONT_HEADERS = {
'Content-Type': 'font/woff2',
'Cache-Control': 'public, max-age=31536000, immutable'
};
export function getDevOverlayFontMiddleware() {
return async function devOverlayFontMiddleware(req, res, next) {
try {
const { pathname } = new URL(`http://n${req.url}`);
if (!pathname.startsWith(FONT_PREFIX)) {
return next();
}
const fontFile = pathname.replace(FONT_PREFIX, '');
if (!VALID_FONTS.includes(fontFile)) {
return middlewareResponse.notFound(res);
}
const fontPath = path.resolve(__dirname, fontFile);
const fileExists = await checkFileExists(fontPath);
if (!fileExists) {
return middlewareResponse.notFound(res);
}
const fontData = await fs.readFile(fontPath);
Object.entries(FONT_HEADERS).forEach(([key, value])=>{
res.setHeader(key, value);
});
res.end(fontData);
} catch (err) {
Log.error('Failed to serve font:', err instanceof Error ? err.message : err);
return middlewareResponse.internalServerError(res);
}
};
}
async function checkFileExists(filePath) {
try {
await fs.access(filePath, constants.F_OK);
return true;
} catch {
return false;
}
}
//# sourceMappingURL=get-dev-overlay-font-middleware.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/next-devtools/server/font/get-dev-overlay-font-middleware.ts"],"sourcesContent":["import type { ServerResponse, IncomingMessage } from 'http'\nimport path from 'path'\nimport * as fs from 'fs/promises'\nimport { constants } from 'fs'\nimport * as Log from '../../../build/output/log'\nimport { middlewareResponse } from '../middleware-response'\n\nconst FONT_PREFIX = '/__nextjs_font/'\n\nconst VALID_FONTS = [\n 'geist-latin-ext.woff2',\n 'geist-mono-latin-ext.woff2',\n 'geist-latin.woff2',\n 'geist-mono-latin.woff2',\n]\n\nconst FONT_HEADERS = {\n 'Content-Type': 'font/woff2',\n 'Cache-Control': 'public, max-age=31536000, immutable',\n} as const\n\nexport function getDevOverlayFontMiddleware() {\n return async function devOverlayFontMiddleware(\n req: IncomingMessage,\n res: ServerResponse,\n next: () => void\n ): Promise<void> {\n try {\n const { pathname } = new URL(`http://n${req.url}`)\n\n if (!pathname.startsWith(FONT_PREFIX)) {\n return next()\n }\n\n const fontFile = pathname.replace(FONT_PREFIX, '')\n if (!VALID_FONTS.includes(fontFile)) {\n return middlewareResponse.notFound(res)\n }\n\n const fontPath = path.resolve(__dirname, fontFile)\n const fileExists = await checkFileExists(fontPath)\n\n if (!fileExists) {\n return middlewareResponse.notFound(res)\n }\n\n const fontData = await fs.readFile(fontPath)\n Object.entries(FONT_HEADERS).forEach(([key, value]) => {\n res.setHeader(key, value)\n })\n res.end(fontData)\n } catch (err) {\n Log.error(\n 'Failed to serve font:',\n err instanceof Error ? err.message : err\n )\n return middlewareResponse.internalServerError(res)\n }\n }\n}\n\nasync function checkFileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n"],"names":["path","fs","constants","Log","middlewareResponse","FONT_PREFIX","VALID_FONTS","FONT_HEADERS","getDevOverlayFontMiddleware","devOverlayFontMiddleware","req","res","next","pathname","URL","url","startsWith","fontFile","replace","includes","notFound","fontPath","resolve","__dirname","fileExists","checkFileExists","fontData","readFile","Object","entries","forEach","key","value","setHeader","end","err","error","Error","message","internalServerError","filePath","access","F_OK"],"mappings":"AACA,OAAOA,UAAU,OAAM;AACvB,YAAYC,QAAQ,cAAa;AACjC,SAASC,SAAS,QAAQ,KAAI;AAC9B,YAAYC,SAAS,4BAA2B;AAChD,SAASC,kBAAkB,QAAQ,yBAAwB;AAE3D,MAAMC,cAAc;AAEpB,MAAMC,cAAc;IAClB;IACA;IACA;IACA;CACD;AAED,MAAMC,eAAe;IACnB,gBAAgB;IAChB,iBAAiB;AACnB;AAEA,OAAO,SAASC;IACd,OAAO,eAAeC,yBACpBC,GAAoB,EACpBC,GAAmB,EACnBC,IAAgB;QAEhB,IAAI;YACF,MAAM,EAAEC,QAAQ,EAAE,GAAG,IAAIC,IAAI,CAAC,QAAQ,EAAEJ,IAAIK,GAAG,EAAE;YAEjD,IAAI,CAACF,SAASG,UAAU,CAACX,cAAc;gBACrC,OAAOO;YACT;YAEA,MAAMK,WAAWJ,SAASK,OAAO,CAACb,aAAa;YAC/C,IAAI,CAACC,YAAYa,QAAQ,CAACF,WAAW;gBACnC,OAAOb,mBAAmBgB,QAAQ,CAACT;YACrC;YAEA,MAAMU,WAAWrB,KAAKsB,OAAO,CAACC,WAAWN;YACzC,MAAMO,aAAa,MAAMC,gBAAgBJ;YAEzC,IAAI,CAACG,YAAY;gBACf,OAAOpB,mBAAmBgB,QAAQ,CAACT;YACrC;YAEA,MAAMe,WAAW,MAAMzB,GAAG0B,QAAQ,CAACN;YACnCO,OAAOC,OAAO,CAACtB,cAAcuB,OAAO,CAAC,CAAC,CAACC,KAAKC,MAAM;gBAChDrB,IAAIsB,SAAS,CAACF,KAAKC;YACrB;YACArB,IAAIuB,GAAG,CAACR;QACV,EAAE,OAAOS,KAAK;YACZhC,IAAIiC,KAAK,CACP,yBACAD,eAAeE,QAAQF,IAAIG,OAAO,GAAGH;YAEvC,OAAO/B,mBAAmBmC,mBAAmB,CAAC5B;QAChD;IACF;AACF;AAEA,eAAec,gBAAgBe,QAAgB;IAC7C,IAAI;QACF,MAAMvC,GAAGwC,MAAM,CAACD,UAAUtC,UAAUwC,IAAI;QACxC,OAAO;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,27 @@
import { eventErrorFeedback } from '../../telemetry/events/error-feedback';
import { middlewareResponse } from './middleware-response';
// Handles HTTP requests to /__nextjs_error_feedback endpoint for collecting user feedback on error messages
export function getNextErrorFeedbackMiddleware(telemetry) {
return async function(req, res, next) {
const { pathname, searchParams } = new URL(`http://n${req.url}`);
if (pathname !== '/__nextjs_error_feedback') {
return next();
}
try {
const errorCode = searchParams.get('errorCode');
const wasHelpful = searchParams.get('wasHelpful');
if (!errorCode || !wasHelpful) {
return middlewareResponse.badRequest(res);
}
await telemetry.record(eventErrorFeedback({
errorCode,
wasHelpful: wasHelpful === 'true'
}));
return middlewareResponse.noContent(res);
} catch (error) {
return middlewareResponse.internalServerError(res);
}
};
}
//# sourceMappingURL=get-next-error-feedback-middleware.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/next-devtools/server/get-next-error-feedback-middleware.ts"],"sourcesContent":["import { eventErrorFeedback } from '../../telemetry/events/error-feedback'\nimport { middlewareResponse } from './middleware-response'\nimport type { ServerResponse, IncomingMessage } from 'http'\nimport type { Telemetry } from '../../telemetry/storage'\n\n// Handles HTTP requests to /__nextjs_error_feedback endpoint for collecting user feedback on error messages\nexport function getNextErrorFeedbackMiddleware(telemetry: Telemetry) {\n return async function (\n req: IncomingMessage,\n res: ServerResponse,\n next: () => void\n ): Promise<void> {\n const { pathname, searchParams } = new URL(`http://n${req.url}`)\n\n if (pathname !== '/__nextjs_error_feedback') {\n return next()\n }\n\n try {\n const errorCode = searchParams.get('errorCode')\n const wasHelpful = searchParams.get('wasHelpful')\n\n if (!errorCode || !wasHelpful) {\n return middlewareResponse.badRequest(res)\n }\n\n await telemetry.record(\n eventErrorFeedback({\n errorCode,\n wasHelpful: wasHelpful === 'true',\n })\n )\n\n return middlewareResponse.noContent(res)\n } catch (error) {\n return middlewareResponse.internalServerError(res)\n }\n }\n}\n"],"names":["eventErrorFeedback","middlewareResponse","getNextErrorFeedbackMiddleware","telemetry","req","res","next","pathname","searchParams","URL","url","errorCode","get","wasHelpful","badRequest","record","noContent","error","internalServerError"],"mappings":"AAAA,SAASA,kBAAkB,QAAQ,wCAAuC;AAC1E,SAASC,kBAAkB,QAAQ,wBAAuB;AAI1D,4GAA4G;AAC5G,OAAO,SAASC,+BAA+BC,SAAoB;IACjE,OAAO,eACLC,GAAoB,EACpBC,GAAmB,EACnBC,IAAgB;QAEhB,MAAM,EAAEC,QAAQ,EAAEC,YAAY,EAAE,GAAG,IAAIC,IAAI,CAAC,QAAQ,EAAEL,IAAIM,GAAG,EAAE;QAE/D,IAAIH,aAAa,4BAA4B;YAC3C,OAAOD;QACT;QAEA,IAAI;YACF,MAAMK,YAAYH,aAAaI,GAAG,CAAC;YACnC,MAAMC,aAAaL,aAAaI,GAAG,CAAC;YAEpC,IAAI,CAACD,aAAa,CAACE,YAAY;gBAC7B,OAAOZ,mBAAmBa,UAAU,CAACT;YACvC;YAEA,MAAMF,UAAUY,MAAM,CACpBf,mBAAmB;gBACjBW;gBACAE,YAAYA,eAAe;YAC7B;YAGF,OAAOZ,mBAAmBe,SAAS,CAACX;QACtC,EAAE,OAAOY,OAAO;YACd,OAAOhB,mBAAmBiB,mBAAmB,CAACb;QAChD;IACF;AACF","ignoreList":[0]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,39 @@
import { inspect } from 'util';
export const middlewareResponse = {
noContent (res) {
res.statusCode = 204;
res.end('No Content');
},
badRequest (res, reason) {
res.statusCode = 400;
if (reason !== undefined) {
res.setHeader('Content-Type', 'text/plain');
res.end(reason);
} else {
res.end();
}
},
notFound (res) {
res.statusCode = 404;
res.end('Not Found');
},
methodNotAllowed (res) {
res.statusCode = 405;
res.end('Method Not Allowed');
},
internalServerError (res, error) {
res.statusCode = 500;
res.setHeader('Content-Type', 'text/plain');
res.end(error !== undefined ? inspect(error, {
colors: false
}) : 'Internal Server Error');
},
json (res, data) {
res.setHeader('Content-Type', 'application/json').end(Buffer.from(JSON.stringify(data)));
},
jsonString (res, data) {
res.setHeader('Content-Type', 'application/json').end(Buffer.from(data));
}
};
//# sourceMappingURL=middleware-response.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/next-devtools/server/middleware-response.ts"],"sourcesContent":["import type { ServerResponse } from 'http'\nimport { inspect } from 'util'\n\nexport const middlewareResponse = {\n noContent(res: ServerResponse) {\n res.statusCode = 204\n res.end('No Content')\n },\n badRequest(res: ServerResponse, reason?: string) {\n res.statusCode = 400\n if (reason !== undefined) {\n res.setHeader('Content-Type', 'text/plain')\n res.end(reason)\n } else {\n res.end()\n }\n },\n notFound(res: ServerResponse) {\n res.statusCode = 404\n res.end('Not Found')\n },\n methodNotAllowed(res: ServerResponse) {\n res.statusCode = 405\n res.end('Method Not Allowed')\n },\n internalServerError(res: ServerResponse, error?: unknown) {\n res.statusCode = 500\n res.setHeader('Content-Type', 'text/plain')\n res.end(\n error !== undefined\n ? inspect(error, { colors: false })\n : 'Internal Server Error'\n )\n },\n json(res: ServerResponse, data: any) {\n res\n .setHeader('Content-Type', 'application/json')\n .end(Buffer.from(JSON.stringify(data)))\n },\n jsonString(res: ServerResponse, data: string) {\n res.setHeader('Content-Type', 'application/json').end(Buffer.from(data))\n },\n}\n"],"names":["inspect","middlewareResponse","noContent","res","statusCode","end","badRequest","reason","undefined","setHeader","notFound","methodNotAllowed","internalServerError","error","colors","json","data","Buffer","from","JSON","stringify","jsonString"],"mappings":"AACA,SAASA,OAAO,QAAQ,OAAM;AAE9B,OAAO,MAAMC,qBAAqB;IAChCC,WAAUC,GAAmB;QAC3BA,IAAIC,UAAU,GAAG;QACjBD,IAAIE,GAAG,CAAC;IACV;IACAC,YAAWH,GAAmB,EAAEI,MAAe;QAC7CJ,IAAIC,UAAU,GAAG;QACjB,IAAIG,WAAWC,WAAW;YACxBL,IAAIM,SAAS,CAAC,gBAAgB;YAC9BN,IAAIE,GAAG,CAACE;QACV,OAAO;YACLJ,IAAIE,GAAG;QACT;IACF;IACAK,UAASP,GAAmB;QAC1BA,IAAIC,UAAU,GAAG;QACjBD,IAAIE,GAAG,CAAC;IACV;IACAM,kBAAiBR,GAAmB;QAClCA,IAAIC,UAAU,GAAG;QACjBD,IAAIE,GAAG,CAAC;IACV;IACAO,qBAAoBT,GAAmB,EAAEU,KAAe;QACtDV,IAAIC,UAAU,GAAG;QACjBD,IAAIM,SAAS,CAAC,gBAAgB;QAC9BN,IAAIE,GAAG,CACLQ,UAAUL,YACNR,QAAQa,OAAO;YAAEC,QAAQ;QAAM,KAC/B;IAER;IACAC,MAAKZ,GAAmB,EAAEa,IAAS;QACjCb,IACGM,SAAS,CAAC,gBAAgB,oBAC1BJ,GAAG,CAACY,OAAOC,IAAI,CAACC,KAAKC,SAAS,CAACJ;IACpC;IACAK,YAAWlB,GAAmB,EAAEa,IAAY;QAC1Cb,IAAIM,SAAS,CAAC,gBAAgB,oBAAoBJ,GAAG,CAACY,OAAOC,IAAI,CAACF;IACpE;AACF,EAAC","ignoreList":[0]}

View File

@@ -0,0 +1,62 @@
import { RESTART_EXIT_CODE } from '../../server/lib/utils';
import { middlewareResponse } from './middleware-response';
import { invalidateFileSystemCache as invalidateWebpackFileSystemCache } from '../../build/webpack/cache-invalidation';
const EVENT_DEV_OVERLAY_RESTART_SERVER = 'DEV_OVERLAY_RESTART_SERVER';
export function getRestartDevServerMiddleware({ telemetry, turbopackProject, webpackCacheDirectories }) {
/**
* Some random value between 1 and Number.MAX_SAFE_INTEGER (inclusive). The same value is returned
* on every call to `__nextjs_server_status` until the server is restarted.
*
* Can be used to determine if two server status responses are from the same process or a
* different (restarted) process.
*/ const executionId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + 1;
async function handleRestartRequest(req, res, searchParams) {
if (req.method !== 'POST') {
return middlewareResponse.methodNotAllowed(res);
}
const shouldInvalidateFileSystemCache = searchParams.has('invalidateFileSystemCache');
if (shouldInvalidateFileSystemCache) {
if (webpackCacheDirectories != null) {
await Promise.all(Array.from(webpackCacheDirectories).map(invalidateWebpackFileSystemCache));
}
if (turbopackProject != null) {
await turbopackProject.invalidateFileSystemCache();
}
}
telemetry.record({
eventName: EVENT_DEV_OVERLAY_RESTART_SERVER,
payload: {
invalidateFileSystemCache: shouldInvalidateFileSystemCache
}
});
// TODO: Use flushDetached
await telemetry.flush();
// do this async to try to give the response a chance to send
// it's not really important if it doesn't though
setTimeout(()=>{
process.exit(RESTART_EXIT_CODE);
}, 0);
return middlewareResponse.noContent(res);
}
async function handleServerStatus(req, res) {
if (req.method !== 'GET') {
return middlewareResponse.methodNotAllowed(res);
}
return middlewareResponse.json(res, {
executionId
});
}
return async function(req, res, next) {
const { pathname, searchParams } = new URL(`http://n${req.url}`);
switch(pathname){
case '/__nextjs_restart_dev':
return await handleRestartRequest(req, res, searchParams);
case '/__nextjs_server_status':
return await handleServerStatus(req, res);
default:
return next();
}
};
}
//# sourceMappingURL=restart-dev-server-middleware.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,35 @@
import { codeFrameColumns } from 'next/dist/compiled/babel/code-frame';
import isInternal from '../../shared/lib/is-internal';
import { ignoreListAnonymousStackFramesIfSandwiched as ignoreListAnonymousStackFramesIfSandwichedGeneric } from '../../server/lib/source-maps';
export function ignoreListAnonymousStackFramesIfSandwiched(responses) {
ignoreListAnonymousStackFramesIfSandwichedGeneric(responses, (response)=>{
return response.status === 'fulfilled' && response.value.originalStackFrame !== null && response.value.originalStackFrame.file === '<anonymous>';
}, (response)=>{
return response.status === 'fulfilled' && response.value.originalStackFrame !== null && response.value.originalStackFrame.ignored === true;
}, (response)=>{
return response.status === 'fulfilled' && response.value.originalStackFrame !== null ? response.value.originalStackFrame.methodName : '';
}, (response)=>{
;
response.value.originalStackFrame.ignored = true;
});
}
/**
* It looks up the code frame of the traced source.
* @note It ignores Next.js/React internals, as these can often be huge bundled files.
*/ export function getOriginalCodeFrame(frame, source, colors = process.stdout.isTTY) {
if (!source || isInternal(frame.file)) {
return null;
}
return codeFrameColumns(source, {
start: {
// 1-based, but -1 means start line without highlighting
line: frame.line1 ?? -1,
// 1-based, but 0 means whole line without column highlighting
column: frame.column1 ?? 0
}
}, {
forceColor: colors
});
}
//# sourceMappingURL=shared.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/next-devtools/server/shared.ts"],"sourcesContent":["import { codeFrameColumns } from 'next/dist/compiled/babel/code-frame'\nimport isInternal from '../../shared/lib/is-internal'\nimport type { StackFrame } from '../../server/lib/parse-stack'\nimport { ignoreListAnonymousStackFramesIfSandwiched as ignoreListAnonymousStackFramesIfSandwichedGeneric } from '../../server/lib/source-maps'\n\nexport type { StackFrame }\n\nexport interface IgnorableStackFrame extends StackFrame {\n ignored: boolean\n}\n\nexport interface OriginalStackFramesRequest {\n frames: readonly StackFrame[]\n isServer: boolean\n isEdgeServer: boolean\n isAppDirectory: boolean\n}\n\nexport type OriginalStackFramesResponse = OriginalStackFrameResponseResult[]\n\nexport type OriginalStackFrameResponseResult =\n PromiseSettledResult<OriginalStackFrameResponse>\n\nexport interface OriginalStackFrameResponse {\n originalStackFrame: (StackFrame & { ignored: boolean }) | null\n originalCodeFrame: string | null\n}\n\nexport function ignoreListAnonymousStackFramesIfSandwiched(\n responses: OriginalStackFramesResponse\n): void {\n ignoreListAnonymousStackFramesIfSandwichedGeneric(\n responses,\n (response) => {\n return (\n response.status === 'fulfilled' &&\n response.value.originalStackFrame !== null &&\n response.value.originalStackFrame.file === '<anonymous>'\n )\n },\n (response) => {\n return (\n response.status === 'fulfilled' &&\n response.value.originalStackFrame !== null &&\n response.value.originalStackFrame.ignored === true\n )\n },\n (response) => {\n return response.status === 'fulfilled' &&\n response.value.originalStackFrame !== null\n ? response.value.originalStackFrame.methodName\n : ''\n },\n (response) => {\n ;(\n response as PromiseFulfilledResult<OriginalStackFrameResponse>\n ).value.originalStackFrame!.ignored = true\n }\n )\n}\n\n/**\n * It looks up the code frame of the traced source.\n * @note It ignores Next.js/React internals, as these can often be huge bundled files.\n */\nexport function getOriginalCodeFrame(\n frame: IgnorableStackFrame,\n source: string | null,\n colors: boolean = process.stdout.isTTY\n): string | null {\n if (!source || isInternal(frame.file)) {\n return null\n }\n\n return codeFrameColumns(\n source,\n {\n start: {\n // 1-based, but -1 means start line without highlighting\n line: frame.line1 ?? -1,\n // 1-based, but 0 means whole line without column highlighting\n column: frame.column1 ?? 0,\n },\n },\n { forceColor: colors }\n )\n}\n"],"names":["codeFrameColumns","isInternal","ignoreListAnonymousStackFramesIfSandwiched","ignoreListAnonymousStackFramesIfSandwichedGeneric","responses","response","status","value","originalStackFrame","file","ignored","methodName","getOriginalCodeFrame","frame","source","colors","process","stdout","isTTY","start","line","line1","column","column1","forceColor"],"mappings":"AAAA,SAASA,gBAAgB,QAAQ,sCAAqC;AACtE,OAAOC,gBAAgB,+BAA8B;AAErD,SAASC,8CAA8CC,iDAAiD,QAAQ,+BAA8B;AAyB9I,OAAO,SAASD,2CACdE,SAAsC;IAEtCD,kDACEC,WACA,CAACC;QACC,OACEA,SAASC,MAAM,KAAK,eACpBD,SAASE,KAAK,CAACC,kBAAkB,KAAK,QACtCH,SAASE,KAAK,CAACC,kBAAkB,CAACC,IAAI,KAAK;IAE/C,GACA,CAACJ;QACC,OACEA,SAASC,MAAM,KAAK,eACpBD,SAASE,KAAK,CAACC,kBAAkB,KAAK,QACtCH,SAASE,KAAK,CAACC,kBAAkB,CAACE,OAAO,KAAK;IAElD,GACA,CAACL;QACC,OAAOA,SAASC,MAAM,KAAK,eACzBD,SAASE,KAAK,CAACC,kBAAkB,KAAK,OACpCH,SAASE,KAAK,CAACC,kBAAkB,CAACG,UAAU,GAC5C;IACN,GACA,CAACN;;QAEGA,SACAE,KAAK,CAACC,kBAAkB,CAAEE,OAAO,GAAG;IACxC;AAEJ;AAEA;;;CAGC,GACD,OAAO,SAASE,qBACdC,KAA0B,EAC1BC,MAAqB,EACrBC,SAAkBC,QAAQC,MAAM,CAACC,KAAK;IAEtC,IAAI,CAACJ,UAAUb,WAAWY,MAAMJ,IAAI,GAAG;QACrC,OAAO;IACT;IAEA,OAAOT,iBACLc,QACA;QACEK,OAAO;YACL,wDAAwD;YACxDC,MAAMP,MAAMQ,KAAK,IAAI,CAAC;YACtB,8DAA8D;YAC9DC,QAAQT,MAAMU,OAAO,IAAI;QAC3B;IACF,GACA;QAAEC,YAAYT;IAAO;AAEzB","ignoreList":[0]}