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,12 @@
/**
* Starts recording garbage collection events in the process and warn on long
* running GCs. To disable, call `stopObservingGc`.
*/
export declare function startObservingGc(): void;
export declare function stopObservingGc(): void;
/**
* Returns all recorded garbage collection events. This function will only
* return information from when `startObservingGc` was enabled and before
* `stopObservingGc` was called.
*/
export declare function getGcEvents(): PerformanceEntry[];

View File

@@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
getGcEvents: null,
startObservingGc: null,
stopObservingGc: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
getGcEvents: function() {
return getGcEvents;
},
startObservingGc: function() {
return startObservingGc;
},
stopObservingGc: function() {
return stopObservingGc;
}
});
const _perf_hooks = require("perf_hooks");
const _log = require("../../build/output/log");
const _picocolors = require("../picocolors");
const LONG_RUNNING_GC_THRESHOLD_MS = 15;
const gcEvents = [];
const obs = new _perf_hooks.PerformanceObserver((list)=>{
const entry = list.getEntries()[0];
gcEvents.push(entry);
if (entry.duration > LONG_RUNNING_GC_THRESHOLD_MS) {
(0, _log.warn)((0, _picocolors.bold)(`Long running GC detected: ${entry.duration.toFixed(2)}ms`));
}
});
function startObservingGc() {
obs.observe({
entryTypes: [
'gc'
]
});
}
function stopObservingGc() {
obs.disconnect();
}
function getGcEvents() {
return gcEvents;
}
//# sourceMappingURL=gc-observer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/lib/memory/gc-observer.ts"],"sourcesContent":["import { PerformanceObserver } from 'perf_hooks'\nimport { warn } from '../../build/output/log'\nimport { bold } from '../picocolors'\n\nconst LONG_RUNNING_GC_THRESHOLD_MS = 15\n\nconst gcEvents: PerformanceEntry[] = []\nconst obs = new PerformanceObserver((list) => {\n const entry = list.getEntries()[0]\n gcEvents.push(entry)\n\n if (entry.duration > LONG_RUNNING_GC_THRESHOLD_MS) {\n warn(bold(`Long running GC detected: ${entry.duration.toFixed(2)}ms`))\n }\n})\n\n/**\n * Starts recording garbage collection events in the process and warn on long\n * running GCs. To disable, call `stopObservingGc`.\n */\nexport function startObservingGc() {\n obs.observe({ entryTypes: ['gc'] })\n}\n\nexport function stopObservingGc() {\n obs.disconnect()\n}\n\n/**\n * Returns all recorded garbage collection events. This function will only\n * return information from when `startObservingGc` was enabled and before\n * `stopObservingGc` was called.\n */\nexport function getGcEvents() {\n return gcEvents\n}\n"],"names":["getGcEvents","startObservingGc","stopObservingGc","LONG_RUNNING_GC_THRESHOLD_MS","gcEvents","obs","PerformanceObserver","list","entry","getEntries","push","duration","warn","bold","toFixed","observe","entryTypes","disconnect"],"mappings":";;;;;;;;;;;;;;;;IAiCgBA,WAAW;eAAXA;;IAbAC,gBAAgB;eAAhBA;;IAIAC,eAAe;eAAfA;;;4BAxBoB;qBACf;4BACA;AAErB,MAAMC,+BAA+B;AAErC,MAAMC,WAA+B,EAAE;AACvC,MAAMC,MAAM,IAAIC,+BAAmB,CAAC,CAACC;IACnC,MAAMC,QAAQD,KAAKE,UAAU,EAAE,CAAC,EAAE;IAClCL,SAASM,IAAI,CAACF;IAEd,IAAIA,MAAMG,QAAQ,GAAGR,8BAA8B;QACjDS,IAAAA,SAAI,EAACC,IAAAA,gBAAI,EAAC,CAAC,0BAA0B,EAAEL,MAAMG,QAAQ,CAACG,OAAO,CAAC,GAAG,EAAE,CAAC;IACtE;AACF;AAMO,SAASb;IACdI,IAAIU,OAAO,CAAC;QAAEC,YAAY;YAAC;SAAK;IAAC;AACnC;AAEO,SAASd;IACdG,IAAIY,UAAU;AAChB;AAOO,SAASjB;IACd,OAAOI;AACT","ignoreList":[0]}

View File

@@ -0,0 +1 @@
export declare function disableMemoryDebuggingMode(): void;

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "disableMemoryDebuggingMode", {
enumerable: true,
get: function() {
return disableMemoryDebuggingMode;
}
});
const _log = require("../../build/output/log");
const _picocolors = require("../picocolors");
const _gcobserver = require("./gc-observer");
const _trace = require("./trace");
function disableMemoryDebuggingMode() {
(0, _trace.stopPeriodicMemoryUsageTracing)();
(0, _gcobserver.stopObservingGc)();
(0, _log.info)((0, _picocolors.bold)('Memory usage report:'));
const gcEvents = (0, _gcobserver.getGcEvents)();
const totalTimeInGcMs = gcEvents.reduce((acc, event)=>acc + event.duration, 0);
(0, _log.info)(` - Total time spent in GC: ${totalTimeInGcMs.toFixed(2)}ms`);
const allMemoryUsage = (0, _trace.getAllMemoryUsageSpans)();
const peakHeapUsage = Math.max(...allMemoryUsage.map((usage)=>usage['memory.heapUsed']));
const peakRssUsage = Math.max(...allMemoryUsage.map((usage)=>usage['memory.rss']));
(0, _log.info)(` - Peak heap usage: ${(peakHeapUsage / 1024 / 1024).toFixed(2)} MB`);
(0, _log.info)(` - Peak RSS usage: ${(peakRssUsage / 1024 / 1024).toFixed(2)} MB`);
}
//# sourceMappingURL=shutdown.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/lib/memory/shutdown.ts"],"sourcesContent":["import { info } from '../../build/output/log'\nimport { bold } from '../picocolors'\nimport { getGcEvents, stopObservingGc } from './gc-observer'\nimport { getAllMemoryUsageSpans, stopPeriodicMemoryUsageTracing } from './trace'\n\nexport function disableMemoryDebuggingMode(): void {\n stopPeriodicMemoryUsageTracing()\n stopObservingGc()\n\n info(bold('Memory usage report:'))\n\n const gcEvents = getGcEvents()\n const totalTimeInGcMs = gcEvents.reduce(\n (acc, event) => acc + event.duration,\n 0\n )\n info(` - Total time spent in GC: ${totalTimeInGcMs.toFixed(2)}ms`)\n\n const allMemoryUsage = getAllMemoryUsageSpans()\n const peakHeapUsage = Math.max(\n ...allMemoryUsage.map((usage) => usage['memory.heapUsed'])\n )\n const peakRssUsage = Math.max(\n ...allMemoryUsage.map((usage) => usage['memory.rss'])\n )\n info(` - Peak heap usage: ${(peakHeapUsage / 1024 / 1024).toFixed(2)} MB`)\n info(` - Peak RSS usage: ${(peakRssUsage / 1024 / 1024).toFixed(2)} MB`)\n}\n"],"names":["disableMemoryDebuggingMode","stopPeriodicMemoryUsageTracing","stopObservingGc","info","bold","gcEvents","getGcEvents","totalTimeInGcMs","reduce","acc","event","duration","toFixed","allMemoryUsage","getAllMemoryUsageSpans","peakHeapUsage","Math","max","map","usage","peakRssUsage"],"mappings":";;;;+BAKgBA;;;eAAAA;;;qBALK;4BACA;4BACwB;uBAC0B;AAEhE,SAASA;IACdC,IAAAA,qCAA8B;IAC9BC,IAAAA,2BAAe;IAEfC,IAAAA,SAAI,EAACC,IAAAA,gBAAI,EAAC;IAEV,MAAMC,WAAWC,IAAAA,uBAAW;IAC5B,MAAMC,kBAAkBF,SAASG,MAAM,CACrC,CAACC,KAAKC,QAAUD,MAAMC,MAAMC,QAAQ,EACpC;IAEFR,IAAAA,SAAI,EAAC,CAAC,2BAA2B,EAAEI,gBAAgBK,OAAO,CAAC,GAAG,EAAE,CAAC;IAEjE,MAAMC,iBAAiBC,IAAAA,6BAAsB;IAC7C,MAAMC,gBAAgBC,KAAKC,GAAG,IACzBJ,eAAeK,GAAG,CAAC,CAACC,QAAUA,KAAK,CAAC,kBAAkB;IAE3D,MAAMC,eAAeJ,KAAKC,GAAG,IACxBJ,eAAeK,GAAG,CAAC,CAACC,QAAUA,KAAK,CAAC,aAAa;IAEtDhB,IAAAA,SAAI,EAAC,CAAC,oBAAoB,EAAE,AAACY,CAAAA,gBAAgB,OAAO,IAAG,EAAGH,OAAO,CAAC,GAAG,GAAG,CAAC;IACzET,IAAAA,SAAI,EAAC,CAAC,mBAAmB,EAAE,AAACiB,CAAAA,eAAe,OAAO,IAAG,EAAGR,OAAO,CAAC,GAAG,GAAG,CAAC;AACzE","ignoreList":[0]}

View File

@@ -0,0 +1 @@
export declare function enableMemoryDebuggingMode(): void;

View File

@@ -0,0 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "enableMemoryDebuggingMode", {
enumerable: true,
get: function() {
return enableMemoryDebuggingMode;
}
});
const _v8 = /*#__PURE__*/ _interop_require_default(require("v8"));
const _log = require("../../build/output/log");
const _picocolors = require("../picocolors");
const _gcobserver = require("./gc-observer");
const _trace = require("./trace");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function enableMemoryDebuggingMode() {
// This will generate a heap snapshot when the program is close to the
// memory limit. It does not give any warning to the user though which
// can be jarring. If memory is large, this may take a long time.
if ('setHeapSnapshotNearHeapLimit' in _v8.default) {
_v8.default.setHeapSnapshotNearHeapLimit(1);
}
// This flag will kill the process when it starts to GC thrash when it's
// close to the memory limit rather than continuing to try to collect
// memory ineffectively.
_v8.default.setFlagsFromString('--detect-ineffective-gcs-near-heap-limit');
// This allows users to generate a heap snapshot on demand just by sending
// a signal to the process.
process.on('SIGUSR2', ()=>{
(0, _log.warn)(`Received SIGUSR2 signal. Generating heap snapshot. ${(0, _picocolors.italic)('Note: this will take some time.')}`);
_v8.default.writeHeapSnapshot();
});
(0, _gcobserver.startObservingGc)();
(0, _trace.startPeriodicMemoryUsageTracing)();
(0, _log.warn)(`Memory debugging mode is enabled. ${(0, _picocolors.italic)('Note: This will affect performance.')}`);
(0, _log.info)(' - Heap snapshots will be automatically generated when the process reaches more than 70% of the memory limit and again when the process is just about to run out of memory.');
(0, _log.info)(` - To manually generate a heap snapshot, send the process a SIGUSR2 signal: \`kill -SIGUSR2 ${process.pid}\``);
(0, _log.info)(' - Heap snapshots when there is high memory will take a very long time to complete and may be difficult to analyze in tools.');
(0, _log.info)(' - See https://nextjs.org/docs/app/building-your-application/optimizing/memory-usage for more information.');
}
//# sourceMappingURL=startup.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/lib/memory/startup.ts"],"sourcesContent":["import v8 from 'v8'\nimport { info, warn } from '../../build/output/log'\nimport { italic } from '../picocolors'\nimport { startObservingGc } from './gc-observer'\nimport { startPeriodicMemoryUsageTracing } from './trace'\n\nexport function enableMemoryDebuggingMode(): void {\n // This will generate a heap snapshot when the program is close to the\n // memory limit. It does not give any warning to the user though which\n // can be jarring. If memory is large, this may take a long time.\n if ('setHeapSnapshotNearHeapLimit' in v8) {\n v8.setHeapSnapshotNearHeapLimit(1)\n }\n\n // This flag will kill the process when it starts to GC thrash when it's\n // close to the memory limit rather than continuing to try to collect\n // memory ineffectively.\n v8.setFlagsFromString('--detect-ineffective-gcs-near-heap-limit')\n\n // This allows users to generate a heap snapshot on demand just by sending\n // a signal to the process.\n process.on('SIGUSR2', () => {\n warn(\n `Received SIGUSR2 signal. Generating heap snapshot. ${italic(\n 'Note: this will take some time.'\n )}`\n )\n v8.writeHeapSnapshot()\n })\n\n startObservingGc()\n startPeriodicMemoryUsageTracing()\n\n warn(\n `Memory debugging mode is enabled. ${italic(\n 'Note: This will affect performance.'\n )}`\n )\n info(\n ' - Heap snapshots will be automatically generated when the process reaches more than 70% of the memory limit and again when the process is just about to run out of memory.'\n )\n info(\n ` - To manually generate a heap snapshot, send the process a SIGUSR2 signal: \\`kill -SIGUSR2 ${process.pid}\\``\n )\n info(\n ' - Heap snapshots when there is high memory will take a very long time to complete and may be difficult to analyze in tools.'\n )\n info(\n ' - See https://nextjs.org/docs/app/building-your-application/optimizing/memory-usage for more information.'\n )\n}\n"],"names":["enableMemoryDebuggingMode","v8","setHeapSnapshotNearHeapLimit","setFlagsFromString","process","on","warn","italic","writeHeapSnapshot","startObservingGc","startPeriodicMemoryUsageTracing","info","pid"],"mappings":";;;;+BAMgBA;;;eAAAA;;;2DAND;qBACY;4BACJ;4BACU;uBACe;;;;;;AAEzC,SAASA;IACd,sEAAsE;IACtE,sEAAsE;IACtE,iEAAiE;IACjE,IAAI,kCAAkCC,WAAE,EAAE;QACxCA,WAAE,CAACC,4BAA4B,CAAC;IAClC;IAEA,wEAAwE;IACxE,qEAAqE;IACrE,wBAAwB;IACxBD,WAAE,CAACE,kBAAkB,CAAC;IAEtB,0EAA0E;IAC1E,2BAA2B;IAC3BC,QAAQC,EAAE,CAAC,WAAW;QACpBC,IAAAA,SAAI,EACF,CAAC,mDAAmD,EAAEC,IAAAA,kBAAM,EAC1D,oCACC;QAELN,WAAE,CAACO,iBAAiB;IACtB;IAEAC,IAAAA,4BAAgB;IAChBC,IAAAA,sCAA+B;IAE/BJ,IAAAA,SAAI,EACF,CAAC,kCAAkC,EAAEC,IAAAA,kBAAM,EACzC,wCACC;IAELI,IAAAA,SAAI,EACF;IAEFA,IAAAA,SAAI,EACF,CAAC,4FAA4F,EAAEP,QAAQQ,GAAG,CAAC,EAAE,CAAC;IAEhHD,IAAAA,SAAI,EACF;IAEFA,IAAAA,SAAI,EACF;AAEJ","ignoreList":[0]}

View File

@@ -0,0 +1,23 @@
import { type Span } from '../../trace';
interface MemoryUsage {
'memory.rss': number;
'memory.heapUsed': number;
'memory.heapTotal': number;
'memory.heapMax': number;
}
/**
* Begins a timer that will record memory usage periodically to understand
* memory usage across the lifetime of the process.
*/
export declare function startPeriodicMemoryUsageTracing(): void;
export declare function stopPeriodicMemoryUsageTracing(): void;
/**
* Returns the list of all recorded memory usage snapshots from the process.
*/
export declare function getAllMemoryUsageSpans(): MemoryUsage[];
/**
* Records a snapshot of memory usage at this moment in time to the .next/trace
* file.
*/
export declare function traceMemoryUsage(description: string, parentSpan?: Span | undefined): void;
export {};

View File

@@ -0,0 +1,109 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
getAllMemoryUsageSpans: null,
startPeriodicMemoryUsageTracing: null,
stopPeriodicMemoryUsageTracing: null,
traceMemoryUsage: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
getAllMemoryUsageSpans: function() {
return getAllMemoryUsageSpans;
},
startPeriodicMemoryUsageTracing: function() {
return startPeriodicMemoryUsageTracing;
},
stopPeriodicMemoryUsageTracing: function() {
return stopPeriodicMemoryUsageTracing;
},
traceMemoryUsage: function() {
return traceMemoryUsage;
}
});
const _v8 = /*#__PURE__*/ _interop_require_default(require("v8"));
const _log = require("../../build/output/log");
const _trace = require("../../trace");
const _picocolors = require("../picocolors");
const _path = require("path");
const _shared = require("../../trace/shared");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
const HEAP_SNAPSHOT_THRESHOLD_PERCENT = 70;
let alreadyGeneratedHeapSnapshot = false;
const TRACE_MEMORY_USAGE_TIMER_MS = 20000;
let traceMemoryUsageTimer;
const allMemoryUsage = [];
function startPeriodicMemoryUsageTracing() {
traceMemoryUsageTimer = setTimeout(()=>{
traceMemoryUsage('periodic memory snapshot');
startPeriodicMemoryUsageTracing();
}, TRACE_MEMORY_USAGE_TIMER_MS);
}
function stopPeriodicMemoryUsageTracing() {
if (traceMemoryUsageTimer) {
clearTimeout(traceMemoryUsageTimer);
}
}
function getAllMemoryUsageSpans() {
return allMemoryUsage;
}
function traceMemoryUsage(description, parentSpan) {
const memoryUsage = process.memoryUsage();
const v8HeapStatistics = _v8.default.getHeapStatistics();
const heapUsed = v8HeapStatistics.used_heap_size;
const heapMax = v8HeapStatistics.heap_size_limit;
const tracedMemoryUsage = {
'memory.rss': memoryUsage.rss,
'memory.heapUsed': heapUsed,
'memory.heapTotal': memoryUsage.heapTotal,
'memory.heapMax': heapMax
};
allMemoryUsage.push(tracedMemoryUsage);
const tracedMemoryUsageAsStrings = Object.fromEntries(Object.entries(tracedMemoryUsage).map(([key, value])=>[
key,
String(value)
]));
if (parentSpan) {
parentSpan.traceChild('memory-usage', tracedMemoryUsageAsStrings);
} else {
(0, _trace.trace)('memory-usage', undefined, tracedMemoryUsageAsStrings);
}
if (process.env.EXPERIMENTAL_DEBUG_MEMORY_USAGE) {
const percentageHeapUsed = 100 * heapUsed / heapMax;
(0, _log.info)('');
(0, _log.info)('***************************************');
(0, _log.info)(`Memory usage report at "${description}":`);
(0, _log.info)(` - RSS: ${(memoryUsage.rss / 1024 / 1024).toFixed(2)} MB`);
(0, _log.info)(` - Heap Used: ${(heapUsed / 1024 / 1024).toFixed(2)} MB`);
(0, _log.info)(` - Heap Total Allocated: ${(memoryUsage.heapTotal / 1024 / 1024).toFixed(2)} MB`);
(0, _log.info)(` - Heap Max: ${(heapMax / 1024 / 1024).toFixed(2)} MB`);
(0, _log.info)(` - Percentage Heap Used: ${percentageHeapUsed.toFixed(2)}%`);
(0, _log.info)('***************************************');
(0, _log.info)('');
if (percentageHeapUsed > HEAP_SNAPSHOT_THRESHOLD_PERCENT) {
const distDir = _shared.traceGlobals.get('distDir');
const heapFilename = (0, _path.join)(distDir, `${description.replace(' ', '-')}.heapsnapshot`);
(0, _log.warn)((0, _picocolors.bold)(`Heap usage is close to the limit. ${percentageHeapUsed.toFixed(2)}% of heap has been used.`));
if (!alreadyGeneratedHeapSnapshot) {
(0, _log.warn)((0, _picocolors.bold)(`Saving heap snapshot to ${heapFilename}. ${(0, _picocolors.italic)('Note: this will take some time.')}`));
_v8.default.writeHeapSnapshot(heapFilename);
alreadyGeneratedHeapSnapshot = true;
} else {
(0, _log.warn)('Skipping heap snapshot generation since heap snapshot has already been generated.');
}
}
}
}
//# sourceMappingURL=trace.js.map

File diff suppressed because one or more lines are too long