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,20 @@
import { PureComponent } from 'react';
import type { GlobalErrorState } from '../../../client/components/app-router-instance';
type AppDevOverlayErrorBoundaryProps = {
children: React.ReactNode;
globalError: GlobalErrorState;
};
type AppDevOverlayErrorBoundaryState = {
reactError: unknown;
};
export declare class AppDevOverlayErrorBoundary extends PureComponent<AppDevOverlayErrorBoundaryProps, AppDevOverlayErrorBoundaryState> {
state: {
reactError: null;
};
static getDerivedStateFromError(error: Error): {
reactError: Error;
};
componentDidCatch(err: Error): void;
render(): string | number | bigint | boolean | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | Iterable<import("react").ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
}
export {};

View File

@@ -0,0 +1,73 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "AppDevOverlayErrorBoundary", {
enumerable: true,
get: function() {
return AppDevOverlayErrorBoundary;
}
});
const _interop_require_default = require("@swc/helpers/_/_interop_require_default");
const _jsxruntime = require("react/jsx-runtime");
const _react = require("react");
const _nextdevtools = require("next/dist/compiled/next-devtools");
const _runtimeerrorhandler = require("../../../client/dev/runtime-error-handler");
const _errorboundary = require("../../../client/components/error-boundary");
const _globalerror = /*#__PURE__*/ _interop_require_default._(require("../../../client/components/builtin/global-error"));
const _segmentexplorernode = require("./segment-explorer-node");
function ErroredHtml({ globalError: [GlobalError, globalErrorStyles], error }) {
if (!error) {
return /*#__PURE__*/ (0, _jsxruntime.jsxs)("html", {
children: [
/*#__PURE__*/ (0, _jsxruntime.jsx)("head", {}),
/*#__PURE__*/ (0, _jsxruntime.jsx)("body", {})
]
});
}
return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_errorboundary.ErrorBoundary, {
errorComponent: _globalerror.default,
children: [
globalErrorStyles,
/*#__PURE__*/ (0, _jsxruntime.jsx)(GlobalError, {
error: error
})
]
});
}
class AppDevOverlayErrorBoundary extends _react.PureComponent {
static getDerivedStateFromError(error) {
_runtimeerrorhandler.RuntimeErrorHandler.hadRuntimeError = true;
return {
reactError: error
};
}
componentDidCatch(err) {
if (process.env.NODE_ENV === 'development' && err.message === _segmentexplorernode.SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE) {
return;
}
_nextdevtools.dispatcher.openErrorOverlay();
}
render() {
const { children, globalError } = this.props;
const { reactError } = this.state;
const fallback = /*#__PURE__*/ (0, _jsxruntime.jsx)(ErroredHtml, {
globalError: globalError,
error: reactError
});
return reactError !== null ? fallback : children;
}
constructor(...args){
super(...args), this.state = {
reactError: null
};
}
}
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}
//# sourceMappingURL=app-dev-overlay-error-boundary.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/next-devtools/userspace/app/app-dev-overlay-error-boundary.tsx"],"sourcesContent":["import { PureComponent } from 'react'\nimport { dispatcher } from 'next/dist/compiled/next-devtools'\nimport { RuntimeErrorHandler } from '../../../client/dev/runtime-error-handler'\nimport { ErrorBoundary } from '../../../client/components/error-boundary'\nimport DefaultGlobalError from '../../../client/components/builtin/global-error'\nimport type { GlobalErrorState } from '../../../client/components/app-router-instance'\nimport { SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE } from './segment-explorer-node'\n\ntype AppDevOverlayErrorBoundaryProps = {\n children: React.ReactNode\n globalError: GlobalErrorState\n}\n\ntype AppDevOverlayErrorBoundaryState = {\n reactError: unknown\n}\n\nfunction ErroredHtml({\n globalError: [GlobalError, globalErrorStyles],\n error,\n}: {\n globalError: GlobalErrorState\n error: unknown\n}) {\n if (!error) {\n return (\n <html>\n <head />\n <body />\n </html>\n )\n }\n return (\n <ErrorBoundary errorComponent={DefaultGlobalError}>\n {globalErrorStyles}\n <GlobalError error={error} />\n </ErrorBoundary>\n )\n}\n\nexport class AppDevOverlayErrorBoundary extends PureComponent<\n AppDevOverlayErrorBoundaryProps,\n AppDevOverlayErrorBoundaryState\n> {\n state = { reactError: null }\n\n static getDerivedStateFromError(error: Error) {\n RuntimeErrorHandler.hadRuntimeError = true\n\n return {\n reactError: error,\n }\n }\n\n componentDidCatch(err: Error) {\n if (\n process.env.NODE_ENV === 'development' &&\n err.message === SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE\n ) {\n return\n }\n dispatcher.openErrorOverlay()\n }\n\n render() {\n const { children, globalError } = this.props\n const { reactError } = this.state\n\n const fallback = (\n <ErroredHtml globalError={globalError} error={reactError} />\n )\n\n return reactError !== null ? fallback : children\n }\n}\n"],"names":["AppDevOverlayErrorBoundary","ErroredHtml","globalError","GlobalError","globalErrorStyles","error","html","head","body","ErrorBoundary","errorComponent","DefaultGlobalError","PureComponent","getDerivedStateFromError","RuntimeErrorHandler","hadRuntimeError","reactError","componentDidCatch","err","process","env","NODE_ENV","message","SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE","dispatcher","openErrorOverlay","render","children","props","state","fallback"],"mappings":";;;;+BAwCaA;;;eAAAA;;;;;uBAxCiB;8BACH;qCACS;+BACN;sEACC;qCAE0B;AAWzD,SAASC,YAAY,EACnBC,aAAa,CAACC,aAAaC,kBAAkB,EAC7CC,KAAK,EAIN;IACC,IAAI,CAACA,OAAO;QACV,qBACE,sBAACC;;8BACC,qBAACC;8BACD,qBAACC;;;IAGP;IACA,qBACE,sBAACC,4BAAa;QAACC,gBAAgBC,oBAAkB;;YAC9CP;0BACD,qBAACD;gBAAYE,OAAOA;;;;AAG1B;AAEO,MAAML,mCAAmCY,oBAAa;IAM3D,OAAOC,yBAAyBR,KAAY,EAAE;QAC5CS,wCAAmB,CAACC,eAAe,GAAG;QAEtC,OAAO;YACLC,YAAYX;QACd;IACF;IAEAY,kBAAkBC,GAAU,EAAE;QAC5B,IACEC,QAAQC,GAAG,CAACC,QAAQ,KAAK,iBACzBH,IAAII,OAAO,KAAKC,6DAAwC,EACxD;YACA;QACF;QACAC,wBAAU,CAACC,gBAAgB;IAC7B;IAEAC,SAAS;QACP,MAAM,EAAEC,QAAQ,EAAEzB,WAAW,EAAE,GAAG,IAAI,CAAC0B,KAAK;QAC5C,MAAM,EAAEZ,UAAU,EAAE,GAAG,IAAI,CAACa,KAAK;QAEjC,MAAMC,yBACJ,qBAAC7B;YAAYC,aAAaA;YAAaG,OAAOW;;QAGhD,OAAOA,eAAe,OAAOc,WAAWH;IAC1C;;QAjCK,qBAILE,QAAQ;YAAEb,YAAY;QAAK;;AA8B7B","ignoreList":[0]}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const _interceptconsoleerror = require("./errors/intercept-console-error");
const _useerrorhandler = require("./errors/use-error-handler");
const _forwardlogs = require("./forward-logs");
(0, _useerrorhandler.handleGlobalErrors)();
(0, _interceptconsoleerror.patchConsoleError)();
(0, _forwardlogs.initializeDebugLogForwarding)('app');
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}
//# sourceMappingURL=app-dev-overlay-setup.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/next-devtools/userspace/app/app-dev-overlay-setup.ts"],"sourcesContent":["import { patchConsoleError } from './errors/intercept-console-error'\nimport { handleGlobalErrors } from './errors/use-error-handler'\nimport { initializeDebugLogForwarding } from './forward-logs'\n\nhandleGlobalErrors()\npatchConsoleError()\n\ninitializeDebugLogForwarding('app')\n"],"names":["handleGlobalErrors","patchConsoleError","initializeDebugLogForwarding"],"mappings":";;;;uCAAkC;iCACC;6BACU;AAE7CA,IAAAA,mCAAkB;AAClBC,IAAAA,wCAAiB;AAEjBC,IAAAA,yCAA4B,EAAC","ignoreList":[0]}

View File

@@ -0,0 +1,4 @@
import React from 'react';
export declare function RootLevelDevOverlayElement({ children, }: {
children: React.ReactNode;
}): import("react/jsx-runtime").JSX.Element;

View File

@@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "RootLevelDevOverlayElement", {
enumerable: true,
get: function() {
return RootLevelDevOverlayElement;
}
});
const _interop_require_default = require("@swc/helpers/_/_interop_require_default");
const _jsxruntime = require("react/jsx-runtime");
const _react = /*#__PURE__*/ _interop_require_default._(require("react"));
const _globalerror = /*#__PURE__*/ _interop_require_default._(require("../../../client/components/builtin/global-error"));
const _appdevoverlayerrorboundary = require("./app-dev-overlay-error-boundary");
function RootLevelDevOverlayElement({ children }) {
return /*#__PURE__*/ (0, _jsxruntime.jsx)(_appdevoverlayerrorboundary.AppDevOverlayErrorBoundary, {
globalError: [
_globalerror.default,
null
],
children: children
});
}
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}
//# sourceMappingURL=client-entry.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/next-devtools/userspace/app/client-entry.tsx"],"sourcesContent":["import React from 'react'\nimport DefaultGlobalError from '../../../client/components/builtin/global-error'\nimport { AppDevOverlayErrorBoundary } from './app-dev-overlay-error-boundary'\n\n// If an error is thrown while rendering an RSC stream, this will catch it in\n// dev and show the error overlay.\nexport function RootLevelDevOverlayElement({\n children,\n}: {\n children: React.ReactNode\n}) {\n return (\n <AppDevOverlayErrorBoundary globalError={[DefaultGlobalError, null]}>\n {children}\n </AppDevOverlayErrorBoundary>\n )\n}\n"],"names":["RootLevelDevOverlayElement","children","AppDevOverlayErrorBoundary","globalError","DefaultGlobalError"],"mappings":";;;;+BAMgBA;;;eAAAA;;;;;gEANE;sEACa;4CACY;AAIpC,SAASA,2BAA2B,EACzCC,QAAQ,EAGT;IACC,qBACE,qBAACC,sDAA0B;QAACC,aAAa;YAACC,oBAAkB;YAAE;SAAK;kBAChEH;;AAGP","ignoreList":[0]}

View File

@@ -0,0 +1,3 @@
export { originConsoleError } from './intercept-console-error';
export { handleClientError } from './use-error-handler';
export { decorateDevError } from './stitched-error';

View File

@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
decorateDevError: null,
handleClientError: null,
originConsoleError: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
decorateDevError: function() {
return _stitchederror.decorateDevError;
},
handleClientError: function() {
return _useerrorhandler.handleClientError;
},
originConsoleError: function() {
return _interceptconsoleerror.originConsoleError;
}
});
const _interceptconsoleerror = require("./intercept-console-error");
const _useerrorhandler = require("./use-error-handler");
const _stitchederror = require("./stitched-error");
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/next-devtools/userspace/app/errors/index.ts"],"sourcesContent":["export { originConsoleError } from './intercept-console-error'\nexport { handleClientError } from './use-error-handler'\nexport { decorateDevError } from './stitched-error'\n"],"names":["decorateDevError","handleClientError","originConsoleError"],"mappings":";;;;;;;;;;;;;;;;IAESA,gBAAgB;eAAhBA,+BAAgB;;IADhBC,iBAAiB;eAAjBA,kCAAiB;;IADjBC,kBAAkB;eAAlBA,yCAAkB;;;uCAAQ;iCACD;+BACD","ignoreList":[0]}

View File

@@ -0,0 +1,5 @@
export declare const originConsoleError: {
(...data: any[]): void;
(message?: any, ...optionalParams: any[]): void;
};
export declare function patchConsoleError(): void;

View File

@@ -0,0 +1,68 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
originConsoleError: null,
patchConsoleError: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
originConsoleError: function() {
return originConsoleError;
},
patchConsoleError: function() {
return patchConsoleError;
}
});
const _interop_require_default = require("@swc/helpers/_/_interop_require_default");
const _iserror = /*#__PURE__*/ _interop_require_default._(require("../../../../lib/is-error"));
const _isnextroutererror = require("../../../../client/components/is-next-router-error");
const _useerrorhandler = require("./use-error-handler");
const _console = require("../../../../client/lib/console");
const _forwardlogs = require("../forward-logs");
const originConsoleError = globalThis.console.error;
function patchConsoleError() {
// Ensure it's only patched once
if (typeof window === 'undefined') {
return;
}
window.console.error = function error(...args) {
let maybeError;
if (process.env.NODE_ENV !== 'production') {
const { error: replayedError } = (0, _console.parseConsoleArgs)(args);
if (replayedError) {
maybeError = replayedError;
} else if ((0, _iserror.default)(args[0])) {
maybeError = args[0];
} else {
// See https://github.com/facebook/react/blob/d50323eb845c5fde0d720cae888bf35dedd05506/packages/react-reconciler/src/ReactFiberErrorLogger.js#L78
maybeError = args[1];
}
} else {
maybeError = args[0];
}
if (!(0, _isnextroutererror.isNextRouterError)(maybeError)) {
if (process.env.NODE_ENV !== 'production') {
(0, _useerrorhandler.handleConsoleError)(// replayed errors have their own complex format string that should be used,
// but if we pass the error directly, `handleClientError` will ignore it
maybeError, args);
}
(0, _forwardlogs.forwardErrorLog)(args);
originConsoleError.apply(window.console, args);
}
};
}
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}
//# sourceMappingURL=intercept-console-error.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/next-devtools/userspace/app/errors/intercept-console-error.ts"],"sourcesContent":["import isError from '../../../../lib/is-error'\nimport { isNextRouterError } from '../../../../client/components/is-next-router-error'\nimport { handleConsoleError } from './use-error-handler'\nimport { parseConsoleArgs } from '../../../../client/lib/console'\nimport { forwardErrorLog } from '../forward-logs'\n\nexport const originConsoleError = globalThis.console.error\n\n// Patch console.error to collect information about hydration errors\nexport function patchConsoleError() {\n // Ensure it's only patched once\n if (typeof window === 'undefined') {\n return\n }\n window.console.error = function error(...args: any[]) {\n let maybeError: unknown\n if (process.env.NODE_ENV !== 'production') {\n const { error: replayedError } = parseConsoleArgs(args)\n if (replayedError) {\n maybeError = replayedError\n } else if (isError(args[0])) {\n maybeError = args[0]\n } else {\n // See https://github.com/facebook/react/blob/d50323eb845c5fde0d720cae888bf35dedd05506/packages/react-reconciler/src/ReactFiberErrorLogger.js#L78\n maybeError = args[1]\n }\n } else {\n maybeError = args[0]\n }\n\n if (!isNextRouterError(maybeError)) {\n if (process.env.NODE_ENV !== 'production') {\n handleConsoleError(\n // replayed errors have their own complex format string that should be used,\n // but if we pass the error directly, `handleClientError` will ignore it\n maybeError,\n args\n )\n }\n forwardErrorLog(args)\n\n originConsoleError.apply(window.console, args)\n }\n }\n}\n"],"names":["originConsoleError","patchConsoleError","globalThis","console","error","window","args","maybeError","process","env","NODE_ENV","replayedError","parseConsoleArgs","isError","isNextRouterError","handleConsoleError","forwardErrorLog","apply"],"mappings":";;;;;;;;;;;;;;;IAMaA,kBAAkB;eAAlBA;;IAGGC,iBAAiB;eAAjBA;;;;kEATI;mCACc;iCACC;yBACF;6BACD;AAEzB,MAAMD,qBAAqBE,WAAWC,OAAO,CAACC,KAAK;AAGnD,SAASH;IACd,gCAAgC;IAChC,IAAI,OAAOI,WAAW,aAAa;QACjC;IACF;IACAA,OAAOF,OAAO,CAACC,KAAK,GAAG,SAASA,MAAM,GAAGE,IAAW;QAClD,IAAIC;QACJ,IAAIC,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;YACzC,MAAM,EAAEN,OAAOO,aAAa,EAAE,GAAGC,IAAAA,yBAAgB,EAACN;YAClD,IAAIK,eAAe;gBACjBJ,aAAaI;YACf,OAAO,IAAIE,IAAAA,gBAAO,EAACP,IAAI,CAAC,EAAE,GAAG;gBAC3BC,aAAaD,IAAI,CAAC,EAAE;YACtB,OAAO;gBACL,iJAAiJ;gBACjJC,aAAaD,IAAI,CAAC,EAAE;YACtB;QACF,OAAO;YACLC,aAAaD,IAAI,CAAC,EAAE;QACtB;QAEA,IAAI,CAACQ,IAAAA,oCAAiB,EAACP,aAAa;YAClC,IAAIC,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;gBACzCK,IAAAA,mCAAkB,EAChB,4EAA4E;gBAC5E,wEAAwE;gBACxER,YACAD;YAEJ;YACAU,IAAAA,4BAAe,EAACV;YAEhBN,mBAAmBiB,KAAK,CAACZ,OAAOF,OAAO,EAAEG;QAC3C;IACF;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,8 @@
/**
* Needs to be in the same error boundary as the shell.
* If it commits, we know we recovered from an SSR error.
* If it doesn't commit, we errored again and React will take care of error reporting.
*/
export declare function ReplaySsrOnlyErrors({ onBlockingError, }: {
onBlockingError: () => void;
}): null;

View File

@@ -0,0 +1,72 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "ReplaySsrOnlyErrors", {
enumerable: true,
get: function() {
return ReplaySsrOnlyErrors;
}
});
const _react = require("react");
const _useerrorhandler = require("./use-error-handler");
const _isnextroutererror = require("../../../../client/components/is-next-router-error");
const _constants = require("../../../../shared/lib/errors/constants");
function readSsrError() {
if (typeof document === 'undefined') {
return null;
}
const ssrErrorTemplateTag = document.querySelector('template[data-next-error-message]');
if (ssrErrorTemplateTag) {
const message = ssrErrorTemplateTag.getAttribute('data-next-error-message');
const stack = ssrErrorTemplateTag.getAttribute('data-next-error-stack');
const digest = ssrErrorTemplateTag.getAttribute('data-next-error-digest');
const error = Object.defineProperty(new Error(message), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
if (digest) {
;
error.digest = digest;
}
// Skip Next.js SSR'd internal errors that which will be handled by the error boundaries.
if ((0, _isnextroutererror.isNextRouterError)(error)) {
return null;
}
error.stack = stack || '';
return error;
}
return null;
}
function ReplaySsrOnlyErrors({ onBlockingError }) {
if (process.env.NODE_ENV !== 'production') {
// Need to read during render. The attributes will be gone after commit.
const ssrError = readSsrError();
// eslint-disable-next-line react-hooks/rules-of-hooks
(0, _react.useEffect)(()=>{
if (ssrError !== null) {
// TODO(veil): Include original Owner Stack (NDX-905)
// TODO(veil): Mark as recoverable error
// TODO(veil): console.error
(0, _useerrorhandler.handleClientError)(ssrError);
// If it's missing root tags, we can't recover, make it blocking.
if (ssrError.digest === _constants.MISSING_ROOT_TAGS_ERROR) {
onBlockingError();
}
}
}, [
ssrError,
onBlockingError
]);
}
return null;
}
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}
//# sourceMappingURL=replay-ssr-only-errors.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/next-devtools/userspace/app/errors/replay-ssr-only-errors.tsx"],"sourcesContent":["import { useEffect } from 'react'\nimport { handleClientError } from './use-error-handler'\nimport { isNextRouterError } from '../../../../client/components/is-next-router-error'\nimport { MISSING_ROOT_TAGS_ERROR } from '../../../../shared/lib/errors/constants'\n\nfunction readSsrError(): (Error & { digest?: string }) | null {\n if (typeof document === 'undefined') {\n return null\n }\n\n const ssrErrorTemplateTag = document.querySelector(\n 'template[data-next-error-message]'\n )\n if (ssrErrorTemplateTag) {\n const message: string = ssrErrorTemplateTag.getAttribute(\n 'data-next-error-message'\n )!\n const stack = ssrErrorTemplateTag.getAttribute('data-next-error-stack')\n const digest = ssrErrorTemplateTag.getAttribute('data-next-error-digest')\n const error = new Error(message)\n if (digest) {\n ;(error as any).digest = digest\n }\n // Skip Next.js SSR'd internal errors that which will be handled by the error boundaries.\n if (isNextRouterError(error)) {\n return null\n }\n error.stack = stack || ''\n return error\n }\n\n return null\n}\n\n/**\n * Needs to be in the same error boundary as the shell.\n * If it commits, we know we recovered from an SSR error.\n * If it doesn't commit, we errored again and React will take care of error reporting.\n */\nexport function ReplaySsrOnlyErrors({\n onBlockingError,\n}: {\n onBlockingError: () => void\n}) {\n if (process.env.NODE_ENV !== 'production') {\n // Need to read during render. The attributes will be gone after commit.\n const ssrError = readSsrError()\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useEffect(() => {\n if (ssrError !== null) {\n // TODO(veil): Include original Owner Stack (NDX-905)\n // TODO(veil): Mark as recoverable error\n // TODO(veil): console.error\n handleClientError(ssrError)\n\n // If it's missing root tags, we can't recover, make it blocking.\n if (ssrError.digest === MISSING_ROOT_TAGS_ERROR) {\n onBlockingError()\n }\n }\n }, [ssrError, onBlockingError])\n }\n\n return null\n}\n"],"names":["ReplaySsrOnlyErrors","readSsrError","document","ssrErrorTemplateTag","querySelector","message","getAttribute","stack","digest","error","Error","isNextRouterError","onBlockingError","process","env","NODE_ENV","ssrError","useEffect","handleClientError","MISSING_ROOT_TAGS_ERROR"],"mappings":";;;;+BAuCgBA;;;eAAAA;;;uBAvCU;iCACQ;mCACA;2BACM;AAExC,SAASC;IACP,IAAI,OAAOC,aAAa,aAAa;QACnC,OAAO;IACT;IAEA,MAAMC,sBAAsBD,SAASE,aAAa,CAChD;IAEF,IAAID,qBAAqB;QACvB,MAAME,UAAkBF,oBAAoBG,YAAY,CACtD;QAEF,MAAMC,QAAQJ,oBAAoBG,YAAY,CAAC;QAC/C,MAAME,SAASL,oBAAoBG,YAAY,CAAC;QAChD,MAAMG,QAAQ,qBAAkB,CAAlB,IAAIC,MAAML,UAAV,qBAAA;mBAAA;wBAAA;0BAAA;QAAiB;QAC/B,IAAIG,QAAQ;;YACRC,MAAcD,MAAM,GAAGA;QAC3B;QACA,yFAAyF;QACzF,IAAIG,IAAAA,oCAAiB,EAACF,QAAQ;YAC5B,OAAO;QACT;QACAA,MAAMF,KAAK,GAAGA,SAAS;QACvB,OAAOE;IACT;IAEA,OAAO;AACT;AAOO,SAAST,oBAAoB,EAClCY,eAAe,EAGhB;IACC,IAAIC,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;QACzC,wEAAwE;QACxE,MAAMC,WAAWf;QACjB,sDAAsD;QACtDgB,IAAAA,gBAAS,EAAC;YACR,IAAID,aAAa,MAAM;gBACrB,qDAAqD;gBACrD,wCAAwC;gBACxC,4BAA4B;gBAC5BE,IAAAA,kCAAiB,EAACF;gBAElB,iEAAiE;gBACjE,IAAIA,SAASR,MAAM,KAAKW,kCAAuB,EAAE;oBAC/CP;gBACF;YACF;QACF,GAAG;YAACI;YAAUJ;SAAgB;IAChC;IAEA,OAAO;AACT","ignoreList":[0]}

View File

@@ -0,0 +1,5 @@
export declare function getOwnerStack(error: Error): string | null | undefined;
export declare function setOwnerStack(error: Error, stack: string | null): void;
export declare function coerceError(value: unknown): Error;
export declare function setOwnerStackIfAvailable(error: Error): void;
export declare function decorateDevError(thrownValue: unknown): Error;

View File

@@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
coerceError: null,
decorateDevError: null,
getOwnerStack: null,
setOwnerStack: null,
setOwnerStackIfAvailable: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
coerceError: function() {
return coerceError;
},
decorateDevError: function() {
return decorateDevError;
},
getOwnerStack: function() {
return getOwnerStack;
},
setOwnerStack: function() {
return setOwnerStack;
},
setOwnerStackIfAvailable: function() {
return setOwnerStackIfAvailable;
}
});
const _interop_require_default = require("@swc/helpers/_/_interop_require_default");
const _react = /*#__PURE__*/ _interop_require_default._(require("react"));
const _iserror = /*#__PURE__*/ _interop_require_default._(require("../../../../lib/is-error"));
const ownerStacks = new WeakMap();
function getOwnerStack(error) {
return ownerStacks.get(error);
}
function setOwnerStack(error, stack) {
ownerStacks.set(error, stack);
}
function coerceError(value) {
return (0, _iserror.default)(value) ? value : Object.defineProperty(new Error('' + value), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
}
function setOwnerStackIfAvailable(error) {
// React 18 and prod does not have `captureOwnerStack`
if ('captureOwnerStack' in _react.default) {
setOwnerStack(error, _react.default.captureOwnerStack());
}
}
function decorateDevError(thrownValue) {
const error = coerceError(thrownValue);
setOwnerStackIfAvailable(error);
return error;
}
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}
//# sourceMappingURL=stitched-error.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/next-devtools/userspace/app/errors/stitched-error.ts"],"sourcesContent":["import React from 'react'\nimport isError from '../../../../lib/is-error'\n\nconst ownerStacks = new WeakMap<Error, string | null>()\n\nexport function getOwnerStack(error: Error): string | null | undefined {\n return ownerStacks.get(error)\n}\nexport function setOwnerStack(error: Error, stack: string | null) {\n ownerStacks.set(error, stack)\n}\n\nexport function coerceError(value: unknown): Error {\n return isError(value) ? value : new Error('' + value)\n}\n\nexport function setOwnerStackIfAvailable(error: Error): void {\n // React 18 and prod does not have `captureOwnerStack`\n if ('captureOwnerStack' in React) {\n setOwnerStack(error, React.captureOwnerStack())\n }\n}\n\nexport function decorateDevError(thrownValue: unknown) {\n const error = coerceError(thrownValue)\n setOwnerStackIfAvailable(error)\n return error\n}\n"],"names":["coerceError","decorateDevError","getOwnerStack","setOwnerStack","setOwnerStackIfAvailable","ownerStacks","WeakMap","error","get","stack","set","value","isError","Error","React","captureOwnerStack","thrownValue"],"mappings":";;;;;;;;;;;;;;;;;;IAYgBA,WAAW;eAAXA;;IAWAC,gBAAgB;eAAhBA;;IAlBAC,aAAa;eAAbA;;IAGAC,aAAa;eAAbA;;IAQAC,wBAAwB;eAAxBA;;;;gEAhBE;kEACE;AAEpB,MAAMC,cAAc,IAAIC;AAEjB,SAASJ,cAAcK,KAAY;IACxC,OAAOF,YAAYG,GAAG,CAACD;AACzB;AACO,SAASJ,cAAcI,KAAY,EAAEE,KAAoB;IAC9DJ,YAAYK,GAAG,CAACH,OAAOE;AACzB;AAEO,SAAST,YAAYW,KAAc;IACxC,OAAOC,IAAAA,gBAAO,EAACD,SAASA,QAAQ,qBAAqB,CAArB,IAAIE,MAAM,KAAKF,QAAf,qBAAA;eAAA;oBAAA;sBAAA;IAAoB;AACtD;AAEO,SAASP,yBAAyBG,KAAY;IACnD,sDAAsD;IACtD,IAAI,uBAAuBO,cAAK,EAAE;QAChCX,cAAcI,OAAOO,cAAK,CAACC,iBAAiB;IAC9C;AACF;AAEO,SAASd,iBAAiBe,WAAoB;IACnD,MAAMT,QAAQP,YAAYgB;IAC1BZ,yBAAyBG;IACzB,OAAOA;AACT","ignoreList":[0]}

View File

@@ -0,0 +1,6 @@
type ErrorHandler = (error: Error) => void;
export declare function handleConsoleError(originError: unknown, consoleErrorArgs: any[]): void;
export declare function handleClientError(error: Error): void;
export declare function useErrorHandler(handleOnUnhandledError: ErrorHandler, handleOnUnhandledRejection: ErrorHandler): void;
export declare function handleGlobalErrors(): void;
export {};

View File

@@ -0,0 +1,139 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
handleClientError: null,
handleConsoleError: null,
handleGlobalErrors: null,
useErrorHandler: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
handleClientError: function() {
return handleClientError;
},
handleConsoleError: function() {
return handleConsoleError;
},
handleGlobalErrors: function() {
return handleGlobalErrors;
},
useErrorHandler: function() {
return useErrorHandler;
}
});
const _interop_require_default = require("@swc/helpers/_/_interop_require_default");
const _react = require("react");
const _isnextroutererror = require("../../../../client/components/is-next-router-error");
const _console = require("../../../../client/lib/console");
const _iserror = /*#__PURE__*/ _interop_require_default._(require("../../../../lib/is-error"));
const _consoleerror = require("../../../shared/console-error");
const _stitchederror = require("./stitched-error");
const _forwardlogs = require("../forward-logs");
const queueMicroTask = globalThis.queueMicrotask || ((cb)=>Promise.resolve().then(cb));
const errorQueue = [];
const errorHandlers = [];
const rejectionQueue = [];
const rejectionHandlers = [];
function handleConsoleError(originError, consoleErrorArgs) {
let error;
const { environmentName } = (0, _console.parseConsoleArgs)(consoleErrorArgs);
if ((0, _iserror.default)(originError)) {
error = (0, _consoleerror.createConsoleError)(originError, environmentName);
} else {
error = (0, _consoleerror.createConsoleError)((0, _console.formatConsoleArgs)(consoleErrorArgs), environmentName);
}
(0, _stitchederror.setOwnerStackIfAvailable)(error);
errorQueue.push(error);
for (const handler of errorHandlers){
// Delayed the error being passed to React Dev Overlay,
// avoid the state being synchronously updated in the component.
queueMicroTask(()=>{
handler(error);
});
}
}
function handleClientError(error) {
errorQueue.push(error);
for (const handler of errorHandlers){
// Delayed the error being passed to React Dev Overlay,
// avoid the state being synchronously updated in the component.
queueMicroTask(()=>{
handler(error);
});
}
}
function useErrorHandler(handleOnUnhandledError, handleOnUnhandledRejection) {
(0, _react.useEffect)(()=>{
// Handle queued errors.
errorQueue.forEach(handleOnUnhandledError);
rejectionQueue.forEach(handleOnUnhandledRejection);
// Listen to new errors.
errorHandlers.push(handleOnUnhandledError);
rejectionHandlers.push(handleOnUnhandledRejection);
return ()=>{
// Remove listeners.
errorHandlers.splice(errorHandlers.indexOf(handleOnUnhandledError), 1);
rejectionHandlers.splice(rejectionHandlers.indexOf(handleOnUnhandledRejection), 1);
// Reset error queues.
errorQueue.splice(0, errorQueue.length);
rejectionQueue.splice(0, rejectionQueue.length);
};
}, [
handleOnUnhandledError,
handleOnUnhandledRejection
]);
}
function onUnhandledError(event) {
const thrownValue = event.error;
if ((0, _isnextroutererror.isNextRouterError)(thrownValue)) {
event.preventDefault();
return false;
}
// When there's an error property present, we log the error to error overlay.
// Otherwise we don't do anything as it's not logging in the console either.
if (thrownValue) {
const error = (0, _stitchederror.coerceError)(thrownValue);
(0, _stitchederror.setOwnerStackIfAvailable)(error);
handleClientError(error);
(0, _forwardlogs.forwardUnhandledError)(error);
}
}
function onUnhandledRejection(ev) {
const reason = ev?.reason;
if ((0, _isnextroutererror.isNextRouterError)(reason)) {
ev.preventDefault();
return;
}
const error = (0, _stitchederror.coerceError)(reason);
(0, _stitchederror.setOwnerStackIfAvailable)(error);
rejectionQueue.push(error);
for (const handler of rejectionHandlers){
handler(error);
}
(0, _forwardlogs.logUnhandledRejection)(reason);
}
function handleGlobalErrors() {
if (typeof window !== 'undefined') {
try {
// Increase the number of stack frames on the client
Error.stackTraceLimit = 50;
} catch {}
window.addEventListener('error', onUnhandledError);
window.addEventListener('unhandledrejection', onUnhandledRejection);
}
}
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}
//# sourceMappingURL=use-error-handler.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
export declare const safeStringifyWithDepth: typeof import("safe-stable-stringify").stringify;
/**
* allows us to:
* - revive the undefined log in the server as it would look in the browser
* - not read/attempt to serialize promises (next will console error if you do that, and will cause this program to infinitely recurse)
* - if we read a proxy that throws (no way to detect if something is a proxy), explain to the user we can't read this data
*/
export declare function preLogSerializationClone<T>(value: T, seen?: WeakMap<WeakKey, any>): any;
export declare const logStringify: (data: unknown) => string;

View File

@@ -0,0 +1,95 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
logStringify: null,
preLogSerializationClone: null,
safeStringifyWithDepth: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
logStringify: function() {
return logStringify;
},
preLogSerializationClone: function() {
return preLogSerializationClone;
},
safeStringifyWithDepth: function() {
return safeStringifyWithDepth;
}
});
const _safestablestringify = require("next/dist/compiled/safe-stable-stringify");
const _terminalloggingconfig = require("./terminal-logging-config");
const _forwardlogsshared = require("../../shared/forward-logs-shared");
const terminalLoggingConfig = (0, _terminalloggingconfig.getTerminalLoggingConfig)();
const PROMISE_MARKER = 'Promise {}';
const UNAVAILABLE_MARKER = '[Unable to view]';
const maximumDepth = typeof terminalLoggingConfig === 'object' && terminalLoggingConfig.depthLimit ? terminalLoggingConfig.depthLimit : 5;
const maximumBreadth = typeof terminalLoggingConfig === 'object' && terminalLoggingConfig.edgeLimit ? terminalLoggingConfig.edgeLimit : 100;
const safeStringifyWithDepth = (0, _safestablestringify.configure)({
maximumDepth,
maximumBreadth
});
function preLogSerializationClone(value, seen = new WeakMap()) {
if (value === undefined) return _forwardlogsshared.UNDEFINED_MARKER;
if (value === null || typeof value !== 'object') return value;
if (seen.has(value)) return seen.get(value);
try {
Object.keys(value);
} catch {
return UNAVAILABLE_MARKER;
}
try {
if (typeof value.then === 'function') return PROMISE_MARKER;
} catch {
return UNAVAILABLE_MARKER;
}
if (Array.isArray(value)) {
const out = [];
seen.set(value, out);
for (const item of value){
try {
out.push(preLogSerializationClone(item, seen));
} catch {
out.push(UNAVAILABLE_MARKER);
}
}
return out;
}
const proto = Object.getPrototypeOf(value);
if (proto === Object.prototype || proto === null) {
const out = {};
seen.set(value, out);
for (const key of Object.keys(value)){
try {
out[key] = preLogSerializationClone(value[key], seen);
} catch {
out[key] = UNAVAILABLE_MARKER;
}
}
return out;
}
return Object.prototype.toString.call(value);
}
const logStringify = (data)=>{
try {
const result = safeStringifyWithDepth(data);
return result ?? `"${UNAVAILABLE_MARKER}"`;
} catch {
return `"${UNAVAILABLE_MARKER}"`;
}
};
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}
//# sourceMappingURL=forward-logs-utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/next-devtools/userspace/app/forward-logs-utils.ts"],"sourcesContent":["import { configure } from 'next/dist/compiled/safe-stable-stringify'\nimport { getTerminalLoggingConfig } from './terminal-logging-config'\nimport { UNDEFINED_MARKER } from '../../shared/forward-logs-shared'\n\nconst terminalLoggingConfig = getTerminalLoggingConfig()\n\nconst PROMISE_MARKER = 'Promise {}'\nconst UNAVAILABLE_MARKER = '[Unable to view]'\n\nconst maximumDepth =\n typeof terminalLoggingConfig === 'object' && terminalLoggingConfig.depthLimit\n ? terminalLoggingConfig.depthLimit\n : 5\nconst maximumBreadth =\n typeof terminalLoggingConfig === 'object' && terminalLoggingConfig.edgeLimit\n ? terminalLoggingConfig.edgeLimit\n : 100\n\nexport const safeStringifyWithDepth = configure({\n maximumDepth,\n maximumBreadth,\n})\n\n/**\n * allows us to:\n * - revive the undefined log in the server as it would look in the browser\n * - not read/attempt to serialize promises (next will console error if you do that, and will cause this program to infinitely recurse)\n * - if we read a proxy that throws (no way to detect if something is a proxy), explain to the user we can't read this data\n */\nexport function preLogSerializationClone<T>(\n value: T,\n seen = new WeakMap()\n): any {\n if (value === undefined) return UNDEFINED_MARKER\n if (value === null || typeof value !== 'object') return value\n if (seen.has(value as object)) return seen.get(value as object)\n\n try {\n Object.keys(value as object)\n } catch {\n return UNAVAILABLE_MARKER\n }\n\n try {\n if (typeof (value as any).then === 'function') return PROMISE_MARKER\n } catch {\n return UNAVAILABLE_MARKER\n }\n\n if (Array.isArray(value)) {\n const out: any[] = []\n seen.set(value, out)\n for (const item of value) {\n try {\n out.push(preLogSerializationClone(item, seen))\n } catch {\n out.push(UNAVAILABLE_MARKER)\n }\n }\n return out\n }\n\n const proto = Object.getPrototypeOf(value)\n if (proto === Object.prototype || proto === null) {\n const out: Record<string, unknown> = {}\n seen.set(value as object, out)\n for (const key of Object.keys(value as object)) {\n try {\n out[key] = preLogSerializationClone((value as any)[key], seen)\n } catch {\n out[key] = UNAVAILABLE_MARKER\n }\n }\n return out\n }\n\n return Object.prototype.toString.call(value)\n}\n\n// only safe if passed safeClone data\nexport const logStringify = (data: unknown): string => {\n try {\n const result = safeStringifyWithDepth(data)\n return result ?? `\"${UNAVAILABLE_MARKER}\"`\n } catch {\n return `\"${UNAVAILABLE_MARKER}\"`\n }\n}\n"],"names":["logStringify","preLogSerializationClone","safeStringifyWithDepth","terminalLoggingConfig","getTerminalLoggingConfig","PROMISE_MARKER","UNAVAILABLE_MARKER","maximumDepth","depthLimit","maximumBreadth","edgeLimit","configure","value","seen","WeakMap","undefined","UNDEFINED_MARKER","has","get","Object","keys","then","Array","isArray","out","set","item","push","proto","getPrototypeOf","prototype","key","toString","call","data","result"],"mappings":";;;;;;;;;;;;;;;;IAgFaA,YAAY;eAAZA;;IAnDGC,wBAAwB;eAAxBA;;IAXHC,sBAAsB;eAAtBA;;;qCAlBa;uCACe;mCACR;AAEjC,MAAMC,wBAAwBC,IAAAA,+CAAwB;AAEtD,MAAMC,iBAAiB;AACvB,MAAMC,qBAAqB;AAE3B,MAAMC,eACJ,OAAOJ,0BAA0B,YAAYA,sBAAsBK,UAAU,GACzEL,sBAAsBK,UAAU,GAChC;AACN,MAAMC,iBACJ,OAAON,0BAA0B,YAAYA,sBAAsBO,SAAS,GACxEP,sBAAsBO,SAAS,GAC/B;AAEC,MAAMR,yBAAyBS,IAAAA,8BAAS,EAAC;IAC9CJ;IACAE;AACF;AAQO,SAASR,yBACdW,KAAQ,EACRC,OAAO,IAAIC,SAAS;IAEpB,IAAIF,UAAUG,WAAW,OAAOC,mCAAgB;IAChD,IAAIJ,UAAU,QAAQ,OAAOA,UAAU,UAAU,OAAOA;IACxD,IAAIC,KAAKI,GAAG,CAACL,QAAkB,OAAOC,KAAKK,GAAG,CAACN;IAE/C,IAAI;QACFO,OAAOC,IAAI,CAACR;IACd,EAAE,OAAM;QACN,OAAON;IACT;IAEA,IAAI;QACF,IAAI,OAAO,AAACM,MAAcS,IAAI,KAAK,YAAY,OAAOhB;IACxD,EAAE,OAAM;QACN,OAAOC;IACT;IAEA,IAAIgB,MAAMC,OAAO,CAACX,QAAQ;QACxB,MAAMY,MAAa,EAAE;QACrBX,KAAKY,GAAG,CAACb,OAAOY;QAChB,KAAK,MAAME,QAAQd,MAAO;YACxB,IAAI;gBACFY,IAAIG,IAAI,CAAC1B,yBAAyByB,MAAMb;YAC1C,EAAE,OAAM;gBACNW,IAAIG,IAAI,CAACrB;YACX;QACF;QACA,OAAOkB;IACT;IAEA,MAAMI,QAAQT,OAAOU,cAAc,CAACjB;IACpC,IAAIgB,UAAUT,OAAOW,SAAS,IAAIF,UAAU,MAAM;QAChD,MAAMJ,MAA+B,CAAC;QACtCX,KAAKY,GAAG,CAACb,OAAiBY;QAC1B,KAAK,MAAMO,OAAOZ,OAAOC,IAAI,CAACR,OAAkB;YAC9C,IAAI;gBACFY,GAAG,CAACO,IAAI,GAAG9B,yBAAyB,AAACW,KAAa,CAACmB,IAAI,EAAElB;YAC3D,EAAE,OAAM;gBACNW,GAAG,CAACO,IAAI,GAAGzB;YACb;QACF;QACA,OAAOkB;IACT;IAEA,OAAOL,OAAOW,SAAS,CAACE,QAAQ,CAACC,IAAI,CAACrB;AACxC;AAGO,MAAMZ,eAAe,CAACkC;IAC3B,IAAI;QACF,MAAMC,SAASjC,uBAAuBgC;QACtC,OAAOC,UAAU,CAAC,CAAC,EAAE7B,mBAAmB,CAAC,CAAC;IAC5C,EAAE,OAAM;QACN,OAAO,CAAC,CAAC,EAAEA,mBAAmB,CAAC,CAAC;IAClC;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,15 @@
import { type ClientLogEntry } from '../../shared/forward-logs-shared';
export declare const logQueue: {
entries: Array<ClientLogEntry>;
onSocketReady: (socket: WebSocket) => void;
flushScheduled: boolean;
socket: WebSocket | null;
cancelFlush: (() => void) | null;
sourceType?: 'server' | 'edge-server';
router: 'app' | 'pages' | null;
scheduleLogSend: (entry: ClientLogEntry) => void;
};
export declare const forwardErrorLog: (args: any[]) => void;
export declare function logUnhandledRejection(reason: unknown): void;
export declare function forwardUnhandledError(error: Error): void;
export declare const initializeDebugLogForwarding: (router: "app" | "pages") => void;

View File

@@ -0,0 +1,505 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
forwardErrorLog: null,
forwardUnhandledError: null,
initializeDebugLogForwarding: null,
logQueue: null,
logUnhandledRejection: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
forwardErrorLog: function() {
return forwardErrorLog;
},
forwardUnhandledError: function() {
return forwardUnhandledError;
},
initializeDebugLogForwarding: function() {
return initializeDebugLogForwarding;
},
logQueue: function() {
return logQueue;
},
logUnhandledRejection: function() {
return logUnhandledRejection;
}
});
const _stitchederror = require("./errors/stitched-error");
const _errorsource = require("../../../shared/lib/error-source");
const _terminalloggingconfig = require("./terminal-logging-config");
const _forwardlogsshared = require("../../shared/forward-logs-shared");
const _forwardlogsutils = require("./forward-logs-utils");
// Client-side file logger for browser logs
class ClientFileLogger {
formatTimestamp() {
const now = new Date();
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
const seconds = now.getSeconds().toString().padStart(2, '0');
const milliseconds = now.getMilliseconds().toString().padStart(3, '0');
return `${hours}:${minutes}:${seconds}.${milliseconds}`;
}
log(level, args) {
if (isReactServerReplayedLog(args)) {
return;
}
// Format the args into a message string
const message = args.map((arg)=>{
if (typeof arg === 'string') return arg;
if (typeof arg === 'number' || typeof arg === 'boolean') return String(arg);
if (arg === null) return 'null';
if (arg === undefined) return 'undefined';
// Handle DOM nodes - only log the tag name to avoid React proxied elements
if (arg instanceof Element) {
return `<${arg.tagName.toLowerCase()}>`;
}
return (0, _forwardlogsutils.safeStringifyWithDepth)(arg);
}).join(' ');
const logEntry = {
timestamp: this.formatTimestamp(),
level: level.toUpperCase(),
message
};
this.logEntries.push(logEntry);
// Schedule flush when new log is added
scheduleLogFlush();
}
getLogs() {
return [
...this.logEntries
];
}
clear() {
this.logEntries = [];
}
constructor(){
this.logEntries = [];
}
}
const clientFileLogger = new ClientFileLogger();
// Set up flush-based sending of client file logs
let logFlushTimeout = null;
let heartbeatInterval = null;
const scheduleLogFlush = ()=>{
if (logFlushTimeout) {
clearTimeout(logFlushTimeout);
}
logFlushTimeout = setTimeout(()=>{
sendClientFileLogs();
logFlushTimeout = null;
}, 100) // Send after 100ms (much faster with debouncing)
;
};
const cancelLogFlush = ()=>{
if (logFlushTimeout) {
clearTimeout(logFlushTimeout);
logFlushTimeout = null;
}
};
const startHeartbeat = ()=>{
if (heartbeatInterval) return;
heartbeatInterval = setInterval(()=>{
if (logQueue.socket && logQueue.socket.readyState === WebSocket.OPEN) {
try {
// Send a ping to keep the connection alive
logQueue.socket.send(JSON.stringify({
event: 'ping'
}));
} catch (error) {
// Connection might be closed, stop heartbeat
stopHeartbeat();
}
} else {
stopHeartbeat();
}
}, 5000) // Send ping every 5 seconds
;
};
const stopHeartbeat = ()=>{
if (heartbeatInterval) {
clearInterval(heartbeatInterval);
heartbeatInterval = null;
}
};
const isTerminalLoggingEnabled = (0, _terminalloggingconfig.getIsTerminalLoggingEnabled)();
const methods = [
'log',
'info',
'warn',
'debug',
'table',
'assert',
'dir',
'dirxml',
'group',
'groupCollapsed',
'groupEnd',
'trace'
];
const afterThisFrame = (cb)=>{
let timeout;
const rafId = requestAnimationFrame(()=>{
timeout = setTimeout(()=>{
cb();
});
});
return ()=>{
cancelAnimationFrame(rafId);
clearTimeout(timeout);
};
};
let isPatched = false;
const serializeEntries = (entries)=>entries.map((clientEntry)=>{
switch(clientEntry.kind){
case 'any-logged-error':
case 'console':
{
return {
...clientEntry,
args: clientEntry.args.map(stringifyUserArg)
};
}
case 'formatted-error':
{
return clientEntry;
}
default:
{
return null;
}
}
});
// Function to send client file logs to server
const sendClientFileLogs = ()=>{
if (!logQueue.socket || logQueue.socket.readyState !== WebSocket.OPEN) {
return;
}
const logs = clientFileLogger.getLogs();
if (logs.length === 0) {
return;
}
try {
const payload = JSON.stringify({
event: 'client-file-logs',
logs: logs
});
logQueue.socket.send(payload);
} catch (error) {
console.error(error);
} finally{
// Clear logs regardless of send success to prevent memory leaks
clientFileLogger.clear();
}
};
const logQueue = {
entries: [],
flushScheduled: false,
cancelFlush: null,
socket: null,
sourceType: undefined,
router: null,
scheduleLogSend: (entry)=>{
logQueue.entries.push(entry);
if (logQueue.flushScheduled) {
return;
}
// safe to deref and use in setTimeout closure since we cancel on new socket
const socket = logQueue.socket;
if (!socket) {
return;
}
// we probably dont need this
logQueue.flushScheduled = true;
// non blocking log flush, runs at most once per frame
logQueue.cancelFlush = afterThisFrame(()=>{
logQueue.flushScheduled = false;
// just incase
try {
const payload = JSON.stringify({
event: 'browser-logs',
entries: serializeEntries(logQueue.entries),
router: logQueue.router,
// needed for source mapping, we just assign the sourceType from the last error for the whole batch
sourceType: logQueue.sourceType
});
socket.send(payload);
logQueue.entries = [];
logQueue.sourceType = undefined;
// Also send client file logs
sendClientFileLogs();
} catch {
// error (make sure u don't infinite loop)
/* noop */ }
});
},
onSocketReady: (socket)=>{
// When MCP or terminal logging is enabled, we enable the socket connection,
// otherwise it will not proceed.
if (!isTerminalLoggingEnabled && !process.env.__NEXT_MCP_SERVER) {
return;
}
if (socket.readyState !== WebSocket.OPEN) {
// invariant
return;
}
// incase an existing timeout was going to run with a stale socket
logQueue.cancelFlush?.();
logQueue.socket = socket;
// Add socket event listeners to track connection state
socket.addEventListener('close', ()=>{
cancelLogFlush();
stopHeartbeat();
});
// Only send terminal logs if enabled
if (isTerminalLoggingEnabled) {
try {
const payload = JSON.stringify({
event: 'browser-logs',
entries: serializeEntries(logQueue.entries),
router: logQueue.router,
sourceType: logQueue.sourceType
});
socket.send(payload);
logQueue.entries = [];
logQueue.sourceType = undefined;
} catch {
/** noop just incase */ }
}
// Always send client file logs when socket is ready
sendClientFileLogs();
// Start heartbeat to keep connection alive
startHeartbeat();
}
};
const stringifyUserArg = (arg)=>{
if (arg.kind !== 'arg') {
return arg;
}
return {
...arg,
data: (0, _forwardlogsutils.logStringify)(arg.data)
};
};
const createErrorArg = (error)=>{
const stack = stackWithOwners(error);
return {
kind: 'formatted-error-arg',
prefix: error.message ? `${error.name}: ${error.message}` : `${error.name}`,
stack
};
};
const createLogEntry = (level, args)=>{
// Always log to client file logger with args (formatting done inside log method)
clientFileLogger.log(level, args);
// Only forward to terminal if enabled
if (!isTerminalLoggingEnabled) {
return;
}
// do not abstract this, it implicitly relies on which functions call it. forcing the inlined implementation makes you think about callers
// error capture stack trace maybe
const stack = stackWithOwners(new Error());
const stackLines = stack?.split('\n');
const cleanStack = stackLines?.slice(3).join('\n') // this is probably ignored anyways
;
const entry = {
kind: 'console',
consoleMethodStack: cleanStack ?? null,
method: level,
args: args.map((arg)=>{
if (arg instanceof Error) {
return createErrorArg(arg);
}
return {
kind: 'arg',
data: (0, _forwardlogsutils.preLogSerializationClone)(arg)
};
})
};
logQueue.scheduleLogSend(entry);
};
const forwardErrorLog = (args)=>{
// Always log to client file logger with args (formatting done inside log method)
clientFileLogger.log('error', args);
// Only forward to terminal if enabled
if (!isTerminalLoggingEnabled) {
return;
}
const errorObjects = args.filter((arg)=>arg instanceof Error);
const first = errorObjects.at(0);
if (first) {
const source = (0, _errorsource.getErrorSource)(first);
if (source) {
logQueue.sourceType = source;
}
}
/**
* browser shows stack regardless of type of data passed to console.error, so we should do the same
*
* do not abstract this, it implicitly relies on which functions call it. forcing the inlined implementation makes you think about callers
*/ const stack = stackWithOwners(new Error());
const stackLines = stack?.split('\n');
const cleanStack = stackLines?.slice(3).join('\n');
const entry = {
kind: 'any-logged-error',
method: 'error',
consoleErrorStack: cleanStack ?? '',
args: args.map((arg)=>{
if (arg instanceof Error) {
return createErrorArg(arg);
}
return {
kind: 'arg',
data: (0, _forwardlogsutils.preLogSerializationClone)(arg)
};
})
};
logQueue.scheduleLogSend(entry);
};
const createUncaughtErrorEntry = (errorName, errorMessage, fullStack)=>{
const entry = {
kind: 'formatted-error',
prefix: `Uncaught ${errorName}: ${errorMessage}`,
stack: fullStack,
method: 'error'
};
logQueue.scheduleLogSend(entry);
};
const stackWithOwners = (error)=>{
let ownerStack = '';
(0, _stitchederror.setOwnerStackIfAvailable)(error);
ownerStack = (0, _stitchederror.getOwnerStack)(error) || '';
const stack = (error.stack || '') + ownerStack;
return stack;
};
function logUnhandledRejection(reason) {
// Always log to client file logger
const message = reason instanceof Error ? `${reason.name}: ${reason.message}` : JSON.stringify(reason);
clientFileLogger.log('error', [
`unhandledRejection: ${message}`
]);
// Only forward to terminal if enabled
if (!isTerminalLoggingEnabled) {
return;
}
if (reason instanceof Error) {
createUnhandledRejectionErrorEntry(reason, stackWithOwners(reason));
return;
}
createUnhandledRejectionNonErrorEntry(reason);
}
const createUnhandledRejectionErrorEntry = (error, fullStack)=>{
const source = (0, _errorsource.getErrorSource)(error);
if (source) {
logQueue.sourceType = source;
}
const entry = {
kind: 'formatted-error',
prefix: ` unhandledRejection: ${error.name}: ${error.message}`,
stack: fullStack,
method: 'error'
};
logQueue.scheduleLogSend(entry);
};
const createUnhandledRejectionNonErrorEntry = (reason)=>{
const entry = {
kind: 'any-logged-error',
// we can't access the stack since the event is dispatched async and creating an inline error would be meaningless
consoleErrorStack: '',
method: 'error',
args: [
{
kind: 'arg',
data: ` unhandledRejection:`,
isRejectionMessage: true
},
{
kind: 'arg',
data: (0, _forwardlogsutils.preLogSerializationClone)(reason)
}
]
};
logQueue.scheduleLogSend(entry);
};
const isHMR = (args)=>{
const firstArg = args[0];
if (typeof firstArg !== 'string') {
return false;
}
if (firstArg.startsWith('[Fast Refresh]')) {
return true;
}
if (firstArg.startsWith('[HMR]')) {
return true;
}
return false;
};
/**
* Matches the format of logs arguments React replayed from the RSC.
*/ const isReactServerReplayedLog = (args)=>{
if (args.length < 3) {
return false;
}
const [format, styles, label] = args;
if (typeof format !== 'string' || typeof styles !== 'string' || typeof label !== 'string') {
return false;
}
return format.startsWith('%c%s%c') && styles.includes('background:');
};
function forwardUnhandledError(error) {
// Always log to client file logger
clientFileLogger.log('error', [
`uncaughtError: ${error.name}: ${error.message}`
]);
// Only forward to terminal if enabled
if (!isTerminalLoggingEnabled) {
return;
}
createUncaughtErrorEntry(error.name, error.message, stackWithOwners(error));
}
const initializeDebugLogForwarding = (router)=>{
// probably don't need this
if (isPatched) {
return;
}
// TODO(rob): why does this break rendering on server, important to know incase the same bug appears in browser
if (typeof window === 'undefined') {
return;
}
// better to be safe than sorry
try {
methods.forEach((method)=>(0, _forwardlogsshared.patchConsoleMethod)(method, (_, ...args)=>{
if (isHMR(args)) {
return;
}
if (isReactServerReplayedLog(args)) {
return;
}
createLogEntry(method, args);
}));
} catch {}
logQueue.router = router;
isPatched = true;
// Cleanup on page unload
window.addEventListener('beforeunload', ()=>{
cancelLogFlush();
stopHeartbeat();
// Send any remaining logs before page unloads
sendClientFileLogs();
});
};
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}
//# sourceMappingURL=forward-logs.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,25 @@
import type { ReactNode } from 'react';
export type SegmentBoundaryType = 'not-found' | 'error' | 'loading' | 'global-error';
export declare const SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE = "NEXT_DEVTOOLS_SIMULATED_ERROR";
export type SegmentNodeState = {
type: string;
pagePath: string;
boundaryType: string | null;
setBoundaryType: (type: SegmentBoundaryType | null) => void;
};
export declare function SegmentViewStateNode({ page }: {
page: string;
}): null;
export declare function SegmentBoundaryTriggerNode(): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | null;
export declare function SegmentViewNode({ type, pagePath, children, }: {
type: string;
pagePath: string;
children?: ReactNode;
}): React.ReactNode;
export declare function SegmentStateProvider({ children }: {
children: ReactNode;
}): import("react/jsx-runtime").JSX.Element;
export declare function useSegmentState(): {
boundaryType: SegmentBoundaryType | null;
setBoundaryType: (type: SegmentBoundaryType | null) => void;
};

View File

@@ -0,0 +1,156 @@
'use client';
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE: null,
SegmentBoundaryTriggerNode: null,
SegmentStateProvider: null,
SegmentViewNode: null,
SegmentViewStateNode: null,
useSegmentState: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE: function() {
return SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE;
},
SegmentBoundaryTriggerNode: function() {
return SegmentBoundaryTriggerNode;
},
SegmentStateProvider: function() {
return SegmentStateProvider;
},
SegmentViewNode: function() {
return SegmentViewNode;
},
SegmentViewStateNode: function() {
return SegmentViewStateNode;
},
useSegmentState: function() {
return useSegmentState;
}
});
const _jsxruntime = require("react/jsx-runtime");
const _react = require("react");
const _nextdevtools = require("next/dist/compiled/next-devtools");
const _notfound = require("../../../client/components/not-found");
const SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE = 'NEXT_DEVTOOLS_SIMULATED_ERROR';
function SegmentTrieNode({ type, pagePath }) {
const { boundaryType, setBoundaryType } = useSegmentState();
const nodeState = (0, _react.useMemo)(()=>{
return {
type,
pagePath,
boundaryType,
setBoundaryType
};
}, [
type,
pagePath,
boundaryType,
setBoundaryType
]);
// Use `useLayoutEffect` to ensure the state is updated during suspense.
// `useEffect` won't work as the state is preserved during suspense.
(0, _react.useLayoutEffect)(()=>{
_nextdevtools.dispatcher.segmentExplorerNodeAdd(nodeState);
return ()=>{
_nextdevtools.dispatcher.segmentExplorerNodeRemove(nodeState);
};
}, [
nodeState
]);
return null;
}
function NotFoundSegmentNode() {
(0, _notfound.notFound)();
}
function ErrorSegmentNode() {
throw Object.defineProperty(new Error(SEGMENT_EXPLORER_SIMULATED_ERROR_MESSAGE), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
}
const forever = new Promise(()=>{});
function LoadingSegmentNode() {
(0, _react.use)(forever);
return null;
}
function SegmentViewStateNode({ page }) {
(0, _react.useLayoutEffect)(()=>{
_nextdevtools.dispatcher.segmentExplorerUpdateRouteState(page);
return ()=>{
_nextdevtools.dispatcher.segmentExplorerUpdateRouteState('');
};
}, [
page
]);
return null;
}
function SegmentBoundaryTriggerNode() {
const { boundaryType } = useSegmentState();
let segmentNode = null;
if (boundaryType === 'loading') {
segmentNode = /*#__PURE__*/ (0, _jsxruntime.jsx)(LoadingSegmentNode, {});
} else if (boundaryType === 'not-found') {
segmentNode = /*#__PURE__*/ (0, _jsxruntime.jsx)(NotFoundSegmentNode, {});
} else if (boundaryType === 'error') {
segmentNode = /*#__PURE__*/ (0, _jsxruntime.jsx)(ErrorSegmentNode, {});
}
return segmentNode;
}
function SegmentViewNode({ type, pagePath, children }) {
const segmentNode = /*#__PURE__*/ (0, _jsxruntime.jsx)(SegmentTrieNode, {
type: type,
pagePath: pagePath
}, type);
return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
children: [
segmentNode,
children
]
});
}
const SegmentStateContext = /*#__PURE__*/ (0, _react.createContext)({
boundaryType: null,
setBoundaryType: ()=>{}
});
function SegmentStateProvider({ children }) {
const [boundaryType, setBoundaryType] = (0, _react.useState)(null);
const [errorBoundaryKey, setErrorBoundaryKey] = (0, _react.useState)(0);
const reloadBoundary = (0, _react.useCallback)(()=>setErrorBoundaryKey((prev)=>prev + 1), []);
const setBoundaryTypeAndReload = (0, _react.useCallback)((type)=>{
if (type === null) {
reloadBoundary();
}
setBoundaryType(type);
}, [
reloadBoundary
]);
return /*#__PURE__*/ (0, _jsxruntime.jsx)(SegmentStateContext.Provider, {
value: {
boundaryType,
setBoundaryType: setBoundaryTypeAndReload
},
children: children
}, errorBoundaryKey);
}
function useSegmentState() {
return (0, _react.useContext)(SegmentStateContext);
}
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}
//# sourceMappingURL=segment-explorer-node.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
export declare function getTerminalLoggingConfig(): false | boolean | {
depthLimit?: number;
edgeLimit?: number;
showSourceLocation?: boolean;
};
export declare function getIsTerminalLoggingEnabled(): boolean;

View File

@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
getIsTerminalLoggingEnabled: null,
getTerminalLoggingConfig: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
getIsTerminalLoggingEnabled: function() {
return getIsTerminalLoggingEnabled;
},
getTerminalLoggingConfig: function() {
return getTerminalLoggingConfig;
}
});
function getTerminalLoggingConfig() {
try {
return JSON.parse(process.env.__NEXT_BROWSER_DEBUG_INFO_IN_TERMINAL || 'false');
} catch {
return false;
}
}
function getIsTerminalLoggingEnabled() {
const config = getTerminalLoggingConfig();
return Boolean(config);
}
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}
//# sourceMappingURL=terminal-logging-config.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/next-devtools/userspace/app/terminal-logging-config.ts"],"sourcesContent":["export function getTerminalLoggingConfig():\n | false\n | boolean\n | {\n depthLimit?: number\n edgeLimit?: number\n showSourceLocation?: boolean\n } {\n try {\n return JSON.parse(\n process.env.__NEXT_BROWSER_DEBUG_INFO_IN_TERMINAL || 'false'\n )\n } catch {\n return false\n }\n}\n\nexport function getIsTerminalLoggingEnabled(): boolean {\n const config = getTerminalLoggingConfig()\n return Boolean(config)\n}\n"],"names":["getIsTerminalLoggingEnabled","getTerminalLoggingConfig","JSON","parse","process","env","__NEXT_BROWSER_DEBUG_INFO_IN_TERMINAL","config","Boolean"],"mappings":";;;;;;;;;;;;;;;IAiBgBA,2BAA2B;eAA3BA;;IAjBAC,wBAAwB;eAAxBA;;;AAAT,SAASA;IAQd,IAAI;QACF,OAAOC,KAAKC,KAAK,CACfC,QAAQC,GAAG,CAACC,qCAAqC,IAAI;IAEzD,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEO,SAASN;IACd,MAAMO,SAASN;IACf,OAAOO,QAAQD;AACjB","ignoreList":[0]}