@interfere/next 12.3.4 → 12.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -155,40 +155,45 @@ The mode and the hosting platform come only from the surface configuration that
155
155
 
156
156
  ### Composing with an existing Vercel OTel setup
157
157
 
158
- Use the kit instead of calling Interfere's standalone `register()` alongside `registerOTel()`. For a Vercel deployment with a configured Trace Drain:
158
+ Use the kit instead of calling Interfere's standalone `register()` alongside `registerOTel()`:
159
159
 
160
160
  ```ts
161
161
  import { registerOTel } from "@vercel/otel";
162
162
  import { buildInterfereOtelKit } from "@interfere/next/instrumentation";
163
163
 
164
- export function register() {
165
- const kit = buildInterfereOtelKit({ serverTracing: "drain" });
164
+ export { onRequestError } from "@interfere/next/instrumentation";
166
165
 
167
- if (!kit) return;
166
+ export function register() {
167
+ const kit = buildInterfereOtelKit({ serviceName: "my-app" });
168
168
 
169
169
  registerOTel({
170
170
  serviceName: "my-app",
171
- attributes: kit.resourceAttributes,
172
- idGenerator: kit.idGenerator,
173
- spanProcessors: ["auto", ...kit.spanProcessors],
174
- logRecordProcessors: kit.logRecordProcessors,
175
- propagators: ["auto", kit.traceContextPropagator, ...kit.propagators],
176
- instrumentations: ["auto", ...kit.instrumentations],
171
+ attributes: { ...kit?.resourceAttributes },
172
+ ...(kit && { idGenerator: kit.idGenerator }),
173
+ spanProcessors: ["auto", ...(kit?.spanProcessors ?? [])],
174
+ logRecordProcessors: [...(kit?.logRecordProcessors ?? [])],
175
+ propagators: [
176
+ "auto",
177
+ ...(kit ? [kit.traceContextPropagator, ...kit.propagators] : []),
178
+ ],
177
179
  });
178
180
 
179
- kit.enableConsoleBridge();
181
+ kit?.enableConsoleBridge();
182
+ void kit?.fetchRemoteConfig();
180
183
  }
181
-
182
- export { onRequestError } from "@interfere/next/instrumentation";
183
184
  ```
184
185
 
185
- Keep `"auto"` in `spanProcessors` for Vercel drain delivery. For direct Interfere export through a customer-owned provider, use `serverTracing: "full"` and `spanProcessors: kit.spanProcessors` to choose a single export path. The host owns flushing and shutdown in composed setups.
186
+ The kit returns `null` when `INTERFERE_PUBLIC_KEY` is unset and on the Edge runtime, so the optional chaining keeps `registerOTel()` working in both cases. Keep the propagators in this order: the freshness propagator must run last.
187
+
188
+ The build-injected surface configuration selects the export path; there is no option to set. With a Vercel Trace Drain, the kit leaves out its trace exporter and `"auto"` delivers spans to the drain, so keep `"auto"` in `spanProcessors`. Without a drain, the kit's span processors export to Interfere directly; do not also point the `OTEL_EXPORTER_OTLP_*` variables at Interfere. The host owns flushing and shutdown in composed setups.
189
+
190
+ If another library, such as Sentry, registers an OpenTelemetry tracer provider before Interfere's standalone `register()` runs, `register()` still completes. It logs one warning, keeps capturing server errors through its own exporter, and skips request tracing because OpenTelemetry allows only one global tracer provider. To restore tracing, add the kit's span processors and propagators to the provider that library creates.
186
191
 
187
192
  `@vercel/otel@2.1.3` supports OTel API `>=1.9.0 <2`, stable SDK peers `>=2 <3`, and experimental peers `>=0.200.0 <0.300.0`. The SDK declares all required peers as dependencies, including `sdk-metrics`. Custom compositions must use compatible versions too.
188
193
 
189
194
  ### Browser session correlation
190
195
 
191
- The browser SDK sends `session.id` in W3C baggage on instrumented requests to the same origin and configured `tracing.apiHosts`, unless analytics consent is explicitly denied. Cross-origin APIs must allow `baggage` and `traceparent` in CORS. The server kit's existing baggage processor attaches the session ID to request and child spans; `traceparent` continues to determine trace linkage. No additional flag is required.
196
+ The browser SDK sends `session.id` in W3C baggage on instrumented requests to the page's own site (same registrable domain, any subdomain or port), to backend hosts Interfere already knows for your organization, and to configured `tracing.apiHosts`, unless analytics consent is explicitly denied. Cross-origin APIs must allow `baggage` and `traceparent` in CORS. The server kit's existing baggage processor attaches the session ID to request and child spans; `traceparent` continues to determine trace linkage. No additional flag is required.
192
197
 
193
198
  ## Ingest Endpoint
194
199
 
@@ -1 +1 @@
1
- import{readInterfereEnv}from"./internal/env.mjs";import{resolveReleaseSlug}from"./internal/release-slug.mjs";import{PRODUCER_VERSION}from"./internal/version.mjs";import{setServerFlush}from"./internal/server/exporters/server-flush.mjs";import{fetchAndCacheRemoteConfig}from"./internal/server/remote-config.mjs";import{onRequestError}from"./internal/server/capture/capture.mjs";import{CacheSafeTraceContextPropagator}from"./internal/server/tracing/cache-safe-trace-context.mjs";import{bridgeConsoleToOtel}from"./internal/server/capture/console-bridge.mjs";import{FreshnessStampingPropagator}from"./internal/server/tracing/freshness-propagator.mjs";import{installGlobalHandlers}from"./internal/server/capture/global-handlers.mjs";import{PrerenderSafeIdGenerator}from"./internal/server/tracing/id-generator.mjs";import{drainUrl}from"@interfere/constants/api";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{CompositePropagator,W3CBaggagePropagator,getStringFromEnv}from"@opentelemetry/core";import{createUrlRedactionSpanProcessor,resolveUrlRedaction}from"@interfere/types/sdk/url-redaction";import{ProxyTracerProvider,propagation,trace}from"@opentelemetry/api";import{defaultServiceName,resourceFromAttributes}from"@opentelemetry/resources";import{BatchSpanProcessor}from"@opentelemetry/sdk-trace-base";import{readLogSourcingMode}from"@interfere/types/sdk/log-sourcing";import{VERCEL_EXPORT_TIMEOUT_MS,readServerTracing}from"@interfere/types/sdk/server-tracing";import{BaggageSpanProcessor}from"@opentelemetry/baggage-span-processor";import{AsyncLocalStorageContextManager}from"@opentelemetry/context-async-hooks";import{OTLPLogExporter}from"@opentelemetry/exporter-logs-otlp-http";import{OTLPTraceExporter}from"@opentelemetry/exporter-trace-otlp-http";import{registerInstrumentations}from"@opentelemetry/instrumentation";import{CompressionAlgorithm}from"@opentelemetry/otlp-exporter-base";import{BatchLogRecordProcessor,LoggerProvider}from"@opentelemetry/sdk-logs";import{NodeTracerProvider}from"@opentelemetry/sdk-trace-node";const FLUSH_TIMEOUT_MS=75e3;function exportTimeouts(vercel){return vercel?{attempt:VERCEL_EXPORT_TIMEOUT_MS,batch:VERCEL_EXPORT_TIMEOUT_MS,flush:VERCEL_EXPORT_TIMEOUT_MS}:{attempt:65e3,batch:7e4,flush:FLUSH_TIMEOUT_MS}}let flushTimeoutMs=FLUSH_TIMEOUT_MS;function canFlush(provider){return`forceFlush`in provider&&typeof provider.forceFlush==`function`}let registered=!1,tracerProvider=null,loggerProvider=null;async function flush(){await Promise.all([tracerProvider?.forceFlush({timeoutMillis:flushTimeoutMs}),loggerProvider?.forceFlush({timeoutMillis:flushTimeoutMs})])}function buildInterfereOtelKit(opts={}){let env=readInterfereEnv();if(!env.publicKey)return null;let{slug:releaseSlug}=resolveReleaseSlug();releaseSlug||console.warn("[interfere] No commit SHA available; server spans will ship without `release.slug`. Set `INTERFERE_SOURCE_ID` (or any of `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA`) on the runtime env.");let tracesUrl=drainUrl(env.apiUrl,`traces`,env.publicKey),logsUrl=drainUrl(env.apiUrl,`logs`,env.publicKey),exporterHeaders={"x-interfere-producer-version":PRODUCER_VERSION},resourceAttributes=omitUndefined({"deployment.environment.name":env.nodeEnvironment,"interfere.sdk.name":`@interfere/next`,"interfere.sdk.version":PRODUCER_VERSION,"service.name":opts.serviceName||getStringFromEnv(`OTEL_SERVICE_NAME`)||env.serviceName||defaultServiceName(),"service.namespace":opts.serviceNamespace,"telemetry.sdk.language":`nodejs`,"release.slug":releaseSlug||void 0}),compression=opts.compression===`gzip`?CompressionAlgorithm.GZIP:CompressionAlgorithm.NONE,tracing=readServerTracing(),isDrainMode=tracing.mode===`drain`,timeouts=exportTimeouts(tracing.vercel),spanProcessors=[createUrlRedactionSpanProcessor(resolveUrlRedaction(opts.privacy?.urls)),new BaggageSpanProcessor(()=>!0),...isDrainMode?[]:[new BatchSpanProcessor(new OTLPTraceExporter({compression,headers:exporterHeaders,timeoutMillis:timeouts.attempt,url:tracesUrl}),{exportTimeoutMillis:timeouts.batch})],...opts._internalAdditionalSpanProcessors??[]],isLogDrainMode=readLogSourcingMode()===`drain`,logRecordProcessors=[...isLogDrainMode?[]:[new BatchLogRecordProcessor({exportTimeoutMillis:timeouts.batch,exporter:new OTLPLogExporter({compression,headers:exporterHeaders,timeoutMillis:timeouts.attempt,url:logsUrl})})],...opts._internalAdditionalLogRecordProcessors??[]];return{enableConsoleBridge:()=>isLogDrainMode?()=>{}:bridgeConsoleToOtel(),fetchRemoteConfig:async()=>fetchAndCacheRemoteConfig(),idGenerator:new PrerenderSafeIdGenerator,instrumentations:[],traceContextPropagator:new CacheSafeTraceContextPropagator,logRecordProcessors,propagators:[new FreshnessStampingPropagator,new W3CBaggagePropagator],resourceAttributes,spanProcessors}}async function register(opts={}){if(registered)return;let tracing=readServerTracing(),drainMode=tracing.mode===`drain`;if(flushTimeoutMs=exportTimeouts(tracing.vercel).flush,drainMode&&process.env.OTEL_SDK_DISABLED?.trim())return;let kit=buildInterfereOtelKit(opts);if(!kit)return;let resource=resourceFromAttributes(kit.resourceAttributes);if(drainMode){let{registerOTel}=await import(`@vercel/otel`);registerOTel({serviceName:String(kit.resourceAttributes[`service.name`]),attributes:kit.resourceAttributes,idGenerator:kit.idGenerator,instrumentations:kit.instrumentations,propagators:[`auto`,kit.traceContextPropagator,...kit.propagators],spanProcessors:[`auto`,...kit.spanProcessors]});let provider=trace.getTracerProvider(),delegate=provider instanceof ProxyTracerProvider?provider.getDelegate():provider;if(!canFlush(delegate))throw Error(`Interfere could not register the Vercel tracer provider. Use buildInterfereOtelKit with an existing OpenTelemetry bootstrap.`);tracerProvider=delegate}else{let provider=new NodeTracerProvider({idGenerator:kit.idGenerator,resource,spanProcessors:kit.spanProcessors});propagation.fields().length===0&&propagation.setGlobalPropagator(new CompositePropagator({propagators:[kit.traceContextPropagator,...kit.propagators]})),provider.register({contextManager:new AsyncLocalStorageContextManager().enable()}),tracerProvider=provider}loggerProvider=new LoggerProvider({processors:kit.logRecordProcessors,resource});let{logs:logsApi}=await import(`@opentelemetry/api-logs`);logsApi.setGlobalLoggerProvider(loggerProvider),registerInstrumentations({instrumentations:kit.instrumentations,tracerProvider:trace.getTracerProvider()}),opts.consoleBridge!==!1&&kit.enableConsoleBridge(),installGlobalHandlers({captureUncaughtException:opts.captureUncaughtException??!0,captureUnhandledRejection:opts.captureUnhandledRejection??!0,exitOnUncaughtException:process.env.NODE_ENV===`production`,flush}),setServerFlush(flush),registered=!0}export{PrerenderSafeIdGenerator,buildInterfereOtelKit,flush,onRequestError,register};
1
+ import{readInterfereEnv}from"./internal/env.mjs";import{resolveReleaseSlug}from"./internal/release-slug.mjs";import{PRODUCER_VERSION}from"./internal/version.mjs";import{setServerFlush}from"./internal/server/exporters/server-flush.mjs";import{fetchAndCacheRemoteConfig}from"./internal/server/remote-config.mjs";import{onRequestError,setIsolatedCaptureProvider}from"./internal/server/capture/capture.mjs";import{CacheSafeTraceContextPropagator}from"./internal/server/tracing/cache-safe-trace-context.mjs";import{bridgeConsoleToOtel}from"./internal/server/capture/console-bridge.mjs";import{FreshnessStampingPropagator}from"./internal/server/tracing/freshness-propagator.mjs";import{installGlobalHandlers}from"./internal/server/capture/global-handlers.mjs";import{PrerenderSafeIdGenerator}from"./internal/server/tracing/id-generator.mjs";import{drainUrl}from"@interfere/constants/api";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{CompositePropagator,W3CBaggagePropagator,getStringFromEnv}from"@opentelemetry/core";import{createUrlRedactionSpanProcessor,resolveUrlRedaction}from"@interfere/types/sdk/url-redaction";import{ProxyTracerProvider,propagation,trace}from"@opentelemetry/api";import{defaultServiceName,resourceFromAttributes}from"@opentelemetry/resources";import{BasicTracerProvider,BatchSpanProcessor}from"@opentelemetry/sdk-trace-base";import{readLogSourcingMode}from"@interfere/types/sdk/log-sourcing";import{VERCEL_EXPORT_TIMEOUT_MS,readServerTracing}from"@interfere/types/sdk/server-tracing";import{BaggageSpanProcessor}from"@opentelemetry/baggage-span-processor";import{AsyncLocalStorageContextManager}from"@opentelemetry/context-async-hooks";import{OTLPLogExporter}from"@opentelemetry/exporter-logs-otlp-http";import{OTLPTraceExporter}from"@opentelemetry/exporter-trace-otlp-http";import{registerInstrumentations}from"@opentelemetry/instrumentation";import{CompressionAlgorithm}from"@opentelemetry/otlp-exporter-base";import{BatchLogRecordProcessor,LoggerProvider}from"@opentelemetry/sdk-logs";import{NodeTracerProvider}from"@opentelemetry/sdk-trace-node";const FLUSH_TIMEOUT_MS=75e3,OTEL_API_REGISTRY=Symbol.for(`opentelemetry.js.api.1`);function hasGlobalTracerProvider(){return globalThis[OTEL_API_REGISTRY]?.trace!==void 0}function exportTimeouts(vercel){return vercel?{attempt:VERCEL_EXPORT_TIMEOUT_MS,batch:VERCEL_EXPORT_TIMEOUT_MS,flush:VERCEL_EXPORT_TIMEOUT_MS}:{attempt:65e3,batch:7e4,flush:FLUSH_TIMEOUT_MS}}let flushTimeoutMs=FLUSH_TIMEOUT_MS;function canFlush(provider){return`forceFlush`in provider&&typeof provider.forceFlush==`function`}let registered=!1,tracerProvider=null,loggerProvider=null;async function flush(){await Promise.all([tracerProvider?.forceFlush({timeoutMillis:flushTimeoutMs}),loggerProvider?.forceFlush({timeoutMillis:flushTimeoutMs})])}function buildInterfereOtelKit(opts={}){return createKit(opts,!1)}function createKit(opts,directExport){let env=readInterfereEnv();if(!env.publicKey)return null;let{slug:releaseSlug}=resolveReleaseSlug();releaseSlug||console.warn("[interfere] No commit SHA available; server spans will ship without `release.slug`. Set `INTERFERE_SOURCE_ID` (or any of `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA`) on the runtime env.");let tracesUrl=drainUrl(env.apiUrl,`traces`,env.publicKey),logsUrl=drainUrl(env.apiUrl,`logs`,env.publicKey),exporterHeaders={"x-interfere-producer-version":PRODUCER_VERSION},resourceAttributes=omitUndefined({"deployment.environment.name":env.nodeEnvironment,"interfere.sdk.name":`@interfere/next`,"interfere.sdk.version":PRODUCER_VERSION,"service.name":opts.serviceName||getStringFromEnv(`OTEL_SERVICE_NAME`)||env.serviceName||defaultServiceName(),"service.namespace":opts.serviceNamespace,"telemetry.sdk.language":`nodejs`,"release.slug":releaseSlug||void 0}),compression=opts.compression===`gzip`?CompressionAlgorithm.GZIP:CompressionAlgorithm.NONE,tracing=readServerTracing(),isDrainMode=tracing.mode===`drain`&&!directExport,timeouts=exportTimeouts(tracing.vercel),spanProcessors=[createUrlRedactionSpanProcessor(resolveUrlRedaction(opts.privacy?.urls)),new BaggageSpanProcessor(()=>!0),...isDrainMode?[]:[new BatchSpanProcessor(new OTLPTraceExporter({compression,headers:exporterHeaders,timeoutMillis:timeouts.attempt,url:tracesUrl}),{exportTimeoutMillis:timeouts.batch})],...opts._internalAdditionalSpanProcessors??[]],isLogDrainMode=readLogSourcingMode()===`drain`,logRecordProcessors=[...isLogDrainMode?[]:[new BatchLogRecordProcessor({exportTimeoutMillis:timeouts.batch,exporter:new OTLPLogExporter({compression,headers:exporterHeaders,timeoutMillis:timeouts.attempt,url:logsUrl})})],...opts._internalAdditionalLogRecordProcessors??[]];return{enableConsoleBridge:()=>isLogDrainMode?()=>{}:bridgeConsoleToOtel(),fetchRemoteConfig:async()=>fetchAndCacheRemoteConfig(),idGenerator:new PrerenderSafeIdGenerator,instrumentations:[],traceContextPropagator:new CacheSafeTraceContextPropagator,logRecordProcessors,propagators:[new FreshnessStampingPropagator,new W3CBaggagePropagator],resourceAttributes,spanProcessors}}async function register(opts={}){if(registered)return;let tracing=readServerTracing(),drainMode=tracing.mode===`drain`;if(flushTimeoutMs=exportTimeouts(tracing.vercel).flush,drainMode&&process.env.OTEL_SDK_DISABLED?.trim())return;let foreignProvider=hasGlobalTracerProvider(),kit=createKit(opts,foreignProvider);if(!kit)return;let resource=resourceFromAttributes(kit.resourceAttributes);if(foreignProvider){console.warn(`[interfere] Another OpenTelemetry tracer provider is already registered (for example by Sentry), so Interfere captures server errors but not request traces. Add the span processors from buildInterfereOtelKit() to that provider to restore tracing.`);let provider=new BasicTracerProvider({idGenerator:kit.idGenerator,resource,spanProcessors:kit.spanProcessors});setIsolatedCaptureProvider(provider),tracerProvider=provider}else if(drainMode){let{registerOTel}=await import(`@vercel/otel`);registerOTel({serviceName:String(kit.resourceAttributes[`service.name`]),attributes:kit.resourceAttributes,idGenerator:kit.idGenerator,instrumentations:kit.instrumentations,propagators:[`auto`,kit.traceContextPropagator,...kit.propagators],spanProcessors:[`auto`,...kit.spanProcessors]});let provider=trace.getTracerProvider(),delegate=provider instanceof ProxyTracerProvider?provider.getDelegate():provider;canFlush(delegate)&&(tracerProvider=delegate)}else{let provider=new NodeTracerProvider({idGenerator:kit.idGenerator,resource,spanProcessors:kit.spanProcessors});propagation.fields().length===0&&propagation.setGlobalPropagator(new CompositePropagator({propagators:[kit.traceContextPropagator,...kit.propagators]})),provider.register({contextManager:new AsyncLocalStorageContextManager().enable()}),tracerProvider=provider}loggerProvider=new LoggerProvider({processors:kit.logRecordProcessors,resource});let{logs:logsApi}=await import(`@opentelemetry/api-logs`);logsApi.setGlobalLoggerProvider(loggerProvider),registerInstrumentations({instrumentations:kit.instrumentations,tracerProvider:trace.getTracerProvider()}),opts.consoleBridge!==!1&&kit.enableConsoleBridge(),installGlobalHandlers({captureUncaughtException:opts.captureUncaughtException??!0,captureUnhandledRejection:opts.captureUnhandledRejection??!0,exitOnUncaughtException:process.env.NODE_ENV===`production`,flush}),setServerFlush(flush),registered=!0}export{PrerenderSafeIdGenerator,buildInterfereOtelKit,flush,onRequestError,register};
@@ -1,5 +1,7 @@
1
1
  import { CaptureErrorContext, OnRequestErrorContext } from "../types.mjs";
2
+ import { TracerProvider } from "@opentelemetry/api";
2
3
  //#region src/internal/server/capture/capture.d.ts
4
+ export declare function setIsolatedCaptureProvider(provider: TracerProvider): void;
3
5
  export declare function captureError(error: unknown, _request?: unknown, context?: CaptureErrorContext): void;
4
6
  interface OnRequestErrorRequest {
5
7
  readonly headers?: Record<string, string>;
@@ -1 +1 @@
1
- import{isEnabledOnServer}from"../../env.mjs";import{flushServerTelemetry}from"../exporters/server-flush.mjs";import{isPluginEnabled}from"../remote-config.mjs";import{readServerAction}from"../actions/server-action-marker.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{SpanKind,SpanStatusCode,trace}from"@opentelemetry/api";import{MECHANISM_TYPE,isNonErrorException,toException}from"@interfere/types/sdk/errors";import{exceptionSpanName}from"@interfere/types/sdk/throw-site";const ON_REQUEST_ERROR_MECHANISM={handled:!1,synthetic:!1,type:MECHANISM_TYPE.nextjs.onRequestError},SERVER_ACTION_MECHANISM={handled:!1,synthetic:!1,type:MECHANISM_TYPE.nextjs.serverAction},ROUTE_HANDLER_MECHANISM={handled:!1,synthetic:!1,type:MECHANISM_TYPE.nextjs.routeHandler},DEFAULT_CAPTURE_ERROR_MECHANISM={handled:!0,type:MECHANISM_TYPE.nextjs.captureError},CONTROL_FLOW_DIGEST_PREFIXES=[`NEXT_REDIRECT`,`NEXT_NOT_FOUND`,`NEXT_HTTP_ERROR_FALLBACK`];function isNextControlFlowError(error){if(!(error instanceof Error))return!1;let digest=`digest`in error?error.digest:void 0;return typeof digest==`string`&&CONTROL_FLOW_DIGEST_PREFIXES.some(prefix=>digest.startsWith(prefix))}const seen=new WeakSet;function addNextjsAttrs(attrs,nextjs,handled){nextjs&&(nextjs.errorDigest&&(attrs[`interfere.error.digest`]=nextjs.errorDigest),nextjs.requestMethod&&(attrs[`http.request.method`]=nextjs.requestMethod),nextjs.requestPath&&(attrs[`url.path`]=nextjs.requestPath),nextjs.routePath&&(attrs[`http.route`]=nextjs.routePath),nextjs.routeType===`route`&&!handled&&(attrs[`http.response.status_code`]=500))}function addRequestContextAttrs(attrs,context){if(context?.serverAction&&(attrs[`interfere.server_action.name`]=context.serverAction.name,context.serverAction.argsShape&&(attrs[`interfere.server_action.args_shape`]=context.serverAction.argsShape)),context?.requestHeaders)for(let[name,headerValue]of Object.entries(context.requestHeaders))attrs[`http.request.header.${name}`]=headerValue}function addExceptionAttrs(attrs,value){if(isNonErrorException(value)){attrs[`exception.type`]=value.type,attrs[`exception.message`]=value.value,attrs[`interfere.exception.kind`]=`non-error`;return}attrs[`exception.type`]=value.name,attrs[`exception.message`]=value.message,attrs[`interfere.exception.kind`]=`error`,value.stack&&(attrs[`exception.stacktrace`]=value.stack)}function buildAttrs(value,mechanism,context){let attrs={"interfere.exception.handled":mechanism.handled,"interfere.exception.mechanism":mechanism.type};return addNextjsAttrs(attrs,context?.nextjs,mechanism.handled),addRequestContextAttrs(attrs,context),addExceptionAttrs(attrs,value),attrs}function recordException({error,mechanism,context}){let value=toException(error),attrs=buildAttrs(value,mechanism,context),tracer=trace.getTracer(`@interfere/next/server`),active=trace.getActiveSpan();if(active?.isRecording()){active.addEvent(`exception`,attrs),mechanism.handled||active.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message});return}let span=tracer.startSpan(exceptionSpanName(value),{attributes:{"interfere.capture.kind":`exception`},kind:SpanKind.INTERNAL});span.addEvent(`exception`,attrs),mechanism.handled||span.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message}),span.end()}function captureError(error,_request,context){if(isEnabledOnServer()&&isPluginEnabled(`errors`)&&!isNextControlFlowError(error)){if(error instanceof Error){if(seen.has(error))return;seen.add(error)}recordException({context,error,mechanism:context?.mechanism??DEFAULT_CAPTURE_ERROR_MECHANISM})}}const CAPTURED_REQUEST_HEADERS=[`accept`,`accept-language`,`content-length`,`content-type`,`host`,`referer`,`user-agent`,`x-forwarded-for`,`x-forwarded-host`,`x-forwarded-proto`,`x-vercel-id`];function pickAllowedHeaders(headers){if(!headers)return;let picked={};for(let name of CAPTURED_REQUEST_HEADERS){let value=headers[name];typeof value==`string`&&(picked[name]=value)}return Object.keys(picked).length>0?picked:void 0}async function onRequestError(error,request,context){if(isNextControlFlowError(error)||seen.has(error)||(seen.add(error),!isEnabledOnServer())||!isPluginEnabled(`errors`))return;let action=readServerAction(error),mechanism=ON_REQUEST_ERROR_MECHANISM;action?mechanism=SERVER_ACTION_MECHANISM:context.routeType===`route`&&(mechanism=ROUTE_HANDLER_MECHANISM);let requestHeaders=pickAllowedHeaders(request?.headers);recordException({context:omitUndefined({mechanism,nextjs:omitUndefined({...context,requestMethod:request?.method||void 0,requestPath:request?.path||void 0,errorDigest:error.digest||void 0}),requestHeaders,serverAction:action??void 0}),error,mechanism});try{await flushServerTelemetry()}catch{return}}export{captureError,onRequestError};
1
+ import{isEnabledOnServer}from"../../env.mjs";import{flushServerTelemetry}from"../exporters/server-flush.mjs";import{isPluginEnabled}from"../remote-config.mjs";import{readServerAction}from"../actions/server-action-marker.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{SpanKind,SpanStatusCode,trace}from"@opentelemetry/api";import{MECHANISM_TYPE,isNonErrorException,toException}from"@interfere/types/sdk/errors";import{exceptionSpanName}from"@interfere/types/sdk/throw-site";let isolatedProvider=null;function setIsolatedCaptureProvider(provider){isolatedProvider=provider}const ON_REQUEST_ERROR_MECHANISM={handled:!1,synthetic:!1,type:MECHANISM_TYPE.nextjs.onRequestError},SERVER_ACTION_MECHANISM={handled:!1,synthetic:!1,type:MECHANISM_TYPE.nextjs.serverAction},ROUTE_HANDLER_MECHANISM={handled:!1,synthetic:!1,type:MECHANISM_TYPE.nextjs.routeHandler},DEFAULT_CAPTURE_ERROR_MECHANISM={handled:!0,type:MECHANISM_TYPE.nextjs.captureError},CONTROL_FLOW_DIGEST_PREFIXES=[`NEXT_REDIRECT`,`NEXT_NOT_FOUND`,`NEXT_HTTP_ERROR_FALLBACK`];function isNextControlFlowError(error){if(!(error instanceof Error))return!1;let digest=`digest`in error?error.digest:void 0;return typeof digest==`string`&&CONTROL_FLOW_DIGEST_PREFIXES.some(prefix=>digest.startsWith(prefix))}const seen=new WeakSet;function addNextjsAttrs(attrs,nextjs,handled){nextjs&&(nextjs.errorDigest&&(attrs[`interfere.error.digest`]=nextjs.errorDigest),nextjs.requestMethod&&(attrs[`http.request.method`]=nextjs.requestMethod),nextjs.requestPath&&(attrs[`url.path`]=nextjs.requestPath),nextjs.routePath&&(attrs[`http.route`]=nextjs.routePath),nextjs.routeType===`route`&&!handled&&(attrs[`http.response.status_code`]=500))}function addRequestContextAttrs(attrs,context){if(context?.serverAction&&(attrs[`interfere.server_action.name`]=context.serverAction.name,context.serverAction.argsShape&&(attrs[`interfere.server_action.args_shape`]=context.serverAction.argsShape)),context?.requestHeaders)for(let[name,headerValue]of Object.entries(context.requestHeaders))attrs[`http.request.header.${name}`]=headerValue}function addExceptionAttrs(attrs,value){if(isNonErrorException(value)){attrs[`exception.type`]=value.type,attrs[`exception.message`]=value.value,attrs[`interfere.exception.kind`]=`non-error`;return}attrs[`exception.type`]=value.name,attrs[`exception.message`]=value.message,attrs[`interfere.exception.kind`]=`error`,value.stack&&(attrs[`exception.stacktrace`]=value.stack)}function buildAttrs(value,mechanism,context){let attrs={"interfere.exception.handled":mechanism.handled,"interfere.exception.mechanism":mechanism.type};return addNextjsAttrs(attrs,context?.nextjs,mechanism.handled),addRequestContextAttrs(attrs,context),addExceptionAttrs(attrs,value),attrs}function recordException({error,mechanism,context}){let value=toException(error),attrs=buildAttrs(value,mechanism,context),tracer=(isolatedProvider??trace.getTracerProvider()).getTracer(`@interfere/next/server`),active=isolatedProvider?void 0:trace.getActiveSpan();if(active?.isRecording()){active.addEvent(`exception`,attrs),mechanism.handled||active.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message});return}let span=tracer.startSpan(exceptionSpanName(value),{attributes:{"interfere.capture.kind":`exception`},kind:SpanKind.INTERNAL});span.addEvent(`exception`,attrs),mechanism.handled||span.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message}),span.end()}function captureError(error,_request,context){if(isEnabledOnServer()&&isPluginEnabled(`errors`)&&!isNextControlFlowError(error)){if(error instanceof Error){if(seen.has(error))return;seen.add(error)}recordException({context,error,mechanism:context?.mechanism??DEFAULT_CAPTURE_ERROR_MECHANISM})}}const CAPTURED_REQUEST_HEADERS=[`accept`,`accept-language`,`content-length`,`content-type`,`host`,`referer`,`user-agent`,`x-forwarded-for`,`x-forwarded-host`,`x-forwarded-proto`,`x-vercel-id`];function pickAllowedHeaders(headers){if(!headers)return;let picked={};for(let name of CAPTURED_REQUEST_HEADERS){let value=headers[name];typeof value==`string`&&(picked[name]=value)}return Object.keys(picked).length>0?picked:void 0}async function onRequestError(error,request,context){if(isNextControlFlowError(error)||seen.has(error)||(seen.add(error),!isEnabledOnServer())||!isPluginEnabled(`errors`))return;let action=readServerAction(error),mechanism=ON_REQUEST_ERROR_MECHANISM;action?mechanism=SERVER_ACTION_MECHANISM:context.routeType===`route`&&(mechanism=ROUTE_HANDLER_MECHANISM);let requestHeaders=pickAllowedHeaders(request?.headers);recordException({context:omitUndefined({mechanism,nextjs:omitUndefined({...context,requestMethod:request?.method||void 0,requestPath:request?.path||void 0,errorDigest:error.digest||void 0}),requestHeaders,serverAction:action??void 0}),error,mechanism});try{await flushServerTelemetry()}catch{return}}export{captureError,onRequestError,setIsolatedCaptureProvider};
package/dist/package.mjs CHANGED
@@ -1 +1 @@
1
- var name=`@interfere/next`,version=`12.3.4`;export{name,version};
1
+ var name=`@interfere/next`,version=`12.4.2`;export{name,version};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interfere/next",
3
- "version": "12.3.4",
3
+ "version": "12.4.2",
4
4
  "description": "Build software that never breaks.",
5
5
  "keywords": [
6
6
  "error-tracking",
@@ -98,12 +98,12 @@
98
98
  "typecheck": "bun check"
99
99
  },
100
100
  "dependencies": {
101
- "@interfere/build": "^0.4.1",
101
+ "@interfere/build": "^0.4.3",
102
102
  "@interfere/constants": "^9.3.0",
103
103
  "@interfere/helpers": "^9.3.0",
104
104
  "@interfere/javascript": "^0.3.1",
105
- "@interfere/react": "^12.3.4",
106
- "@interfere/types": "^10.5.1",
105
+ "@interfere/react": "^12.4.2",
106
+ "@interfere/types": "^10.6.1",
107
107
  "@opentelemetry/api": "1.9.1",
108
108
  "@opentelemetry/api-logs": "0.222.0",
109
109
  "@opentelemetry/baggage-span-processor": "0.5.0",