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,100 @@
/**
* Contains predefined constants for the trace span name in next/server.
*
* Currently, next/server/tracer is internal implementation only for tracking
* next.js's implementation only with known span names defined here.
**/
declare enum BaseServerSpan {
handleRequest = "BaseServer.handleRequest",
run = "BaseServer.run",
pipe = "BaseServer.pipe",
getStaticHTML = "BaseServer.getStaticHTML",
render = "BaseServer.render",
renderToResponseWithComponents = "BaseServer.renderToResponseWithComponents",
renderToResponse = "BaseServer.renderToResponse",
renderToHTML = "BaseServer.renderToHTML",
renderError = "BaseServer.renderError",
renderErrorToResponse = "BaseServer.renderErrorToResponse",
renderErrorToHTML = "BaseServer.renderErrorToHTML",
render404 = "BaseServer.render404"
}
declare enum LoadComponentsSpan {
loadDefaultErrorComponents = "LoadComponents.loadDefaultErrorComponents",
loadComponents = "LoadComponents.loadComponents"
}
declare enum NextServerSpan {
getRequestHandler = "NextServer.getRequestHandler",
getRequestHandlerWithMetadata = "NextServer.getRequestHandlerWithMetadata",
getServer = "NextServer.getServer",
getServerRequestHandler = "NextServer.getServerRequestHandler",
createServer = "createServer.createServer"
}
declare enum NextNodeServerSpan {
compression = "NextNodeServer.compression",
getBuildId = "NextNodeServer.getBuildId",
createComponentTree = "NextNodeServer.createComponentTree",
clientComponentLoading = "NextNodeServer.clientComponentLoading",
getLayoutOrPageModule = "NextNodeServer.getLayoutOrPageModule",
generateStaticRoutes = "NextNodeServer.generateStaticRoutes",
generateFsStaticRoutes = "NextNodeServer.generateFsStaticRoutes",
generatePublicRoutes = "NextNodeServer.generatePublicRoutes",
generateImageRoutes = "NextNodeServer.generateImageRoutes.route",
sendRenderResult = "NextNodeServer.sendRenderResult",
proxyRequest = "NextNodeServer.proxyRequest",
runApi = "NextNodeServer.runApi",
render = "NextNodeServer.render",
renderHTML = "NextNodeServer.renderHTML",
imageOptimizer = "NextNodeServer.imageOptimizer",
getPagePath = "NextNodeServer.getPagePath",
getRoutesManifest = "NextNodeServer.getRoutesManifest",
findPageComponents = "NextNodeServer.findPageComponents",
getFontManifest = "NextNodeServer.getFontManifest",
getServerComponentManifest = "NextNodeServer.getServerComponentManifest",
getRequestHandler = "NextNodeServer.getRequestHandler",
renderToHTML = "NextNodeServer.renderToHTML",
renderError = "NextNodeServer.renderError",
renderErrorToHTML = "NextNodeServer.renderErrorToHTML",
render404 = "NextNodeServer.render404",
startResponse = "NextNodeServer.startResponse",
route = "route",
onProxyReq = "onProxyReq",
apiResolver = "apiResolver",
internalFetch = "internalFetch"
}
declare enum StartServerSpan {
startServer = "startServer.startServer"
}
declare enum RenderSpan {
getServerSideProps = "Render.getServerSideProps",
getStaticProps = "Render.getStaticProps",
renderToString = "Render.renderToString",
renderDocument = "Render.renderDocument",
createBodyResult = "Render.createBodyResult"
}
declare enum AppRenderSpan {
renderToString = "AppRender.renderToString",
renderToReadableStream = "AppRender.renderToReadableStream",
getBodyResult = "AppRender.getBodyResult",
fetch = "AppRender.fetch"
}
declare enum RouterSpan {
executeRoute = "Router.executeRoute"
}
declare enum NodeSpan {
runHandler = "Node.runHandler"
}
declare enum AppRouteRouteHandlersSpan {
runHandler = "AppRouteRouteHandlers.runHandler"
}
declare enum ResolveMetadataSpan {
generateMetadata = "ResolveMetadata.generateMetadata",
generateViewport = "ResolveMetadata.generateViewport"
}
declare enum MiddlewareSpan {
execute = "Middleware.execute"
}
type SpanTypes = `${BaseServerSpan}` | `${LoadComponentsSpan}` | `${NextServerSpan}` | `${StartServerSpan}` | `${NextNodeServerSpan}` | `${RenderSpan}` | `${RouterSpan}` | `${AppRenderSpan}` | `${NodeSpan}` | `${AppRouteRouteHandlersSpan}` | `${ResolveMetadataSpan}` | `${MiddlewareSpan}`;
export declare const NextVanillaSpanAllowlist: Set<BaseServerSpan | NextNodeServerSpan | RenderSpan | AppRenderSpan | NodeSpan | AppRouteRouteHandlersSpan | ResolveMetadataSpan | MiddlewareSpan>;
export declare const LogSpanAllowList: Set<NextNodeServerSpan>;
export { BaseServerSpan, LoadComponentsSpan, NextServerSpan, NextNodeServerSpan, StartServerSpan, RenderSpan, RouterSpan, AppRenderSpan, NodeSpan, AppRouteRouteHandlersSpan, ResolveMetadataSpan, MiddlewareSpan, };
export type { SpanTypes };

View File

@@ -0,0 +1,203 @@
/**
* Contains predefined constants for the trace span name in next/server.
*
* Currently, next/server/tracer is internal implementation only for tracking
* next.js's implementation only with known span names defined here.
**/ // eslint typescript has a bug with TS enums
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
AppRenderSpan: null,
AppRouteRouteHandlersSpan: null,
BaseServerSpan: null,
LoadComponentsSpan: null,
LogSpanAllowList: null,
MiddlewareSpan: null,
NextNodeServerSpan: null,
NextServerSpan: null,
NextVanillaSpanAllowlist: null,
NodeSpan: null,
RenderSpan: null,
ResolveMetadataSpan: null,
RouterSpan: null,
StartServerSpan: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
AppRenderSpan: function() {
return AppRenderSpan;
},
AppRouteRouteHandlersSpan: function() {
return AppRouteRouteHandlersSpan;
},
BaseServerSpan: function() {
return BaseServerSpan;
},
LoadComponentsSpan: function() {
return LoadComponentsSpan;
},
LogSpanAllowList: function() {
return LogSpanAllowList;
},
MiddlewareSpan: function() {
return MiddlewareSpan;
},
NextNodeServerSpan: function() {
return NextNodeServerSpan;
},
NextServerSpan: function() {
return NextServerSpan;
},
NextVanillaSpanAllowlist: function() {
return NextVanillaSpanAllowlist;
},
NodeSpan: function() {
return NodeSpan;
},
RenderSpan: function() {
return RenderSpan;
},
ResolveMetadataSpan: function() {
return ResolveMetadataSpan;
},
RouterSpan: function() {
return RouterSpan;
},
StartServerSpan: function() {
return StartServerSpan;
}
});
var BaseServerSpan = /*#__PURE__*/ function(BaseServerSpan) {
BaseServerSpan["handleRequest"] = "BaseServer.handleRequest";
BaseServerSpan["run"] = "BaseServer.run";
BaseServerSpan["pipe"] = "BaseServer.pipe";
BaseServerSpan["getStaticHTML"] = "BaseServer.getStaticHTML";
BaseServerSpan["render"] = "BaseServer.render";
BaseServerSpan["renderToResponseWithComponents"] = "BaseServer.renderToResponseWithComponents";
BaseServerSpan["renderToResponse"] = "BaseServer.renderToResponse";
BaseServerSpan["renderToHTML"] = "BaseServer.renderToHTML";
BaseServerSpan["renderError"] = "BaseServer.renderError";
BaseServerSpan["renderErrorToResponse"] = "BaseServer.renderErrorToResponse";
BaseServerSpan["renderErrorToHTML"] = "BaseServer.renderErrorToHTML";
BaseServerSpan["render404"] = "BaseServer.render404";
return BaseServerSpan;
}(BaseServerSpan || {});
var LoadComponentsSpan = /*#__PURE__*/ function(LoadComponentsSpan) {
LoadComponentsSpan["loadDefaultErrorComponents"] = "LoadComponents.loadDefaultErrorComponents";
LoadComponentsSpan["loadComponents"] = "LoadComponents.loadComponents";
return LoadComponentsSpan;
}(LoadComponentsSpan || {});
var NextServerSpan = /*#__PURE__*/ function(NextServerSpan) {
NextServerSpan["getRequestHandler"] = "NextServer.getRequestHandler";
NextServerSpan["getRequestHandlerWithMetadata"] = "NextServer.getRequestHandlerWithMetadata";
NextServerSpan["getServer"] = "NextServer.getServer";
NextServerSpan["getServerRequestHandler"] = "NextServer.getServerRequestHandler";
NextServerSpan["createServer"] = "createServer.createServer";
return NextServerSpan;
}(NextServerSpan || {});
var NextNodeServerSpan = /*#__PURE__*/ function(NextNodeServerSpan) {
NextNodeServerSpan["compression"] = "NextNodeServer.compression";
NextNodeServerSpan["getBuildId"] = "NextNodeServer.getBuildId";
NextNodeServerSpan["createComponentTree"] = "NextNodeServer.createComponentTree";
NextNodeServerSpan["clientComponentLoading"] = "NextNodeServer.clientComponentLoading";
NextNodeServerSpan["getLayoutOrPageModule"] = "NextNodeServer.getLayoutOrPageModule";
NextNodeServerSpan["generateStaticRoutes"] = "NextNodeServer.generateStaticRoutes";
NextNodeServerSpan["generateFsStaticRoutes"] = "NextNodeServer.generateFsStaticRoutes";
NextNodeServerSpan["generatePublicRoutes"] = "NextNodeServer.generatePublicRoutes";
NextNodeServerSpan["generateImageRoutes"] = "NextNodeServer.generateImageRoutes.route";
NextNodeServerSpan["sendRenderResult"] = "NextNodeServer.sendRenderResult";
NextNodeServerSpan["proxyRequest"] = "NextNodeServer.proxyRequest";
NextNodeServerSpan["runApi"] = "NextNodeServer.runApi";
NextNodeServerSpan["render"] = "NextNodeServer.render";
NextNodeServerSpan["renderHTML"] = "NextNodeServer.renderHTML";
NextNodeServerSpan["imageOptimizer"] = "NextNodeServer.imageOptimizer";
NextNodeServerSpan["getPagePath"] = "NextNodeServer.getPagePath";
NextNodeServerSpan["getRoutesManifest"] = "NextNodeServer.getRoutesManifest";
NextNodeServerSpan["findPageComponents"] = "NextNodeServer.findPageComponents";
NextNodeServerSpan["getFontManifest"] = "NextNodeServer.getFontManifest";
NextNodeServerSpan["getServerComponentManifest"] = "NextNodeServer.getServerComponentManifest";
NextNodeServerSpan["getRequestHandler"] = "NextNodeServer.getRequestHandler";
NextNodeServerSpan["renderToHTML"] = "NextNodeServer.renderToHTML";
NextNodeServerSpan["renderError"] = "NextNodeServer.renderError";
NextNodeServerSpan["renderErrorToHTML"] = "NextNodeServer.renderErrorToHTML";
NextNodeServerSpan["render404"] = "NextNodeServer.render404";
NextNodeServerSpan["startResponse"] = "NextNodeServer.startResponse";
// nested inner span, does not require parent scope name
NextNodeServerSpan["route"] = "route";
NextNodeServerSpan["onProxyReq"] = "onProxyReq";
NextNodeServerSpan["apiResolver"] = "apiResolver";
NextNodeServerSpan["internalFetch"] = "internalFetch";
return NextNodeServerSpan;
}(NextNodeServerSpan || {});
var StartServerSpan = /*#__PURE__*/ function(StartServerSpan) {
StartServerSpan["startServer"] = "startServer.startServer";
return StartServerSpan;
}(StartServerSpan || {});
var RenderSpan = /*#__PURE__*/ function(RenderSpan) {
RenderSpan["getServerSideProps"] = "Render.getServerSideProps";
RenderSpan["getStaticProps"] = "Render.getStaticProps";
RenderSpan["renderToString"] = "Render.renderToString";
RenderSpan["renderDocument"] = "Render.renderDocument";
RenderSpan["createBodyResult"] = "Render.createBodyResult";
return RenderSpan;
}(RenderSpan || {});
var AppRenderSpan = /*#__PURE__*/ function(AppRenderSpan) {
AppRenderSpan["renderToString"] = "AppRender.renderToString";
AppRenderSpan["renderToReadableStream"] = "AppRender.renderToReadableStream";
AppRenderSpan["getBodyResult"] = "AppRender.getBodyResult";
AppRenderSpan["fetch"] = "AppRender.fetch";
return AppRenderSpan;
}(AppRenderSpan || {});
var RouterSpan = /*#__PURE__*/ function(RouterSpan) {
RouterSpan["executeRoute"] = "Router.executeRoute";
return RouterSpan;
}(RouterSpan || {});
var NodeSpan = /*#__PURE__*/ function(NodeSpan) {
NodeSpan["runHandler"] = "Node.runHandler";
return NodeSpan;
}(NodeSpan || {});
var AppRouteRouteHandlersSpan = /*#__PURE__*/ function(AppRouteRouteHandlersSpan) {
AppRouteRouteHandlersSpan["runHandler"] = "AppRouteRouteHandlers.runHandler";
return AppRouteRouteHandlersSpan;
}(AppRouteRouteHandlersSpan || {});
var ResolveMetadataSpan = /*#__PURE__*/ function(ResolveMetadataSpan) {
ResolveMetadataSpan["generateMetadata"] = "ResolveMetadata.generateMetadata";
ResolveMetadataSpan["generateViewport"] = "ResolveMetadata.generateViewport";
return ResolveMetadataSpan;
}(ResolveMetadataSpan || {});
var MiddlewareSpan = /*#__PURE__*/ function(MiddlewareSpan) {
MiddlewareSpan["execute"] = "Middleware.execute";
return MiddlewareSpan;
}(MiddlewareSpan || {});
const NextVanillaSpanAllowlist = new Set([
"Middleware.execute",
"BaseServer.handleRequest",
"Render.getServerSideProps",
"Render.getStaticProps",
"AppRender.fetch",
"AppRender.getBodyResult",
"Render.renderDocument",
"Node.runHandler",
"AppRouteRouteHandlers.runHandler",
"ResolveMetadata.generateMetadata",
"ResolveMetadata.generateViewport",
"NextNodeServer.createComponentTree",
"NextNodeServer.findPageComponents",
"NextNodeServer.getLayoutOrPageModule",
"NextNodeServer.startResponse",
"NextNodeServer.clientComponentLoading"
]);
const LogSpanAllowList = new Set([
"NextNodeServer.findPageComponents",
"NextNodeServer.createComponentTree",
"NextNodeServer.clientComponentLoading"
]);
//# sourceMappingURL=constants.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,115 @@
import type { FetchEventResult } from '../../web/types';
import type { SpanTypes } from './constants';
import type { ContextAPI, Span, SpanOptions, AttributeValue, TextMapGetter } from 'next/dist/compiled/@opentelemetry/api';
declare const SpanStatusCode: typeof import("@opentelemetry/api").SpanStatusCode, SpanKind: typeof import("@opentelemetry/api").SpanKind;
export declare class BubbledError extends Error {
readonly bubble?: boolean | undefined;
readonly result?: FetchEventResult | undefined;
constructor(bubble?: boolean | undefined, result?: FetchEventResult | undefined);
}
export declare function isBubbledError(error: unknown): error is BubbledError;
type TracerSpanOptions = Omit<SpanOptions, 'attributes'> & {
parentSpan?: Span;
spanName?: string;
attributes?: Partial<Record<AttributeNames, AttributeValue | undefined>>;
hideSpan?: boolean;
};
interface NextTracer {
getContext(): ContextAPI;
/**
* Instruments a function by automatically creating a span activated on its
* scope.
*
* The span will automatically be finished when one of these conditions is
* met:
*
* * The function returns a promise, in which case the span will finish when
* the promise is resolved or rejected.
* * The function takes a callback as its second parameter, in which case the
* span will finish when that callback is called.
* * The function doesn't accept a callback and doesn't return a promise, in
* which case the span will finish at the end of the function execution.
*
*/
trace<T>(type: SpanTypes, fn: (span?: Span, done?: (error?: Error) => any) => Promise<T>): Promise<T>;
trace<T>(type: SpanTypes, fn: (span?: Span, done?: (error?: Error) => any) => T): T;
trace<T>(type: SpanTypes, options: TracerSpanOptions, fn: (span?: Span, done?: (error?: Error) => any) => Promise<T>): Promise<T>;
trace<T>(type: SpanTypes, options: TracerSpanOptions, fn: (span?: Span, done?: (error?: Error) => any) => T): T;
/**
* Wrap a function to automatically create a span activated on its
* scope when it's called.
*
* The span will automatically be finished when one of these conditions is
* met:
*
* * The function returns a promise, in which case the span will finish when
* the promise is resolved or rejected.
* * The function takes a callback as its last parameter, in which case the
* span will finish when that callback is called.
* * The function doesn't accept a callback and doesn't return a promise, in
* which case the span will finish at the end of the function execution.
*/
wrap<T = (...args: Array<any>) => any>(type: SpanTypes, fn: T): T;
wrap<T = (...args: Array<any>) => any>(type: SpanTypes, options: TracerSpanOptions, fn: T): T;
wrap<T = (...args: Array<any>) => any>(type: SpanTypes, options: (...args: any[]) => TracerSpanOptions, fn: T): T;
/**
* Starts and returns a new Span representing a logical unit of work.
*
* This method do NOT modify the current Context by default. In result, any inner span will not
* automatically set its parent context to the span created by this method unless manually activate
* context via `tracer.getContext().with`. `trace`, or `wrap` is generally recommended as it gracefully
* handles context activation. (ref: https://github.com/open-telemetry/opentelemetry-js/issues/1923)
*/
startSpan(type: SpanTypes): Span;
startSpan(type: SpanTypes, options: TracerSpanOptions): Span;
/**
* Returns currently activated span if current context is in the scope of the span.
* Returns undefined otherwise.
*/
getActiveScopeSpan(): Span | undefined;
/**
* Returns trace propagation data for the currently active context. The format is equal to data provided
* through the OpenTelemetry propagator API.
*/
getTracePropagationData(): ClientTraceDataEntry[];
/**
* Executes a function with the given span set as the active span in the context.
* This allows child spans created within the function to automatically parent to this span.
*/
withSpan<T>(span: Span, fn: () => T): T;
}
type NextAttributeNames = 'next.route' | 'next.page' | 'next.rsc' | 'next.segment' | 'next.span_name' | 'next.span_type' | 'next.clientComponentLoadCount';
type OTELAttributeNames = `http.${string}` | `net.${string}`;
type AttributeNames = NextAttributeNames | OTELAttributeNames;
export interface ClientTraceDataEntry {
key: string;
value: string;
}
declare class NextTracerImpl implements NextTracer {
/**
* Returns an instance to the trace with configured name.
* Since wrap / trace can be defined in any place prior to actual trace subscriber initialization,
* This should be lazily evaluated.
*/
private getTracerInstance;
getContext(): ContextAPI;
getTracePropagationData(): ClientTraceDataEntry[];
getActiveScopeSpan(): Span | undefined;
withPropagatedContext<T, C>(carrier: C, fn: () => T, getter?: TextMapGetter<C>): T;
trace<T>(type: SpanTypes, fn: (span?: Span, done?: (error?: Error) => any) => Promise<T>): Promise<T>;
trace<T>(type: SpanTypes, fn: (span?: Span, done?: (error?: Error) => any) => T): T;
trace<T>(type: SpanTypes, options: TracerSpanOptions, fn: (span?: Span, done?: (error?: Error) => any) => Promise<T>): Promise<T>;
trace<T>(type: SpanTypes, options: TracerSpanOptions, fn: (span?: Span, done?: (error?: Error) => any) => T): T;
wrap<T = (...args: Array<any>) => any>(type: SpanTypes, fn: T): T;
wrap<T = (...args: Array<any>) => any>(type: SpanTypes, options: TracerSpanOptions, fn: T): T;
wrap<T = (...args: Array<any>) => any>(type: SpanTypes, options: (...args: any[]) => TracerSpanOptions, fn: T): T;
startSpan(type: SpanTypes): Span;
startSpan(type: SpanTypes, options: TracerSpanOptions): Span;
private getSpanContext;
getRootSpanAttributes(): Map<AttributeNames, AttributeValue | undefined> | undefined;
setRootSpanAttribute(key: AttributeNames, value: AttributeValue): void;
withSpan<T>(span: Span, fn: () => T): T;
}
declare const getTracer: () => NextTracerImpl;
export { getTracer, SpanStatusCode, SpanKind };
export type { NextTracer, Span, SpanOptions, ContextAPI, TracerSpanOptions };

View File

@@ -0,0 +1,270 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
BubbledError: null,
SpanKind: null,
SpanStatusCode: null,
getTracer: null,
isBubbledError: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
BubbledError: function() {
return BubbledError;
},
SpanKind: function() {
return SpanKind;
},
SpanStatusCode: function() {
return SpanStatusCode;
},
getTracer: function() {
return getTracer;
},
isBubbledError: function() {
return isBubbledError;
}
});
const _constants = require("./constants");
const _isthenable = require("../../../shared/lib/is-thenable");
const NEXT_OTEL_PERFORMANCE_PREFIX = process.env.NEXT_OTEL_PERFORMANCE_PREFIX;
let api;
// we want to allow users to use their own version of @opentelemetry/api if they
// want to, so we try to require it first, and if it fails we fall back to the
// version that is bundled with Next.js
// this is because @opentelemetry/api has to be synced with the version of
// @opentelemetry/tracing that is used, and we don't want to force users to use
// the version that is bundled with Next.js.
// the API is ~stable, so this should be fine
if (process.env.NEXT_RUNTIME === 'edge') {
api = require('@opentelemetry/api');
} else {
try {
api = require('@opentelemetry/api');
} catch (err) {
api = require('next/dist/compiled/@opentelemetry/api');
}
}
const { context, propagation, trace, SpanStatusCode, SpanKind, ROOT_CONTEXT } = api;
class BubbledError extends Error {
constructor(bubble, result){
super(), this.bubble = bubble, this.result = result;
}
}
function isBubbledError(error) {
if (typeof error !== 'object' || error === null) return false;
return error instanceof BubbledError;
}
const closeSpanWithError = (span, error)=>{
if (isBubbledError(error) && error.bubble) {
span.setAttribute('next.bubble', true);
} else {
if (error) {
span.recordException(error);
span.setAttribute('error.type', error.name);
}
span.setStatus({
code: SpanStatusCode.ERROR,
message: error == null ? void 0 : error.message
});
}
span.end();
};
/** we use this map to propagate attributes from nested spans to the top span */ const rootSpanAttributesStore = new Map();
const rootSpanIdKey = api.createContextKey('next.rootSpanId');
let lastSpanId = 0;
const getSpanId = ()=>lastSpanId++;
const clientTraceDataSetter = {
set (carrier, key, value) {
carrier.push({
key,
value
});
}
};
class NextTracerImpl {
/**
* Returns an instance to the trace with configured name.
* Since wrap / trace can be defined in any place prior to actual trace subscriber initialization,
* This should be lazily evaluated.
*/ getTracerInstance() {
return trace.getTracer('next.js', '0.0.1');
}
getContext() {
return context;
}
getTracePropagationData() {
const activeContext = context.active();
const entries = [];
propagation.inject(activeContext, entries, clientTraceDataSetter);
return entries;
}
getActiveScopeSpan() {
return trace.getSpan(context == null ? void 0 : context.active());
}
withPropagatedContext(carrier, fn, getter) {
const activeContext = context.active();
if (trace.getSpanContext(activeContext)) {
// Active span is already set, too late to propagate.
return fn();
}
const remoteContext = propagation.extract(activeContext, carrier, getter);
return context.with(remoteContext, fn);
}
trace(...args) {
const [type, fnOrOptions, fnOrEmpty] = args;
// coerce options form overload
const { fn, options } = typeof fnOrOptions === 'function' ? {
fn: fnOrOptions,
options: {}
} : {
fn: fnOrEmpty,
options: {
...fnOrOptions
}
};
const spanName = options.spanName ?? type;
if (!_constants.NextVanillaSpanAllowlist.has(type) && process.env.NEXT_OTEL_VERBOSE !== '1' || options.hideSpan) {
return fn();
}
// Trying to get active scoped span to assign parent. If option specifies parent span manually, will try to use it.
let spanContext = this.getSpanContext((options == null ? void 0 : options.parentSpan) ?? this.getActiveScopeSpan());
if (!spanContext) {
spanContext = (context == null ? void 0 : context.active()) ?? ROOT_CONTEXT;
}
// Check if there's already a root span in the store for this trace
// We are intentionally not checking whether there is an active context
// from outside of nextjs to ensure that we can provide the same level
// of telemetry when using a custom server
const existingRootSpanId = spanContext.getValue(rootSpanIdKey);
const isRootSpan = typeof existingRootSpanId !== 'number' || !rootSpanAttributesStore.has(existingRootSpanId);
const spanId = getSpanId();
options.attributes = {
'next.span_name': spanName,
'next.span_type': type,
...options.attributes
};
return context.with(spanContext.setValue(rootSpanIdKey, spanId), ()=>this.getTracerInstance().startActiveSpan(spanName, options, (span)=>{
let startTime;
if (NEXT_OTEL_PERFORMANCE_PREFIX && type && _constants.LogSpanAllowList.has(type)) {
startTime = 'performance' in globalThis && 'measure' in performance ? globalThis.performance.now() : undefined;
}
let cleanedUp = false;
const onCleanup = ()=>{
if (cleanedUp) return;
cleanedUp = true;
rootSpanAttributesStore.delete(spanId);
if (startTime) {
performance.measure(`${NEXT_OTEL_PERFORMANCE_PREFIX}:next-${(type.split('.').pop() || '').replace(/[A-Z]/g, (match)=>'-' + match.toLowerCase())}`, {
start: startTime,
end: performance.now()
});
}
};
if (isRootSpan) {
rootSpanAttributesStore.set(spanId, new Map(Object.entries(options.attributes ?? {})));
}
if (fn.length > 1) {
try {
return fn(span, (err)=>closeSpanWithError(span, err));
} catch (err) {
closeSpanWithError(span, err);
throw err;
} finally{
onCleanup();
}
}
try {
const result = fn(span);
if ((0, _isthenable.isThenable)(result)) {
// If there's error make sure it throws
return result.then((res)=>{
span.end();
// Need to pass down the promise result,
// it could be react stream response with error { error, stream }
return res;
}).catch((err)=>{
closeSpanWithError(span, err);
throw err;
}).finally(onCleanup);
} else {
span.end();
onCleanup();
}
return result;
} catch (err) {
closeSpanWithError(span, err);
onCleanup();
throw err;
}
}));
}
wrap(...args) {
const tracer = this;
const [name, options, fn] = args.length === 3 ? args : [
args[0],
{},
args[1]
];
if (!_constants.NextVanillaSpanAllowlist.has(name) && process.env.NEXT_OTEL_VERBOSE !== '1') {
return fn;
}
return function() {
let optionsObj = options;
if (typeof optionsObj === 'function' && typeof fn === 'function') {
optionsObj = optionsObj.apply(this, arguments);
}
const lastArgId = arguments.length - 1;
const cb = arguments[lastArgId];
if (typeof cb === 'function') {
const scopeBoundCb = tracer.getContext().bind(context.active(), cb);
return tracer.trace(name, optionsObj, (_span, done)=>{
arguments[lastArgId] = function(err) {
done == null ? void 0 : done(err);
return scopeBoundCb.apply(this, arguments);
};
return fn.apply(this, arguments);
});
} else {
return tracer.trace(name, optionsObj, ()=>fn.apply(this, arguments));
}
};
}
startSpan(...args) {
const [type, options] = args;
const spanContext = this.getSpanContext((options == null ? void 0 : options.parentSpan) ?? this.getActiveScopeSpan());
return this.getTracerInstance().startSpan(type, options, spanContext);
}
getSpanContext(parentSpan) {
const spanContext = parentSpan ? trace.setSpan(context.active(), parentSpan) : undefined;
return spanContext;
}
getRootSpanAttributes() {
const spanId = context.active().getValue(rootSpanIdKey);
return rootSpanAttributesStore.get(spanId);
}
setRootSpanAttribute(key, value) {
const spanId = context.active().getValue(rootSpanIdKey);
const attributes = rootSpanAttributesStore.get(spanId);
if (attributes && !attributes.has(key)) {
attributes.set(key, value);
}
}
withSpan(span, fn) {
const spanContext = trace.setSpan(context.active(), span);
return context.with(spanContext, fn);
}
}
const getTracer = (()=>{
const tracer = new NextTracerImpl();
return ()=>tracer;
})();
//# sourceMappingURL=tracer.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
import type { ClientTraceDataEntry } from './tracer';
/**
* Takes OpenTelemetry client trace data and the `clientTraceMetadata` option configured in the Next.js config (currently
* experimental) and returns a filtered/allowed list of client trace data entries.
*/
export declare function getTracedMetadata(traceData: ClientTraceDataEntry[], clientTraceMetadata: string[] | undefined): ClientTraceDataEntry[] | undefined;

View File

@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "getTracedMetadata", {
enumerable: true,
get: function() {
return getTracedMetadata;
}
});
function getTracedMetadata(traceData, clientTraceMetadata) {
if (!clientTraceMetadata) return undefined;
return traceData.filter(({ key })=>clientTraceMetadata.includes(key));
}
//# sourceMappingURL=utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/server/lib/trace/utils.ts"],"sourcesContent":["import type { ClientTraceDataEntry } from './tracer'\n\n/**\n * Takes OpenTelemetry client trace data and the `clientTraceMetadata` option configured in the Next.js config (currently\n * experimental) and returns a filtered/allowed list of client trace data entries.\n */\nexport function getTracedMetadata(\n traceData: ClientTraceDataEntry[],\n clientTraceMetadata: string[] | undefined\n): ClientTraceDataEntry[] | undefined {\n if (!clientTraceMetadata) return undefined\n return traceData.filter(({ key }) => clientTraceMetadata.includes(key))\n}\n"],"names":["getTracedMetadata","traceData","clientTraceMetadata","undefined","filter","key","includes"],"mappings":";;;;+BAMgBA;;;eAAAA;;;AAAT,SAASA,kBACdC,SAAiC,EACjCC,mBAAyC;IAEzC,IAAI,CAACA,qBAAqB,OAAOC;IACjC,OAAOF,UAAUG,MAAM,CAAC,CAAC,EAAEC,GAAG,EAAE,GAAKH,oBAAoBI,QAAQ,CAACD;AACpE","ignoreList":[0]}