@interfere/next 12.1.4 → 12.2.0
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 +9 -7
- package/dist/instrumentation.d.mts +2 -2
- package/dist/instrumentation.edge.d.mts +1 -1
- package/dist/instrumentation.edge.mjs +1 -1
- package/dist/instrumentation.mjs +1 -1
- package/dist/internal/build/pipeline.mjs +1 -1
- package/dist/internal/server/{server-action-marker.d.mts → actions/server-action-marker.d.mts} +1 -1
- package/dist/internal/server/{wrap-proxy.d.mts → actions/wrap-proxy.d.mts} +1 -1
- package/dist/internal/server/actions/wrap-proxy.mjs +1 -0
- package/dist/internal/server/{wrap-server-action.d.mts → actions/wrap-server-action.d.mts} +1 -1
- package/dist/internal/server/{capture.d.mts → capture/capture.d.mts} +2 -2
- package/dist/internal/server/capture/capture.mjs +1 -0
- package/dist/internal/server/{console-bridge.d.mts → capture/console-bridge.d.mts} +1 -1
- package/dist/internal/server/{global-handlers.d.mts → capture/global-handlers.d.mts} +1 -1
- package/dist/internal/server/{edge-exporter.d.mts → exporters/edge-exporter.d.mts} +1 -1
- package/dist/internal/server/exporters/edge-exporter.mjs +1 -0
- package/dist/internal/server/{server-flush.d.mts → exporters/server-flush.d.mts} +1 -1
- package/dist/internal/server/{cache-safe-trace-context.d.mts → tracing/cache-safe-trace-context.d.mts} +1 -1
- package/dist/internal/server/tracing/cache-safe-trace-context.mjs +1 -0
- package/dist/internal/server/{counter-id-generator.d.mts → tracing/counter-id-generator.d.mts} +1 -1
- package/dist/internal/server/{freshness-propagator.d.mts → tracing/freshness-propagator.d.mts} +1 -1
- package/dist/internal/server/tracing/freshness-propagator.mjs +1 -0
- package/dist/internal/server/{id-generator.d.mts → tracing/id-generator.d.mts} +1 -1
- package/dist/internal/server/{trace-meta.d.mts → tracing/trace-meta.d.mts} +1 -1
- package/dist/internal/server/tracing/trace-meta.mjs +1 -0
- package/dist/internal/server/{trace-payload.d.mts → tracing/trace-payload.d.mts} +1 -1
- package/dist/internal/server/{traceparent.d.mts → tracing/traceparent.d.mts} +1 -1
- package/dist/internal/server/types.d.mts +1 -1
- package/dist/package.mjs +1 -1
- package/dist/server.d.mts +4 -4
- package/dist/server.mjs +1 -1
- package/package.json +7 -7
- package/dist/internal/server/cache-safe-trace-context.mjs +0 -1
- package/dist/internal/server/capture.mjs +0 -1
- package/dist/internal/server/edge-exporter.mjs +0 -1
- package/dist/internal/server/freshness-propagator.mjs +0 -1
- package/dist/internal/server/trace-meta.mjs +0 -1
- package/dist/internal/server/wrap-proxy.mjs +0 -1
- /package/dist/internal/server/{server-action-marker.mjs → actions/server-action-marker.mjs} +0 -0
- /package/dist/internal/server/{wrap-server-action.mjs → actions/wrap-server-action.mjs} +0 -0
- /package/dist/internal/server/{console-bridge.mjs → capture/console-bridge.mjs} +0 -0
- /package/dist/internal/server/{global-handlers.mjs → capture/global-handlers.mjs} +0 -0
- /package/dist/internal/server/{server-flush.mjs → exporters/server-flush.mjs} +0 -0
- /package/dist/internal/server/{counter-id-generator.mjs → tracing/counter-id-generator.mjs} +0 -0
- /package/dist/internal/server/{id-generator.mjs → tracing/id-generator.mjs} +0 -0
- /package/dist/internal/server/{trace-payload.mjs → tracing/trace-payload.mjs} +0 -0
- /package/dist/internal/server/{traceparent.mjs → tracing/traceparent.mjs} +0 -0
package/README.md
CHANGED
|
@@ -86,12 +86,12 @@ export default function RootLayout({
|
|
|
86
86
|
|
|
87
87
|
### Environment Variables
|
|
88
88
|
|
|
89
|
-
| Variable | Required | Description
|
|
90
|
-
| ---------------------------------------- | ------------------------ |
|
|
91
|
-
| `INTERFERE_PUBLIC_KEY` | Yes | Surface public key in the `interfere_public_us_*` or `interfere_public_eu_*` format. Public by design; used for ingestion. A build without it logs a warning and sends no events.
|
|
92
|
-
| `INTERFERE_API_KEY` | Yes for release metadata |
|
|
93
|
-
| `INTERFERE_SOURCEMAP_UPLOAD_CONCURRENCY` | No | Maximum concurrent source-map uploads. Defaults to 20; must be a positive integer.
|
|
94
|
-
| `INTERFERE_API_URL` | No | Override the ingest endpoint in `direct` mode (e.g. a regional endpoint). Defaults to Interfere's endpoint.
|
|
89
|
+
| Variable | Required | Description |
|
|
90
|
+
| ---------------------------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
91
|
+
| `INTERFERE_PUBLIC_KEY` | Yes | Surface public key in the `interfere_public_us_*` or `interfere_public_eu_*` format. Public by design; used for ingestion, and at build time to name the app that a release belongs to. A build without it logs a warning and sends no events. |
|
|
92
|
+
| `INTERFERE_API_KEY` | Yes for release metadata | Workspace secret key in the `interfere_secret_us_*` or `interfere_secret_eu_*` format, with the **Publish releases** (`release:write`) permission. One key publishes releases for every app in the workspace. Server-side build credential; never expose in browser code. A build without it skips the release. |
|
|
93
|
+
| `INTERFERE_SOURCEMAP_UPLOAD_CONCURRENCY` | No | Maximum concurrent source-map uploads. Defaults to 20; must be a positive integer. |
|
|
94
|
+
| `INTERFERE_API_URL` | No | Override the ingest endpoint in `direct` mode (e.g. a regional endpoint). Defaults to Interfere's endpoint. |
|
|
95
95
|
|
|
96
96
|
### Connect releases to Vercel
|
|
97
97
|
|
|
@@ -129,12 +129,14 @@ A production build registers a release when `INTERFERE_API_KEY` is set, so set i
|
|
|
129
129
|
|
|
130
130
|
To turn Interfere off for a build, pass `withInterfere({ disable: true }, nextConfig)`. The build skips the release, the source-map upload and every Interfere check, and the browser and server SDKs send no events.
|
|
131
131
|
|
|
132
|
-
Only configuration that is wrong fails a production build: an `INTERFERE_API_KEY` with the wrong prefix, a missing commit SHA when the key is set, an invalid `apiHost`, or a public key without an `instrumentation-client` file. A missing `INTERFERE_PUBLIC_KEY` logs a warning. Errors from the Interfere API, including a rejected secret key, are logged and do not fail the build.
|
|
132
|
+
Only configuration that is wrong fails a production build: an `INTERFERE_API_KEY` with the wrong prefix, a missing commit SHA when the key is set, an invalid `apiHost`, or a public key without an `instrumentation-client` file. A missing `INTERFERE_PUBLIC_KEY` logs a warning; with a workspace secret key, the build also cannot publish its release. Errors from the Interfere API, including a rejected secret key, are logged and do not fail the build.
|
|
133
133
|
|
|
134
134
|
### Migrating From 0.x
|
|
135
135
|
|
|
136
136
|
Upgrade all `@interfere/*` SDK packages together. Keep the env var names, but replace old `int_pub_*` / `interfere_pk_*` values with the dashboard's regional public key, and replace old `ak_*` / `interfere_ak_*` build keys with the regional Interfere secret key.
|
|
137
137
|
|
|
138
|
+
Secret keys that were created for one app keep working. New secret keys belong to the workspace, so set `INTERFERE_PUBLIC_KEY` on the build machine as well.
|
|
139
|
+
|
|
138
140
|
## Server tracing and Vercel drains
|
|
139
141
|
|
|
140
142
|
The standard instrumentation export works on Vercel and other Next.js hosts. In `full` mode, Interfere exports application spans directly. When the build config selects `drain`, the Node SDK initializes `@vercel/otel` with its default processors so Vercel can forward application spans through the configured Trace Drain. Platform spans and application spans remain separate observations.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { PrerenderSafeIdGenerator } from "./internal/server/id-generator.mjs";
|
|
1
|
+
import { PrerenderSafeIdGenerator } from "./internal/server/tracing/id-generator.mjs";
|
|
2
2
|
import { ServerInstrumentationOptions } from "./internal/server/instrumentation-options.mjs";
|
|
3
|
-
import { onRequestError } from "./internal/server/capture.mjs";
|
|
3
|
+
import { onRequestError } from "./internal/server/capture/capture.mjs";
|
|
4
4
|
import { AttributeValue, TextMapPropagator } from "@opentelemetry/api";
|
|
5
5
|
import { SpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
6
6
|
import { Instrumentation } from "@opentelemetry/instrumentation";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ServerInstrumentationOptions } from "./internal/server/instrumentation-options.mjs";
|
|
2
|
-
import { onRequestError } from "./internal/server/capture.mjs";
|
|
2
|
+
import { onRequestError } from "./internal/server/capture/capture.mjs";
|
|
3
3
|
import { InterfereOtelKit } from "./instrumentation.mjs";
|
|
4
4
|
//#region src/instrumentation.edge.d.ts
|
|
5
5
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readInterfereEnv}from"./internal/env.mjs";import{resolveReleaseSlug}from"./internal/release-slug.mjs";import{PRODUCER_VERSION}from"./internal/version.mjs";import{FetchTraceExporter}from"./internal/server/edge-exporter.mjs";import{setServerFlush}from"./internal/server/server-flush.mjs";import{onRequestError}from"./internal/server/capture.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{LOCAL_DEV_RELEASE_SLUG}from"@interfere/types/releases/slug";import{getStringFromEnv}from"@opentelemetry/core";import{createUrlRedactionSpanProcessor,resolveUrlRedaction}from"@interfere/types/sdk/url-redaction";import{trace}from"@opentelemetry/api";import{defaultServiceName,resourceFromAttributes}from"@opentelemetry/resources";import{BasicTracerProvider,SimpleSpanProcessor}from"@opentelemetry/sdk-trace-base";let registered=!1;async function register(opts={}){if(registered)return Promise.resolve();let env=readInterfereEnv();if(!env.publicKey)return Promise.resolve();let releaseSlug=process.env.NEXT_PUBLIC_INTERFERE_FORCE_ENABLE?LOCAL_DEV_RELEASE_SLUG:resolveReleaseSlug().slug;releaseSlug||console.warn(`[interfere] edge runtime has no release.slug; configure a build release or INTERFERE_SOURCE_ID.`);let provider=new BasicTracerProvider({resource:resourceFromAttributes(omitUndefined({"deployment.environment.name":env.nodeEnvironment,"interfere.sdk.name":`@interfere/next`,"interfere.sdk.version":PRODUCER_VERSION,"service.name":opts.serviceName||getStringFromEnv(`OTEL_SERVICE_NAME`)||defaultServiceName(),"service.namespace":opts.serviceNamespace,"telemetry.sdk.language":`nodejs`,"release.slug":releaseSlug||void 0})),spanProcessors:[createUrlRedactionSpanProcessor(resolveUrlRedaction(opts.privacy?.urls)),new SimpleSpanProcessor(new FetchTraceExporter({baseUrl:env.apiUrl,publicKeyHeader:{apiKey:env.publicKey},headers:{"x-interfere-producer-version":PRODUCER_VERSION}}))]});return trace.setGlobalTracerProvider(provider),setServerFlush(async()=>provider.forceFlush({timeoutMillis:75e3})),registered=!0,Promise.resolve()}function buildInterfereOtelKit(_opts={}){return null}export{buildInterfereOtelKit,onRequestError,register};
|
|
1
|
+
import{readInterfereEnv}from"./internal/env.mjs";import{resolveReleaseSlug}from"./internal/release-slug.mjs";import{PRODUCER_VERSION}from"./internal/version.mjs";import{FetchTraceExporter}from"./internal/server/exporters/edge-exporter.mjs";import{setServerFlush}from"./internal/server/exporters/server-flush.mjs";import{onRequestError}from"./internal/server/capture/capture.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{LOCAL_DEV_RELEASE_SLUG}from"@interfere/types/releases/slug";import{getStringFromEnv}from"@opentelemetry/core";import{createUrlRedactionSpanProcessor,resolveUrlRedaction}from"@interfere/types/sdk/url-redaction";import{trace}from"@opentelemetry/api";import{defaultServiceName,resourceFromAttributes}from"@opentelemetry/resources";import{BasicTracerProvider,SimpleSpanProcessor}from"@opentelemetry/sdk-trace-base";let registered=!1;async function register(opts={}){if(registered)return Promise.resolve();let env=readInterfereEnv();if(!env.publicKey)return Promise.resolve();let releaseSlug=process.env.NEXT_PUBLIC_INTERFERE_FORCE_ENABLE?LOCAL_DEV_RELEASE_SLUG:resolveReleaseSlug().slug;releaseSlug||console.warn(`[interfere] edge runtime has no release.slug; configure a build release or INTERFERE_SOURCE_ID.`);let provider=new BasicTracerProvider({resource:resourceFromAttributes(omitUndefined({"deployment.environment.name":env.nodeEnvironment,"interfere.sdk.name":`@interfere/next`,"interfere.sdk.version":PRODUCER_VERSION,"service.name":opts.serviceName||getStringFromEnv(`OTEL_SERVICE_NAME`)||defaultServiceName(),"service.namespace":opts.serviceNamespace,"telemetry.sdk.language":`nodejs`,"release.slug":releaseSlug||void 0})),spanProcessors:[createUrlRedactionSpanProcessor(resolveUrlRedaction(opts.privacy?.urls)),new SimpleSpanProcessor(new FetchTraceExporter({baseUrl:env.apiUrl,publicKeyHeader:{apiKey:env.publicKey},headers:{"x-interfere-producer-version":PRODUCER_VERSION}}))]});return trace.setGlobalTracerProvider(provider),setServerFlush(async()=>provider.forceFlush({timeoutMillis:75e3})),registered=!0,Promise.resolve()}function buildInterfereOtelKit(_opts={}){return null}export{buildInterfereOtelKit,onRequestError,register};
|
package/dist/instrumentation.mjs
CHANGED
|
@@ -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/server-flush.mjs";import{fetchAndCacheRemoteConfig}from"./internal/server/remote-config.mjs";import{onRequestError}from"./internal/server/capture.mjs";import{CacheSafeTraceContextPropagator}from"./internal/server/cache-safe-trace-context.mjs";import{bridgeConsoleToOtel}from"./internal/server/console-bridge.mjs";import{FreshnessStampingPropagator}from"./internal/server/freshness-propagator.mjs";import{installGlobalHandlers}from"./internal/server/global-handlers.mjs";import{PrerenderSafeIdGenerator}from"./internal/server/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{readServerTracingMode}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 EXPORT_TIMEOUT_MS=65e3;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:75e3}),loggerProvider?.forceFlush({timeoutMillis:75e3})])}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`)||defaultServiceName(),"service.namespace":opts.serviceNamespace,"telemetry.sdk.language":`nodejs`,"release.slug":releaseSlug||void 0}),compression=opts.compression===`gzip`?CompressionAlgorithm.GZIP:CompressionAlgorithm.NONE,isDrainMode=(opts.serverTracing??readServerTracingMode())===`drain`,spanProcessors=[createUrlRedactionSpanProcessor(resolveUrlRedaction(opts.privacy?.urls)),new BaggageSpanProcessor(()=>!0),...isDrainMode?[]:[new BatchSpanProcessor(new OTLPTraceExporter({compression,headers:exporterHeaders,timeoutMillis:EXPORT_TIMEOUT_MS,url:tracesUrl}),{exportTimeoutMillis:7e4})],...opts._internalAdditionalSpanProcessors??[]],isLogDrainMode=readLogSourcingMode()===`drain`,logRecordProcessors=[...isLogDrainMode?[]:[new BatchLogRecordProcessor({exportTimeoutMillis:7e4,exporter:new OTLPLogExporter({compression,headers:exporterHeaders,timeoutMillis:EXPORT_TIMEOUT_MS,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 drainMode=(opts.serverTracing??readServerTracingMode())===`drain`;if(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}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{readServerTracingMode}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 EXPORT_TIMEOUT_MS=65e3;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:75e3}),loggerProvider?.forceFlush({timeoutMillis:75e3})])}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`)||defaultServiceName(),"service.namespace":opts.serviceNamespace,"telemetry.sdk.language":`nodejs`,"release.slug":releaseSlug||void 0}),compression=opts.compression===`gzip`?CompressionAlgorithm.GZIP:CompressionAlgorithm.NONE,isDrainMode=(opts.serverTracing??readServerTracingMode())===`drain`,spanProcessors=[createUrlRedactionSpanProcessor(resolveUrlRedaction(opts.privacy?.urls)),new BaggageSpanProcessor(()=>!0),...isDrainMode?[]:[new BatchSpanProcessor(new OTLPTraceExporter({compression,headers:exporterHeaders,timeoutMillis:EXPORT_TIMEOUT_MS,url:tracesUrl}),{exportTimeoutMillis:7e4})],...opts._internalAdditionalSpanProcessors??[]],isLogDrainMode=readLogSourcingMode()===`drain`,logRecordProcessors=[...isLogDrainMode?[]:[new BatchLogRecordProcessor({exportTimeoutMillis:7e4,exporter:new OTLPLogExporter({compression,headers:exporterHeaders,timeoutMillis:EXPORT_TIMEOUT_MS,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 drainMode=(opts.serverTracing??readServerTracingMode())===`drain`;if(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 +1 @@
|
|
|
1
|
-
import{log}from"../logger.mjs";import{readNextMajorVersion}from"../setup-warnings.mjs";import{detectActiveBundler}from"./detect-bundler.mjs";import{resolveReleaseRequest}from"./release/index.mjs";import{discoverSourceMaps}from"./source-maps/discover.mjs";import{reportUnmappedClientChunks}from"./source-maps/guard.mjs";import{EMPTY_TELEMETRY}from"./telemetry.mjs";import{classifyReleasePipelineError}from"@interfere/types/sdk/build-diagnostics";import{InterfereClient}from"@interfere/javascript";import{sourceMapUploadFetch}from"@interfere/build/source-map-upload-fetch";import{cleanupSourceMaps}from"@interfere/build/cleanup-source-maps";import{uploadSourceMapChunks}from"@interfere/build/upload-source-map-chunks";import{completeSourceMapsResponse}from"@interfere/types/data/source-maps";import{releaseBuildResultSchema}from"@interfere/types/releases/build-telemetry";import{createReleaseResponse}from"@interfere/types/releases/definition";import{extractRichness}from"@interfere/types/sdk/source-map-richness";async function runBuildPipeline(context,metadata){let client=new InterfereClient({fetch:sourceMapUploadFetch,baseUrl:metadata.apiUrl,apiKey:{token:metadata.apiKey},maxRetries:4,timeoutInSeconds:10}),start=performance.now(),timing={...EMPTY_TELEMETRY},{releaseSlug}=metadata,files=[],cleanupFailures=[],reportResult=`pipeline_failed`;try{let bundler=detectActiveBundler(process.env,process.argv,readNextMajorVersion(context.projectDir)),discovered=metadata.uploadSourceMaps||metadata.purgeSourceMaps?await discoverSourceMaps({bundler,...context}):{files:[],missingSourceMapCount:0,sourceFileCount:0};({files}=discovered);let{sourceFileCount}=discovered;metadata.uploadSourceMaps&&discovered.missingSourceMapCount>0&&log.warn(`Some source maps are missing`,[`${discovered.missingSourceMapCount} referenced source map(s) not found. Errors in those chunks will not resolve to source.`]);let config=await client.releases.getReleasesConfig({abortSignal:AbortSignal.timeout(6e4)});timing.discover=Math.round(performance.now()-start),timing.fileCount=files.length;let request=resolveReleaseRequest(metadata.buildId,metadata.environment,metadata.releaseSlug,config);if(!request.ok)return reportResult=null,{accepted:!1,fileCount:0,reason:request.reason};if(metadata.uploadSourceMaps&&files.length===0)return reportResult=`no_source_maps`,{accepted:!1,fileCount:0,reason:`no_source_maps`};metadata.uploadSourceMaps&&await reportUnmappedClientChunks({discovered:files,...context});let registering=performance.now(),release=createReleaseResponse.parse(await client.releases.createRelease(request.request,{abortSignal:AbortSignal.timeout(6e4)})),registered=performance.now();if(releaseSlug=release.destination.slug,timing.createRelease=Math.round(registered-registering),metadata.uploadSourceMaps){let signFiles=files.map(file=>({path:file.path,sizeBytes:new TextEncoder().encode(file.content).byteLength,...extractRichness(file.content)})),environment=metadata.environment??void 0,signed=await client.releases.signSourceMapUploads({releaseSlug,environment,files:signFiles},{abortSignal:AbortSignal.timeout(6e4)});timing.totalBytes=await uploadSourceMapChunks({compression:`gzip`,client,environment,releaseSlug,files,signed});let accepted=completeSourceMapsResponse.safeParse(await client.releases.finalizeSourceMaps({releaseSlug,environment,bundler,sourceFileCount,files:files.map(({path,hash,debugId,chunkUrl})=>({path,hash,debugId,chunkUrl}))},{abortSignal:AbortSignal.timeout(Math.max(0,signed.expiresAt*1e3-Date.now()))}));if(!accepted.success||!accepted.data.ok||accepted.data.fileCount!==files.length)throw Error(`Source-map server did not accept the complete batch`)}return timing.upload=metadata.uploadSourceMaps?Math.round(performance.now()-registered):0,reportResult=`ok`,{accepted:!0,buildId:release.build.hash,cleanupFailures,config,fileCount:metadata.uploadSourceMaps?files.length:0,release,timing}}catch(error){throw reportResult=releaseBuildResultSchema.safeParse(classifyReleasePipelineError(error).toLowerCase()).data??`pipeline_failed`,error}finally{let cleanupStart=performance.now();if(metadata.purgeSourceMaps)try{cleanupFailures.push(...await cleanupSourceMaps(files))}catch(error){cleanupFailures.push({absolute:context.distDir,code:void 0,message:error instanceof Error?error.message:String(error)})}timing.cleanup=Math.round(performance.now()-cleanupStart),cleanupFailures.length>0&&log.warn(`Source-map cleanup left files on disk`,cleanupFailures.map(failure=>`${failure.absolute}: ${failure.message}`)),timing.total=Math.round(performance.now()-start),reportResult!==null&&await client.releases.reportTelemetry({...timing,result:reportResult,releaseSlug},{abortSignal:AbortSignal.timeout(1e4)}).catch(()=>void 0)}}export{runBuildPipeline};
|
|
1
|
+
import{log}from"../logger.mjs";import{readNextMajorVersion}from"../setup-warnings.mjs";import{detectActiveBundler}from"./detect-bundler.mjs";import{resolveReleaseRequest}from"./release/index.mjs";import{discoverSourceMaps}from"./source-maps/discover.mjs";import{reportUnmappedClientChunks}from"./source-maps/guard.mjs";import{EMPTY_TELEMETRY}from"./telemetry.mjs";import{PUBLIC_KEY_HEADER}from"@interfere/constants/api";import{classifyReleasePipelineError}from"@interfere/types/sdk/build-diagnostics";import{InterfereClient}from"@interfere/javascript";import{sourceMapUploadFetch}from"@interfere/build/source-map-upload-fetch";import{cleanupSourceMaps}from"@interfere/build/cleanup-source-maps";import{uploadSourceMapChunks}from"@interfere/build/upload-source-map-chunks";import{completeSourceMapsResponse}from"@interfere/types/data/source-maps";import{releaseBuildResultSchema}from"@interfere/types/releases/build-telemetry";import{createReleaseResponse}from"@interfere/types/releases/definition";import{extractRichness}from"@interfere/types/sdk/source-map-richness";async function runBuildPipeline(context,metadata){let client=new InterfereClient({fetch:sourceMapUploadFetch,baseUrl:metadata.apiUrl,apiKey:{token:metadata.apiKey},headers:{[PUBLIC_KEY_HEADER]:metadata.publicKey},maxRetries:4,timeoutInSeconds:10}),start=performance.now(),timing={...EMPTY_TELEMETRY},{releaseSlug}=metadata,files=[],cleanupFailures=[],reportResult=`pipeline_failed`;try{let bundler=detectActiveBundler(process.env,process.argv,readNextMajorVersion(context.projectDir)),discovered=metadata.uploadSourceMaps||metadata.purgeSourceMaps?await discoverSourceMaps({bundler,...context}):{files:[],missingSourceMapCount:0,sourceFileCount:0};({files}=discovered);let{sourceFileCount}=discovered;metadata.uploadSourceMaps&&discovered.missingSourceMapCount>0&&log.warn(`Some source maps are missing`,[`${discovered.missingSourceMapCount} referenced source map(s) not found. Errors in those chunks will not resolve to source.`]);let config=await client.releases.getReleasesConfig({abortSignal:AbortSignal.timeout(6e4)});timing.discover=Math.round(performance.now()-start),timing.fileCount=files.length;let request=resolveReleaseRequest(metadata.buildId,metadata.environment,metadata.releaseSlug,config);if(!request.ok)return reportResult=null,{accepted:!1,fileCount:0,reason:request.reason};if(metadata.uploadSourceMaps&&files.length===0)return reportResult=`no_source_maps`,{accepted:!1,fileCount:0,reason:`no_source_maps`};metadata.uploadSourceMaps&&await reportUnmappedClientChunks({discovered:files,...context});let registering=performance.now(),release=createReleaseResponse.parse(await client.releases.createRelease(request.request,{abortSignal:AbortSignal.timeout(6e4)})),registered=performance.now();if(releaseSlug=release.destination.slug,timing.createRelease=Math.round(registered-registering),metadata.uploadSourceMaps){let signFiles=files.map(file=>({path:file.path,sizeBytes:new TextEncoder().encode(file.content).byteLength,...extractRichness(file.content)})),environment=metadata.environment??void 0,signed=await client.releases.signSourceMapUploads({releaseSlug,environment,files:signFiles},{abortSignal:AbortSignal.timeout(6e4)});timing.totalBytes=await uploadSourceMapChunks({compression:`gzip`,client,environment,releaseSlug,files,signed});let accepted=completeSourceMapsResponse.safeParse(await client.releases.finalizeSourceMaps({releaseSlug,environment,bundler,sourceFileCount,files:files.map(({path,hash,debugId,chunkUrl})=>({path,hash,debugId,chunkUrl}))},{abortSignal:AbortSignal.timeout(Math.max(0,signed.expiresAt*1e3-Date.now()))}));if(!accepted.success||!accepted.data.ok||accepted.data.fileCount!==files.length)throw Error(`Source-map server did not accept the complete batch`)}return timing.upload=metadata.uploadSourceMaps?Math.round(performance.now()-registered):0,reportResult=`ok`,{accepted:!0,buildId:release.build.hash,cleanupFailures,config,fileCount:metadata.uploadSourceMaps?files.length:0,release,timing}}catch(error){throw reportResult=releaseBuildResultSchema.safeParse(classifyReleasePipelineError(error).toLowerCase()).data??`pipeline_failed`,error}finally{let cleanupStart=performance.now();if(metadata.purgeSourceMaps)try{cleanupFailures.push(...await cleanupSourceMaps(files))}catch(error){cleanupFailures.push({absolute:context.distDir,code:void 0,message:error instanceof Error?error.message:String(error)})}timing.cleanup=Math.round(performance.now()-cleanupStart),cleanupFailures.length>0&&log.warn(`Source-map cleanup left files on disk`,cleanupFailures.map(failure=>`${failure.absolute}: ${failure.message}`)),timing.total=Math.round(performance.now()-start),reportResult!==null&&await client.releases.reportTelemetry({...timing,result:reportResult,releaseSlug},{abortSignal:AbortSignal.timeout(1e4)}).catch(()=>void 0)}}export{runBuildPipeline};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextMiddleware, NextRequest } from "next/server";
|
|
2
|
-
//#region src/internal/server/wrap-proxy.d.ts
|
|
2
|
+
//#region src/internal/server/actions/wrap-proxy.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Wraps a Next.js proxy (formerly `middleware`) so thrown errors are captured
|
|
5
5
|
* by Interfere (mechanism `auto.function.nextjs.proxy`, with request method +
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{captureError}from"../capture/capture.mjs";import{MECHANISM_TYPE}from"@interfere/types/sdk/errors";function withInterfereProxy(proxy){return((request,event)=>{try{let result=proxy(request,event);return result instanceof Promise?result.catch(error=>{throw reportProxyError(error,request),error}):result}catch(error){throw reportProxyError(error,request),error}})}function reportProxyError(error,request){captureError(error,request,{mechanism:{handled:!1,type:MECHANISM_TYPE.nextjs.proxy},nextjs:{requestMethod:request.method,requestPath:request.nextUrl.pathname}})}export{withInterfereProxy};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/internal/server/wrap-server-action.d.ts
|
|
1
|
+
//#region src/internal/server/actions/wrap-server-action.d.ts
|
|
2
2
|
interface ServerActionOptions {
|
|
3
3
|
/** Derives argument types and class names for args_shape without recording values. */
|
|
4
4
|
readonly args?: readonly unknown[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CaptureErrorContext, OnRequestErrorContext } from "
|
|
2
|
-
//#region src/internal/server/capture.d.ts
|
|
1
|
+
import { CaptureErrorContext, OnRequestErrorContext } from "../types.mjs";
|
|
2
|
+
//#region src/internal/server/capture/capture.d.ts
|
|
3
3
|
export declare function captureError(error: unknown, _request?: unknown, context?: CaptureErrorContext): void;
|
|
4
4
|
interface OnRequestErrorRequest {
|
|
5
5
|
readonly headers?: Record<string, string>;
|
|
@@ -0,0 +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,7 +1,7 @@
|
|
|
1
1
|
import { ExportResult } from "@opentelemetry/core";
|
|
2
2
|
import { ReadableSpan, SpanExporter } from "@opentelemetry/sdk-trace-base";
|
|
3
3
|
import { InterfereClient } from "@interfere/javascript";
|
|
4
|
-
//#region src/internal/server/edge-exporter.d.ts
|
|
4
|
+
//#region src/internal/server/exporters/edge-exporter.d.ts
|
|
5
5
|
export declare class FetchTraceExporter implements SpanExporter {
|
|
6
6
|
private readonly client;
|
|
7
7
|
constructor(options: InterfereClient.Options);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{exportTracePayload}from"../tracing/trace-payload.mjs";import{ExportResultCode}from"@opentelemetry/core";import{TELEMETRY_EXPORT_TIMEOUT_MS,retryTelemetry}from"@interfere/helpers/retry-telemetry";import{InterfereClient}from"@interfere/javascript";import{JsonTraceSerializer}from"@opentelemetry/otlp-transformer";var FetchTraceExporter=class{client;constructor(options){this.client=new InterfereClient({...options,maxRetries:0,timeoutInSeconds:TELEMETRY_EXPORT_TIMEOUT_MS/1e3,fetch:Object.assign(async(url,init)=>retryTelemetry(async signal=>globalThis.fetch(url,{...init,keepalive:!0,signal}),init?.signal),globalThis.fetch)})}export(spans,resultCallback){if(spans.length===0){resultCallback({code:ExportResultCode.SUCCESS});return}let payload=JsonTraceSerializer.serializeRequest(spans);exportTracePayload(payload,this.client,resultCallback)}async shutdown(){return Promise.resolve()}async forceFlush(){return Promise.resolve()}};export{FetchTraceExporter};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { W3CTraceContextPropagator } from "@opentelemetry/core";
|
|
2
2
|
import { Context, TextMapSetter } from "@opentelemetry/api";
|
|
3
|
-
//#region src/internal/server/cache-safe-trace-context.d.ts
|
|
3
|
+
//#region src/internal/server/tracing/cache-safe-trace-context.d.ts
|
|
4
4
|
export interface CacheSafeTraceContextPropagatorOptions {
|
|
5
5
|
isStaticRender?: () => boolean;
|
|
6
6
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isStaticGenerationRender}from"../static-generation.mjs";import{W3CTraceContextPropagator}from"@opentelemetry/core";import{z}from"zod";const metadataEntrySchema=z.object({key:z.string()});var CacheSafeTraceContextPropagator=class extends W3CTraceContextPropagator{isStaticRender;constructor(options={}){super(),this.isStaticRender=options.isStaticRender??isStaticGenerationRender}inject(context,carrier,setter){if(Array.isArray(carrier)){for(let index=carrier.length-1;index>=0;index--){let entry=metadataEntrySchema.safeParse(carrier[index]);entry.success&&(entry.data.key===`traceparent`||entry.data.key===`tracestate`)&&carrier.splice(index,1)}if(this.isStaticRender())return}super.inject(context,carrier,setter)}};export{CacheSafeTraceContextPropagator};
|
package/dist/internal/server/{counter-id-generator.d.mts → tracing/counter-id-generator.d.mts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IdGenerator } from "@opentelemetry/sdk-trace-base";
|
|
2
|
-
//#region src/internal/server/counter-id-generator.d.ts
|
|
2
|
+
//#region src/internal/server/tracing/counter-id-generator.d.ts
|
|
3
3
|
export declare class CounterIdGenerator implements IdGenerator {
|
|
4
4
|
private readonly tracePrefix;
|
|
5
5
|
private readonly spanPrefix;
|
package/dist/internal/server/{freshness-propagator.d.mts → tracing/freshness-propagator.d.mts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Context, TextMapPropagator, TextMapSetter } from "@opentelemetry/api";
|
|
2
|
-
//#region src/internal/server/freshness-propagator.d.ts
|
|
2
|
+
//#region src/internal/server/tracing/freshness-propagator.d.ts
|
|
3
3
|
export interface FreshnessStampingPropagatorOptions {
|
|
4
4
|
isStaticRender?: () => boolean;
|
|
5
5
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isStaticGenerationRender}from"../static-generation.mjs";import{TraceState,isTracingSuppressed}from"@opentelemetry/core";import{isSpanContextValid,trace}from"@opentelemetry/api";import{TRACESTATE_FRESHNESS_KEY,freshnessStamp}from"@interfere/types/sdk/trace-freshness";const TRACE_STATE_HEADER=`tracestate`;function carrierHoldsTraceparent(carrier){return carrier.some(entry=>typeof entry==`object`&&!!entry&&`key`in entry&&entry.key===`traceparent`)}var FreshnessStampingPropagator=class{isStaticRender;constructor(options={}){this.isStaticRender=options.isStaticRender??isStaticGenerationRender}inject(context,carrier,setter){let spanContext=trace.getSpanContext(context);if(!spanContext||!isSpanContextValid(spanContext)||isTracingSuppressed(context)||Array.isArray(carrier)&&this.isStaticRender()&&!carrierHoldsTraceparent(carrier))return;let traceState=(spanContext.traceState??new TraceState).set(TRACESTATE_FRESHNESS_KEY,freshnessStamp(Date.now()));setter.set(carrier,TRACE_STATE_HEADER,traceState.serialize())}extract(context){return context}fields(){return[TRACE_STATE_HEADER]}};export{FreshnessStampingPropagator};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IdGenerator } from "@opentelemetry/sdk-trace-base";
|
|
2
|
-
//#region src/internal/server/id-generator.d.ts
|
|
2
|
+
//#region src/internal/server/tracing/id-generator.d.ts
|
|
3
3
|
/** Generates random telemetry IDs, with counter IDs when runtime randomness is unavailable. */
|
|
4
4
|
export declare class PrerenderSafeIdGenerator implements IdGenerator {
|
|
5
5
|
private readonly random;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isStaticGenerationRender}from"../static-generation.mjs";import{activeTraceparent}from"./traceparent.mjs";import{jsx}from"react/jsx-runtime";function TraceMeta(){if(isStaticGenerationRender())return null;let traceparent=activeTraceparent();return traceparent?jsx(`meta`,{content:traceparent,name:`traceparent`}):null}export{TraceMeta};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ExportResult } from "@opentelemetry/core";
|
|
2
2
|
import { InterfereClient } from "@interfere/javascript";
|
|
3
|
-
//#region src/internal/server/trace-payload.d.ts
|
|
3
|
+
//#region src/internal/server/tracing/trace-payload.d.ts
|
|
4
4
|
export declare function exportTracePayload(payload: Uint8Array | null | undefined, client: InterfereClient, resultCallback: (result: ExportResult) => void): void;
|
|
5
5
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ServerActionMarker } from "./server-action-marker.mjs";
|
|
1
|
+
import { ServerActionMarker } from "./actions/server-action-marker.mjs";
|
|
2
2
|
import { NextjsContext } from "@interfere/types/sdk/plugins/context/next";
|
|
3
3
|
import { ErrorMechanism } from "@interfere/types/sdk/plugins/payload/errors";
|
|
4
4
|
//#region src/internal/server/types.d.ts
|
package/dist/package.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var name=`@interfere/next`,version=`12.
|
|
1
|
+
var name=`@interfere/next`,version=`12.2.0`;export{name,version};
|
package/dist/server.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CaptureErrorContext, OnRequestErrorContext } from "./internal/server/types.mjs";
|
|
2
|
-
import { captureError, onRequestError } from "./internal/server/capture.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { captureError, onRequestError } from "./internal/server/capture/capture.mjs";
|
|
3
|
+
import { withInterfereProxy } from "./internal/server/actions/wrap-proxy.mjs";
|
|
4
|
+
import { withInterfereServerAction } from "./internal/server/actions/wrap-server-action.mjs";
|
|
5
|
+
import { TraceMeta } from "./internal/server/tracing/trace-meta.mjs";
|
|
6
6
|
export { type CaptureErrorContext, type OnRequestErrorContext, TraceMeta, captureError, onRequestError, withInterfereProxy, withInterfereServerAction };
|
package/dist/server.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{captureError,onRequestError}from"./internal/server/capture.mjs";import{TraceMeta}from"./internal/server/trace-meta.mjs";import{withInterfereProxy}from"./internal/server/wrap-proxy.mjs";import{withInterfereServerAction}from"./internal/server/wrap-server-action.mjs";export{TraceMeta,captureError,onRequestError,withInterfereProxy,withInterfereServerAction};
|
|
1
|
+
import{captureError,onRequestError}from"./internal/server/capture/capture.mjs";import{TraceMeta}from"./internal/server/tracing/trace-meta.mjs";import{withInterfereProxy}from"./internal/server/actions/wrap-proxy.mjs";import{withInterfereServerAction}from"./internal/server/actions/wrap-server-action.mjs";export{TraceMeta,captureError,onRequestError,withInterfereProxy,withInterfereServerAction};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interfere/next",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.2.0",
|
|
4
4
|
"description": "Build software that never breaks.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"error-tracking",
|
|
@@ -97,12 +97,12 @@
|
|
|
97
97
|
"typecheck": "bun check"
|
|
98
98
|
},
|
|
99
99
|
"dependencies": {
|
|
100
|
-
"@interfere/build": "^0.3.
|
|
101
|
-
"@interfere/constants": "^9.
|
|
102
|
-
"@interfere/helpers": "^9.
|
|
100
|
+
"@interfere/build": "^0.3.3",
|
|
101
|
+
"@interfere/constants": "^9.3.0",
|
|
102
|
+
"@interfere/helpers": "^9.3.0",
|
|
103
103
|
"@interfere/javascript": "^0.3.0",
|
|
104
|
-
"@interfere/react": "^12.
|
|
105
|
-
"@interfere/types": "^10.
|
|
104
|
+
"@interfere/react": "^12.2.0",
|
|
105
|
+
"@interfere/types": "^10.4.0",
|
|
106
106
|
"@opentelemetry/api": "1.9.1",
|
|
107
107
|
"@opentelemetry/api-logs": "0.222.0",
|
|
108
108
|
"@opentelemetry/baggage-span-processor": "0.5.0",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"zod": "4.6.6"
|
|
124
124
|
},
|
|
125
125
|
"devDependencies": {
|
|
126
|
-
"@interfere/test-utils": "^10.0.
|
|
126
|
+
"@interfere/test-utils": "^10.0.2",
|
|
127
127
|
"@interfere/typescript-config": "^9.0.2",
|
|
128
128
|
"@interfere/vitest-config": "0.0.0",
|
|
129
129
|
"@playwright/test": "1.63.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{isStaticGenerationRender}from"./static-generation.mjs";import{W3CTraceContextPropagator}from"@opentelemetry/core";import{z}from"zod";const metadataEntrySchema=z.object({key:z.string()});var CacheSafeTraceContextPropagator=class extends W3CTraceContextPropagator{isStaticRender;constructor(options={}){super(),this.isStaticRender=options.isStaticRender??isStaticGenerationRender}inject(context,carrier,setter){if(Array.isArray(carrier)){for(let index=carrier.length-1;index>=0;index--){let entry=metadataEntrySchema.safeParse(carrier[index]);entry.success&&(entry.data.key===`traceparent`||entry.data.key===`tracestate`)&&carrier.splice(index,1)}if(this.isStaticRender())return}super.inject(context,carrier,setter)}};export{CacheSafeTraceContextPropagator};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{isEnabledOnServer}from"../env.mjs";import{flushServerTelemetry}from"./server-flush.mjs";import{isPluginEnabled}from"./remote-config.mjs";import{readServerAction}from"./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";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(`interfere.captureError`,{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 +0,0 @@
|
|
|
1
|
-
import{exportTracePayload}from"./trace-payload.mjs";import{ExportResultCode}from"@opentelemetry/core";import{TELEMETRY_EXPORT_TIMEOUT_MS,retryTelemetry}from"@interfere/helpers/retry-telemetry";import{InterfereClient}from"@interfere/javascript";import{JsonTraceSerializer}from"@opentelemetry/otlp-transformer";var FetchTraceExporter=class{client;constructor(options){this.client=new InterfereClient({...options,maxRetries:0,timeoutInSeconds:TELEMETRY_EXPORT_TIMEOUT_MS/1e3,fetch:Object.assign(async(url,init)=>retryTelemetry(async signal=>globalThis.fetch(url,{...init,keepalive:!0,signal}),init?.signal),globalThis.fetch)})}export(spans,resultCallback){if(spans.length===0){resultCallback({code:ExportResultCode.SUCCESS});return}let payload=JsonTraceSerializer.serializeRequest(spans);exportTracePayload(payload,this.client,resultCallback)}async shutdown(){return Promise.resolve()}async forceFlush(){return Promise.resolve()}};export{FetchTraceExporter};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{isStaticGenerationRender}from"./static-generation.mjs";import{TraceState,isTracingSuppressed}from"@opentelemetry/core";import{isSpanContextValid,trace}from"@opentelemetry/api";import{TRACESTATE_FRESHNESS_KEY,freshnessStamp}from"@interfere/types/sdk/trace-freshness";const TRACE_STATE_HEADER=`tracestate`;function carrierHoldsTraceparent(carrier){return carrier.some(entry=>typeof entry==`object`&&!!entry&&`key`in entry&&entry.key===`traceparent`)}var FreshnessStampingPropagator=class{isStaticRender;constructor(options={}){this.isStaticRender=options.isStaticRender??isStaticGenerationRender}inject(context,carrier,setter){let spanContext=trace.getSpanContext(context);if(!spanContext||!isSpanContextValid(spanContext)||isTracingSuppressed(context)||Array.isArray(carrier)&&this.isStaticRender()&&!carrierHoldsTraceparent(carrier))return;let traceState=(spanContext.traceState??new TraceState).set(TRACESTATE_FRESHNESS_KEY,freshnessStamp(Date.now()));setter.set(carrier,TRACE_STATE_HEADER,traceState.serialize())}extract(context){return context}fields(){return[TRACE_STATE_HEADER]}};export{FreshnessStampingPropagator};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{isStaticGenerationRender}from"./static-generation.mjs";import{activeTraceparent}from"./traceparent.mjs";import{jsx}from"react/jsx-runtime";function TraceMeta(){if(isStaticGenerationRender())return null;let traceparent=activeTraceparent();return traceparent?jsx(`meta`,{content:traceparent,name:`traceparent`}):null}export{TraceMeta};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{captureError}from"./capture.mjs";import{MECHANISM_TYPE}from"@interfere/types/sdk/errors";function withInterfereProxy(proxy){return((request,event)=>{try{let result=proxy(request,event);return result instanceof Promise?result.catch(error=>{throw reportProxyError(error,request),error}):result}catch(error){throw reportProxyError(error,request),error}})}function reportProxyError(error,request){captureError(error,request,{mechanism:{handled:!1,type:MECHANISM_TYPE.nextjs.proxy},nextjs:{requestMethod:request.method,requestPath:request.nextUrl.pathname}})}export{withInterfereProxy};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|