@interfere/next 11.2.1 → 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +25 -25
  2. package/dist/config.d.mts +7 -8
  3. package/dist/config.mjs +1 -1
  4. package/dist/instrument-client.d.mts +5 -5
  5. package/dist/instrument-client.mjs +1 -1
  6. package/dist/instrumentation.d.mts +11 -8
  7. package/dist/instrumentation.edge.d.mts +3 -3
  8. package/dist/instrumentation.edge.mjs +1 -1
  9. package/dist/instrumentation.mjs +1 -1
  10. package/dist/internal/build/configure-build.d.mts +21 -15
  11. package/dist/internal/build/configure-build.mjs +1 -1
  12. package/dist/internal/build/detect-bundler.d.mts +3 -4
  13. package/dist/internal/build/pipeline.d.mts +40 -27
  14. package/dist/internal/build/pipeline.mjs +1 -1
  15. package/dist/internal/build/release/git.d.mts +2 -3
  16. package/dist/internal/build/release/index.d.mts +51 -5
  17. package/dist/internal/build/release/index.mjs +1 -1
  18. package/dist/internal/build/source-maps/discover-turbopack.d.mts +2 -3
  19. package/dist/internal/build/source-maps/discover-turbopack.mjs +1 -1
  20. package/dist/internal/build/source-maps/discover-webpack.d.mts +3 -4
  21. package/dist/internal/build/source-maps/discover-webpack.mjs +1 -1
  22. package/dist/internal/build/source-maps/discover.d.mts +9 -5
  23. package/dist/internal/build/source-maps/discover.mjs +1 -1
  24. package/dist/internal/build/source-maps/guard.d.mts +2 -3
  25. package/dist/internal/build/source-maps/guard.mjs +1 -1
  26. package/dist/internal/build/source-maps/paths.d.mts +5 -6
  27. package/dist/internal/build/source-maps/paths.mjs +1 -1
  28. package/dist/internal/build/telemetry.d.mts +5 -0
  29. package/dist/internal/build/telemetry.mjs +1 -0
  30. package/dist/internal/build/value-injection-loader.d.mts +5 -8
  31. package/dist/internal/build/value-injection-loader.mjs +2 -2
  32. package/dist/internal/build-marker-guard.d.mts +5 -6
  33. package/dist/internal/build-marker-guard.mjs +1 -1
  34. package/dist/internal/env.d.mts +13 -15
  35. package/dist/internal/env.mjs +1 -1
  36. package/dist/internal/logger.d.mts +5 -6
  37. package/dist/internal/logger.mjs +1 -1
  38. package/dist/internal/release-slug.d.mts +3 -5
  39. package/dist/internal/release-slug.mjs +1 -1
  40. package/dist/internal/server/cache-safe-trace-context.d.mts +24 -0
  41. package/dist/internal/server/cache-safe-trace-context.mjs +1 -0
  42. package/dist/internal/server/capture.d.mts +3 -4
  43. package/dist/internal/server/capture.mjs +1 -1
  44. package/dist/internal/server/console-bridge.d.mts +2 -3
  45. package/dist/internal/server/console-bridge.mjs +1 -1
  46. package/dist/internal/server/edge-exporter.d.mts +5 -6
  47. package/dist/internal/server/edge-exporter.mjs +1 -1
  48. package/dist/internal/server/freshness-propagator.d.mts +41 -0
  49. package/dist/internal/server/freshness-propagator.mjs +1 -0
  50. package/dist/internal/server/global-handlers.d.mts +4 -5
  51. package/dist/internal/server/global-handlers.mjs +1 -1
  52. package/dist/internal/server/id-generator.d.mts +2 -3
  53. package/dist/internal/server/instrumentation-options.d.mts +3 -4
  54. package/dist/internal/server/remote-config.d.mts +3 -4
  55. package/dist/internal/server/remote-config.mjs +1 -1
  56. package/dist/internal/server/server-action-marker.d.mts +10 -5
  57. package/dist/internal/server/server-action-marker.mjs +1 -1
  58. package/dist/internal/server/server-flush.d.mts +3 -4
  59. package/dist/internal/server/server-flush.mjs +1 -1
  60. package/dist/internal/server/static-generation.d.mts +17 -0
  61. package/dist/internal/server/static-generation.mjs +1 -0
  62. package/dist/internal/server/trace-meta.d.mts +13 -26
  63. package/dist/internal/server/trace-meta.mjs +1 -1
  64. package/dist/internal/server/traceparent.d.mts +2 -3
  65. package/dist/internal/server/traceparent.mjs +1 -1
  66. package/dist/internal/server/types.d.mts +5 -8
  67. package/dist/internal/server/wrap-proxy.d.mts +3 -4
  68. package/dist/internal/server/wrap-proxy.mjs +1 -1
  69. package/dist/internal/server/wrap-server-action.d.mts +2 -3
  70. package/dist/internal/server/wrap-server-action.mjs +1 -1
  71. package/dist/internal/setup-warnings.d.mts +3 -4
  72. package/dist/internal/setup-warnings.mjs +1 -1
  73. package/dist/internal/version.d.mts +2 -3
  74. package/dist/package.mjs +1 -1
  75. package/dist/provider.d.mts +3 -3
  76. package/dist/provider.mjs +1 -1
  77. package/package.json +49 -42
  78. package/dist/internal/build/release/destinations/cli.d.mts +0 -6
  79. package/dist/internal/build/release/destinations/cli.mjs +0 -1
  80. package/dist/internal/build/release/destinations/cloudflare.d.mts +0 -5
  81. package/dist/internal/build/release/destinations/cloudflare.mjs +0 -1
  82. package/dist/internal/build/release/destinations/index.d.mts +0 -14
  83. package/dist/internal/build/release/destinations/index.mjs +0 -1
  84. package/dist/internal/build/release/destinations/vercel.d.mts +0 -5
  85. package/dist/internal/build/release/destinations/vercel.mjs +0 -1
  86. package/dist/internal/build/release/sources/github.d.mts +0 -8
  87. package/dist/internal/build/release/sources/github.mjs +0 -1
  88. package/dist/internal/build/release/sources/index.d.mts +0 -20
  89. package/dist/internal/build/release/sources/index.mjs +0 -1
  90. package/dist/internal/build/source-maps/concurrency.d.mts +0 -4
  91. package/dist/internal/build/source-maps/concurrency.mjs +0 -1
  92. package/dist/internal/build/source-maps/index.d.mts +0 -6
  93. package/dist/internal/build/source-maps/index.mjs +0 -1
  94. package/dist/internal/build/source-maps/upload.d.mts +0 -39
  95. package/dist/internal/build/source-maps/upload.mjs +0 -1
  96. package/dist/internal/url.d.mts +0 -4
  97. 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 | 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. |
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 | 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>`) |
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 | Description |
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()` | 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. |
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 | Plugins | Gated? |
189
- | --- | --- | --- |
190
- | `necessary` | Error tracking | Always on |
191
- | `analytics` | Page events, rage clicks, fingerprint | Yes |
192
- | `replay` | Session replay | Yes |
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 disables gating entirely (all features load). Passing it enables gating — only `necessary` plugins plus explicitly consented categories will activate.
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(); // grant all
205
- consent.get(); // current state, or null if no gating
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(settingsOrConfig={},maybeConfig){let[settings,nextConfig]=maybeConfig===void 0?[{},settingsOrConfig]:[settingsOrConfig,maybeConfig];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{[RELEASE_SLUG_GLOBAL]:releaseSlug,[BUILD_MARKER_GLOBAL]:!0,...config.publicKey&&{[PUBLIC_KEY_GLOBAL]:config.publicKey,...apiHost&&{[API_URL_GLOBAL]:apiHost}},...config.environment&&{[ENVIRONMENT_GLOBAL]:config.environment},...forceEnable&&{[FORCE_ENABLE_GLOBAL]:!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({existingWebpack:webpack,existingTurbopack:turbopack,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,...userEnv?{env:userEnv}:{},compiler:interfereCompilerConfig(compiler,config),productionBrowserSourceMaps:resolveBrowserSourceMaps(config,productionBrowserSourceMaps),turbopack:build.turbopack,webpack:build.webpack,...experimental?{experimental}:{}})}function interfereExperimentalConfig({existing,instrumentationHook,traceMetadata}){let merged={...existing,...instrumentationHook?{instrumentationHook:!0}:{},...traceMetadata?{clientTraceMetadata:[...new Set([...existing?.clientTraceMetadata??[],...CLIENT_TRACE_METADATA_KEYS])]}:{}};return 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,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))return reportReleaseDiagnostic(`API_KEY_WRONG_PREFIX`,{apiKey});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.ready){log.warn(`Skipping`,[`No source maps found`]);return}log.info(`Completed`,[`https://interfere.com/~/${result.config?.org.slug}/surfaces/${result.config?.surface.slug}`,`Release: ${result.release?.destination.slug??`Unknown`}`,`Build: ${result.buildId}`,`Artifacts: ${result.fileCount} source maps`])}catch(error){let message=error instanceof Error?error.message:String(error);reportReleaseDiagnostic(classifyReleasePipelineError(error),{errorMessage:message})}}export{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, Kernel as Kernel$1, KernelInternalOptions, KernelOptions, KernelOptions as KernelOptions$1 } from "@interfere/react/internal/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$1, getKernelOrNull: () => Kernel$1 | null, identity: {
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$1 | null>;
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, close, consent, getKernel, getKernelOrNull, identity, init, span, subscribeToKernel };
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({producerVersion:PRODUCER_VERSION,initEntryName:`instrumentation-client`}),{close,consent,getKernel,getKernelOrNull,identity,subscribeToKernel}=wrapper;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};
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. Trace context is intentionally *not*
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. Composing this on top extends rather
59
- * than overrides.
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 { InterfereOtelKit, PrerenderSafeIdGenerator, type ServerInstrumentationOptions, buildInterfereOtelKit, flush, onRequestError, register };
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, buildInterfereOtelKit, onRequestError, register };
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{withPublicKey}from"./internal/url.mjs";import{onRequestError}from"./internal/server/capture.mjs";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;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({"service.name":opts.serviceName??`interfere-sdk-next-edge`,"service.namespace":`interfere`,"deployment.environment.name":env.nodeEnvironment,"telemetry.sdk.language":`nodejs`,"interfere.sdk.name":`@interfere/next`,"interfere.sdk.version":PRODUCER_VERSION,...releaseSlug?{"release.slug":releaseSlug}:{}}),spanProcessors:isDrainMode?[]:[new SimpleSpanProcessor(new FetchTraceExporter(withPublicKey(`${env.apiUrl}/v2/sink`,env.publicKey),{"x-interfere-producer-version":PRODUCER_VERSION}))]});return trace.setGlobalTracerProvider(provider),setServerFlush(()=>provider.forceFlush()),registered=!0,Promise.resolve()}function buildInterfereOtelKit(_opts={}){return null}export{buildInterfereOtelKit,onRequestError,register};
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};
@@ -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{withPublicKey}from"./internal/url.mjs";import{fetchAndCacheRemoteConfig}from"./internal/server/remote-config.mjs";import{onRequestError}from"./internal/server/capture.mjs";import{bridgeConsoleToOtel}from"./internal/server/console-bridge.mjs";import{installGlobalHandlers}from"./internal/server/global-handlers.mjs";import{PrerenderSafeIdGenerator}from"./internal/server/id-generator.mjs";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,W3CTraceContextPropagator}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 sinkUrl=withPublicKey(`${env.apiUrl}/v2/sink`,env.publicKey),exporterHeaders={"x-interfere-producer-version":PRODUCER_VERSION},resourceAttributes={"service.name":opts.serviceName??`interfere-sdk-next-server`,"service.namespace":`interfere`,"deployment.environment.name":env.nodeEnvironment,"telemetry.sdk.language":`nodejs`,"interfere.sdk.name":`@interfere/next`,"interfere.sdk.version":PRODUCER_VERSION,...releaseSlug?{"release.slug":releaseSlug}:{}},compression=opts.compression===`gzip`?CompressionAlgorithm.GZIP:CompressionAlgorithm.NONE,isDrainMode=readServerTracingMode()===`drain`,spanProcessors=[new BaggageSpanProcessor(()=>!0),...isDrainMode?[]:[new BatchSpanProcessor(new OTLPTraceExporter({url:sinkUrl,headers:exporterHeaders,compression,timeoutMillis:EXPORT_TIMEOUT_MS}))],...opts._internalAdditionalSpanProcessors??[]],isLogDrainMode=readLogSourcingMode()===`drain`;return{resourceAttributes,spanProcessors,logRecordProcessors:[...isLogDrainMode?[]:[new BatchLogRecordProcessor({exporter:new OTLPLogExporter({url:sinkUrl,headers:exporterHeaders,compression,timeoutMillis:EXPORT_TIMEOUT_MS})})],...opts._internalAdditionalLogRecordProcessors??[]],propagators:[new W3CBaggagePropagator],instrumentations:[],enableConsoleBridge:()=>isLogDrainMode?()=>{}:bridgeConsoleToOtel(),fetchRemoteConfig:()=>fetchAndCacheRemoteConfig()}}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({resource,spanProcessors:kit.spanProcessors,idGenerator:new PrerenderSafeIdGenerator}),propagation.fields().length===0&&propagation.setGlobalPropagator(new CompositePropagator({propagators:[new W3CTraceContextPropagator,...kit.propagators]})),tracerProvider.register({contextManager:new AsyncLocalStorageContextManager().enable()}),loggerProvider=new LoggerProvider({resource,processors:kit.logRecordProcessors});let{logs:logsApi}=await import(`@opentelemetry/api-logs`);logsApi.setGlobalLoggerProvider(loggerProvider),registerInstrumentations({tracerProvider,instrumentations:kit.instrumentations}),opts.consoleBridge!==!1&&kit.enableConsoleBridge(),installGlobalHandlers({captureUncaughtException:opts.captureUncaughtException??!0,captureUnhandledRejection:opts.captureUnhandledRejection??!0,exitOnUncaughtException:process.env.NODE_ENV===`production`,flush}),setServerFlush(flush),registered=!0}export{PrerenderSafeIdGenerator,buildInterfereOtelKit,flush,onRequestError,register};
1
+ import{readInterfereEnv}from"./internal/env.mjs";import{resolveReleaseSlug}from"./internal/release-slug.mjs";import{PRODUCER_VERSION}from"./internal/version.mjs";import{setServerFlush}from"./internal/server/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
- * Inputs for the loader's build-time `/v2/config` fetch that resolves the
13
- * surface's remote config (`serverTracing` mode + `ingestUrl`). Omitted
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
- interface ConfigureBuildOutput {
30
- readonly clientInstrumentationApplies: boolean;
31
- readonly turbopack: NextConfig["turbopack"] | undefined;
32
- readonly webpack: NextConfig["webpack"] | undefined;
33
- }
34
- declare function configureBuild({ existingWebpack, existingTurbopack, nextMajor, projectDir, remoteConfigFetch, values }: ConfigureBuildInput): ConfigureBuildOutput;
35
- //#endregion
36
- export { RemoteConfigFetch, configureBuild };
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,dirname,join,normalize,resolve}from"node:path";import{fileURLToPath}from"node:url";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,webpack:buildWebpackHook(existingWebpack,targets,values,remoteConfigFetch,injectionApplies),turbopack:buildTurbopackConfig(existingTurbopack,targets,values,remoteConfigFetch,injectionApplies,nextMajor)}}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,mutableConfig=outputConfig;return mutableConfig.module??={},mutableConfig.module.rules??=[],mutableConfig.module.rules.push({test:resource=>!!resource&&normalizedTargets.has(normalize(resource)),use:[{loader:injectionLoaderPath,options:omitUndefined({serializedValues:JSON.stringify(values),serializedFetch:remoteConfigFetch?JSON.stringify(remoteConfigFetch):void 0})}]}),outputConfig}}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({serializedValues:JSON.stringify(values),serializedFetch:remoteConfigFetch?JSON.stringify(remoteConfigFetch):void 0})})}return omitUndefined({...baseConfig,debugIds,rules})}function appendTurbopackLoader(existing,loader){let rule=existing??{},loaders=Array.isArray(rule.loaders)?rule.loaders:[];return{...rule,loaders:[...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(dirname(fileURLToPath(import.meta.url)));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
+ 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): NextBuildBundler;
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 { ReleasesConfigResponse } from "@interfere/sdk/models/releases-config-response.js";
3
+ import { Interfere } from "@interfere/javascript";
4
4
  //#region src/internal/build/pipeline.d.ts
5
- interface BuildTiming {
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
- ready: boolean;
21
- reason: string;
10
+ accepted: false;
22
11
  fileCount: number;
23
- release?: never;
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
- ready: true;
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
- org: import("@interfere/sdk/models").OrgRef;
33
- surface: import("@interfere/sdk/models").SurfaceRef;
34
- source: import("@interfere/sdk/models").ReleaseSourceRef;
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
- config: ReleasesConfigResponse;
43
- buildId: string;
44
- timing: BuildTiming;
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{uploadSourceMaps}from"./source-maps/upload.mjs";import{cleanupSourceMaps}from"./source-maps/index.mjs";import{releaseSlugSchema}from"@interfere/types/releases/slug";import{HTTPClient,Interfere}from"@interfere/sdk";async function timed(fn){let start=performance.now();return[await fn(),Math.round(performance.now()-start)]}async function runBuildPipeline(context,metadata){let{apiUrl,apiKey}=metadata,httpClient=new HTTPClient;httpClient.addHook(`beforeRequest`,request=>{let nextRequest=new Request(request);return nextRequest.headers.set(`Authorization`,`Bearer ${apiKey}`),nextRequest});let sdk=new Interfere({serverURL:apiUrl,httpClient}),security={apiKey},start=performance.now(),bundler=detectActiveBundler(process.env,process.argv,readNextMajorVersion(context.projectDir)),[{discovered,config},discoverMs]=await timed(async()=>{let[discovered,config]=await Promise.all([discoverSourceMaps({bundler,projectDir:context.projectDir,distDir:context.distDir}),sdk.releases.getConfig(security)]);return{discovered,config}});if(discovered.files.length===0)return{ready:!1,reason:`no_source_maps`,fileCount:0};await assertClientChunksAreMapped({projectDir:context.projectDir,distDir:context.distDir,discovered:discovered.files});let releaseRequest=resolveReleaseRequest(metadata.buildId,metadata.environment,metadata.releaseSlug,config),[release,createReleaseMs]=await timed(()=>sdk.releases.create(security,releaseRequest)),releaseSlug=releaseSlugSchema.parse(release.destination.slug),buildId=release.build.hash??metadata.buildId,[{totalBytes},uploadMs]=await timed(()=>uploadSourceMaps({apiUrl,bundler,discovered,httpClient,releaseSlug})),[,preflightMs]=await timed(()=>sdk.releases.preflight(security,{releaseSlug})),[,cleanupMs]=await timed(async()=>{metadata.purgeSourceMaps&&await cleanupSourceMaps(discovered.files)}),timing={discover:discoverMs,createRelease:createReleaseMs,upload:uploadMs,preflight:preflightMs,cleanup:cleanupMs,total:Math.round(performance.now()-start),fileCount:discovered.files.length,totalBytes};return{ready:!0,fileCount:discovered.files.length,release:{...release,destination:{...release.destination,slug:releaseSlug}},config,buildId,timing}}export{runBuildPipeline};
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 { ReleasesConfigResponse } from "@interfere/sdk/models/releases-config-response.js";
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: ReleasesConfigResponse): CreateReleaseRequest;
6
- //#endregion
7
- export { resolveReleaseRequest };
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{destinations}from"./destinations/index.mjs";import{sources}from"./sources/index.mjs";import{applyDeployEnvironment}from"@interfere/types/sdk/deploy-environment";import{configuredSourceProviderSchema,releaseDestinationProviderSchema}from"@interfere/types/integrations";function resolveReleaseRequest(buildId,environment,releaseSlug,config){let sourceProvider=configuredSourceProviderSchema.safeParse(config.surface.sourceProvider);if(!sourceProvider.success)return log.fatal(`Missing version control provider`,[`This surface does not have a version control provider configured.`,`Configure one at https://interfere.com/~/${config.org.slug}/surfaces/${config.surface.slug}`]);let destinationProvider=releaseDestinationProviderSchema.safeParse(config.surface.destinationProvider);return destinationProvider.success?{source:sources[sourceProvider.data](),destination:applyDeployEnvironment(destinations[destinationProvider.data](releaseSlug),environment),buildId,slug:releaseSlug,producerVersion:PRODUCER_VERSION}:log.fatal(`Missing deployment target`,[`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};
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{mapWithConcurrency}from"./concurrency.mjs";import{resolveDistDir,toPublicPath,walkDistTrees}from"./paths.mjs";import{relative}from"node:path";import{createHash}from"node:crypto";import{open,readFile,stat}from"node:fs/promises";const DEBUG_ID_RE=/\/\/[#@]\s*debugId=([0-9a-f-]+)\s*$/m,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)?.[1]??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`))?.[1]??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`)]),debugIdToChunkUrl=new Map;await mapWithConcurrency(jsPaths,16,async jsAbs=>{let debugId=await readChunkDebugId(jsAbs);if(!debugId)return;let chunkUrl=toPublicPath(relative(opts.projectDir,jsAbs),relDistDir);debugIdToChunkUrl.has(debugId)||debugIdToChunkUrl.set(debugId,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.debugId==`string`&&parsed.debugId.length>0&&(debugId=parsed.debugId)}catch{return null}if(!debugId)return null;let chunkUrl=debugIdToChunkUrl.get(debugId);return chunkUrl?{absolute:mapAbs,path:toPublicPath(relative(opts.projectDir,mapAbs),relDistDir),content,hash:createHash(`sha256`).update(content).digest(`hex`),debugId,chunkUrl}:null})).filter(f=>f!==null);return{files:loaded,sourceFileCount:loaded.length}}export{discoverTurbopack};
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};