@interfere/next 11.2.1 → 12.0.1
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 +25 -25
- package/dist/config.d.mts +7 -8
- package/dist/config.mjs +1 -1
- package/dist/instrument-client.d.mts +5 -5
- package/dist/instrument-client.mjs +1 -1
- package/dist/instrumentation.d.mts +11 -8
- package/dist/instrumentation.edge.d.mts +3 -3
- package/dist/instrumentation.edge.mjs +1 -1
- package/dist/instrumentation.mjs +1 -1
- package/dist/internal/build/configure-build.d.mts +21 -15
- package/dist/internal/build/configure-build.mjs +1 -1
- package/dist/internal/build/detect-bundler.d.mts +3 -4
- package/dist/internal/build/pipeline.d.mts +40 -27
- package/dist/internal/build/pipeline.mjs +1 -1
- package/dist/internal/build/release/git.d.mts +2 -3
- package/dist/internal/build/release/index.d.mts +51 -5
- package/dist/internal/build/release/index.mjs +1 -1
- package/dist/internal/build/source-maps/discover-turbopack.d.mts +2 -3
- package/dist/internal/build/source-maps/discover-turbopack.mjs +1 -1
- package/dist/internal/build/source-maps/discover-webpack.d.mts +3 -4
- package/dist/internal/build/source-maps/discover-webpack.mjs +1 -1
- package/dist/internal/build/source-maps/discover.d.mts +9 -5
- package/dist/internal/build/source-maps/discover.mjs +1 -1
- package/dist/internal/build/source-maps/guard.d.mts +2 -3
- package/dist/internal/build/source-maps/guard.mjs +1 -1
- package/dist/internal/build/source-maps/paths.d.mts +5 -6
- package/dist/internal/build/source-maps/paths.mjs +1 -1
- package/dist/internal/build/telemetry.d.mts +5 -0
- package/dist/internal/build/telemetry.mjs +1 -0
- package/dist/internal/build/value-injection-loader.d.mts +5 -8
- package/dist/internal/build/value-injection-loader.mjs +2 -2
- package/dist/internal/build-marker-guard.d.mts +5 -6
- package/dist/internal/build-marker-guard.mjs +1 -1
- package/dist/internal/env.d.mts +13 -15
- package/dist/internal/env.mjs +1 -1
- package/dist/internal/logger.d.mts +5 -6
- package/dist/internal/logger.mjs +1 -1
- package/dist/internal/release-slug.d.mts +3 -5
- package/dist/internal/release-slug.mjs +1 -1
- package/dist/internal/server/cache-safe-trace-context.d.mts +24 -0
- package/dist/internal/server/cache-safe-trace-context.mjs +1 -0
- package/dist/internal/server/capture.d.mts +3 -4
- package/dist/internal/server/capture.mjs +1 -1
- package/dist/internal/server/console-bridge.d.mts +2 -3
- package/dist/internal/server/console-bridge.mjs +1 -1
- package/dist/internal/server/edge-exporter.d.mts +5 -6
- package/dist/internal/server/edge-exporter.mjs +1 -1
- package/dist/internal/server/freshness-propagator.d.mts +41 -0
- package/dist/internal/server/freshness-propagator.mjs +1 -0
- package/dist/internal/server/global-handlers.d.mts +4 -5
- package/dist/internal/server/global-handlers.mjs +1 -1
- package/dist/internal/server/id-generator.d.mts +2 -3
- package/dist/internal/server/instrumentation-options.d.mts +3 -4
- package/dist/internal/server/remote-config.d.mts +3 -4
- package/dist/internal/server/remote-config.mjs +1 -1
- package/dist/internal/server/server-action-marker.d.mts +10 -5
- package/dist/internal/server/server-action-marker.mjs +1 -1
- package/dist/internal/server/server-flush.d.mts +3 -4
- package/dist/internal/server/server-flush.mjs +1 -1
- package/dist/internal/server/static-generation.d.mts +17 -0
- package/dist/internal/server/static-generation.mjs +1 -0
- package/dist/internal/server/trace-meta.d.mts +13 -26
- package/dist/internal/server/trace-meta.mjs +1 -1
- package/dist/internal/server/traceparent.d.mts +2 -3
- package/dist/internal/server/traceparent.mjs +1 -1
- package/dist/internal/server/types.d.mts +5 -8
- package/dist/internal/server/wrap-proxy.d.mts +3 -4
- package/dist/internal/server/wrap-proxy.mjs +1 -1
- package/dist/internal/server/wrap-server-action.d.mts +2 -3
- package/dist/internal/server/wrap-server-action.mjs +1 -1
- package/dist/internal/setup-warnings.d.mts +3 -4
- package/dist/internal/setup-warnings.mjs +1 -1
- package/dist/internal/version.d.mts +2 -3
- package/dist/package.mjs +1 -1
- package/dist/provider.d.mts +3 -3
- package/dist/provider.mjs +1 -1
- package/package.json +49 -42
- package/dist/internal/build/release/destinations/cli.d.mts +0 -6
- package/dist/internal/build/release/destinations/cli.mjs +0 -1
- package/dist/internal/build/release/destinations/cloudflare.d.mts +0 -5
- package/dist/internal/build/release/destinations/cloudflare.mjs +0 -1
- package/dist/internal/build/release/destinations/index.d.mts +0 -14
- package/dist/internal/build/release/destinations/index.mjs +0 -1
- package/dist/internal/build/release/destinations/vercel.d.mts +0 -5
- package/dist/internal/build/release/destinations/vercel.mjs +0 -1
- package/dist/internal/build/release/sources/github.d.mts +0 -8
- package/dist/internal/build/release/sources/github.mjs +0 -1
- package/dist/internal/build/release/sources/index.d.mts +0 -20
- package/dist/internal/build/release/sources/index.mjs +0 -1
- package/dist/internal/build/source-maps/concurrency.d.mts +0 -4
- package/dist/internal/build/source-maps/concurrency.mjs +0 -1
- package/dist/internal/build/source-maps/index.d.mts +0 -6
- package/dist/internal/build/source-maps/index.mjs +0 -1
- package/dist/internal/build/source-maps/upload.d.mts +0 -39
- package/dist/internal/build/source-maps/upload.mjs +0 -1
- package/dist/internal/url.d.mts +0 -4
- package/dist/internal/url.mjs +0 -1
package/README.md
CHANGED
|
@@ -84,11 +84,11 @@ export default function RootLayout({
|
|
|
84
84
|
|
|
85
85
|
### Environment Variables
|
|
86
86
|
|
|
87
|
-
| Variable
|
|
88
|
-
|
|
|
89
|
-
| `INTERFERE_PUBLIC_KEY` | Yes
|
|
90
|
-
| `INTERFERE_API_KEY`
|
|
91
|
-
| `INTERFERE_API_URL`
|
|
87
|
+
| Variable | Required | Description |
|
|
88
|
+
| ---------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
89
|
+
| `INTERFERE_PUBLIC_KEY` | Yes | Surface public key in the `interfere_public_us_*` or `interfere_public_eu_*` format. Public by design; used for ingestion. |
|
|
90
|
+
| `INTERFERE_API_KEY` | Yes for release metadata | Interfere secret key in the `interfere_secret_us_*` or `interfere_secret_eu_*` format. Server-side build credential; never expose in browser code. |
|
|
91
|
+
| `INTERFERE_API_URL` | No | Override the ingest endpoint in `direct` mode (e.g. a regional endpoint). Defaults to Interfere's endpoint. |
|
|
92
92
|
|
|
93
93
|
### Migrating From 0.x
|
|
94
94
|
|
|
@@ -139,22 +139,22 @@ function useInterfereIdentity() {
|
|
|
139
139
|
|
|
140
140
|
### Parameters
|
|
141
141
|
|
|
142
|
-
| Field
|
|
143
|
-
|
|
|
144
|
-
| `identifier` | Yes
|
|
145
|
-
| `source`
|
|
146
|
-
| `name`
|
|
147
|
-
| `email`
|
|
148
|
-
| `avatar`
|
|
149
|
-
| `traits`
|
|
142
|
+
| Field | Required | Description |
|
|
143
|
+
| ------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
144
|
+
| `identifier` | Yes | Unique user ID (your internal ID, not email) |
|
|
145
|
+
| `source` | Yes | Auth source: `{ type: "clerk", name: "Clerk" }`, `{ type: "auth0", name: "Auth0" }`, or `{ type: "custom", name: "Your Provider" }` |
|
|
146
|
+
| `name` | No | Display name |
|
|
147
|
+
| `email` | No | Email address |
|
|
148
|
+
| `avatar` | No | Avatar URL |
|
|
149
|
+
| `traits` | No | Arbitrary key-value metadata (`Record<string, unknown>`) |
|
|
150
150
|
|
|
151
151
|
### API
|
|
152
152
|
|
|
153
|
-
| Method
|
|
154
|
-
|
|
|
153
|
+
| Method | Description |
|
|
154
|
+
| ---------------------- | --------------------------------------------------------------------------------------------------- |
|
|
155
155
|
| `identity.set(params)` | Link the current session to a user. Deduplicated per session — only the first call sends a request. |
|
|
156
|
-
| `identity.clear()`
|
|
157
|
-
| `identity.get()`
|
|
156
|
+
| `identity.clear()` | Clears the linked identity and rotates the session. Call on logout. |
|
|
157
|
+
| `identity.get()` | Returns the current `IdentifyParams`, or `null` if no identity has been set. |
|
|
158
158
|
|
|
159
159
|
Identity is automatically cleared when the SDK is closed. Call `identity.clear()` on logout to start a fresh anonymous session.
|
|
160
160
|
|
|
@@ -185,13 +185,13 @@ export default function RootLayout({
|
|
|
185
185
|
|
|
186
186
|
### Consent categories
|
|
187
187
|
|
|
188
|
-
| Category
|
|
189
|
-
|
|
|
190
|
-
| `necessary` | Error tracking
|
|
191
|
-
| `analytics` | Page events, rage clicks, fingerprint | Yes
|
|
192
|
-
| `replay`
|
|
188
|
+
| Category | Plugins | Gated? |
|
|
189
|
+
| ----------- | ------------------------------------- | --------- |
|
|
190
|
+
| `necessary` | Error tracking | Always on |
|
|
191
|
+
| `analytics` | Page events, rage clicks, fingerprint | Yes |
|
|
192
|
+
| `replay` | Session replay | Yes |
|
|
193
193
|
|
|
194
|
-
Omitting the `consent` prop
|
|
194
|
+
Omitting the `consent` prop preserves consent supplied to `init()`, including later updates through `consent.set()`. If no initial consent was supplied, omitting the prop allows all features. Passing the prop enables only `necessary` plugins and explicitly consented categories.
|
|
195
195
|
|
|
196
196
|
### Imperative API
|
|
197
197
|
|
|
@@ -201,8 +201,8 @@ Use `consent.set()` and `consent.get()` from the `useInterfere` hook:
|
|
|
201
201
|
const { consent } = useInterfere();
|
|
202
202
|
|
|
203
203
|
consent.set({ analytics: true, replay: true }); // selective
|
|
204
|
-
consent.set();
|
|
205
|
-
consent.get();
|
|
204
|
+
consent.set(); // grant all
|
|
205
|
+
consent.get(); // current state, or null if no gating
|
|
206
206
|
```
|
|
207
207
|
|
|
208
208
|
### Integration with consent libraries
|
package/dist/config.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ReleaseSlug } from "@interfere/types/releases/slug";
|
|
2
2
|
import { NextConfig } from "next";
|
|
3
3
|
//#region src/config.d.ts
|
|
4
|
-
interface NextConfigWithInterfere extends NextConfig {
|
|
4
|
+
export interface NextConfigWithInterfere extends NextConfig {
|
|
5
5
|
interfere?: {
|
|
6
6
|
/**
|
|
7
7
|
* Reserved. Setting `buildId` here is rejected loudly — it caused
|
|
@@ -25,7 +25,7 @@ interface NextConfigWithInterfere extends NextConfig {
|
|
|
25
25
|
* `withInterfere(settings, nextConfig)`. Kept separate from the Next.js
|
|
26
26
|
* config so SDK options never collide with Next's own keys.
|
|
27
27
|
*/
|
|
28
|
-
interface NextPluginConfig {
|
|
28
|
+
export interface NextPluginConfig {
|
|
29
29
|
readonly sourceMaps?: {
|
|
30
30
|
/**
|
|
31
31
|
* Delete source maps from the build output after they've been uploaded
|
|
@@ -50,7 +50,7 @@ interface NextPluginConfig {
|
|
|
50
50
|
readonly purge?: boolean;
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
-
interface ResolvedBuildConfig {
|
|
53
|
+
export interface ResolvedBuildConfig {
|
|
54
54
|
readonly apiKey: string | null;
|
|
55
55
|
readonly apiUrl: string;
|
|
56
56
|
/**
|
|
@@ -95,11 +95,10 @@ type ResolvedInterfereConfig<TConfig> = [TConfig] extends [(...args: never[]) =>
|
|
|
95
95
|
type WithInterfereResult<TConfig> = (phase: string, context?: {
|
|
96
96
|
defaultConfig: NextConfig;
|
|
97
97
|
}) => ResolvedInterfereConfig<TConfig>;
|
|
98
|
-
declare function withInterfere<TConfig extends InterfereConfigInput>(upstream?: TConfig): WithInterfereResult<TConfig>;
|
|
99
|
-
declare function withInterfere<TConfig extends InterfereConfigInput>(settings: NextPluginConfig, config: TConfig): WithInterfereResult<TConfig>;
|
|
100
|
-
interface ProductionCompileContext {
|
|
98
|
+
export declare function withInterfere<TConfig extends InterfereConfigInput>(upstream?: TConfig): WithInterfereResult<TConfig>;
|
|
99
|
+
export declare function withInterfere<TConfig extends InterfereConfigInput>(settings: NextPluginConfig, config: TConfig): WithInterfereResult<TConfig>;
|
|
100
|
+
export interface ProductionCompileContext {
|
|
101
101
|
readonly distDir: string;
|
|
102
102
|
readonly projectDir: string;
|
|
103
103
|
}
|
|
104
|
-
//#endregion
|
|
105
|
-
export { NextConfigWithInterfere, NextPluginConfig, ProductionCompileContext, ResolvedBuildConfig, withInterfere };
|
|
104
|
+
//#endregion
|
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{readInterfereEnv}from"./internal/env.mjs";import{FatalError,log}from"./internal/logger.mjs";import{resolveReleaseSlug}from"./internal/release-slug.mjs";import{readNextMajorVersion,warnIfServerInstrumentationMissing}from"./internal/setup-warnings.mjs";import{API_URL}from"@interfere/constants/api";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{LOCAL_DEV_RELEASE_SLUG}from"@interfere/types/releases/slug";import{classifyReleasePipelineError,diagnoseRelease,isInterfereApiKey}from"@interfere/types/sdk/build-diagnostics";import{resolveDeployEnvironment}from"@interfere/types/sdk/deploy-environment";import{API_URL_GLOBAL,BUILD_MARKER_GLOBAL,ENVIRONMENT_GLOBAL,FORCE_ENABLE_GLOBAL,PUBLIC_KEY_GLOBAL,RELEASE_SLUG_GLOBAL}from"@interfere/types/sdk/injected-globals";import{PHASE_PRODUCTION_BUILD}from"next/constants.js";const CLIENT_TRACE_METADATA_KEYS=[`traceparent`,`baggage`],DIAGNOSTIC_CONTEXT={publicKeyEnvVar:`INTERFERE_PUBLIC_KEY`};function reportReleaseDiagnostic(code,input){let diagnostic=diagnoseRelease(code,DIAGNOSTIC_CONTEXT,input),lines=[...diagnostic.details];if(diagnostic.severity===`fatal`){log.fatal(diagnostic.title,lines);return}log.warn(diagnostic.title,lines)}function isThenable(value){return typeof value==`object`&&!!value&&`then`in value&&typeof value.then==`function`}function withInterfere(
|
|
1
|
+
import{configureBuild}from"./internal/build/configure-build.mjs";import{runGitCommand}from"./internal/build/release/git.mjs";import{readInterfereEnv}from"./internal/env.mjs";import{FatalError,log}from"./internal/logger.mjs";import{resolveReleaseSlug}from"./internal/release-slug.mjs";import{readNextMajorVersion,warnIfServerInstrumentationMissing}from"./internal/setup-warnings.mjs";import{API_URL}from"@interfere/constants/api";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{LOCAL_DEV_RELEASE_SLUG}from"@interfere/types/releases/slug";import{classifyReleasePipelineError,diagnoseRelease,isInterfereApiKey}from"@interfere/types/sdk/build-diagnostics";import{resolveDeployEnvironment}from"@interfere/types/sdk/deploy-environment";import{API_URL_GLOBAL,BUILD_MARKER_GLOBAL,ENVIRONMENT_GLOBAL,FORCE_ENABLE_GLOBAL,PUBLIC_KEY_GLOBAL,RELEASE_SLUG_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`};function reportReleaseDiagnostic(code,input){let diagnostic=diagnoseRelease(code,DIAGNOSTIC_CONTEXT,input),lines=[...diagnostic.details];if(diagnostic.severity===`fatal`){log.fatal(diagnostic.title,lines);return}log.warn(diagnostic.title,lines)}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(config=>buildInterfereConfig(phase,settings,config)):buildInterfereConfig(phase,settings,resolved)}}function interfereInjectedValues(config,apiHost,forceEnable){let releaseSlug=forceEnable?LOCAL_DEV_RELEASE_SLUG:config.releaseSlug;return omitUndefined({[RELEASE_SLUG_GLOBAL]: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,[FORCE_ENABLE_GLOBAL]:forceEnable?!0:void 0})}function interfereCompilerConfig(compiler,config){let existingHook=compiler?.runAfterProductionCompile;return omitUndefined({...compiler,async runAfterProductionCompile(context){existingHook&&await existingHook(context);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){return config.apiKey!==null&&productionBrowserSourceMaps===!1&&log.warn(`Forcing productionBrowserSourceMaps`,[`withInterfere() requires production source maps to symbolicate errors.`,"Your `productionBrowserSourceMaps: false` was overridden to `true`."]),config.apiKey!==null||productionBrowserSourceMaps}function buildInterfereConfig(phase,settings,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=resolveBuildConfig(settings,phase),apiHost=resolveBrowserApiHost(interfere?.apiHost,config.apiUrl),forceEnable=!!process.env.NEXT_PUBLIC_INTERFERE_FORCE_ENABLE,nextMajor=readNextMajorVersion(projectDir),build=configureBuild({existingTurbopack:turbopack,existingWebpack:webpack,nextMajor,projectDir,remoteConfigFetch:resolveRemoteConfigFetch(config,phase),values:interfereInjectedValues(config,apiHost,forceEnable)});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||forceEnable});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,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{apiKey,buildId,releaseSlug}=config;if(!apiKey)return log.fatal(`Secret key not set`,[`withInterfere() requires a secret key to publish release metadata during production builds.`,`Set the INTERFERE_API_KEY environment variable, or remove withInterfere() from your Next.js config.`,`See: https://interfere.com/docs/sdk/next-js`]);if(!isInterfereApiKey(apiKey)){reportReleaseDiagnostic(`API_KEY_WRONG_PREFIX`,{apiKey});return}if(!(buildId&&releaseSlug))return log.fatal(`Commit SHA missing`,[`Could not resolve a commit SHA from environment variables or git.`,"Set `INTERFERE_SOURCE_ID`, ensure your CI exposes its git SHA env var (e.g. `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA`), or run inside a git checkout."]);let{runBuildPipeline}=await import(`./internal/build/pipeline.mjs`);try{let result=await runBuildPipeline(context,{...config,apiKey,buildId,releaseSlug});if(!result.accepted){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}/surfaces/${result.config.surface.slug}`,`Release: ${result.release.destination.slug}`,`Build: ${result.buildId}`,`Artifacts: ${result.fileCount} source maps`]),result.cleanupFailures.length>0&&log.warn(`Source-map cleanup left files on disk`,result.cleanupFailures.map(failure=>`${failure.absolute}: ${failure.message}`))}catch(error){let message=error instanceof Error?error.message:String(error);reportReleaseDiagnostic(classifyReleasePipelineError(error),{errorMessage:message})}}export{withInterfere};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { capture, span } from "@interfere/react/api";
|
|
2
|
-
import { Kernel,
|
|
2
|
+
import { Kernel, KernelInternalOptions, KernelOptions, KernelOptions as KernelOptions$1 } from "@interfere/react/internal/kernel";
|
|
3
3
|
import { ConsentCategory, ConsentState, GateableCategory } from "@interfere/types/sdk/plugins/manifest";
|
|
4
4
|
//#region src/instrument-client.d.ts
|
|
5
|
-
declare const close: () => Promise<void>, consent: {
|
|
5
|
+
export declare const close: () => Promise<void>, consent: {
|
|
6
6
|
get: () => import("@interfere/types/sdk/plugins/manifest").ConsentState | null;
|
|
7
7
|
set: (value?: import("@interfere/types/sdk/plugins/manifest").ConsentState) => void;
|
|
8
|
-
}, getKernel: () => Kernel
|
|
8
|
+
}, getKernel: () => import("@interfere/react/internal/kernel").Kernel, getKernelOrNull: () => import("@interfere/react/internal/kernel").Kernel | null, identity: {
|
|
9
9
|
clear: () => void;
|
|
10
10
|
get: () => import("@interfere/types/sdk/identify").IdentifyParams | null;
|
|
11
11
|
set: (params: import("@interfere/types/sdk/identify").IdentifyParams) => Promise<void>;
|
|
12
12
|
}, subscribeToKernel: (listener: () => void) => () => void;
|
|
13
|
-
declare function init(opts?: KernelOptions$1): Promise<Kernel
|
|
13
|
+
export declare function init(opts?: KernelOptions$1): Promise<import("@interfere/react/internal/kernel").Kernel | null>;
|
|
14
14
|
//#endregion
|
|
15
|
-
export { type ConsentCategory, type ConsentState, type GateableCategory, type Kernel, type KernelInternalOptions, type KernelOptions, capture,
|
|
15
|
+
export { type ConsentCategory, type ConsentState, type GateableCategory, type Kernel, type KernelInternalOptions, type KernelOptions, capture, span };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{buildIntegrationRan,hasEnvCredentials,warnBuildIntegrationDegraded,warnBuildIntegrationMissing}from"./internal/build-marker-guard.mjs";import{PRODUCER_VERSION}from"./internal/version.mjs";import{createWrapperSingleton}from"@interfere/react/internal/wrapper-singleton";import{capture,span}from"@interfere/react/api";const wrapper=createWrapperSingleton({
|
|
1
|
+
import{buildIntegrationRan,hasEnvCredentials,warnBuildIntegrationDegraded,warnBuildIntegrationMissing}from"./internal/build-marker-guard.mjs";import{PRODUCER_VERSION}from"./internal/version.mjs";import{createWrapperSingleton}from"@interfere/react/internal/wrapper-singleton";import{capture,span}from"@interfere/react/api";const wrapper=createWrapperSingleton({initEntryName:`instrumentation-client`,producerVersion:PRODUCER_VERSION}),{close,consent,getKernel,getKernelOrNull,identity,subscribeToKernel}=wrapper;async function init(opts){return buildIntegrationRan()?wrapper.init(opts):initAfterHoistedImports(opts)}async function initAfterHoistedImports(opts){if(await Promise.resolve(),!buildIntegrationRan()){let{promise,resolve}=Promise.withResolvers();setTimeout(resolve,0),await promise}return buildIntegrationRan()?wrapper.init(opts):hasEnvCredentials()?(warnBuildIntegrationDegraded(),wrapper.init(opts)):(warnBuildIntegrationMissing(),null)}export{capture,close,consent,getKernel,getKernelOrNull,identity,init,span,subscribeToKernel};
|
|
@@ -6,7 +6,7 @@ import { SpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
|
6
6
|
import { Instrumentation } from "@opentelemetry/instrumentation";
|
|
7
7
|
import { LogRecordProcessor } from "@opentelemetry/sdk-logs";
|
|
8
8
|
//#region src/instrumentation.d.ts
|
|
9
|
-
declare function flush(): Promise<void>;
|
|
9
|
+
export declare function flush(): Promise<void>;
|
|
10
10
|
/**
|
|
11
11
|
* Composable building blocks the Interfere SDK contributes to OTel —
|
|
12
12
|
* span / log / metric processors, baggage propagator, plus the
|
|
@@ -25,7 +25,7 @@ declare function flush(): Promise<void>;
|
|
|
25
25
|
* Returns `null` when `INTERFERE_PUBLIC_KEY` is unset — callers can spread
|
|
26
26
|
* `kit?.spanProcessors ?? []` without a no-op guard.
|
|
27
27
|
*/
|
|
28
|
-
interface InterfereOtelKit {
|
|
28
|
+
export interface InterfereOtelKit {
|
|
29
29
|
/**
|
|
30
30
|
* Patches `console.{debug,log,info,warn,error}` to also emit OTel
|
|
31
31
|
* `LogRecord`s via the global `LoggerProvider`. Caller is responsible
|
|
@@ -52,11 +52,14 @@ interface InterfereOtelKit {
|
|
|
52
52
|
instrumentations: Instrumentation[];
|
|
53
53
|
logRecordProcessors: LogRecordProcessor[];
|
|
54
54
|
/**
|
|
55
|
-
* W3C baggage propagator
|
|
55
|
+
* W3C baggage propagator plus the SDK's `tracestate` freshness
|
|
56
|
+
* stamper (guards browsers against stitching onto ISR/CDN-cached
|
|
57
|
+
* trace context). Trace context itself is intentionally *not*
|
|
56
58
|
* included — `@vercel/otel`'s `propagators: ["auto"]` already
|
|
57
59
|
* registers it, and standalone `register()` adds it itself when
|
|
58
|
-
* the global slot is empty.
|
|
59
|
-
*
|
|
60
|
+
* the global slot is empty. Spread these *after* your trace-context
|
|
61
|
+
* propagator (e.g. `propagators: ["auto", ...kit.propagators]`) so
|
|
62
|
+
* the freshness stamp wins the `tracestate` write.
|
|
60
63
|
*/
|
|
61
64
|
propagators: TextMapPropagator[];
|
|
62
65
|
/**
|
|
@@ -76,7 +79,7 @@ interface InterfereOtelKit {
|
|
|
76
79
|
* unconditionally spread `kit?.spanProcessors ?? []` in dev where the
|
|
77
80
|
* SDK isn't configured.
|
|
78
81
|
*/
|
|
79
|
-
declare function buildInterfereOtelKit(opts?: ServerInstrumentationOptions): InterfereOtelKit | null;
|
|
82
|
+
export declare function buildInterfereOtelKit(opts?: ServerInstrumentationOptions): InterfereOtelKit | null;
|
|
80
83
|
/**
|
|
81
84
|
* One-line server-side bootstrap for Next.js. Customers' `instrumentation.ts`
|
|
82
85
|
* becomes:
|
|
@@ -116,6 +119,6 @@ declare function buildInterfereOtelKit(opts?: ServerInstrumentationOptions): Int
|
|
|
116
119
|
* Bails silently when `INTERFERE_PUBLIC_KEY` is unset so dev runs without
|
|
117
120
|
* the SDK configured don't crash.
|
|
118
121
|
*/
|
|
119
|
-
declare function register(opts?: ServerInstrumentationOptions): Promise<void>;
|
|
122
|
+
export declare function register(opts?: ServerInstrumentationOptions): Promise<void>;
|
|
120
123
|
//#endregion
|
|
121
|
-
export {
|
|
124
|
+
export { PrerenderSafeIdGenerator, type ServerInstrumentationOptions, onRequestError };
|
|
@@ -18,12 +18,12 @@ import { InterfereOtelKit } from "./instrumentation.mjs";
|
|
|
18
18
|
* Release slug is `LOCAL_DEV_RELEASE_SLUG` under force-enable (dev); a
|
|
19
19
|
* build-baked slug for production edge is a follow-up.
|
|
20
20
|
*/
|
|
21
|
-
declare function register(opts?: ServerInstrumentationOptions): Promise<void>;
|
|
21
|
+
export declare function register(opts?: ServerInstrumentationOptions): Promise<void>;
|
|
22
22
|
/**
|
|
23
23
|
* Edge stub for the kit factory. Composition into a host OTel bootstrap isn't
|
|
24
24
|
* supported on edge yet; returns `null` so callers spreading
|
|
25
25
|
* `kit?.spanProcessors ?? []` behave as if the SDK is unconfigured.
|
|
26
26
|
*/
|
|
27
|
-
declare function buildInterfereOtelKit(_opts?: ServerInstrumentationOptions): null;
|
|
27
|
+
export declare function buildInterfereOtelKit(_opts?: ServerInstrumentationOptions): null;
|
|
28
28
|
//#endregion
|
|
29
|
-
export { type InterfereOtelKit, type ServerInstrumentationOptions,
|
|
29
|
+
export { type InterfereOtelKit, type ServerInstrumentationOptions, onRequestError };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readInterfereEnv}from"./internal/env.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{
|
|
1
|
+
import{readInterfereEnv}from"./internal/env.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{readServerTracingMode}from"@interfere/types/sdk/server-tracing";import{trace}from"@opentelemetry/api";import{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:null;releaseSlug||console.warn(`[interfere] edge runtime has no build-baked release.slug; the collector will reject edge spans until one is wired (production follow-up).`);let isDrainMode=readServerTracingMode()===`drain`;isDrainMode&&console.info(`[interfere] edge server tracing in drain mode; SDK trace exporter disabled — server traces come from the platform trace drain.`);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??`interfere-sdk-next-edge`,"service.namespace":`interfere`,"telemetry.sdk.language":`nodejs`,"release.slug":releaseSlug||void 0})),spanProcessors:isDrainMode?[]:[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()),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{
|
|
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{readServerTracingMode}from"@interfere/types/sdk/server-tracing";import{propagation}from"@opentelemetry/api";import{resourceFromAttributes}from"@opentelemetry/resources";import{BatchSpanProcessor}from"@opentelemetry/sdk-trace-base";import{CompositePropagator,W3CBaggagePropagator}from"@opentelemetry/core";import{readLogSourcingMode}from"@interfere/types/sdk/log-sourcing";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=2e3;let registered=!1,tracerProvider=null,loggerProvider=null;async function flush(){await Promise.all([tracerProvider?.forceFlush(),loggerProvider?.forceFlush()])}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??`interfere-sdk-next-server`,"service.namespace":`interfere`,"telemetry.sdk.language":`nodejs`,"release.slug":releaseSlug||void 0}),compression=opts.compression===`gzip`?CompressionAlgorithm.GZIP:CompressionAlgorithm.NONE,isDrainMode=readServerTracingMode()===`drain`,spanProcessors=[new BaggageSpanProcessor(()=>!0),...isDrainMode?[]:[new BatchSpanProcessor(new OTLPTraceExporter({compression,headers:exporterHeaders,timeoutMillis:EXPORT_TIMEOUT_MS,url:tracesUrl}))],...opts._internalAdditionalSpanProcessors??[]],isLogDrainMode=readLogSourcingMode()===`drain`;return{enableConsoleBridge:()=>isLogDrainMode?()=>{}:bridgeConsoleToOtel(),fetchRemoteConfig:async()=>fetchAndCacheRemoteConfig(),instrumentations:[],logRecordProcessors:[...isLogDrainMode?[]:[new BatchLogRecordProcessor({exporter:new OTLPLogExporter({compression,headers:exporterHeaders,timeoutMillis:EXPORT_TIMEOUT_MS,url:logsUrl})})],...opts._internalAdditionalLogRecordProcessors??[]],propagators:[new FreshnessStampingPropagator,new W3CBaggagePropagator],resourceAttributes,spanProcessors}}async function register(opts={}){if(registered)return;let kit=buildInterfereOtelKit(opts);if(!kit)return;readServerTracingMode()===`drain`&&console.info(`[interfere] server tracing in drain mode; SDK trace exporter disabled — server traces come from the platform trace drain.`);let resource=resourceFromAttributes(kit.resourceAttributes);tracerProvider=new NodeTracerProvider({idGenerator:new PrerenderSafeIdGenerator,resource,spanProcessors:kit.spanProcessors}),propagation.fields().length===0&&propagation.setGlobalPropagator(new CompositePropagator({propagators:[new CacheSafeTraceContextPropagator,...kit.propagators]})),tracerProvider.register({contextManager:new AsyncLocalStorageContextManager().enable()}),loggerProvider=new LoggerProvider({processors:kit.logRecordProcessors,resource});let{logs:logsApi}=await import(`@opentelemetry/api-logs`);logsApi.setGlobalLoggerProvider(loggerProvider),registerInstrumentations({instrumentations:kit.instrumentations,tracerProvider}),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};
|
|
@@ -8,13 +8,9 @@ interface InterfereInjectedValues {
|
|
|
8
8
|
readonly __INTERFERE_PUBLIC_KEY__?: string;
|
|
9
9
|
readonly __INTERFERE_RELEASE_SLUG__: string | null;
|
|
10
10
|
}
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* outside production builds or when no public key is set — the loader then
|
|
15
|
-
* bakes no remote-config globals.
|
|
16
|
-
*/
|
|
17
|
-
interface RemoteConfigFetch {
|
|
11
|
+
/** Build-time `/v3/config` request options for remote SDK configuration. */
|
|
12
|
+
export interface RemoteConfigFetch {
|
|
13
|
+
readonly cacheScope?: string;
|
|
18
14
|
readonly apiUrl: string;
|
|
19
15
|
readonly publicKey: string;
|
|
20
16
|
}
|
|
@@ -26,11 +22,21 @@ interface ConfigureBuildInput {
|
|
|
26
22
|
readonly remoteConfigFetch?: RemoteConfigFetch | undefined;
|
|
27
23
|
readonly values: InterfereInjectedValues;
|
|
28
24
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
export declare function configureBuild({ existingWebpack, existingTurbopack, nextMajor, projectDir, remoteConfigFetch, values }: ConfigureBuildInput): {
|
|
26
|
+
clientInstrumentationApplies: boolean;
|
|
27
|
+
turbopack: import("@interfere/helpers/omit-undefined").OmitUndefined<{
|
|
28
|
+
resolveAlias?: Record<string, string | string[] | Record<string, string | string[]>>;
|
|
29
|
+
resolveExtensions?: string[];
|
|
30
|
+
rules?: Record<string, import("next/dist/server/config-shared").TurbopackRuleConfigCollection>;
|
|
31
|
+
root?: string;
|
|
32
|
+
ignoreIssue?: Array<{
|
|
33
|
+
path: string | RegExp;
|
|
34
|
+
title?: string | RegExp;
|
|
35
|
+
description?: string | RegExp;
|
|
36
|
+
}>;
|
|
37
|
+
chunkLoadingGlobal?: string;
|
|
38
|
+
debugIds: boolean | undefined;
|
|
39
|
+
}>;
|
|
40
|
+
webpack: import("next/dist/server/config-shared").NextJsWebpackConfig | null | undefined;
|
|
41
|
+
};
|
|
42
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{omitUndefined}from"@interfere/helpers/omit-undefined";import{existsSync}from"node:fs";import{basename,
|
|
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__!==null||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,6 +1,5 @@
|
|
|
1
1
|
import { ManifestBundler } from "@interfere/types/data/source-maps";
|
|
2
2
|
//#region src/internal/build/detect-bundler.d.ts
|
|
3
|
-
type NextBuildBundler = Extract<ManifestBundler, "webpack" | "turbopack">;
|
|
4
|
-
declare function detectActiveBundler(env?: Readonly<Record<string, string | undefined>>, argv?: readonly string[], nextMajor?: number | null):
|
|
5
|
-
//#endregion
|
|
6
|
-
export { NextBuildBundler, detectActiveBundler };
|
|
3
|
+
export type NextBuildBundler = Extract<ManifestBundler, "webpack" | "turbopack">;
|
|
4
|
+
export declare function detectActiveBundler(env?: Readonly<Record<string, string | undefined>>, argv?: readonly string[], nextMajor?: number | null): "turbopack" | "webpack";
|
|
5
|
+
//#endregion
|
|
@@ -1,47 +1,60 @@
|
|
|
1
1
|
import { ProductionCompileContext, ResolvedBuildConfig } from "../../config.mjs";
|
|
2
2
|
import { ReleaseSlug } from "@interfere/types/releases/slug";
|
|
3
|
-
import {
|
|
3
|
+
import { Interfere } from "@interfere/javascript";
|
|
4
4
|
//#region src/internal/build/pipeline.d.ts
|
|
5
|
-
|
|
6
|
-
cleanup: number;
|
|
7
|
-
createRelease: number;
|
|
8
|
-
discover: number;
|
|
9
|
-
fileCount: number;
|
|
10
|
-
preflight: number;
|
|
11
|
-
total: number;
|
|
12
|
-
totalBytes: number;
|
|
13
|
-
upload: number;
|
|
14
|
-
}
|
|
15
|
-
declare function runBuildPipeline(context: ProductionCompileContext, metadata: ResolvedBuildConfig & {
|
|
5
|
+
export declare function runBuildPipeline(context: ProductionCompileContext, metadata: ResolvedBuildConfig & {
|
|
16
6
|
apiKey: string;
|
|
17
7
|
buildId: string;
|
|
18
8
|
releaseSlug: ReleaseSlug;
|
|
19
9
|
}): Promise<{
|
|
20
|
-
|
|
21
|
-
reason: string;
|
|
10
|
+
accepted: false;
|
|
22
11
|
fileCount: number;
|
|
23
|
-
|
|
24
|
-
config?: never;
|
|
12
|
+
reason: "no_source_maps";
|
|
25
13
|
buildId?: never;
|
|
14
|
+
cleanupFailures?: never;
|
|
15
|
+
config?: never;
|
|
16
|
+
release?: never;
|
|
26
17
|
timing?: never;
|
|
27
18
|
} | {
|
|
28
19
|
reason?: never;
|
|
29
|
-
|
|
20
|
+
accepted: true;
|
|
21
|
+
buildId: string;
|
|
22
|
+
cleanupFailures: {
|
|
23
|
+
absolute: string;
|
|
24
|
+
code: string | undefined;
|
|
25
|
+
message: string;
|
|
26
|
+
}[];
|
|
27
|
+
config: Interfere.ReleasesConfigResponse;
|
|
30
28
|
fileCount: number;
|
|
31
29
|
release: {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
build: import("@interfere/sdk/models").ReleaseBuildRef;
|
|
30
|
+
build: {
|
|
31
|
+
hash: string;
|
|
32
|
+
};
|
|
36
33
|
destination: {
|
|
37
34
|
id: string;
|
|
38
|
-
environment: string | null;
|
|
39
35
|
slug: `rel_${string}` & import("zod").$brand<"ReleaseSlug">;
|
|
36
|
+
environment: string | null;
|
|
37
|
+
};
|
|
38
|
+
org: {
|
|
39
|
+
id: string;
|
|
40
|
+
slug: string;
|
|
41
|
+
};
|
|
42
|
+
source: {
|
|
43
|
+
id: string;
|
|
44
|
+
};
|
|
45
|
+
surface: {
|
|
46
|
+
id: string;
|
|
47
|
+
slug: string;
|
|
40
48
|
};
|
|
41
49
|
};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
timing: {
|
|
51
|
+
cleanup: number;
|
|
52
|
+
createRelease: number;
|
|
53
|
+
discover: number;
|
|
54
|
+
fileCount: number;
|
|
55
|
+
total: number;
|
|
56
|
+
totalBytes: number;
|
|
57
|
+
upload: number;
|
|
58
|
+
};
|
|
45
59
|
}>;
|
|
46
|
-
//#endregion
|
|
47
|
-
export { runBuildPipeline };
|
|
60
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
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{assertClientChunksAreMapped}from"./source-maps/guard.mjs";import{
|
|
1
|
+
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{assertClientChunksAreMapped}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{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({baseUrl:metadata.apiUrl,apiKey:{token:metadata.apiKey},maxRetries:4,timeoutInSeconds:10}),start=performance.now(),timing={...EMPTY_TELEMETRY},{releaseSlug}=metadata,report=async result=>{timing.total=Math.round(performance.now()-start),await client.releases.reportTelemetry({...timing,result,releaseSlug},{abortSignal:AbortSignal.timeout(1e4)}).catch(()=>void 0)};try{let bundler=detectActiveBundler(process.env,process.argv,readNextMajorVersion(context.projectDir)),[discovered,config]=await Promise.all([discoverSourceMaps({bundler,distDir:context.distDir,projectDir:context.projectDir}),client.releases.getReleasesConfig({abortSignal:AbortSignal.timeout(6e4)})]),{files,sourceFileCount}=discovered;if(timing.discover=Math.round(performance.now()-start),timing.fileCount=files.length,files.length===0)return await report(`no_source_maps`),{accepted:!1,fileCount:0,reason:`no_source_maps`};await assertClientChunksAreMapped({discovered:files,distDir:context.distDir,projectDir:context.projectDir});let registering=performance.now(),release=createReleaseResponse.parse(await client.releases.createRelease(resolveReleaseRequest(metadata.buildId,metadata.environment,metadata.releaseSlug,config),{abortSignal:AbortSignal.timeout(6e4)})),registered=performance.now();releaseSlug=release.destination.slug,timing.createRelease=Math.round(registered-registering);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({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`);let cleanupStart=performance.now();timing.upload=Math.round(cleanupStart-registered);let cleanupFailures=metadata.purgeSourceMaps?await cleanupSourceMaps(files):[];return timing.cleanup=Math.round(performance.now()-cleanupStart),await report(`ok`),{accepted:!0,buildId:release.build.hash,cleanupFailures,config,fileCount:files.length,release,timing}}catch(error){let parsed=releaseBuildResultSchema.safeParse(classifyReleasePipelineError(error).toLowerCase());throw await report(parsed.success?parsed.data:`pipeline_failed`),error}}export{runBuildPipeline};
|
|
@@ -12,6 +12,5 @@
|
|
|
12
12
|
* import graph (the SDK ships that entrypoint to both Node + Edge
|
|
13
13
|
* runtimes via conditional exports).
|
|
14
14
|
*/
|
|
15
|
-
declare function runGitCommand(command: string): string | null;
|
|
16
|
-
//#endregion
|
|
17
|
-
export { runGitCommand };
|
|
15
|
+
export declare function runGitCommand(command: string): string | null;
|
|
16
|
+
//#endregion
|
|
@@ -1,7 +1,53 @@
|
|
|
1
1
|
import { ReleaseSlug } from "@interfere/types/releases/slug";
|
|
2
|
-
import {
|
|
3
|
-
import { CreateReleaseRequest } from "@interfere/types/releases/definition";
|
|
2
|
+
import { resolveReleaseRequest as resolveReleaseRequest$1 } from "@interfere/build/release-request";
|
|
4
3
|
//#region src/internal/build/release/index.d.ts
|
|
5
|
-
declare function resolveReleaseRequest(buildId: string, environment: string | null, releaseSlug: ReleaseSlug, config:
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
export declare function resolveReleaseRequest(buildId: string, environment: string | null, releaseSlug: ReleaseSlug, config: Parameters<typeof resolveReleaseRequest$1>[0]["config"]): {
|
|
5
|
+
buildId: string;
|
|
6
|
+
destination: {
|
|
7
|
+
deploymentId: string | null;
|
|
8
|
+
deploymentUrl: string | null;
|
|
9
|
+
destinationReleaseId: string | null;
|
|
10
|
+
environment: string | null;
|
|
11
|
+
environmentName: string | null;
|
|
12
|
+
environmentTarget: string | null;
|
|
13
|
+
provider: "vercel";
|
|
14
|
+
} | {
|
|
15
|
+
accountId: string | null;
|
|
16
|
+
deploymentUrl: string | null;
|
|
17
|
+
destinationReleaseId: string | null;
|
|
18
|
+
environment: string | null;
|
|
19
|
+
environmentName: string | null;
|
|
20
|
+
environmentTarget: string | null;
|
|
21
|
+
provider: "cloudflare";
|
|
22
|
+
scriptName: string | null;
|
|
23
|
+
versionId: string | null;
|
|
24
|
+
} | {
|
|
25
|
+
deploymentUrl: string | null;
|
|
26
|
+
destinationReleaseId: string | null;
|
|
27
|
+
environment: string | null;
|
|
28
|
+
provider: "cli";
|
|
29
|
+
} | null;
|
|
30
|
+
producerVersion?: string | undefined;
|
|
31
|
+
slug?: string | undefined;
|
|
32
|
+
source: {
|
|
33
|
+
branch: string;
|
|
34
|
+
commitMessage: string;
|
|
35
|
+
commitSha: string | null;
|
|
36
|
+
provider: "github";
|
|
37
|
+
runId?: string | null | undefined;
|
|
38
|
+
runUrl?: string | null | undefined;
|
|
39
|
+
runnerName?: string | null | undefined;
|
|
40
|
+
} | {
|
|
41
|
+
commitMessage: string | null;
|
|
42
|
+
commitRef: string | null;
|
|
43
|
+
commitSha: string | null;
|
|
44
|
+
commitUrl: string | null;
|
|
45
|
+
deploymentUrl?: string | null | undefined;
|
|
46
|
+
environment?: string | null | undefined;
|
|
47
|
+
provider: "cli";
|
|
48
|
+
runId: string | null;
|
|
49
|
+
runUrl: string | null;
|
|
50
|
+
runnerName: string | null;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{log}from"../../logger.mjs";import{PRODUCER_VERSION}from"../../version.mjs";import{
|
|
1
|
+
import{log}from"../../logger.mjs";import{PRODUCER_VERSION}from"../../version.mjs";import{resolveReleaseRequest as resolveReleaseRequest$1}from"@interfere/build/release-request";function resolveReleaseRequest(buildId,environment,releaseSlug,config){let result=resolveReleaseRequest$1({buildId,config,environment,producerVersion:PRODUCER_VERSION,releaseSlug});if(result.ok)return result.request;let sourceMissing=result.reason===`missing_source_provider`;return log.fatal(sourceMissing?`Missing version control provider`:`Missing deployment target`,[sourceMissing?`This surface does not have a version control provider configured.`:`This surface does not have a deployment target integration configured.`,`Configure one at https://interfere.com/~/${config.org.slug}/surfaces/${config.surface.slug}`])}export{resolveReleaseRequest};
|
|
@@ -20,12 +20,11 @@ import { SourceMapFile } from "./discover.mjs";
|
|
|
20
20
|
* Maps without a debugId, or whose debugId never appears in any chunk,
|
|
21
21
|
* are silently skipped — they have no way to resolve at runtime.
|
|
22
22
|
*/
|
|
23
|
-
declare function discoverTurbopack(opts: {
|
|
23
|
+
export declare function discoverTurbopack(opts: {
|
|
24
24
|
projectDir: string;
|
|
25
25
|
distDir: string;
|
|
26
26
|
}): Promise<{
|
|
27
27
|
files: SourceMapFile[];
|
|
28
28
|
sourceFileCount: number;
|
|
29
29
|
}>;
|
|
30
|
-
//#endregion
|
|
31
|
-
export { discoverTurbopack };
|
|
30
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{resolveDistDir,toPublicPath,walkDistTrees}from"./paths.mjs";import{relative}from"node:path";import{createHash}from"node:crypto";import{mapWithConcurrency}from"@interfere/helpers/map-with-concurrency";import{open,readFile,stat}from"node:fs/promises";const DEBUG_ID_RE=/\/\/[#@]\s*debugId=(?<debugId>[0-9a-f-]+)\s*$/mu,DEBUG_ID_TAIL_BYTES=65536;async function readChunkDebugId(jsAbs){let fromTail=await readTailDebugId(jsAbs).catch(()=>null);if(fromTail!==null)return fromTail;let content=await readFile(jsAbs,`utf8`).catch(()=>null);return content===null?null:DEBUG_ID_RE.exec(content)?.groups?.debugId??null}async function readTailDebugId(jsAbs){let{size}=await stat(jsAbs);if(size<=DEBUG_ID_TAIL_BYTES)return null;let handle=await open(jsAbs,`r`);try{let buf=Buffer.allocUnsafe(DEBUG_ID_TAIL_BYTES),{bytesRead}=await handle.read(buf,0,DEBUG_ID_TAIL_BYTES,size-DEBUG_ID_TAIL_BYTES);return DEBUG_ID_RE.exec(buf.subarray(0,bytesRead).toString(`utf8`))?.groups?.debugId??null}finally{await handle.close()}}async function discoverTurbopack(opts){let absDistDir=resolveDistDir(opts.projectDir,opts.distDir),relDistDir=relative(opts.projectDir,absDistDir),[mapPaths,jsPaths]=await Promise.all([walkDistTrees(absDistDir,`.js.map`),walkDistTrees(absDistDir,`.js`)]),chunksByDebugId=new Map;await mapWithConcurrency(jsPaths,16,async jsAbs=>{let debugId=await readChunkDebugId(jsAbs);if(!debugId)return;let chunkUrl=toPublicPath(relative(opts.projectDir,jsAbs),relDistDir),chunk=chunksByDebugId.get(debugId);chunk?chunk.chunkAbsolutes.push(jsAbs):chunksByDebugId.set(debugId,{chunkAbsolutes:[jsAbs],chunkUrl})});let loaded=(await mapWithConcurrency(mapPaths,16,async mapAbs=>{let content=await readFile(mapAbs,`utf8`).catch(()=>null);if(content===null)return null;let debugId;try{let parsed=JSON.parse(content);typeof parsed==`object`&&parsed&&`debugId`in parsed&&typeof parsed.debugId==`string`&&parsed.debugId.length>0&&({debugId}=parsed)}catch{return null}if(!debugId)return null;let chunk=chunksByDebugId.get(debugId);return chunk?{absolute:mapAbs,chunkAbsolutes:chunk.chunkAbsolutes,chunkUrl:chunk.chunkUrl,content,debugId,hash:createHash(`sha256`).update(content).digest(`hex`),path:toPublicPath(relative(opts.projectDir,mapAbs),relDistDir)}:null})).filter(f=>f!==null);return{files:loaded,sourceFileCount:loaded.length}}export{discoverTurbopack};
|