@interfere/next 12.4.2 → 12.4.4
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 +7 -6
- package/dist/config.d.mts +3 -2
- package/dist/config.mjs +1 -1
- package/dist/instrumentation.d.mts +1 -1
- package/dist/instrumentation.edge.d.mts +1 -0
- package/dist/instrumentation.edge.mjs +1 -1
- package/dist/instrumentation.mjs +1 -1
- package/dist/internal/build/configure-build.d.mts +1 -2
- package/dist/internal/build/configure-build.mjs +1 -1
- package/dist/internal/build/injected-values.d.mts +1 -2
- package/dist/internal/build/injected-values.mjs +1 -1
- package/dist/internal/build/pipeline.d.mts +1 -1
- package/dist/internal/build/pipeline.mjs +1 -1
- package/dist/internal/build/preflight.d.mts +5 -0
- package/dist/internal/build/preflight.mjs +1 -0
- package/dist/internal/build/release/index.d.mts +3 -1
- package/dist/internal/build/value-injection-loader.mjs +1 -1
- package/dist/internal/build-marker-guard.mjs +1 -1
- package/dist/internal/env.d.mts +1 -9
- package/dist/internal/env.mjs +1 -1
- package/dist/internal/server/actions/wrap-server-action.mjs +1 -1
- package/dist/internal/server/capture/capture.mjs +1 -1
- package/dist/internal/server/capture/global-handlers.mjs +1 -1
- package/dist/internal/server/contain.d.mts +4 -0
- package/dist/internal/server/contain.mjs +1 -0
- package/dist/internal/server/exporters/edge-exporter.d.mts +1 -1
- package/dist/internal/server/exporters/edge-exporter.mjs +1 -1
- package/dist/internal/server/exporters/server-flush.d.mts +2 -0
- package/dist/internal/server/exporters/server-flush.mjs +1 -1
- package/dist/internal/server/tracing/contained-span-processor.d.mts +10 -0
- package/dist/internal/server/tracing/contained-span-processor.mjs +1 -0
- package/dist/internal/server/tracing/trace-meta.mjs +1 -1
- package/dist/internal/server/tracing/trace-payload.d.mts +1 -1
- package/dist/package.mjs +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -96,12 +96,11 @@ An explicit `serviceName` passed to browser `init()` or server/edge `register()`
|
|
|
96
96
|
|
|
97
97
|
### Environment Variables
|
|
98
98
|
|
|
99
|
-
| Variable
|
|
100
|
-
|
|
|
101
|
-
| `INTERFERE_PUBLIC_KEY`
|
|
102
|
-
| `INTERFERE_API_KEY`
|
|
103
|
-
| `
|
|
104
|
-
| `INTERFERE_API_URL` | No | Override the ingest endpoint in `direct` mode (e.g. a regional endpoint). Defaults to Interfere's endpoint. |
|
|
99
|
+
| Variable | Required | Description |
|
|
100
|
+
| ---------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
101
|
+
| `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. |
|
|
102
|
+
| `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. |
|
|
103
|
+
| `INTERFERE_API_URL` | No | Override the ingest endpoint in `direct` mode (e.g. a regional endpoint). Defaults to Interfere's endpoint. |
|
|
105
104
|
|
|
106
105
|
### Connect releases to Vercel
|
|
107
106
|
|
|
@@ -189,6 +188,8 @@ The build-injected surface configuration selects the export path; there is no op
|
|
|
189
188
|
|
|
190
189
|
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.
|
|
191
190
|
|
|
191
|
+
Interfere's server code never fails your app. If `register()`, `onRequestError`, `captureError`, `flush()`, `withInterfereProxy`, `withInterfereServerAction` or `TraceMeta` hits an error inside Interfere, it logs one `[interfere] … failed inside the Interfere SDK` warning for that call site and your code continues without Interfere. The wrappers always rethrow your own error unchanged.
|
|
192
|
+
|
|
192
193
|
`@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.
|
|
193
194
|
|
|
194
195
|
### Browser session correlation
|
package/dist/config.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Interfere } from "@interfere/javascript";
|
|
1
2
|
import { ReleaseSlug } from "@interfere/types/releases/slug";
|
|
2
3
|
import { NextConfig } from "next";
|
|
3
4
|
//#region src/config.d.ts
|
|
@@ -38,6 +39,7 @@ export interface NextPluginConfig {
|
|
|
38
39
|
readonly disable?: boolean;
|
|
39
40
|
}
|
|
40
41
|
export interface ResolvedBuildConfig {
|
|
42
|
+
readonly releaseConfig?: Interfere.ReleasesConfigResponse | undefined;
|
|
41
43
|
readonly apiKey: string | null;
|
|
42
44
|
readonly apiUrl: string;
|
|
43
45
|
/**
|
|
@@ -79,10 +81,9 @@ type InterfereConfigFn = (phase: string, context: {
|
|
|
79
81
|
defaultConfig: NextConfig;
|
|
80
82
|
}) => NextConfigWithInterfere | Promise<NextConfigWithInterfere> | PromiseLike<NextConfigWithInterfere>;
|
|
81
83
|
type InterfereConfigInput = NextConfigWithInterfere | InterfereConfigFn;
|
|
82
|
-
type ResolvedInterfereConfig<TConfig> = [TConfig] extends [(...args: never[]) => infer R] ? R extends Promise<unknown> ? Promise<NextConfig> : R extends PromiseLike<unknown> ? PromiseLike<NextConfig> : NextConfig : NextConfig;
|
|
83
84
|
type WithInterfereResult<TConfig> = (phase: string, context?: {
|
|
84
85
|
defaultConfig: NextConfig;
|
|
85
|
-
}) =>
|
|
86
|
+
}) => TConfig extends ((...args: never[]) => PromiseLike<NextConfigWithInterfere>) ? Promise<NextConfig> : NextConfig | Promise<NextConfig>;
|
|
86
87
|
export declare function withInterfere<TConfig extends InterfereConfigInput>(upstream?: TConfig): WithInterfereResult<TConfig>;
|
|
87
88
|
export declare function withInterfere<TConfig extends InterfereConfigInput>(settings: NextPluginConfig, config: TConfig): WithInterfereResult<TConfig>;
|
|
88
89
|
export interface ProductionCompileContext {
|
package/dist/config.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{configureBuild}from"./internal/build/configure-build.mjs";import{runGitCommand}from"./internal/build/release/git.mjs";import{interfereInjectedValues}from"./internal/build/injected-values.mjs";import{readInterfereEnv}from"./internal/env.mjs";import{
|
|
1
|
+
import{FatalError,log}from"./internal/logger.mjs";import{withBuildPreflight}from"./internal/build/preflight.mjs";import{configureBuild}from"./internal/build/configure-build.mjs";import{runGitCommand}from"./internal/build/release/git.mjs";import{interfereInjectedValues}from"./internal/build/injected-values.mjs";import{readInterfereEnv}from"./internal/env.mjs";import{resolveReleaseSlug}from"./internal/release-slug.mjs";import{readNextMajorVersion,warnIfServerInstrumentationMissing}from"./internal/setup-warnings.mjs";import{resolveBuildServiceName}from"@interfere/build/service-name";import{API_URL}from"@interfere/constants/api";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{checkReleaseConfig,diagnoseRelease}from"@interfere/types/sdk/build-diagnostics";import{resolveDeployEnvironment}from"@interfere/types/sdk/deploy-environment";import{BUILD_MARKER_GLOBAL,DISABLED_GLOBAL}from"@interfere/types/sdk/injected-globals";import{PHASE_PRODUCTION_BUILD}from"next/constants.js";const CLIENT_TRACE_METADATA_KEYS=[`traceparent`,`tracestate`,`baggage`],DIAGNOSTIC_CONTEXT={publicKeyEnvVar:`INTERFERE_PUBLIC_KEY`,docsUrl:`https://interfere.com/docs/sdk/next-js`};function isThenable(value){return typeof value==`object`&&!!value&&`then`in value&&typeof value.then==`function`}function withInterfere(...args){let[settings,nextConfig]=args.length===2?[args[0],args[1]]:[{},args[0]??{}];return(phase,context)=>{let resolved=typeof nextConfig==`function`?nextConfig(phase,context??{defaultConfig:{}}):nextConfig;return isThenable(resolved)?Promise.resolve(resolved).then(async config=>buildInterfereConfig(phase,settings,config)):buildInterfereConfig(phase,settings,resolved)}}function interfereCompilerConfig(compiler,config){let existingHook=compiler?.runAfterProductionCompile;return omitUndefined({...compiler,async runAfterProductionCompile(context){existingHook&&await existingHook(context),config.publicKey===null&&log.warn(`Public key not set`,[`INTERFERE_PUBLIC_KEY is not set, so this build sends no events.`,`Set it on builds that you deploy.`]);try{await runBuildReleasePipeline(context,config)}catch(error){if(error instanceof FatalError)throw error;let message=error instanceof Error?error.message:String(error);log.warn(`Skipping release pipeline`,[`Interfere encountered an unexpected error.`,`Build will continue, but this release's data will be ingested, but ignored as finding when generating insights.`,`Error: ${message}`])}}})}function resolveBrowserSourceMaps(config,productionBrowserSourceMaps){let releasing=config.apiKey!==null&&config.uploadSourceMaps;return releasing&&productionBrowserSourceMaps===!1&&log.warn(`Forcing productionBrowserSourceMaps`,[`withInterfere() requires production source maps to symbolicate errors.`,"Your `productionBrowserSourceMaps: false` was overridden to `true`."]),releasing?!0:productionBrowserSourceMaps}function disabledInterfereConfig(nextConfig){let{interfere:_interfere,webpack,turbopack,...rest}=nextConfig,projectDir=process.cwd(),build=configureBuild({existingTurbopack:turbopack,existingWebpack:webpack,nextMajor:readNextMajorVersion(projectDir),projectDir,remoteConfigFetch:void 0,values:{[BUILD_MARKER_GLOBAL]:!0,[DISABLED_GLOBAL]:!0}});return omitUndefined({...rest,turbopack:build.turbopack,webpack:build.webpack})}function buildInterfereConfig(phase,settings,nextConfig){let config=resolveBuildConfig(settings,phase);return withBuildPreflight(!settings.disable&&phase===PHASE_PRODUCTION_BUILD,config,releaseConfig=>buildResolvedInterfereConfig(phase,settings,nextConfig,{...config,releaseConfig}),()=>disabledInterfereConfig(nextConfig))}function buildResolvedInterfereConfig(phase,settings,nextConfig,resolvedBuild){if(settings.disable)return disabledInterfereConfig(nextConfig);let{interfere,env:userEnv,webpack,turbopack,compiler,productionBrowserSourceMaps,...rest}=nextConfig;if(interfere&&`buildId`in interfere&&interfere.buildId!==void 0)return log.fatal(`Removed: interfere.buildId`,["`interfere.buildId` was removed in SDK 10. It only worked at build time, so server-side spans drifted from the bundle's slug.","Override the commit SHA via the `INTERFERE_SOURCE_ID` env var on both build and runtime envs instead."]);let projectDir=process.cwd(),config=resolvedBuild??resolveBuildConfig(settings,phase),apiHost=resolveBrowserApiHost(interfere?.apiHost,config.apiUrl),nextMajor=readNextMajorVersion(projectDir),build=configureBuild({existingTurbopack:turbopack,existingWebpack:webpack,nextMajor,projectDir,remoteConfigFetch:resolveRemoteConfigFetch(config,phase),values:interfereInjectedValues(config,apiHost,resolveBuildServiceName({root:projectDir,option:settings.serviceName}))});if(config.publicKey!==null&&!build.clientInstrumentationApplies)return log.fatal(`Missing instrumentation client`,[`INTERFERE_PUBLIC_KEY is set but no instrumentation-client file was found.`,`Create an instrumentation-client.ts file in your project root or src/ directory.`]);config.publicKey!==null&&warnIfServerInstrumentationMissing(projectDir);let experimental=interfereExperimentalConfig({existing:rest.experimental,instrumentationHook:nextMajor===null||nextMajor<=14,traceMetadata:config.publicKey!==null});return omitUndefined({...rest,env:userEnv,compiler:interfereCompilerConfig(compiler,config),productionBrowserSourceMaps:resolveBrowserSourceMaps(config,productionBrowserSourceMaps),turbopack:build.turbopack,webpack:build.webpack,experimental})}function interfereExperimentalConfig({existing,instrumentationHook,traceMetadata}){let merged={...existing};return instrumentationHook&&(merged.instrumentationHook=!0),traceMetadata&&(merged.clientTraceMetadata=[...new Set([...existing?.clientTraceMetadata??[],...CLIENT_TRACE_METADATA_KEYS])]),Object.keys(merged).length>0?merged:void 0}function resolveBrowserApiHost(apiHost,apiUrl){let host=apiHost??apiUrl;if(host!==API_URL)return normalizeHost(host)}function normalizeHost(host){try{return new URL(host).origin}catch{return log.fatal(`Invalid apiHost`,[`interfere.apiHost must be a full URL, e.g. "https://in.yourapp.com".`,`Received: ${host}`])}}function resolveBuildConfig(settings,phase){let{apiKey,apiUrl,publicKey}=readInterfereEnv(),{commitSha,slug}=resolveReleaseSlug(runGitCommand),environment=phase===PHASE_PRODUCTION_BUILD?resolveDeployEnvironment(process.env):null;return{apiKey:apiKey??null,apiUrl,buildId:commitSha,environment,publicKey:publicKey??null,purgeSourceMaps:settings.sourceMaps?.purge??!0,uploadSourceMaps:settings.sourceMaps?.upload??!0,releaseSlug:slug}}function resolveRemoteConfigFetch(config,phase){if(phase===PHASE_PRODUCTION_BUILD&&config.publicKey)return{apiUrl:config.apiUrl,cacheScope:crypto.randomUUID(),publicKey:config.publicKey}}async function runBuildReleasePipeline(context,config){let release=checkReleaseConfig(DIAGNOSTIC_CONTEXT,{apiKey:config.apiKey,commitSha:config.buildId,releaseSlug:config.releaseSlug});if(release.status===`skip`){log.warn(release.title,[...release.details]);return}if(release.status===`fail`)return log.fatal(release.title,[...release.details]);let{runBuildPipeline}=await import(`./internal/build/pipeline.mjs`);try{let result=await runBuildPipeline(context,{...config,apiKey:release.apiKey,buildId:release.commitSha,releaseSlug:release.releaseSlug});if(!result.accepted){result.reason===`no_source_maps`&&log.warn(`Release remains unready`,[`No source maps found. Enable source-map generation and upload them during the build.`]);return}log.info(`Completed`,[`https://interfere.com/~/${result.config.org.slug}/apps/${result.config.surface.slug}`,`Release: ${result.release.destination.slug}`,`Build: ${result.buildId}`,config.uploadSourceMaps?`Artifacts: ${result.fileCount} source maps`:`Source-map uploads disabled`])}catch(error){let diagnostic=diagnoseRelease(DIAGNOSTIC_CONTEXT,error);log[diagnostic.severity](diagnostic.title,[...diagnostic.details])}}export{withInterfere};
|
|
@@ -31,7 +31,7 @@ export interface InterfereOtelKit {
|
|
|
31
31
|
}
|
|
32
32
|
/** Builds components without registering providers. Returns null when INTERFERE_PUBLIC_KEY is unset. */
|
|
33
33
|
export declare function buildInterfereOtelKit(opts?: ServerInstrumentationOptions): InterfereOtelKit | null;
|
|
34
|
-
/** Registers standalone tracing, using Vercel's runtime export path in drain mode. Use the kit when another bootstrap owns the provider. */
|
|
34
|
+
/** Registers standalone tracing, using Vercel's runtime export path in drain mode. Use the kit when another bootstrap owns the provider. Never rejects: an Interfere failure logs one warning and the app runs without Interfere. */
|
|
35
35
|
export declare function register(opts?: ServerInstrumentationOptions): Promise<void>;
|
|
36
36
|
//#endregion
|
|
37
37
|
export { PrerenderSafeIdGenerator, type ServerInstrumentationOptions, onRequestError };
|
|
@@ -12,6 +12,7 @@ import { InterfereOtelKit } from "./instrumentation.mjs";
|
|
|
12
12
|
* instead: a
|
|
13
13
|
* `BasicTracerProvider` + `SimpleSpanProcessor` exporting via {@link FetchTraceExporter}.
|
|
14
14
|
* Edge always exports directly because Vercel Trace Drains do not forward custom Edge spans.
|
|
15
|
+
* Never rejects: an Interfere failure logs one warning and the app runs without Interfere.
|
|
15
16
|
*/
|
|
16
17
|
export declare function register(opts?: ServerInstrumentationOptions): Promise<void>;
|
|
17
18
|
/**
|
|
@@ -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/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={}){
|
|
1
|
+
import{readInterfereEnv}from"./internal/env.mjs";import{resolveReleaseSlug}from"./internal/release-slug.mjs";import{PRODUCER_VERSION}from"./internal/version.mjs";import{contain}from"./internal/server/contain.mjs";import{FetchTraceExporter}from"./internal/server/exporters/edge-exporter.mjs";import{setServerFlush}from"./internal/server/exporters/server-flush.mjs";import{containSpanProcessor}from"./internal/server/tracing/contained-span-processor.mjs";import{onRequestError}from"./internal/server/capture/capture.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{FORCE_ENABLE_GLOBAL}from"@interfere/types/sdk/injected-globals";import{getGlobal}from"@interfere/react/util/global";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={}){return contain(`register()`,()=>{start(opts)}),Promise.resolve()}function start(opts){if(registered)return;let env=readInterfereEnv();if(!env.publicKey)return;let releaseSlug=getGlobal(FORCE_ENABLE_GLOBAL)===!0?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`)||env.serviceName||defaultServiceName(),"service.namespace":opts.serviceNamespace,"telemetry.sdk.language":`nodejs`,"release.slug":releaseSlug||void 0})),spanProcessors:[containSpanProcessor(createUrlRedactionSpanProcessor(resolveUrlRedaction(opts.privacy?.urls))),new SimpleSpanProcessor(new FetchTraceExporter({baseUrl:env.apiUrl,publicKeyHeader:{apiKey:env.publicKey},headers:{"x-interfere-producer-version":PRODUCER_VERSION}}))]});trace.setGlobalTracerProvider(provider),setServerFlush(async()=>provider.forceFlush({timeoutMillis:75e3})),registered=!0}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/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
|
+
import{readInterfereEnv}from"./internal/env.mjs";import{resolveReleaseSlug}from"./internal/release-slug.mjs";import{PRODUCER_VERSION}from"./internal/version.mjs";import{containAsync}from"./internal/server/contain.mjs";import{setServerFlush}from"./internal/server/exporters/server-flush.mjs";import{containSpanProcessor}from"./internal/server/tracing/contained-span-processor.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 containAsync(`flush()`,async()=>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=[containSpanProcessor(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={}){await containAsync(`register()`,async()=>start(opts))}async function start(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};
|
|
@@ -5,9 +5,8 @@ interface InterfereInjectedValues {
|
|
|
5
5
|
readonly __INTERFERE_BUILD__?: boolean;
|
|
6
6
|
readonly __INTERFERE_DISABLED__?: boolean;
|
|
7
7
|
readonly __INTERFERE_ENVIRONMENT__?: string;
|
|
8
|
-
readonly __INTERFERE_FORCE_ENABLE__?: boolean;
|
|
9
8
|
readonly __INTERFERE_PUBLIC_KEY__?: string;
|
|
10
|
-
readonly __INTERFERE_RELEASE_SLUG__
|
|
9
|
+
readonly __INTERFERE_RELEASE_SLUG__?: string | null;
|
|
11
10
|
readonly __INTERFERE_SERVICE_NAME__?: string;
|
|
12
11
|
}
|
|
13
12
|
/** Build-time `/v3/config` request options for remote SDK configuration. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{omitUndefined}from"@interfere/helpers/omit-undefined";import{existsSync}from"node:fs";import{basename,join,normalize,resolve}from"node:path";const INSTRUMENTATION_EXTENSIONS=[`ts`,`tsx`,`js`,`jsx`];function instrumentationCandidates(base){return[``,`src/`].flatMap(dir=>INSTRUMENTATION_EXTENSIONS.map(ext=>`${dir}${base}.${ext}`))}const LOADER_FILE_CANDIDATES=[`ts`,`js`,`mjs`,`cjs`].map(ext=>`value-injection-loader.${ext}`);function configureBuild({existingWebpack,existingTurbopack,nextMajor,projectDir,remoteConfigFetch,values}){let clientPath=resolveFirstExisting(projectDir,instrumentationCandidates(`instrumentation-client`)),targets=[clientPath,resolveFirstExisting(projectDir,instrumentationCandidates(`instrumentation`))].filter(path=>path!==null),hasMetadata
|
|
1
|
+
import{omitUndefined}from"@interfere/helpers/omit-undefined";import{existsSync}from"node:fs";import{basename,join,normalize,resolve}from"node:path";const INSTRUMENTATION_EXTENSIONS=[`ts`,`tsx`,`js`,`jsx`];function instrumentationCandidates(base){return[``,`src/`].flatMap(dir=>INSTRUMENTATION_EXTENSIONS.map(ext=>`${dir}${base}.${ext}`))}const LOADER_FILE_CANDIDATES=[`ts`,`js`,`mjs`,`cjs`].map(ext=>`value-injection-loader.${ext}`);function configureBuild({existingWebpack,existingTurbopack,nextMajor,projectDir,remoteConfigFetch,values}){let clientPath=resolveFirstExisting(projectDir,instrumentationCandidates(`instrumentation-client`)),targets=[clientPath,resolveFirstExisting(projectDir,instrumentationCandidates(`instrumentation`))].filter(path=>path!==null),hasMetadata=!!values.__INTERFERE_RELEASE_SLUG__||values.__INTERFERE_PUBLIC_KEY__!==void 0,injectionApplies=targets.length>0;return{clientInstrumentationApplies:!!clientPath&&hasMetadata,turbopack:buildTurbopackConfig(existingTurbopack,targets,values,remoteConfigFetch,injectionApplies,nextMajor),webpack:buildWebpackHook(existingWebpack,targets,values,remoteConfigFetch,injectionApplies)}}function buildWebpackHook(existingWebpack,targets,values,remoteConfigFetch,injectionApplies){if(!injectionApplies)return existingWebpack;let normalizedTargets=new Set(targets.map(path=>normalize(path))),injectionLoaderPath=resolveLoaderPath();return((webpackConfig,options)=>{let outputConfig=existingWebpack?existingWebpack(webpackConfig,options):webpackConfig;if(!isWebpackConfig(outputConfig))throw TypeError(`The webpack hook must return a configuration object with an optional module.rules array.`);let mutableConfig=outputConfig;return mutableConfig.module??={},mutableConfig.module.rules??=[],mutableConfig.module.rules.push({test:resource=>!!resource&&normalizedTargets.has(normalize(resource)),use:[{loader:injectionLoaderPath,options:omitUndefined({serializedFetch:remoteConfigFetch?JSON.stringify(remoteConfigFetch):void 0,serializedValues:JSON.stringify(values)})}]}),outputConfig})}function isWebpackConfig(value){if(typeof value!=`object`||!value||Array.isArray(value))return!1;if(!(`module`in value)||value.module===null||value.module===void 0)return!0;let{module}=value;return typeof module==`object`&&!Array.isArray(module)&&(!(`rules`in module)||module.rules===null||module.rules===void 0||Array.isArray(module.rules))}function buildTurbopackConfig(existingTurbopack,targets,values,remoteConfigFetch,injectionApplies,nextMajor){let baseConfig=existingTurbopack??{},debugIds=nextMajor===null||nextMajor>=16?baseConfig.debugIds??!0:baseConfig.debugIds;if(!injectionApplies)return omitUndefined({...baseConfig,debugIds});let rules={...baseConfig.rules},loaderPath=resolveLoaderPath();for(let target of targets){let ruleKey=`**/${basename(target)}`;rules[ruleKey]=appendTurbopackLoader(rules[ruleKey],{loader:loaderPath,options:omitUndefined({serializedFetch:remoteConfigFetch?JSON.stringify(remoteConfigFetch):void 0,serializedValues:JSON.stringify(values)})})}return omitUndefined({...baseConfig,debugIds,rules})}function appendTurbopackLoader(existing,loader){return Array.isArray(existing)?[...existing,{loaders:[loader]}]:{...existing,loaders:[...existing?.loaders??[],loader]}}function resolveFirstExisting(projectDir,candidates){for(let candidate of candidates){let filePath=resolve(projectDir,candidate);if(existsSync(filePath))return filePath}return null}function resolveLoaderPath(){let directory=resolve(import.meta.dirname);for(let candidate of LOADER_FILE_CANDIDATES){let filePath=join(directory,candidate);if(existsSync(filePath))return filePath}return join(directory,`value-injection-loader.js`)}export{configureBuild};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { ResolvedBuildConfig } from "../../config.mjs";
|
|
2
2
|
//#region src/internal/build/injected-values.d.ts
|
|
3
|
-
export declare function interfereInjectedValues(config: ResolvedBuildConfig, apiHost: string | undefined,
|
|
3
|
+
export declare function interfereInjectedValues(config: ResolvedBuildConfig, apiHost: string | undefined, serviceName: string | undefined): import("@interfere/helpers/omit-undefined").OmitUndefined<{
|
|
4
4
|
__INTERFERE_SERVICE_NAME__: string | undefined;
|
|
5
5
|
__INTERFERE_RELEASE_SLUG__: (`rel_${string}` & import("zod").$brand<"ReleaseSlug">) | null;
|
|
6
6
|
__INTERFERE_BUILD__: boolean;
|
|
7
7
|
__INTERFERE_PUBLIC_KEY__: string | undefined;
|
|
8
8
|
__INTERFERE_API_URL__: string | undefined;
|
|
9
9
|
__INTERFERE_ENVIRONMENT__: string | undefined;
|
|
10
|
-
__INTERFERE_FORCE_ENABLE__: boolean | undefined;
|
|
11
10
|
}>;
|
|
12
11
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{omitUndefined}from"@interfere/helpers/omit-undefined";import{API_URL_GLOBAL,BUILD_MARKER_GLOBAL,ENVIRONMENT_GLOBAL,
|
|
1
|
+
import{omitUndefined}from"@interfere/helpers/omit-undefined";import{API_URL_GLOBAL,BUILD_MARKER_GLOBAL,ENVIRONMENT_GLOBAL,PUBLIC_KEY_GLOBAL,RELEASE_SLUG_GLOBAL}from"@interfere/types/sdk/injected-globals";import{SERVICE_NAME_GLOBAL}from"@interfere/types/sdk/service-name";function interfereInjectedValues(config,apiHost,serviceName){return omitUndefined({[SERVICE_NAME_GLOBAL]:serviceName,[RELEASE_SLUG_GLOBAL]:config.releaseSlug,[BUILD_MARKER_GLOBAL]:!0,[PUBLIC_KEY_GLOBAL]:config.publicKey||void 0,[API_URL_GLOBAL]:config.publicKey&&apiHost?apiHost:void 0,[ENVIRONMENT_GLOBAL]:config.environment||void 0})}export{interfereInjectedValues};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ProductionCompileContext, ResolvedBuildConfig } from "../../config.mjs";
|
|
2
|
-
import { ReleaseSlug } from "@interfere/types/releases/slug";
|
|
3
2
|
import { Interfere } from "@interfere/javascript";
|
|
3
|
+
import { ReleaseSlug } from "@interfere/types/releases/slug";
|
|
4
4
|
//#region src/internal/build/pipeline.d.ts
|
|
5
5
|
export declare function runBuildPipeline(context: ProductionCompileContext, metadata: ResolvedBuildConfig & {
|
|
6
6
|
apiKey: string;
|
|
@@ -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{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
|
+
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=metadata.releaseConfig??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};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ResolvedBuildConfig } from "../../config.mjs";
|
|
2
|
+
import { NextConfig } from "next";
|
|
3
|
+
//#region src/internal/build/preflight.d.ts
|
|
4
|
+
export declare const withBuildPreflight: (enabled: boolean, config: ResolvedBuildConfig, configured: (releaseConfig: ResolvedBuildConfig["releaseConfig"]) => NextConfig, disabled: () => NextConfig) => NextConfig | Promise<NextConfig>;
|
|
5
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{log}from"../logger.mjs";import{PUBLIC_KEY_HEADER}from"@interfere/constants/api";import{isInterfereApiKey}from"@interfere/types/sdk/build-diagnostics";import{missingReleaseBinding,preflightReleaseBinding}from"@interfere/build/release-request";import{InterfereClient}from"@interfere/javascript";function preflightBuild(config){if(!config.apiKey||!isInterfereApiKey(config.apiKey)||!config.publicKey||!config.buildId||!config.releaseSlug)return null;let client=new InterfereClient({baseUrl:config.apiUrl,apiKey:{token:config.apiKey},headers:{[PUBLIC_KEY_HEADER]:config.publicKey},maxRetries:0,timeoutInSeconds:10});return preflightReleaseBinding(client).then(releaseConfig=>{let missing=releaseConfig&&missingReleaseBinding(releaseConfig);return missing&&log.warn(missing,[]),{disabled:!!missing,releaseConfig:releaseConfig??void 0}})}const withBuildPreflight=(enabled,config,configured,disabled)=>{let pending=enabled?preflightBuild(config):null;async function complete(){let binding=await pending;return binding?.disabled?disabled():configured(binding?.releaseConfig)}return pending?complete():configured(void 0)};export{withBuildPreflight};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ReleaseSlug } from "@interfere/types/releases/slug";
|
|
2
1
|
import { resolveReleaseRequest as resolveReleaseRequest$1 } from "@interfere/build/release-request";
|
|
2
|
+
import { ReleaseSlug } from "@interfere/types/releases/slug";
|
|
3
3
|
//#region src/internal/build/release/index.d.ts
|
|
4
4
|
export declare function resolveReleaseRequest(buildId: string, environment: string | null, releaseSlug: ReleaseSlug, config: Parameters<typeof resolveReleaseRequest$1>[0]["config"]): {
|
|
5
5
|
ok: false;
|
|
@@ -43,6 +43,8 @@ export declare function resolveReleaseRequest(buildId: string, environment: stri
|
|
|
43
43
|
producerVersion?: string | undefined;
|
|
44
44
|
slug?: string | undefined;
|
|
45
45
|
source: {
|
|
46
|
+
authorLogin?: string | null | undefined;
|
|
47
|
+
authorAvatarUrl?: string | null | undefined;
|
|
46
48
|
branch: string;
|
|
47
49
|
commitMessage: string;
|
|
48
50
|
commitSha: string | null;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{log}from"../logger.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{INGEST_URL_GLOBAL}from"@interfere/types/sdk/injected-globals";import{PLUGINS_GLOBAL}from"@interfere/types/sdk/remote-config";import{LOG_SOURCING_GLOBAL}from"@interfere/types/sdk/log-sourcing";import{SERVER_TRACING_GLOBAL}from"@interfere/types/sdk/server-tracing";import{callbackify}from"node:util";import{createBuildConfigResolver}from"@interfere/build/config";import{PLATFORM_GLOBAL}from"@interfere/types/integrations/platforms/hosting";let remoteConfigFallbackLogged=!1;const resolveBuildConfig=createBuildConfigResolver(result=>{result.status!==`fallback`||remoteConfigFallbackLogged||(remoteConfigFallbackLogged=!0,log.warn(`Using default SDK settings`,[`Could not load SDK settings from Interfere. The build continues with the default settings.`]))});function valueInjectionLoader(userCode){let options=this.getOptions(),callback=this.async(),values=resolveValues(options);callbackify(async()=>{let globals=await resolveRemoteConfig(resolveFetch(options)).catch(()=>({}));return render({...values,...globals},userCode)})(callback)}function remoteGlobals(config){return omitUndefined({[SERVER_TRACING_GLOBAL]:config.serverTracing,[LOG_SOURCING_GLOBAL]:config.logSourcing,[PLATFORM_GLOBAL]:config.platform,[INGEST_URL_GLOBAL]:config.ingestUrl,[PLUGINS_GLOBAL]:config.plugins})}function render(values,userCode){let lines=Object.entries(values).map(([key,value])=>`globalThis[${JSON.stringify(key)}] = ${JSON.stringify(value)};`);return lines.length===0?userCode:`${lines.join(`
|
|
1
|
+
import{log}from"../logger.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{INGEST_URL_GLOBAL}from"@interfere/types/sdk/injected-globals";import{PLUGINS_GLOBAL}from"@interfere/types/sdk/remote-config";import{LOG_SOURCING_GLOBAL}from"@interfere/types/sdk/log-sourcing";import{SERVER_TRACING_GLOBAL}from"@interfere/types/sdk/server-tracing";import{BACKEND_HOSTS_GLOBAL}from"@interfere/types/sdk/remote-config/backend-hosts";import{ANONYMOUS_REPLAY_GLOBAL}from"@interfere/types/sdk/remote-config/anonymous-replay";import{callbackify}from"node:util";import{createBuildConfigResolver}from"@interfere/build/config";import{PLATFORM_GLOBAL}from"@interfere/types/integrations/platforms/hosting";let remoteConfigFallbackLogged=!1;const resolveBuildConfig=createBuildConfigResolver(result=>{result.status!==`fallback`||remoteConfigFallbackLogged||(remoteConfigFallbackLogged=!0,log.warn(`Using default SDK settings`,[`Could not load SDK settings from Interfere. The build continues with the default settings.`]))});function valueInjectionLoader(userCode){let options=this.getOptions(),callback=this.async(),values=resolveValues(options);callbackify(async()=>{let globals=await resolveRemoteConfig(resolveFetch(options)).catch(()=>({}));return render({...values,...globals},userCode)})(callback)}function remoteGlobals(config){return omitUndefined({[SERVER_TRACING_GLOBAL]:config.serverTracing,[LOG_SOURCING_GLOBAL]:config.logSourcing,[PLATFORM_GLOBAL]:config.platform,[INGEST_URL_GLOBAL]:config.ingestUrl,[PLUGINS_GLOBAL]:config.plugins,[BACKEND_HOSTS_GLOBAL]:config.backendHosts,[ANONYMOUS_REPLAY_GLOBAL]:config.anonymousReplayPolicy})}function render(values,userCode){let lines=Object.entries(values).map(([key,value])=>`globalThis[${JSON.stringify(key)}] = ${JSON.stringify(value)};`);return lines.length===0?userCode:`${lines.join(`
|
|
2
2
|
`)}\n${userCode}`}async function resolveRemoteConfig(fetchOptions){return fetchOptions?resolveBuildConfig(fetchOptions.apiUrl,fetchOptions.publicKey,fetchOptions.cacheScope).then(remoteGlobals):{}}function parseJson(text){if(typeof text==`string`)try{return JSON.parse(text)}catch{return}}function resolveFetch(options){let parsed=parseJson(options.serializedFetch);return isRemoteConfigFetch(parsed)?parsed:null}function isRemoteConfigFetch(value){return isRecord(value)&&typeof value.apiUrl==`string`&&typeof value.publicKey==`string`&&(value.cacheScope===void 0||typeof value.cacheScope==`string`)}function resolveValues(options){let parsed=parseJson(options.serializedValues);return isRecord(parsed)?parsed:{}}function isRecord(value){return typeof value==`object`&&!!value&&!Array.isArray(value)}export{valueInjectionLoader as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BUILD_MARKER_GLOBAL,DISABLED_GLOBAL}from"@interfere/types/sdk/injected-globals";function buildIntegrationRan(){let globals=globalThis;return BUILD_MARKER_GLOBAL in globals&&globals[BUILD_MARKER_GLOBAL]!==void 0}function interfereDisabled(){let globals=globalThis;return DISABLED_GLOBAL in globals&&globals[DISABLED_GLOBAL]===!0}function hasEnvCredentials(){return typeof process>`u`?!1:!!process.env.INTERFERE_PUBLIC_KEY
|
|
1
|
+
import{BUILD_MARKER_GLOBAL,DISABLED_GLOBAL}from"@interfere/types/sdk/injected-globals";function buildIntegrationRan(){let globals=globalThis;return BUILD_MARKER_GLOBAL in globals&&globals[BUILD_MARKER_GLOBAL]!==void 0}function interfereDisabled(){let globals=globalThis;return DISABLED_GLOBAL in globals&&globals[DISABLED_GLOBAL]===!0}function hasEnvCredentials(){return typeof process>`u`?!1:!!process.env.INTERFERE_PUBLIC_KEY}function warnBuildIntegrationMissing(){console.warn(`[interfere] withInterfere() did not run for this build, so Interfere is inactive and will send no events. Another Next config wrapper can silently drop it when wrapping withInterfere(). Make withInterfere() the outermost wrapper in next.config, or upgrade the wrapping tool.`)}function warnBuildIntegrationDegraded(){console.warn(`[interfere] withInterfere() did not run for this build. Interfere will still send events using credentials from the environment, but release association and sourcemaps are unavailable. Make withInterfere() the outermost wrapper in next.config to restore them.`)}export{buildIntegrationRan,hasEnvCredentials,interfereDisabled,warnBuildIntegrationDegraded,warnBuildIntegrationMissing};
|
package/dist/internal/env.d.mts
CHANGED
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
//#region src/internal/env.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Server-side gate for the proxy route handler, the `captureError` /
|
|
4
|
-
* `onRequestError` helpers, and the remote-config fetcher. Distinct
|
|
5
|
-
* from the browser-side `isEnabledByEnvironment` (in
|
|
6
|
-
* `@interfere/react/internal/kernel`) because the server has different
|
|
7
|
-
* env conventions: `NEXT_PUBLIC_INTERFERE_FORCE_ENABLE` is the dev
|
|
8
|
-
* opt-in, and there's no "unknown runtime, default enabled" case the
|
|
9
|
-
* way the browser SDK has for plain-browser / Vite hosts.
|
|
10
|
-
*/
|
|
2
|
+
/** Enable server telemetry when its public key is configured and the build has not disabled it. */
|
|
11
3
|
export declare function isEnabledOnServer(): boolean;
|
|
12
4
|
export declare function readInterfereEnv(): {
|
|
13
5
|
serviceName: string | null;
|
package/dist/internal/env.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{interfereDisabled}from"./build-marker-guard.mjs";import{API_URL}from"@interfere/constants/api";import{ENVIRONMENT_GLOBAL}from"@interfere/types/sdk/injected-globals";import{SERVICE_NAME_GLOBAL}from"@interfere/types/sdk/service-name";import{getStringGlobal}from"@interfere/react/util/global";import{parseEnvValue}from"@interfere/types/sdk/env";import{resolveEnvironment}from"@interfere/types/sdk/runtime";function isEnabledOnServer(){return
|
|
1
|
+
import{interfereDisabled}from"./build-marker-guard.mjs";import{API_URL}from"@interfere/constants/api";import{ENVIRONMENT_GLOBAL}from"@interfere/types/sdk/injected-globals";import{SERVICE_NAME_GLOBAL}from"@interfere/types/sdk/service-name";import{getStringGlobal}from"@interfere/react/util/global";import{parseEnvValue}from"@interfere/types/sdk/env";import{resolveEnvironment}from"@interfere/types/sdk/runtime";function isEnabledOnServer(){return!interfereDisabled()&&!!parseEnvValue(process.env.INTERFERE_PUBLIC_KEY)}function readInterfereEnv(){let nodeEnvironment=resolveEnvironment({env:process.env,explicit:getStringGlobal(ENVIRONMENT_GLOBAL)});return{serviceName:getStringGlobal(SERVICE_NAME_GLOBAL)??null,apiKey:parseEnvValue(process.env.INTERFERE_API_KEY),apiUrl:parseEnvValue(process.env.INTERFERE_API_URL)??API_URL,nextRuntime:parseEnvValue(process.env.NEXT_RUNTIME),nodeEnvironment,publicKey:interfereDisabled()?null:parseEnvValue(process.env.INTERFERE_PUBLIC_KEY),release:{destinationId:parseEnvValue(process.env.NEXT_PUBLIC_INTERFERE_RELEASE_ID),sourceId:parseEnvValue(process.env.NEXT_PUBLIC_INTERFERE_BUILD_ID)}}}export{isEnabledOnServer,readInterfereEnv};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{markServerAction}from"./server-action-marker.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{SpanStatusCode,trace}from"@opentelemetry/api";async function withInterfereServerAction(name,action,options){
|
|
1
|
+
import{contain}from"../contain.mjs";import{markServerAction}from"./server-action-marker.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{SpanStatusCode,context,trace}from"@opentelemetry/api";const SITE=`withInterfereServerAction()`;async function withInterfereServerAction(name,action,options){let signature=contain(SITE,()=>options?.args?describeArgsShape(options.args):null),span=contain(SITE,()=>trace.getTracer(`@interfere/next/server`).startSpan(`serverAction ${name}`,{attributes:omitUndefined({"interfere.server_action.name":name,"interfere.server_action.args_shape":signature||void 0})}));try{let result=await(span?context.with(trace.setSpan(context.active(),span),action):action());return contain(SITE,()=>span?.setAttribute(`interfere.server_action.outcome`,`success`)),result}catch(error){throw contain(SITE,()=>{span?.setAttribute(`interfere.server_action.outcome`,`error`),span?.setStatus({code:SpanStatusCode.ERROR}),markServerAction(error,omitUndefined({name,argsShape:signature||void 0}))}),error}finally{contain(SITE,()=>span?.end())}}function describeArgsShape(args){return args.map(arg=>arg===null?`null`:arg===void 0?`undefined`:typeof arg==`object`?arg.constructor?.name??`object`:typeof arg).join(`,`)}export{withInterfereServerAction};
|
|
@@ -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";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()
|
|
1
|
+
import{isEnabledOnServer}from"../../env.mjs";import{contain,containAsync}from"../contain.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){contain(`captureError()`,()=>{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){await containAsync(`onRequestError()`,async()=>{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),recordException({context:omitUndefined({mechanism,nextjs:omitUndefined({...context,requestMethod:request?.method||void 0,requestPath:request?.path||void 0,errorDigest:error.digest||void 0}),requestHeaders:pickAllowedHeaders(request?.headers),serverAction:action??void 0}),error,mechanism}),await flushServerTelemetry()})}export{captureError,onRequestError,setIsolatedCaptureProvider};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{captureError}from"./capture.mjs";import{MECHANISM_TYPE}from"@interfere/types/sdk/errors";let uncaughtHandler=null,unhandledHandler=null;function installGlobalHandlers(options){options.captureUncaughtException&&uncaughtHandler===null&&(uncaughtHandler=error=>{handleUncaughtException(error,options)},process.on(`uncaughtException`,uncaughtHandler)),options.captureUnhandledRejection&&unhandledHandler===null&&(unhandledHandler=reason=>{handleUnhandledRejection(reason,options)},process.on(`unhandledRejection`,unhandledHandler))}function uninstallGlobalHandlers(){uncaughtHandler!==null&&(process.off(`uncaughtException`,uncaughtHandler),uncaughtHandler=null),unhandledHandler!==null&&(process.off(`unhandledRejection`,unhandledHandler),unhandledHandler=null)}async function handleUncaughtException(error,options){captureError(error,void 0,{mechanism:{handled:!1,type:MECHANISM_TYPE.node.uncaughtException}}),await
|
|
1
|
+
import{settleFlush}from"../exporters/server-flush.mjs";import{captureError}from"./capture.mjs";import{MECHANISM_TYPE}from"@interfere/types/sdk/errors";let uncaughtHandler=null,unhandledHandler=null;function installGlobalHandlers(options){options.captureUncaughtException&&uncaughtHandler===null&&(uncaughtHandler=error=>{handleUncaughtException(error,options)},process.on(`uncaughtException`,uncaughtHandler)),options.captureUnhandledRejection&&unhandledHandler===null&&(unhandledHandler=reason=>{handleUnhandledRejection(reason,options)},process.on(`unhandledRejection`,unhandledHandler))}function uninstallGlobalHandlers(){uncaughtHandler!==null&&(process.off(`uncaughtException`,uncaughtHandler),uncaughtHandler=null),unhandledHandler!==null&&(process.off(`unhandledRejection`,unhandledHandler),unhandledHandler=null)}async function handleUncaughtException(error,options){captureError(error,void 0,{mechanism:{handled:!1,type:MECHANISM_TYPE.node.uncaughtException}}),await settleFlush(options.flush),options.exitOnUncaughtException&&process.exit(1)}async function handleUnhandledRejection(reason,options){captureError(reason,void 0,{mechanism:{handled:!1,type:MECHANISM_TYPE.node.unhandledRejection}}),await settleFlush(options.flush)}export{installGlobalHandlers,uninstallGlobalHandlers};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const reported=new Set;function reportContainedFailure(site,error){if(!reported.has(site)){reported.add(site);try{console.warn(`[interfere] ${site} failed inside the Interfere SDK. Your app keeps running; Interfere skipped this step and may send less telemetry.`,error)}catch{return}}}function contain(site,run){try{return run()}catch(error){return reportContainedFailure(site,error),null}}async function containAsync(site,run){try{return await run()}catch(error){return reportContainedFailure(site,error),null}}export{contain,containAsync};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { InterfereClient } from "@interfere/javascript";
|
|
1
2
|
import { ExportResult } from "@opentelemetry/core";
|
|
2
3
|
import { ReadableSpan, SpanExporter } from "@opentelemetry/sdk-trace-base";
|
|
3
|
-
import { InterfereClient } from "@interfere/javascript";
|
|
4
4
|
//#region src/internal/server/exporters/edge-exporter.d.ts
|
|
5
5
|
export declare class FetchTraceExporter implements SpanExporter {
|
|
6
6
|
private readonly client;
|
|
@@ -1 +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{
|
|
1
|
+
import{exportTracePayload}from"../tracing/trace-payload.mjs";import{InterfereClient}from"@interfere/javascript";import{ExportResultCode}from"@opentelemetry/core";import{TELEMETRY_EXPORT_TIMEOUT_MS,retryTelemetry}from"@interfere/helpers/retry-telemetry";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,4 +1,6 @@
|
|
|
1
1
|
//#region src/internal/server/exporters/server-flush.d.ts
|
|
2
2
|
export declare function setServerFlush(fn: () => Promise<void>): void;
|
|
3
|
+
/** Waits at most 2 s for `flush` and never rejects. */
|
|
4
|
+
export declare function settleFlush(flush: () => Promise<void>): Promise<void>;
|
|
3
5
|
export declare function flushServerTelemetry(): Promise<void>;
|
|
4
6
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{after}from"next/server";let flushFn=null;function setServerFlush(fn){flushFn=fn}async function flushServerTelemetry(){let flush=flushFn;if(flush)try{after(flush)}catch{await flush
|
|
1
|
+
import{containAsync}from"../contain.mjs";import{after}from"next/server";let flushFn=null;function setServerFlush(fn){flushFn=fn}async function settleFlush(flush){let{promise:capped,resolve:release}=Promise.withResolvers(),timer=setTimeout(()=>{release(void 0)},2e3);try{await Promise.race([containAsync(`Telemetry flush`,flush),capped])}finally{clearTimeout(timer)}}async function flushServerTelemetry(){let flush=flushFn;if(flush)try{after(async()=>containAsync(`Telemetry flush`,flush))}catch{await settleFlush(flush)}}export{flushServerTelemetry,setServerFlush,settleFlush};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
2
|
+
//#region src/internal/server/tracing/contained-span-processor.d.ts
|
|
3
|
+
export declare function containSpanProcessor(processor: SpanProcessor): {
|
|
4
|
+
forceFlush: () => Promise<void>;
|
|
5
|
+
onEnd: (span: import("@opentelemetry/sdk-trace-base").ReadableSpan) => void | null;
|
|
6
|
+
onEnding: (span: import("@opentelemetry/sdk-trace-base").Span) => void | null | undefined;
|
|
7
|
+
onStart: (span: import("@opentelemetry/sdk-trace-base").Span, parentContext: import("@opentelemetry/api").Context) => void | null;
|
|
8
|
+
shutdown: () => Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{contain}from"../contain.mjs";const SITE=`Span processor`;function containSpanProcessor(processor){return{forceFlush:async()=>processor.forceFlush(),onEnd:span=>contain(SITE,()=>processor.onEnd(span)),onEnding:span=>contain(SITE,()=>processor.onEnding?.(span)),onStart:(span,parentContext)=>contain(SITE,()=>processor.onStart(span,parentContext)),shutdown:async()=>processor.shutdown()}}export{containSpanProcessor};
|
|
@@ -1 +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
|
|
1
|
+
import{contain}from"../contain.mjs";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=contain(`TraceMeta`,activeTraceparent);return traceparent?jsx(`meta`,{content:traceparent,name:`traceparent`}):null}export{TraceMeta};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ExportResult } from "@opentelemetry/core";
|
|
2
1
|
import { InterfereClient } from "@interfere/javascript";
|
|
2
|
+
import { ExportResult } from "@opentelemetry/core";
|
|
3
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
|
package/dist/package.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var name=`@interfere/next`,version=`12.4.
|
|
1
|
+
var name=`@interfere/next`,version=`12.4.4`;export{name,version};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interfere/next",
|
|
3
|
-
"version": "12.4.
|
|
3
|
+
"version": "12.4.4",
|
|
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.
|
|
102
|
-
"@interfere/constants": "^9.3.
|
|
103
|
-
"@interfere/helpers": "^9.3.
|
|
104
|
-
"@interfere/javascript": "^0.3.
|
|
105
|
-
"@interfere/react": "^12.4.
|
|
106
|
-
"@interfere/types": "^10.6.
|
|
101
|
+
"@interfere/build": "^0.4.5",
|
|
102
|
+
"@interfere/constants": "^9.3.1",
|
|
103
|
+
"@interfere/helpers": "^9.3.1",
|
|
104
|
+
"@interfere/javascript": "^0.3.3",
|
|
105
|
+
"@interfere/react": "^12.4.4",
|
|
106
|
+
"@interfere/types": "^10.6.3",
|
|
107
107
|
"@opentelemetry/api": "1.9.1",
|
|
108
108
|
"@opentelemetry/api-logs": "0.222.0",
|
|
109
109
|
"@opentelemetry/baggage-span-processor": "0.5.0",
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
"zod": "4.6.6"
|
|
125
125
|
},
|
|
126
126
|
"devDependencies": {
|
|
127
|
-
"@interfere/test-utils": "^10.0.
|
|
127
|
+
"@interfere/test-utils": "^10.0.3",
|
|
128
128
|
"@interfere/typescript-config": "^9.0.2",
|
|
129
129
|
"@interfere/vitest-config": "0.0.0",
|
|
130
130
|
"@playwright/test": "1.63.0",
|