@interfere/next 10.0.1-canary.1 → 11.0.0-canary.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +1 -1
  2. package/dist/config.d.mts +10 -0
  3. package/dist/config.d.mts.map +1 -1
  4. package/dist/config.mjs +1 -1
  5. package/dist/config.mjs.map +1 -1
  6. package/dist/instrument-client.d.mts.map +1 -1
  7. package/dist/instrumentation.d.mts.map +1 -1
  8. package/dist/instrumentation.edge.d.mts.map +1 -1
  9. package/dist/instrumentation.edge.mjs +1 -1
  10. package/dist/instrumentation.edge.mjs.map +1 -1
  11. package/dist/instrumentation.mjs +1 -1
  12. package/dist/instrumentation.mjs.map +1 -1
  13. package/dist/internal/build/configure-build.d.mts +1 -0
  14. package/dist/internal/build/configure-build.d.mts.map +1 -1
  15. package/dist/internal/build/configure-build.mjs.map +1 -1
  16. package/dist/internal/build/pipeline.d.mts.map +1 -1
  17. package/dist/internal/build/pipeline.mjs +1 -1
  18. package/dist/internal/build/pipeline.mjs.map +1 -1
  19. package/dist/internal/build/release/destinations/vercel.d.mts.map +1 -1
  20. package/dist/internal/build/release/sources/github.d.mts +4 -1
  21. package/dist/internal/build/release/sources/github.d.mts.map +1 -1
  22. package/dist/internal/build/release/sources/github.mjs.map +1 -1
  23. package/dist/internal/build/source-maps/discover-turbopack.mjs.map +1 -1
  24. package/dist/internal/build/source-maps/discover-webpack.mjs.map +1 -1
  25. package/dist/internal/build/source-maps/index.mjs.map +1 -1
  26. package/dist/internal/build/source-maps/upload.d.mts.map +1 -1
  27. package/dist/internal/build/source-maps/upload.mjs.map +1 -1
  28. package/dist/internal/env.d.mts +1 -3
  29. package/dist/internal/env.d.mts.map +1 -1
  30. package/dist/internal/env.mjs +1 -1
  31. package/dist/internal/env.mjs.map +1 -1
  32. package/dist/internal/release-slug.d.mts.map +1 -1
  33. package/dist/internal/route/handle-get.d.mts.map +1 -1
  34. package/dist/internal/route/handle-get.mjs.map +1 -1
  35. package/dist/internal/route/proxy.d.mts.map +1 -1
  36. package/dist/internal/route/proxy.mjs +1 -1
  37. package/dist/internal/route/proxy.mjs.map +1 -1
  38. package/dist/internal/server/capture.d.mts.map +1 -1
  39. package/dist/internal/server/capture.mjs.map +1 -1
  40. package/dist/internal/server/console-bridge.d.mts.map +1 -1
  41. package/dist/internal/server/console-bridge.mjs.map +1 -1
  42. package/dist/internal/server/id-generator.d.mts.map +1 -1
  43. package/dist/internal/server/id-generator.mjs.map +1 -1
  44. package/dist/internal/server/remote-config.d.mts.map +1 -1
  45. package/dist/internal/server/remote-config.mjs +1 -1
  46. package/dist/internal/server/remote-config.mjs.map +1 -1
  47. package/dist/internal/server/trace-meta.d.mts.map +1 -1
  48. package/dist/internal/server/traceparent.d.mts.map +1 -1
  49. package/dist/package.mjs +1 -1
  50. package/dist/provider.d.mts.map +1 -1
  51. package/dist/provider.mjs.map +1 -1
  52. package/dist/route-handler.d.mts.map +1 -1
  53. package/package.json +15 -15
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  <p align="center">
12
12
  <a href="https://www.npmjs.com/package/@interfere/next"><img src="https://img.shields.io/npm/v/@interfere/next.svg" alt="npm version" /></a>
13
- <a href="https://github.com/interfere-inc/interfere/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@interfere/next.svg" alt="License" /></a>
13
+ <a href="https://interfere.ghe.com/interfere/interfere/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@interfere/next.svg" alt="License" /></a>
14
14
  <a href="https://www.npmjs.com/package/@interfere/next"><img src="https://img.shields.io/npm/dm/@interfere/next.svg" alt="npm downloads" /></a>
15
15
  </p>
16
16
 
package/dist/config.d.mts CHANGED
@@ -22,6 +22,16 @@ interface ResolvedBuildConfig {
22
22
  * into the bundle; never exposed via env.
23
23
  */
24
24
  readonly buildId: string | null;
25
+ /**
26
+ * Deploy-environment label (`INTERFERE_ENVIRONMENT` ?? `VERCEL_ENV`)
27
+ * resolved at build time. Stamped into the bundle as
28
+ * `__INTERFERE_ENVIRONMENT__` so the browser kernel — which can't read
29
+ * non-public env vars — labels events with the same environment as the
30
+ * server. `NODE_ENV` is deliberately not folded in: it stays the browser's
31
+ * bundler-inlined runtime fallback, so dev bundles keep resolving
32
+ * `development` even when this is unset.
33
+ */
34
+ readonly environment: string | null;
25
35
  readonly publicKey: string | null;
26
36
  /**
27
37
  * Deterministically derived from the leading 16 hex of the commit SHA, so
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.mts","names":[],"sources":["../src/config.ts"],"mappings":";;;;UAmBiB,uBAAA,SAAgC,UAAU;;AAA3D;;;;;EAOE,SAAA;IAAA,SAAuB,OAAA;EAAA;AAAA;AAAA,UAGR,mBAAA;EAAA,SACN,MAAA;EAAA,SACA,MAAA;EAewB;;;;;EAAA,SATxB,OAAA;EAAA,SACA,SAAA;EAQwB;AAAA;AAGnC;;;;;EAHmC,SAAxB,WAAA,EAAa,WAAW;AAAA;AAAA,iBAGnB,aAAA,CACd,UAAA,GAAY,uBAAA,GACX,UAAU;AAAA,UA6II,wBAAA;EAAA,SACN,OAAA;EAAA,SACA,UAAU;AAAA"}
1
+ {"version":3,"file":"config.d.mts","names":[],"sources":["../src/config.ts"],"mappings":";;;;UAoBiB,uBAAA,SAAgC,UAAU;;AAA3D;;;;;EAOE,SAAA;IAAA,SAAuB,OAAA;EAAA;AAAA;AAAA,UAGR,mBAAA;EAAA,SACN,MAAA;EAAA,SACA,MAAA;EAyBwB;;;;;EAAA,SAnBxB,OAAA;EAmBA;;;AAAwB;AAGnC;;;;;EAHW,SATA,WAAA;EAAA,SACA,SAAA;EAaE;AAAA;AAoIb;;;;AAEqB;EAtIR,SALF,WAAA,EAAa,WAAW;AAAA;AAAA,iBAGnB,aAAA,CACd,UAAA,GAAY,uBAAA,GACX,UAAU;AAAA,UAoII,wBAAA;EAAA,SACN,OAAA;EAAA,SACA,UAAU;AAAA"}
package/dist/config.mjs CHANGED
@@ -1 +1 @@
1
- import{configureBuild}from"./internal/build/configure-build.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{omitUndefined}from"@interfere/helpers/omit-undefined";import{LOCAL_DEV_RELEASE_SLUG}from"@interfere/types/releases/slug";function withInterfere(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(),forceEnable=!!process.env.NEXT_PUBLIC_INTERFERE_FORCE_ENABLE,build=configureBuild({existingWebpack:webpack,existingTurbopack:turbopack,projectDir,values:{__INTERFERE_RELEASE_SLUG__:forceEnable?LOCAL_DEV_RELEASE_SLUG:config.releaseSlug,...config.publicKey&&{__INTERFERE_PROXY_MODE__:!0,__INTERFERE_PUBLIC_KEY__:config.publicKey},...forceEnable&&{__INTERFERE_FORCE_ENABLE__:!0}}});if(config.publicKey!==null&&!build.webpack&&!build.turbopack)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 nextMajor=readNextMajorVersion(projectDir),needsInstrumentationHookFlag=nextMajor===null||nextMajor<=14,existingHook=compiler?.runAfterProductionCompile,browserSourceMaps=config.apiKey===null?productionBrowserSourceMaps:!0;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`."]),{...rest,...userEnv?{env:userEnv}:{},compiler:{...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 evidence when generating insights.`,`Error: ${message}`])}}},...omitUndefined({productionBrowserSourceMaps:browserSourceMaps,turbopack:build.turbopack,webpack:build.webpack}),...needsInstrumentationHookFlag?{experimental:{...rest.experimental??{},instrumentationHook:!0}}:{}}}function resolveBuildConfig(){let{apiKey,apiUrl,publicKey}=readInterfereEnv(),{commitSha,slug}=resolveReleaseSlug();return{apiKey:apiKey??null,apiUrl,buildId:commitSha,publicKey:publicKey??null,releaseSlug:slug}}async function runBuildReleasePipeline(context,config){let{apiKey,buildId,releaseSlug}=config;if(!apiKey)return log.fatal(`API key not set`,[`withInterfere() requires an API 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://support.interfere.com/docs/guides/next-js`]);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);log.warn(`Skipping release pipeline`,[`Interfere failed to process this release.`,`Events from this release will be ingested, but ignored as evidence when generating insights.`,`Please check https://status.interfere.com for any outages, or contact support@interfere.com if the problem persists.`,`Error: ${message}`]);return}}export{withInterfere};
1
+ import{configureBuild}from"./internal/build/configure-build.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{omitUndefined}from"@interfere/helpers/omit-undefined";import{LOCAL_DEV_RELEASE_SLUG}from"@interfere/types/releases/slug";import{readFirstEnvValue}from"@interfere/types/sdk/env";function withInterfere(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(),forceEnable=!!process.env.NEXT_PUBLIC_INTERFERE_FORCE_ENABLE,build=configureBuild({existingWebpack:webpack,existingTurbopack:turbopack,projectDir,values:{__INTERFERE_RELEASE_SLUG__:forceEnable?LOCAL_DEV_RELEASE_SLUG:config.releaseSlug,...config.publicKey&&{__INTERFERE_PROXY_MODE__:!0,__INTERFERE_PUBLIC_KEY__:config.publicKey},...config.environment&&{__INTERFERE_ENVIRONMENT__:config.environment},...forceEnable&&{__INTERFERE_FORCE_ENABLE__:!0}}});if(config.publicKey!==null&&!build.webpack&&!build.turbopack)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 nextMajor=readNextMajorVersion(projectDir),needsInstrumentationHookFlag=nextMajor===null||nextMajor<=14,existingHook=compiler?.runAfterProductionCompile,browserSourceMaps=config.apiKey===null?productionBrowserSourceMaps:!0;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`."]),{...rest,...userEnv?{env:userEnv}:{},compiler:{...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 evidence when generating insights.`,`Error: ${message}`])}}},...omitUndefined({productionBrowserSourceMaps:browserSourceMaps,turbopack:build.turbopack,webpack:build.webpack}),...needsInstrumentationHookFlag?{experimental:{...rest.experimental??{},instrumentationHook:!0}}:{}}}function resolveBuildConfig(){let{apiKey,apiUrl,publicKey}=readInterfereEnv(),{commitSha,slug}=resolveReleaseSlug();return{apiKey:apiKey??null,apiUrl,buildId:commitSha,environment:readFirstEnvValue(process.env,[`INTERFERE_ENVIRONMENT`,`VERCEL_ENV`]),publicKey:publicKey??null,releaseSlug:slug}}async function runBuildReleasePipeline(context,config){let{apiKey,buildId,releaseSlug}=config;if(!apiKey)return log.fatal(`API key not set`,[`withInterfere() requires an API 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://support.interfere.com/docs/guides/next-js`]);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);log.warn(`Skipping release pipeline`,[`Interfere failed to process this release.`,`Events from this release will be ingested, but ignored as evidence when generating insights.`,`Please check https://status.interfere.com for any outages, or contact support@interfere.com if the problem persists.`,`Error: ${message}`]);return}}export{withInterfere};
@@ -1 +1 @@
1
- {"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["import { omitUndefined } from \"@interfere/helpers/omit-undefined\";\nimport {\n LOCAL_DEV_RELEASE_SLUG,\n type ReleaseSlug,\n} from \"@interfere/types/releases/slug\";\n\nimport type { NextConfig } from \"next\";\n\nimport { configureBuild } from \"./internal/build/configure-build.js\";\nimport { readInterfereEnv } from \"./internal/env.js\";\nimport { FatalError, log } from \"./internal/logger.js\";\nimport { resolveReleaseSlug } from \"./internal/release-slug.js\";\nimport {\n readNextMajorVersion,\n warnIfServerInstrumentationMissing,\n} from \"./internal/setup-warnings.js\";\n\nconst NEXT_INSTRUMENTATION_HOOK_FLAG_LAST_MAJOR = 14;\n\nexport interface NextConfigWithInterfere extends NextConfig {\n /**\n * Reserved for future SDK config. Setting `buildId` here is rejected\n * loudly — it caused server/client slug drift in 9.x. Override the\n * commit SHA via the `INTERFERE_SOURCE_ID` env var (set on both build\n * and runtime envs) instead.\n */\n interfere?: { readonly buildId?: never };\n}\n\nexport interface ResolvedBuildConfig {\n readonly apiKey: string | null;\n readonly apiUrl: string;\n /**\n * Server-internal — the build pipeline still uses this to key R2\n * source-map paths until Phase 7 re-keys them to slug. Never injected\n * into the bundle; never exposed via env.\n */\n readonly buildId: string | null;\n readonly publicKey: string | null;\n /**\n * Deterministically derived from the leading 16 hex of the commit SHA, so\n * the build step and the collector arrive at the same\n * `rel_xxxxxxxxxxxxxxxx` without a round-trip. Stamped into the bundle as\n * `__INTERFERE_RELEASE_SLUG__`. `null` when no commit SHA is available —\n * release-pipeline calls bail in that case.\n */\n readonly releaseSlug: ReleaseSlug | null;\n}\n\nexport function withInterfere(\n nextConfig: NextConfigWithInterfere = {}\n): NextConfig {\n const {\n interfere,\n env: userEnv,\n webpack,\n turbopack,\n compiler,\n productionBrowserSourceMaps,\n ...rest\n } = nextConfig;\n\n if (interfere && \"buildId\" in interfere && interfere.buildId !== undefined) {\n return log.fatal(\"Removed: interfere.buildId\", [\n \"`interfere.buildId` was removed in SDK 10. It only worked at build time, so server-side spans drifted from the bundle's slug.\",\n \"Override the commit SHA via the `INTERFERE_SOURCE_ID` env var on both build and runtime envs instead.\",\n ]);\n }\n\n const projectDir = process.cwd();\n const config = resolveBuildConfig();\n const forceEnable = !!process.env[\"NEXT_PUBLIC_INTERFERE_FORCE_ENABLE\"];\n // Force-enable overrides the derived slug with a known sentinel. `next\n // dev` doesn't run the build pipeline, so any commit-derived slug\n // wouldn't be preflight-confirmed at the collector. The local DB seeds\n // this slug as preflight-confirmed (see ENG-1353); prod\n // collectors silently drop force-enabled batches by header (ENG-1356).\n const releaseSlug = forceEnable ? LOCAL_DEV_RELEASE_SLUG : config.releaseSlug;\n const build = configureBuild({\n existingWebpack: webpack,\n existingTurbopack: turbopack,\n projectDir,\n values: {\n __INTERFERE_RELEASE_SLUG__: releaseSlug,\n ...(config.publicKey && {\n __INTERFERE_PROXY_MODE__: true,\n __INTERFERE_PUBLIC_KEY__: config.publicKey,\n }),\n ...(forceEnable && { __INTERFERE_FORCE_ENABLE__: true }),\n },\n });\n\n if (config.publicKey !== null && !build.webpack && !build.turbopack) {\n return log.fatal(\"Missing instrumentation client\", [\n \"INTERFERE_PUBLIC_KEY is set but no instrumentation-client file was found.\",\n \"Create an instrumentation-client.ts file in your project root or src/ directory.\",\n ]);\n }\n\n if (config.publicKey !== null) {\n warnIfServerInstrumentationMissing(projectDir);\n }\n\n // `experimental.instrumentationHook` is required to opt in to\n // `instrumentation.ts` on Next 13–14, then becomes default-on (and the\n // flag is removed) in 15+. Default to setting the flag if we can't read\n // the version (monorepo hoist, weird resolve) — cheap on 15+ and avoids\n // silent breakage on 13/14.\n const nextMajor = readNextMajorVersion(projectDir);\n const needsInstrumentationHookFlag =\n nextMajor === null ||\n nextMajor <= NEXT_INSTRUMENTATION_HOOK_FLAG_LAST_MAJOR;\n\n const existingHook = (compiler as NextCompilerWithProductionHook | undefined)\n ?.runAfterProductionCompile;\n\n // Production source maps are required for symbolication. We force them\n // on whenever an API key is present; flip the customer's explicit\n // `false` to `true` and log so the change is visible.\n const browserSourceMaps =\n config.apiKey === null ? productionBrowserSourceMaps : true;\n if (config.apiKey !== null && productionBrowserSourceMaps === false) {\n log.warn(\"Forcing productionBrowserSourceMaps\", [\n \"withInterfere() requires production source maps to symbolicate errors.\",\n \"Your `productionBrowserSourceMaps: false` was overridden to `true`.\",\n ]);\n }\n\n return {\n ...rest,\n ...(userEnv ? { env: userEnv } : {}),\n\n compiler: {\n ...omitUndefined(compiler ?? {}),\n async runAfterProductionCompile(context: ProductionCompileContext) {\n if (existingHook) {\n await existingHook(context);\n }\n\n try {\n await runBuildReleasePipeline(context, config);\n } catch (error) {\n if (error instanceof FatalError) {\n throw error;\n }\n\n const message =\n error instanceof Error ? error.message : String(error);\n\n log.warn(\"Skipping release pipeline\", [\n \"Interfere encountered an unexpected error.\",\n \"Build will continue, but this release's data will be ingested, but ignored as evidence when generating insights.\",\n `Error: ${message}`,\n ]);\n }\n },\n },\n\n ...omitUndefined({\n productionBrowserSourceMaps: browserSourceMaps,\n turbopack: build.turbopack,\n webpack: build.webpack,\n }),\n\n // `instrumentationHook` is a Next 13–14 flag — required to opt into\n // `instrumentation.ts`, removed in 15+ where the hook is default-on. The\n // current Next type defs (peer-dep `^16`) no longer declare it, so we\n // cast through to set it without touching the input config's type.\n ...(needsInstrumentationHookFlag\n ? {\n experimental: {\n ...(rest.experimental ?? {}),\n instrumentationHook: true,\n } as NonNullable<NextConfig[\"experimental\"]>,\n }\n : {}),\n };\n}\n\nfunction resolveBuildConfig(): ResolvedBuildConfig {\n const { apiKey, apiUrl, publicKey } = readInterfereEnv();\n const { commitSha, slug } = resolveReleaseSlug();\n\n return {\n apiKey: apiKey ?? null,\n apiUrl,\n buildId: commitSha,\n publicKey: publicKey ?? null,\n releaseSlug: slug,\n };\n}\n\nexport interface ProductionCompileContext {\n readonly distDir: string;\n readonly projectDir: string;\n}\n\ntype NextCompilerWithProductionHook = NonNullable<NextConfig[\"compiler\"]> & {\n runAfterProductionCompile?: (\n context: ProductionCompileContext\n ) => void | Promise<void>;\n};\n\nasync function runBuildReleasePipeline(\n context: ProductionCompileContext,\n config: ResolvedBuildConfig\n): Promise<void> {\n const { apiKey, buildId, releaseSlug } = config;\n\n if (!apiKey) {\n return log.fatal(\"API key not set\", [\n \"withInterfere() requires an API key to publish release metadata during production builds.\",\n \"Set the INTERFERE_API_KEY environment variable, or remove withInterfere() from your Next.js config.\",\n \"See: https://support.interfere.com/docs/guides/next-js\",\n ]);\n }\n\n if (!(buildId && releaseSlug)) {\n return log.fatal(\"Commit SHA missing\", [\n \"Could not resolve a commit SHA from environment variables or git.\",\n \"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.\",\n ]);\n }\n\n const { runBuildPipeline } = await import(\"./internal/build/pipeline.js\");\n\n try {\n const result = await runBuildPipeline(context, {\n ...config,\n apiKey,\n buildId,\n releaseSlug,\n });\n\n if (!result.ready) {\n log.warn(\"Skipping\", [\"No source maps found\"]);\n return;\n }\n\n log.info(\"Completed\", [\n `https://interfere.com/~/${result.config?.org.slug}/surfaces/${result.config?.surface.slug}`,\n `Release: ${result.release?.destination.slug ?? \"Unknown\"}`,\n `Build: ${result.buildId}`,\n `Artifacts: ${result.fileCount} source maps`,\n ]);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n log.warn(\"Skipping release pipeline\", [\n \"Interfere failed to process this release.\",\n \"Events from this release will be ingested, but ignored as evidence when generating insights.\",\n \"Please check https://status.interfere.com for any outages, or contact support@interfere.com if the problem persists.\",\n `Error: ${message}`,\n ]);\n\n return;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["import { omitUndefined } from \"@interfere/helpers/omit-undefined\";\nimport {\n LOCAL_DEV_RELEASE_SLUG,\n type ReleaseSlug,\n} from \"@interfere/types/releases/slug\";\nimport { readFirstEnvValue } from \"@interfere/types/sdk/env\";\n\nimport type { NextConfig } from \"next\";\n\nimport { configureBuild } from \"./internal/build/configure-build.js\";\nimport { readInterfereEnv } from \"./internal/env.js\";\nimport { FatalError, log } from \"./internal/logger.js\";\nimport { resolveReleaseSlug } from \"./internal/release-slug.js\";\nimport {\n readNextMajorVersion,\n warnIfServerInstrumentationMissing,\n} from \"./internal/setup-warnings.js\";\n\nconst NEXT_INSTRUMENTATION_HOOK_FLAG_LAST_MAJOR = 14;\n\nexport interface NextConfigWithInterfere extends NextConfig {\n /**\n * Reserved for future SDK config. Setting `buildId` here is rejected\n * loudly — it caused server/client slug drift in 9.x. Override the\n * commit SHA via the `INTERFERE_SOURCE_ID` env var (set on both build\n * and runtime envs) instead.\n */\n interfere?: { readonly buildId?: never };\n}\n\nexport interface ResolvedBuildConfig {\n readonly apiKey: string | null;\n readonly apiUrl: string;\n /**\n * Server-internal — the build pipeline still uses this to key R2\n * source-map paths until Phase 7 re-keys them to slug. Never injected\n * into the bundle; never exposed via env.\n */\n readonly buildId: string | null;\n /**\n * Deploy-environment label (`INTERFERE_ENVIRONMENT` ?? `VERCEL_ENV`)\n * resolved at build time. Stamped into the bundle as\n * `__INTERFERE_ENVIRONMENT__` so the browser kernel — which can't read\n * non-public env vars — labels events with the same environment as the\n * server. `NODE_ENV` is deliberately not folded in: it stays the browser's\n * bundler-inlined runtime fallback, so dev bundles keep resolving\n * `development` even when this is unset.\n */\n readonly environment: string | null;\n readonly publicKey: string | null;\n /**\n * Deterministically derived from the leading 16 hex of the commit SHA, so\n * the build step and the collector arrive at the same\n * `rel_xxxxxxxxxxxxxxxx` without a round-trip. Stamped into the bundle as\n * `__INTERFERE_RELEASE_SLUG__`. `null` when no commit SHA is available —\n * release-pipeline calls bail in that case.\n */\n readonly releaseSlug: ReleaseSlug | null;\n}\n\nexport function withInterfere(\n nextConfig: NextConfigWithInterfere = {}\n): NextConfig {\n const {\n interfere,\n env: userEnv,\n webpack,\n turbopack,\n compiler,\n productionBrowserSourceMaps,\n ...rest\n } = nextConfig;\n\n if (interfere && \"buildId\" in interfere && interfere.buildId !== undefined) {\n return log.fatal(\"Removed: interfere.buildId\", [\n \"`interfere.buildId` was removed in SDK 10. It only worked at build time, so server-side spans drifted from the bundle's slug.\",\n \"Override the commit SHA via the `INTERFERE_SOURCE_ID` env var on both build and runtime envs instead.\",\n ]);\n }\n\n const projectDir = process.cwd();\n const config = resolveBuildConfig();\n const forceEnable = !!process.env[\"NEXT_PUBLIC_INTERFERE_FORCE_ENABLE\"];\n\n const releaseSlug = forceEnable ? LOCAL_DEV_RELEASE_SLUG : config.releaseSlug;\n const build = configureBuild({\n existingWebpack: webpack,\n existingTurbopack: turbopack,\n projectDir,\n values: {\n __INTERFERE_RELEASE_SLUG__: releaseSlug,\n ...(config.publicKey && {\n __INTERFERE_PROXY_MODE__: true,\n __INTERFERE_PUBLIC_KEY__: config.publicKey,\n }),\n ...(config.environment && {\n __INTERFERE_ENVIRONMENT__: config.environment,\n }),\n ...(forceEnable && { __INTERFERE_FORCE_ENABLE__: true }),\n },\n });\n\n if (config.publicKey !== null && !build.webpack && !build.turbopack) {\n return log.fatal(\"Missing instrumentation client\", [\n \"INTERFERE_PUBLIC_KEY is set but no instrumentation-client file was found.\",\n \"Create an instrumentation-client.ts file in your project root or src/ directory.\",\n ]);\n }\n\n if (config.publicKey !== null) {\n warnIfServerInstrumentationMissing(projectDir);\n }\n\n const nextMajor = readNextMajorVersion(projectDir);\n const needsInstrumentationHookFlag =\n nextMajor === null ||\n nextMajor <= NEXT_INSTRUMENTATION_HOOK_FLAG_LAST_MAJOR;\n\n const existingHook = (compiler as NextCompilerWithProductionHook | undefined)\n ?.runAfterProductionCompile;\n\n const browserSourceMaps =\n config.apiKey === null ? productionBrowserSourceMaps : true;\n if (config.apiKey !== null && productionBrowserSourceMaps === false) {\n log.warn(\"Forcing productionBrowserSourceMaps\", [\n \"withInterfere() requires production source maps to symbolicate errors.\",\n \"Your `productionBrowserSourceMaps: false` was overridden to `true`.\",\n ]);\n }\n\n return {\n ...rest,\n ...(userEnv ? { env: userEnv } : {}),\n\n compiler: {\n ...omitUndefined(compiler ?? {}),\n async runAfterProductionCompile(context: ProductionCompileContext) {\n if (existingHook) {\n await existingHook(context);\n }\n\n try {\n await runBuildReleasePipeline(context, config);\n } catch (error) {\n if (error instanceof FatalError) {\n throw error;\n }\n\n const message =\n error instanceof Error ? error.message : String(error);\n\n log.warn(\"Skipping release pipeline\", [\n \"Interfere encountered an unexpected error.\",\n \"Build will continue, but this release's data will be ingested, but ignored as evidence when generating insights.\",\n `Error: ${message}`,\n ]);\n }\n },\n },\n\n ...omitUndefined({\n productionBrowserSourceMaps: browserSourceMaps,\n turbopack: build.turbopack,\n webpack: build.webpack,\n }),\n\n ...(needsInstrumentationHookFlag\n ? {\n experimental: {\n ...(rest.experimental ?? {}),\n instrumentationHook: true,\n } as NonNullable<NextConfig[\"experimental\"]>,\n }\n : {}),\n };\n}\n\nfunction resolveBuildConfig(): ResolvedBuildConfig {\n const { apiKey, apiUrl, publicKey } = readInterfereEnv();\n const { commitSha, slug } = resolveReleaseSlug();\n\n return {\n apiKey: apiKey ?? null,\n apiUrl,\n buildId: commitSha,\n environment: readFirstEnvValue(process.env, [\n \"INTERFERE_ENVIRONMENT\",\n \"VERCEL_ENV\",\n ]),\n publicKey: publicKey ?? null,\n releaseSlug: slug,\n };\n}\n\nexport interface ProductionCompileContext {\n readonly distDir: string;\n readonly projectDir: string;\n}\n\ntype NextCompilerWithProductionHook = NonNullable<NextConfig[\"compiler\"]> & {\n runAfterProductionCompile?: (\n context: ProductionCompileContext\n ) => void | Promise<void>;\n};\n\nasync function runBuildReleasePipeline(\n context: ProductionCompileContext,\n config: ResolvedBuildConfig\n): Promise<void> {\n const { apiKey, buildId, releaseSlug } = config;\n\n if (!apiKey) {\n return log.fatal(\"API key not set\", [\n \"withInterfere() requires an API key to publish release metadata during production builds.\",\n \"Set the INTERFERE_API_KEY environment variable, or remove withInterfere() from your Next.js config.\",\n \"See: https://support.interfere.com/docs/guides/next-js\",\n ]);\n }\n\n if (!(buildId && releaseSlug)) {\n return log.fatal(\"Commit SHA missing\", [\n \"Could not resolve a commit SHA from environment variables or git.\",\n \"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.\",\n ]);\n }\n\n const { runBuildPipeline } = await import(\"./internal/build/pipeline.js\");\n\n try {\n const result = await runBuildPipeline(context, {\n ...config,\n apiKey,\n buildId,\n releaseSlug,\n });\n\n if (!result.ready) {\n log.warn(\"Skipping\", [\"No source maps found\"]);\n return;\n }\n\n log.info(\"Completed\", [\n `https://interfere.com/~/${result.config?.org.slug}/surfaces/${result.config?.surface.slug}`,\n `Release: ${result.release?.destination.slug ?? \"Unknown\"}`,\n `Build: ${result.buildId}`,\n `Artifacts: ${result.fileCount} source maps`,\n ]);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n log.warn(\"Skipping release pipeline\", [\n \"Interfere failed to process this release.\",\n \"Events from this release will be ingested, but ignored as evidence when generating insights.\",\n \"Please check https://status.interfere.com for any outages, or contact support@interfere.com if the problem persists.\",\n `Error: ${message}`,\n ]);\n\n return;\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"instrument-client.d.mts","names":[],"sources":["../src/instrument-client.ts"],"mappings":";;;;;cAuBE,KAAA,QAAK,OAAA,QAAA,OAAA;;;iEAEI,MAAA,EAAA,eAAA,mDACM,MAAA,SAAA,QAAA;;;YAEX,IAAA,8CAAA,aAAA,KAAA,OAAA,4CAAA,MAAA,UAAA,iBAAA,GACa,QAAA"}
1
+ {"version":3,"file":"instrument-client.d.mts","names":[],"sources":["../src/instrument-client.ts"],"mappings":";;;;;cAuBE,KAAA,QAAK,OAAA;;;iEAEI,MAAA,oEACM,MAAA;;;YAEX,IAAA,8CAAA,aAAA,KAAA,OAAA,4CAAA,MAAA,8BACa,QAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.d.mts","names":[],"sources":["../src/instrumentation.ts"],"mappings":";;;;;;;;;;AAqFA;;;;;;;;;;;;;;;;;UAAiB,gBAAA;EAqCf;;;;;;;;EA5BA,mBAAA;EA4CA;;;AAA6B;AAY/B;EAlDE,iBAAA,QAAyB,OAAA;;;;;;;AAoDR;AAqJnB;;;;;;;;AAEU;;;;;EAtLR,gBAAA,EAAkB,eAAA;EAClB,mBAAA,EAAqB,kBAAA;EACrB,aAAA,EAAe,YAAA;;;;;;;;EAQf,WAAA,EAAa,iBAAA;;;;;;EAMb,kBAAA,EAAoB,MAAA,SAAe,cAAA;EACnC,cAAA,EAAgB,aAAA;AAAA;;;;;;;;;;iBAYF,qBAAA,CACd,IAAA,GAAM,4BAAA,GACL,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqJG,QAAA,CACpB,IAAA,GAAM,4BAAA,GACL,OAAO"}
1
+ {"version":3,"file":"instrumentation.d.mts","names":[],"sources":["../src/instrumentation.ts"],"mappings":";;;;;;;;;;AAqFA;;;;;;;;;;;;;;;;;UAAiB,gBAAA;EAqCf;;;;;;;;EA5BA,mBAAA;EA4CA;;;AAA6B;AAY/B;EAlDE,iBAAA,QAAyB,OAAA;;;;;;;AAoDR;AA+HnB;;;;;;;;AAEU;;;;;EAhKR,gBAAA,EAAkB,eAAA;EAClB,mBAAA,EAAqB,kBAAA;EACrB,aAAA,EAAe,YAAA;;;;;;;;EAQf,WAAA,EAAa,iBAAA;;;;;;EAMb,kBAAA,EAAoB,MAAA,SAAe,cAAA;EACnC,cAAA,EAAgB,aAAA;AAAA;;;;;;;;;;iBAYF,qBAAA,CACd,IAAA,GAAM,4BAAA,GACL,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+HG,QAAA,CACpB,IAAA,GAAM,4BAAA,GACL,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.edge.d.mts","names":[],"sources":["../src/instrumentation.edge.ts"],"mappings":";;;;;AA0BA;;;;;;;;AAEU;AAUV;;;;AAC0C;;;;;;;iBAbpB,QAAA,CACpB,KAAA,GAAO,4BAAA,GACN,OAAO;;;;;;;iBAUM,qBAAA,CACd,KAAwC,GAAjC,4BAAiC"}
1
+ {"version":3,"file":"instrumentation.edge.d.mts","names":[],"sources":["../src/instrumentation.edge.ts"],"mappings":";;;;;AA0BA;;;;;;;;AAEU;AAUV;;;;AAC0C;;;;;;;iBAb1B,QAAA,CACd,KAAA,GAAO,4BAAA,GACN,OAAO;;;;;;;iBAUM,qBAAA,CACd,KAAwC,GAAjC,4BAAiC"}
@@ -1 +1 @@
1
- async function register(_opts={}){}function buildInterfereOtelKit(_opts={}){return null}export{buildInterfereOtelKit,register};
1
+ function register(_opts={}){return Promise.resolve()}function buildInterfereOtelKit(_opts={}){return null}export{buildInterfereOtelKit,register};
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.edge.mjs","names":[],"sources":["../src/instrumentation.edge.ts"],"sourcesContent":["import type { ServerInstrumentationOptions } from \"./internal/server/instrumentation-options.js\";\n\nexport type { InterfereOtelKit } from \"./instrumentation.js\";\nexport type { ServerInstrumentationOptions } from \"./internal/server/instrumentation-options.js\";\n\n/**\n * Edge-runtime entry for `@interfere/next/instrumentation`. Selected by\n * `package.json`'s `edge-light` / `workerd` / `worker` export conditions\n * — Next.js + Vercel pick this bundle when compiling\n * `instrumentation.ts` for the Edge runtime.\n *\n * The Node entry (`instrumentation.ts`) is built around\n * `NodeTracerProvider`, `AsyncLocalStorageContextManager`, undici\n * instrumentation, and a console→OTel bridge — none of which work in\n * the Edge runtime. Pulling that import graph into an Edge bundle would\n * (a) trip Next's `node:*`-in-edge warnings (release-slug derivation\n * uses `node:child_process` + `node:crypto`) and (b) crash on module\n * load if the Edge bundle were ever actually invoked.\n *\n * Both `register()` and `buildInterfereOtelKit()` are exported as\n * no-op stubs so customer TypeScript code compiles unchanged\n * regardless of which runtime the file ends up in. Edge-runtime\n * tracing (middleware spans, edge route handlers) needs an\n * `@opentelemetry/sdk-trace-base`-based pipeline with a fetch-based\n * exporter — tracked separately.\n */\nexport async function register(\n _opts: ServerInstrumentationOptions = {}\n): Promise<void> {\n // Intentionally empty. See file header for the design rationale.\n}\n\n/**\n * Edge stub for the kit factory. Returns `null` so callers spreading\n * `kit?.spanProcessors ?? []` into a host bootstrap behave as if the\n * SDK is unconfigured — same shape as the Node entry returns when\n * `INTERFERE_PUBLIC_KEY` is unset.\n */\nexport function buildInterfereOtelKit(\n _opts: ServerInstrumentationOptions = {}\n): null {\n return null;\n}\n"],"mappings":"AA0BA,eAAsB,SACpB,MAAsC,CAAC,EACxB,CAEjB,CAQA,SAAgB,sBACd,MAAsC,CAAC,EACjC,CACN,OAAO,IACT"}
1
+ {"version":3,"file":"instrumentation.edge.mjs","names":[],"sources":["../src/instrumentation.edge.ts"],"sourcesContent":["import type { ServerInstrumentationOptions } from \"./internal/server/instrumentation-options.js\";\n\nexport type { InterfereOtelKit } from \"./instrumentation.js\";\nexport type { ServerInstrumentationOptions } from \"./internal/server/instrumentation-options.js\";\n\n/**\n * Edge-runtime entry for `@interfere/next/instrumentation`. Selected by\n * `package.json`'s `edge-light` / `workerd` / `worker` export conditions\n * — Next.js + Vercel pick this bundle when compiling\n * `instrumentation.ts` for the Edge runtime.\n *\n * The Node entry (`instrumentation.ts`) is built around\n * `NodeTracerProvider`, `AsyncLocalStorageContextManager`, undici\n * instrumentation, and a console→OTel bridge — none of which work in\n * the Edge runtime. Pulling that import graph into an Edge bundle would\n * (a) trip Next's `node:*`-in-edge warnings (release-slug derivation\n * uses `node:child_process` + `node:crypto`) and (b) crash on module\n * load if the Edge bundle were ever actually invoked.\n *\n * Both `register()` and `buildInterfereOtelKit()` are exported as\n * no-op stubs so customer TypeScript code compiles unchanged\n * regardless of which runtime the file ends up in. Edge-runtime\n * tracing (middleware spans, edge route handlers) needs an\n * `@opentelemetry/sdk-trace-base`-based pipeline with a fetch-based\n * exporter — tracked separately.\n */\nexport function register(\n _opts: ServerInstrumentationOptions = {}\n): Promise<void> {\n return Promise.resolve();\n}\n\n/**\n * Edge stub for the kit factory. Returns `null` so callers spreading\n * `kit?.spanProcessors ?? []` into a host bootstrap behave as if the\n * SDK is unconfigured — same shape as the Node entry returns when\n * `INTERFERE_PUBLIC_KEY` is unset.\n */\nexport function buildInterfereOtelKit(\n _opts: ServerInstrumentationOptions = {}\n): null {\n return null;\n}\n"],"mappings":"AA0BA,SAAgB,SACd,MAAsC,CAAC,EACxB,CACf,OAAO,QAAQ,QAAQ,CACzB,CAQA,SAAgB,sBACd,MAAsC,CAAC,EACjC,CACN,OAAO,IACT"}
@@ -1 +1 @@
1
- import{readInterfereEnv}from"./internal/env.mjs";import{resolveReleaseSlug}from"./internal/release-slug.mjs";import{PRODUCER_VERSION}from"./internal/version.mjs";import{bridgeConsoleToOtel}from"./internal/server/console-bridge.mjs";import{PrerenderSafeIdGenerator}from"./internal/server/id-generator.mjs";import{withPublicKey}from"./internal/url.mjs";import{fetchAndCacheRemoteConfig}from"./internal/server/remote-config.mjs";import{metrics,propagation}from"@opentelemetry/api";import{BaggageSpanProcessor}from"@opentelemetry/baggage-span-processor";import{AsyncLocalStorageContextManager}from"@opentelemetry/context-async-hooks";import{CompositePropagator,W3CBaggagePropagator,W3CTraceContextPropagator}from"@opentelemetry/core";import{OTLPLogExporter}from"@opentelemetry/exporter-logs-otlp-http";import{AggregationTemporalityPreference,OTLPMetricExporter}from"@opentelemetry/exporter-metrics-otlp-http";import{OTLPTraceExporter}from"@opentelemetry/exporter-trace-otlp-http";import{registerInstrumentations}from"@opentelemetry/instrumentation";import{UndiciInstrumentation}from"@opentelemetry/instrumentation-undici";import{resourceFromAttributes}from"@opentelemetry/resources";import{BatchLogRecordProcessor,LoggerProvider}from"@opentelemetry/sdk-logs";import{MeterProvider,PeriodicExportingMetricReader}from"@opentelemetry/sdk-metrics";import{BatchSpanProcessor}from"@opentelemetry/sdk-trace-base";import{NodeTracerProvider}from"@opentelemetry/sdk-trace-node";let registered=!1;function matchesAny(url,patterns){for(let pattern of patterns)if(pattern instanceof RegExp?pattern.test(url):url.includes(pattern))return!0;return!1}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),ignoreUrls=[sinkUrl,...opts.ignoreUrls??[]],propagateContextUrls=opts.propagateContextUrls??[],exporterHeaders={"x-interfere-producer-version":PRODUCER_VERSION};return{resourceAttributes:{"service.name":opts.serviceName??`interfere-sdk-next-server`,"service.namespace":`interfere`,"deployment.environment.name":env.nodeEnvironment??`unknown`,"telemetry.sdk.language":`nodejs`,"interfere.sdk.name":`@interfere/next`,"interfere.sdk.version":PRODUCER_VERSION,...releaseSlug?{"release.slug":releaseSlug}:{}},spanProcessors:[new BaggageSpanProcessor(()=>!0),new BatchSpanProcessor(new OTLPTraceExporter({url:sinkUrl,headers:exporterHeaders})),...opts._internalAdditionalSpanProcessors??[]],logRecordProcessors:[new BatchLogRecordProcessor(new OTLPLogExporter({url:sinkUrl,headers:exporterHeaders})),...opts._internalAdditionalLogRecordProcessors??[]],metricReaders:[new PeriodicExportingMetricReader({exporter:new OTLPMetricExporter({url:sinkUrl,headers:exporterHeaders,temporalityPreference:AggregationTemporalityPreference.DELTA}),exportIntervalMillis:3e4}),...opts._internalAdditionalMetricReaders??[]],propagators:[new W3CBaggagePropagator],instrumentations:[new UndiciInstrumentation({ignoreRequestHook:req=>matchesAny(`${req.origin}${req.path}`,ignoreUrls),requestHook:(span,req)=>{matchesAny(`${req.origin}${req.path}`,propagateContextUrls)&&span.setAttribute(`interfere.propagated`,!0)}})],enableConsoleBridge:()=>bridgeConsoleToOtel(),fetchRemoteConfig:()=>fetchAndCacheRemoteConfig()}}async function register(opts={}){if(registered)return;let kit=buildInterfereOtelKit(opts);if(!kit)return;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()});let loggerProvider=new LoggerProvider({resource,processors:kit.logRecordProcessors}),{logs:logsApi}=await import(`@opentelemetry/api-logs`);logsApi.setGlobalLoggerProvider(loggerProvider);let meterProvider=new MeterProvider({resource,readers:kit.metricReaders});metrics.setGlobalMeterProvider(meterProvider),registerInstrumentations({tracerProvider,meterProvider,instrumentations:kit.instrumentations}),opts.consoleBridge!==!1&&kit.enableConsoleBridge(),registered=!0,await kit.fetchRemoteConfig()}export{PrerenderSafeIdGenerator,buildInterfereOtelKit,register};
1
+ import{readInterfereEnv}from"./internal/env.mjs";import{resolveReleaseSlug}from"./internal/release-slug.mjs";import{PRODUCER_VERSION}from"./internal/version.mjs";import{bridgeConsoleToOtel}from"./internal/server/console-bridge.mjs";import{PrerenderSafeIdGenerator}from"./internal/server/id-generator.mjs";import{withPublicKey}from"./internal/url.mjs";import{fetchAndCacheRemoteConfig}from"./internal/server/remote-config.mjs";import{metrics,propagation}from"@opentelemetry/api";import{BaggageSpanProcessor}from"@opentelemetry/baggage-span-processor";import{AsyncLocalStorageContextManager}from"@opentelemetry/context-async-hooks";import{CompositePropagator,W3CBaggagePropagator,W3CTraceContextPropagator}from"@opentelemetry/core";import{OTLPLogExporter}from"@opentelemetry/exporter-logs-otlp-http";import{AggregationTemporalityPreference,OTLPMetricExporter}from"@opentelemetry/exporter-metrics-otlp-http";import{OTLPTraceExporter}from"@opentelemetry/exporter-trace-otlp-http";import{registerInstrumentations}from"@opentelemetry/instrumentation";import{UndiciInstrumentation}from"@opentelemetry/instrumentation-undici";import{resourceFromAttributes}from"@opentelemetry/resources";import{BatchLogRecordProcessor,LoggerProvider}from"@opentelemetry/sdk-logs";import{MeterProvider,PeriodicExportingMetricReader}from"@opentelemetry/sdk-metrics";import{BatchSpanProcessor}from"@opentelemetry/sdk-trace-base";import{NodeTracerProvider}from"@opentelemetry/sdk-trace-node";let registered=!1;function matchesAny(url,patterns){for(let pattern of patterns)if(pattern instanceof RegExp?pattern.test(url):url.includes(pattern))return!0;return!1}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),ignoreUrls=[sinkUrl,...opts.ignoreUrls??[]],propagateContextUrls=opts.propagateContextUrls??[],exporterHeaders={"x-interfere-producer-version":PRODUCER_VERSION};return{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}:{}},spanProcessors:[new BaggageSpanProcessor(()=>!0),new BatchSpanProcessor(new OTLPTraceExporter({url:sinkUrl,headers:exporterHeaders})),...opts._internalAdditionalSpanProcessors??[]],logRecordProcessors:[new BatchLogRecordProcessor(new OTLPLogExporter({url:sinkUrl,headers:exporterHeaders})),...opts._internalAdditionalLogRecordProcessors??[]],metricReaders:[new PeriodicExportingMetricReader({exporter:new OTLPMetricExporter({url:sinkUrl,headers:exporterHeaders,temporalityPreference:AggregationTemporalityPreference.DELTA}),exportIntervalMillis:3e4}),...opts._internalAdditionalMetricReaders??[]],propagators:[new W3CBaggagePropagator],instrumentations:[new UndiciInstrumentation({ignoreRequestHook:req=>matchesAny(`${req.origin}${req.path}`,ignoreUrls),requestHook:(span,req)=>{matchesAny(`${req.origin}${req.path}`,propagateContextUrls)&&span.setAttribute(`interfere.propagated`,!0)}})],enableConsoleBridge:()=>bridgeConsoleToOtel(),fetchRemoteConfig:()=>fetchAndCacheRemoteConfig()}}async function register(opts={}){if(registered)return;let kit=buildInterfereOtelKit(opts);if(!kit)return;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()});let loggerProvider=new LoggerProvider({resource,processors:kit.logRecordProcessors}),{logs:logsApi}=await import(`@opentelemetry/api-logs`);logsApi.setGlobalLoggerProvider(loggerProvider);let meterProvider=new MeterProvider({resource,readers:kit.metricReaders});metrics.setGlobalMeterProvider(meterProvider),registerInstrumentations({tracerProvider,meterProvider,instrumentations:kit.instrumentations}),opts.consoleBridge!==!1&&kit.enableConsoleBridge(),registered=!0,await kit.fetchRemoteConfig()}export{PrerenderSafeIdGenerator,buildInterfereOtelKit,register};
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.mjs","names":[],"sources":["../src/instrumentation.ts"],"sourcesContent":["import type {\n AttributeValue,\n Span,\n TextMapPropagator,\n} from \"@opentelemetry/api\";\nimport { metrics, propagation } from \"@opentelemetry/api\";\nimport { BaggageSpanProcessor } from \"@opentelemetry/baggage-span-processor\";\nimport { AsyncLocalStorageContextManager } from \"@opentelemetry/context-async-hooks\";\nimport {\n CompositePropagator,\n W3CBaggagePropagator,\n W3CTraceContextPropagator,\n} from \"@opentelemetry/core\";\nimport { OTLPLogExporter } from \"@opentelemetry/exporter-logs-otlp-http\";\nimport {\n AggregationTemporalityPreference,\n OTLPMetricExporter,\n} from \"@opentelemetry/exporter-metrics-otlp-http\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport {\n type Instrumentation,\n registerInstrumentations,\n} from \"@opentelemetry/instrumentation\";\nimport { UndiciInstrumentation } from \"@opentelemetry/instrumentation-undici\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport {\n BatchLogRecordProcessor,\n LoggerProvider,\n type LogRecordProcessor,\n} from \"@opentelemetry/sdk-logs\";\nimport {\n MeterProvider,\n type MetricReader,\n PeriodicExportingMetricReader,\n} from \"@opentelemetry/sdk-metrics\";\nimport {\n BatchSpanProcessor,\n type SpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\n\nimport { readInterfereEnv } from \"./internal/env.js\";\nimport { resolveReleaseSlug } from \"./internal/release-slug.js\";\nimport { bridgeConsoleToOtel } from \"./internal/server/console-bridge.js\";\nimport { PrerenderSafeIdGenerator } from \"./internal/server/id-generator.js\";\nimport type { ServerInstrumentationOptions } from \"./internal/server/instrumentation-options.js\";\nimport { fetchAndCacheRemoteConfig } from \"./internal/server/remote-config.js\";\nimport { withPublicKey } from \"./internal/url.js\";\nimport { PRODUCER_VERSION } from \"./internal/version.js\";\n\nexport { PrerenderSafeIdGenerator } from \"./internal/server/id-generator.js\";\nexport type { ServerInstrumentationOptions } from \"./internal/server/instrumentation-options.js\";\n\nconst DEFAULT_SERVICE_NAME = \"interfere-sdk-next-server\";\nconst SERVICE_NAMESPACE = \"interfere\";\n\nlet registered = false;\n\nfunction matchesAny(url: string, patterns: (string | RegExp)[]): boolean {\n for (const pattern of patterns) {\n if (pattern instanceof RegExp ? pattern.test(url) : url.includes(pattern)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Composable building blocks the Interfere SDK contributes to OTel —\n * span / log / metric processors, baggage propagator, undici\n * instrumentation, plus the resource attributes and side-effect\n * installers (console bridge + remote-config fetch).\n *\n * Use this when you want to plug Interfere into an existing OTel\n * bootstrap (e.g. `@vercel/otel`'s `registerOTel({...})`, DataDog's\n * `dd-trace-js`, or any custom `NodeTracerProvider` setup) instead of\n * the standalone `register()` path. The kit holds no global state and\n * touches no global slots — composition is the consumer's job.\n *\n * The standalone `register()` builds the kit internally and wires it\n * into private providers, so customer DX stays a one-liner export.\n *\n * Returns `null` when `INTERFERE_PUBLIC_KEY` is unset — callers can spread\n * `kit?.spanProcessors ?? []` without a no-op guard.\n */\nexport interface InterfereOtelKit {\n /**\n * Patches `console.{debug,log,info,warn,error}` to also emit OTel\n * `LogRecord`s via the global `LoggerProvider`. Caller is responsible\n * for ensuring a `LoggerProvider` is registered globally before\n * calling — `register()` handles this; for composed setups, call\n * after your bootstrap (`registerOTel({...})` etc.) has run. Returns\n * a disposer that restores the original `console.*` methods.\n */\n enableConsoleBridge: () => () => void;\n /**\n * Fetches the SDK's remote plugin config and caches it for the\n * runtime. Customer code's `isPluginEnabled(...)` checks read from\n * the cache. Safe to call repeatedly.\n */\n fetchRemoteConfig: () => Promise<void>;\n /**\n * Instrumentations the SDK ships. Currently `UndiciInstrumentation`\n * pre-configured with `propagateContextUrls` / `ignoreUrls` semantics\n * from `ServerInstrumentationOptions`.\n *\n * **Composition with `@vercel/otel`**: `instrumentations: [\"auto\"]`\n * registers Vercel's `globalThis.fetch` wrapper, which only\n * auto-propagates `traceparent` to Vercel deployment URLs — outbound\n * fetches to a different origin (e.g. `in.interfere.com`) get a\n * span but no header injection unless\n * `instrumentationConfig.fetch.propagateContextUrls` is set, and\n * even then it doesn't cover the lower-level undici dispatcher path\n * Next.js route handlers use. Spread `kit.instrumentations` after\n * `\"auto\"` to layer this UndiciInstrumentation on top — propagation\n * extends to every outbound undici call (filtered via `ignoreUrls`).\n * Risk of double span emission on overlapping call sites is bounded:\n * Vercel's fetch wraps `globalThis.fetch`, this hooks the undici\n * dispatcher one layer lower, so duplicates only appear when both\n * paths actually fire on the same call.\n */\n instrumentations: Instrumentation[];\n logRecordProcessors: LogRecordProcessor[];\n metricReaders: MetricReader[];\n /**\n * W3C baggage propagator. Trace context is intentionally *not*\n * included — `@vercel/otel`'s `propagators: [\"auto\"]` already\n * registers it, and standalone `register()` adds it itself when\n * the global slot is empty. Composing this on top extends rather\n * than overrides.\n */\n propagators: TextMapPropagator[];\n /**\n * Pre-built resource attribute set. Pass into your provider's\n * `Resource` directly, or into `@vercel/otel`'s `attributes` field\n * (which merges with Vercel's own auto-detected `vercel.*` attrs).\n */\n resourceAttributes: Record<string, AttributeValue>;\n spanProcessors: SpanProcessor[];\n}\n\n/**\n * Pure factory — no global registration, no provider construction. Returns\n * the OTel primitives the SDK contributes so a host bootstrap (Vercel,\n * DataDog, custom) can compose them in.\n *\n * Returns `null` when `INTERFERE_PUBLIC_KEY` isn't set, so callers can\n * unconditionally spread `kit?.spanProcessors ?? []` in dev where the\n * SDK isn't configured.\n */\nexport function buildInterfereOtelKit(\n opts: ServerInstrumentationOptions = {}\n): InterfereOtelKit | null {\n const env = readInterfereEnv();\n if (!env.publicKey) {\n return null;\n }\n\n const { slug: releaseSlug } = resolveReleaseSlug();\n if (!releaseSlug) {\n console.warn(\n \"[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.\"\n );\n }\n\n const sinkUrl = withPublicKey(`${env.apiUrl}/v2/sink`, env.publicKey);\n // Always-on ignore: the OTLP exporter posting to /v2/sink would\n // otherwise be re-traced by UndiciInstrumentation in an infinite\n // loop.\n const ignoreUrls = [sinkUrl, ...(opts.ignoreUrls ?? [])];\n const propagateContextUrls = opts.propagateContextUrls ?? [];\n\n const exporterHeaders: Record<string, string> = {\n \"x-interfere-producer-version\": PRODUCER_VERSION,\n };\n\n const resourceAttributes: Record<string, AttributeValue> = {\n \"service.name\": opts.serviceName ?? DEFAULT_SERVICE_NAME,\n \"service.namespace\": SERVICE_NAMESPACE,\n \"deployment.environment.name\": env.nodeEnvironment ?? \"unknown\",\n \"telemetry.sdk.language\": \"nodejs\",\n \"interfere.sdk.name\": \"@interfere/next\",\n \"interfere.sdk.version\": PRODUCER_VERSION,\n ...(releaseSlug ? { \"release.slug\": releaseSlug } : {}),\n };\n\n // `BaggageSpanProcessor` runs at span start (before the batch\n // processor schedules export) so every span carries the full baggage\n // attribute set when batched. Default `keyFilter` accepts everything;\n // browser SDK only writes `interfere.*`-prefixed entries via baggage\n // propagation, so the server side stays scoped without an explicit\n // filter.\n const spanProcessors: SpanProcessor[] = [\n new BaggageSpanProcessor(() => true),\n new BatchSpanProcessor(\n new OTLPTraceExporter({ url: sinkUrl, headers: exporterHeaders })\n ),\n ...(opts._internalAdditionalSpanProcessors ?? []),\n ];\n\n const logRecordProcessors: LogRecordProcessor[] = [\n new BatchLogRecordProcessor(\n new OTLPLogExporter({ url: sinkUrl, headers: exporterHeaders })\n ),\n ...(opts._internalAdditionalLogRecordProcessors ?? []),\n ];\n\n // `DELTA` temporality matches what `@vercel/otel`'s default\n // `metricReader: \"auto\"` used to install (see vercel/otel\n // `defaultMetricReader`) so the wire shape downstream consumers\n // (ClickHouse via Tinybird, the OTel collector's BetterStack export)\n // receive is unchanged across the migration. The 30s interval matches\n // the browser SDK's primary metric reader so server + client\n // datapoints land at the same cadence.\n const metricReaders: MetricReader[] = [\n new PeriodicExportingMetricReader({\n exporter: new OTLPMetricExporter({\n url: sinkUrl,\n headers: exporterHeaders,\n temporalityPreference: AggregationTemporalityPreference.DELTA,\n }),\n exportIntervalMillis: 30_000,\n }),\n ...(opts._internalAdditionalMetricReaders ?? []),\n ];\n\n // Just W3C baggage. Trace context is contributed by the host\n // bootstrap (`@vercel/otel`'s `propagators: [\"auto\"]`, or the\n // CompositePropagator standalone `register()` constructs). Composing\n // this on top extends rather than overrides.\n const propagators: TextMapPropagator[] = [new W3CBaggagePropagator()];\n\n // Standalone-path instrumentations. Conflicts with `@vercel/otel`'s\n // `instrumentations: [\"auto\"]` (Vercel ships its own runtime-aware\n // fetch instrumentation) — see the JSDoc on `InterfereOtelKit`.\n const instrumentations: Instrumentation[] = [\n new UndiciInstrumentation({\n ignoreRequestHook: (req: { origin: string; path: string }) => {\n const url = `${req.origin}${req.path}`;\n return matchesAny(url, ignoreUrls);\n },\n requestHook: (span: Span, req: { origin: string; path: string }) => {\n const url = `${req.origin}${req.path}`;\n if (matchesAny(url, propagateContextUrls)) {\n span.setAttribute(\"interfere.propagated\", true);\n }\n },\n }),\n ];\n\n return {\n resourceAttributes,\n spanProcessors,\n logRecordProcessors,\n metricReaders,\n propagators,\n instrumentations,\n enableConsoleBridge: () => bridgeConsoleToOtel(),\n fetchRemoteConfig: () => fetchAndCacheRemoteConfig(),\n };\n}\n\n/**\n * One-line server-side bootstrap for Next.js. Customers' `instrumentation.ts`\n * becomes:\n *\n * ```ts\n * export { register } from \"@interfere/next/instrumentation\";\n * ```\n *\n * Or, with overrides:\n *\n * ```ts\n * import { register as base } from \"@interfere/next/instrumentation\";\n * export const register = () => base({ serviceName: \"@my-org/api\" });\n * ```\n *\n * Customers who already run another OTel bootstrap (`@vercel/otel`,\n * DataDog `dd-trace-js`, custom `NodeTracerProvider`) should use\n * `buildInterfereOtelKit({...})` instead and compose the returned\n * processors / readers / propagators into their existing setup —\n * see the kit's JSDoc for examples.\n *\n * Constructs private `NodeTracerProvider` / `LoggerProvider` /\n * `MeterProvider` and registers them on the global slots. Last writer\n * wins on the globals, so customers running another OTel bootstrap\n * alongside this one will end up with whichever booted last; the kit\n * path avoids that fight entirely.\n *\n * Wired against the Node OTel SDK (`NodeTracerProvider`,\n * `AsyncLocalStorageContextManager`) — so this entry only ships into the\n * `nodejs` runtime bundle. The Edge runtime gets the no-op stub at\n * `instrumentation.edge.ts` via `package.json`'s `edge-light` /\n * `workerd` export conditions.\n *\n * Idempotent — repeat calls (e.g. HMR) short-circuit on the\n * module-scoped `registered` flag.\n *\n * Bails silently when `INTERFERE_PUBLIC_KEY` is unset so dev runs without\n * the SDK configured don't crash.\n */\nexport async function register(\n opts: ServerInstrumentationOptions = {}\n): Promise<void> {\n if (registered) {\n return;\n }\n\n const kit = buildInterfereOtelKit(opts);\n if (!kit) {\n return;\n }\n\n const resource = resourceFromAttributes(kit.resourceAttributes);\n\n // Next 16's prerender extension throws on `Math.random()` /\n // `crypto.getRandomValues()` calls inside a Server Component that hasn't\n // awaited Request data first. OTel's default `RandomIdGenerator` calls\n // `Math.random()` for every span ID, so any span Next opens around a\n // prerendered route render blows up the build with\n // `next-prerender-random`. The counter-based generator below seeds its\n // randomness once at register-time (outside any prerender ALS frame) and\n // keeps span minting deterministic afterwards.\n const tracerProvider = new NodeTracerProvider({\n resource,\n spanProcessors: kit.spanProcessors,\n idGenerator: new PrerenderSafeIdGenerator(),\n });\n\n // Composed W3C trace context + baggage. `UndiciInstrumentation`\n // injects both `traceparent` and `baggage` on outgoing requests\n // matching `propagateContextUrls`, so cross-process correlation +\n // customer baggage propagation work without per-call code.\n //\n // Only set the global propagator when none is registered — customers\n // who installed their own propagator (B3, Jaeger, composite, ...) are\n // preserved. Mirrors the browser-side guard in\n // `react/internal/otel/provider.ts`.\n if (propagation.fields().length === 0) {\n propagation.setGlobalPropagator(\n new CompositePropagator({\n propagators: [new W3CTraceContextPropagator(), ...kit.propagators],\n })\n );\n }\n\n tracerProvider.register({\n contextManager: new AsyncLocalStorageContextManager().enable(),\n });\n\n const loggerProvider = new LoggerProvider({\n resource,\n processors: kit.logRecordProcessors,\n });\n // `logs.setGlobalLoggerProvider` is the single global slot OTel\n // exposes. Customers with their own LoggerProvider boot last and\n // win; we accept the trade-off — same constraint we live with on\n // the global propagator and context manager.\n const { logs: logsApi } = await import(\"@opentelemetry/api-logs\");\n logsApi.setGlobalLoggerProvider(loggerProvider);\n\n const meterProvider = new MeterProvider({\n resource,\n readers: kit.metricReaders,\n });\n // `MeterProvider` has no `register()` method — wire it into the\n // single global slot manually. Same trade-off as\n // `setGlobalLoggerProvider`: customers with their own MeterProvider\n // boot last and win.\n metrics.setGlobalMeterProvider(meterProvider);\n\n // Pass the meter provider explicitly so `UndiciInstrumentation`\n // records `http.client.request.duration` histograms against it.\n registerInstrumentations({\n tracerProvider,\n meterProvider,\n instrumentations: kit.instrumentations,\n });\n\n if (opts.consoleBridge !== false) {\n kit.enableConsoleBridge();\n }\n\n registered = true;\n\n // Preserve the existing `register` semantics from `@interfere/next/server` —\n // remote config gates plugin enable/disable. Customers migrating to this\n // subpath shouldn't lose that behaviour.\n await kit.fetchRemoteConfig();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"instrumentation.mjs","names":[],"sources":["../src/instrumentation.ts"],"sourcesContent":["import type {\n AttributeValue,\n Span,\n TextMapPropagator,\n} from \"@opentelemetry/api\";\nimport { metrics, propagation } from \"@opentelemetry/api\";\nimport { BaggageSpanProcessor } from \"@opentelemetry/baggage-span-processor\";\nimport { AsyncLocalStorageContextManager } from \"@opentelemetry/context-async-hooks\";\nimport {\n CompositePropagator,\n W3CBaggagePropagator,\n W3CTraceContextPropagator,\n} from \"@opentelemetry/core\";\nimport { OTLPLogExporter } from \"@opentelemetry/exporter-logs-otlp-http\";\nimport {\n AggregationTemporalityPreference,\n OTLPMetricExporter,\n} from \"@opentelemetry/exporter-metrics-otlp-http\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport {\n type Instrumentation,\n registerInstrumentations,\n} from \"@opentelemetry/instrumentation\";\nimport { UndiciInstrumentation } from \"@opentelemetry/instrumentation-undici\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport {\n BatchLogRecordProcessor,\n LoggerProvider,\n type LogRecordProcessor,\n} from \"@opentelemetry/sdk-logs\";\nimport {\n MeterProvider,\n type MetricReader,\n PeriodicExportingMetricReader,\n} from \"@opentelemetry/sdk-metrics\";\nimport {\n BatchSpanProcessor,\n type SpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\n\nimport { readInterfereEnv } from \"./internal/env.js\";\nimport { resolveReleaseSlug } from \"./internal/release-slug.js\";\nimport { bridgeConsoleToOtel } from \"./internal/server/console-bridge.js\";\nimport { PrerenderSafeIdGenerator } from \"./internal/server/id-generator.js\";\nimport type { ServerInstrumentationOptions } from \"./internal/server/instrumentation-options.js\";\nimport { fetchAndCacheRemoteConfig } from \"./internal/server/remote-config.js\";\nimport { withPublicKey } from \"./internal/url.js\";\nimport { PRODUCER_VERSION } from \"./internal/version.js\";\n\nexport { PrerenderSafeIdGenerator } from \"./internal/server/id-generator.js\";\nexport type { ServerInstrumentationOptions } from \"./internal/server/instrumentation-options.js\";\n\nconst DEFAULT_SERVICE_NAME = \"interfere-sdk-next-server\";\nconst SERVICE_NAMESPACE = \"interfere\";\n\nlet registered = false;\n\nfunction matchesAny(url: string, patterns: (string | RegExp)[]): boolean {\n for (const pattern of patterns) {\n if (pattern instanceof RegExp ? pattern.test(url) : url.includes(pattern)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Composable building blocks the Interfere SDK contributes to OTel —\n * span / log / metric processors, baggage propagator, undici\n * instrumentation, plus the resource attributes and side-effect\n * installers (console bridge + remote-config fetch).\n *\n * Use this when you want to plug Interfere into an existing OTel\n * bootstrap (e.g. `@vercel/otel`'s `registerOTel({...})`, DataDog's\n * `dd-trace-js`, or any custom `NodeTracerProvider` setup) instead of\n * the standalone `register()` path. The kit holds no global state and\n * touches no global slots — composition is the consumer's job.\n *\n * The standalone `register()` builds the kit internally and wires it\n * into private providers, so customer DX stays a one-liner export.\n *\n * Returns `null` when `INTERFERE_PUBLIC_KEY` is unset — callers can spread\n * `kit?.spanProcessors ?? []` without a no-op guard.\n */\nexport interface InterfereOtelKit {\n /**\n * Patches `console.{debug,log,info,warn,error}` to also emit OTel\n * `LogRecord`s via the global `LoggerProvider`. Caller is responsible\n * for ensuring a `LoggerProvider` is registered globally before\n * calling — `register()` handles this; for composed setups, call\n * after your bootstrap (`registerOTel({...})` etc.) has run. Returns\n * a disposer that restores the original `console.*` methods.\n */\n enableConsoleBridge: () => () => void;\n /**\n * Fetches the SDK's remote plugin config and caches it for the\n * runtime. Customer code's `isPluginEnabled(...)` checks read from\n * the cache. Safe to call repeatedly.\n */\n fetchRemoteConfig: () => Promise<void>;\n /**\n * Instrumentations the SDK ships. Currently `UndiciInstrumentation`\n * pre-configured with `propagateContextUrls` / `ignoreUrls` semantics\n * from `ServerInstrumentationOptions`.\n *\n * **Composition with `@vercel/otel`**: `instrumentations: [\"auto\"]`\n * registers Vercel's `globalThis.fetch` wrapper, which only\n * auto-propagates `traceparent` to Vercel deployment URLs — outbound\n * fetches to a different origin (e.g. `in.interfere.com`) get a\n * span but no header injection unless\n * `instrumentationConfig.fetch.propagateContextUrls` is set, and\n * even then it doesn't cover the lower-level undici dispatcher path\n * Next.js route handlers use. Spread `kit.instrumentations` after\n * `\"auto\"` to layer this UndiciInstrumentation on top — propagation\n * extends to every outbound undici call (filtered via `ignoreUrls`).\n * Risk of double span emission on overlapping call sites is bounded:\n * Vercel's fetch wraps `globalThis.fetch`, this hooks the undici\n * dispatcher one layer lower, so duplicates only appear when both\n * paths actually fire on the same call.\n */\n instrumentations: Instrumentation[];\n logRecordProcessors: LogRecordProcessor[];\n metricReaders: MetricReader[];\n /**\n * W3C baggage propagator. Trace context is intentionally *not*\n * included — `@vercel/otel`'s `propagators: [\"auto\"]` already\n * registers it, and standalone `register()` adds it itself when\n * the global slot is empty. Composing this on top extends rather\n * than overrides.\n */\n propagators: TextMapPropagator[];\n /**\n * Pre-built resource attribute set. Pass into your provider's\n * `Resource` directly, or into `@vercel/otel`'s `attributes` field\n * (which merges with Vercel's own auto-detected `vercel.*` attrs).\n */\n resourceAttributes: Record<string, AttributeValue>;\n spanProcessors: SpanProcessor[];\n}\n\n/**\n * Pure factory — no global registration, no provider construction. Returns\n * the OTel primitives the SDK contributes so a host bootstrap (Vercel,\n * DataDog, custom) can compose them in.\n *\n * Returns `null` when `INTERFERE_PUBLIC_KEY` isn't set, so callers can\n * unconditionally spread `kit?.spanProcessors ?? []` in dev where the\n * SDK isn't configured.\n */\nexport function buildInterfereOtelKit(\n opts: ServerInstrumentationOptions = {}\n): InterfereOtelKit | null {\n const env = readInterfereEnv();\n if (!env.publicKey) {\n return null;\n }\n\n const { slug: releaseSlug } = resolveReleaseSlug();\n if (!releaseSlug) {\n console.warn(\n \"[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.\"\n );\n }\n\n const sinkUrl = withPublicKey(`${env.apiUrl}/v2/sink`, env.publicKey);\n\n const ignoreUrls = [sinkUrl, ...(opts.ignoreUrls ?? [])];\n const propagateContextUrls = opts.propagateContextUrls ?? [];\n\n const exporterHeaders: Record<string, string> = {\n \"x-interfere-producer-version\": PRODUCER_VERSION,\n };\n\n const resourceAttributes: Record<string, AttributeValue> = {\n \"service.name\": opts.serviceName ?? DEFAULT_SERVICE_NAME,\n \"service.namespace\": SERVICE_NAMESPACE,\n \"deployment.environment.name\": env.nodeEnvironment,\n \"telemetry.sdk.language\": \"nodejs\",\n \"interfere.sdk.name\": \"@interfere/next\",\n \"interfere.sdk.version\": PRODUCER_VERSION,\n ...(releaseSlug ? { \"release.slug\": releaseSlug } : {}),\n };\n\n const spanProcessors: SpanProcessor[] = [\n new BaggageSpanProcessor(() => true),\n new BatchSpanProcessor(\n new OTLPTraceExporter({ url: sinkUrl, headers: exporterHeaders })\n ),\n ...(opts._internalAdditionalSpanProcessors ?? []),\n ];\n\n const logRecordProcessors: LogRecordProcessor[] = [\n new BatchLogRecordProcessor(\n new OTLPLogExporter({ url: sinkUrl, headers: exporterHeaders })\n ),\n ...(opts._internalAdditionalLogRecordProcessors ?? []),\n ];\n\n const metricReaders: MetricReader[] = [\n new PeriodicExportingMetricReader({\n exporter: new OTLPMetricExporter({\n url: sinkUrl,\n headers: exporterHeaders,\n temporalityPreference: AggregationTemporalityPreference.DELTA,\n }),\n exportIntervalMillis: 30_000,\n }),\n ...(opts._internalAdditionalMetricReaders ?? []),\n ];\n\n const propagators: TextMapPropagator[] = [new W3CBaggagePropagator()];\n\n const instrumentations: Instrumentation[] = [\n new UndiciInstrumentation({\n ignoreRequestHook: (req: { origin: string; path: string }) => {\n const url = `${req.origin}${req.path}`;\n return matchesAny(url, ignoreUrls);\n },\n requestHook: (span: Span, req: { origin: string; path: string }) => {\n const url = `${req.origin}${req.path}`;\n if (matchesAny(url, propagateContextUrls)) {\n span.setAttribute(\"interfere.propagated\", true);\n }\n },\n }),\n ];\n\n return {\n resourceAttributes,\n spanProcessors,\n logRecordProcessors,\n metricReaders,\n propagators,\n instrumentations,\n enableConsoleBridge: () => bridgeConsoleToOtel(),\n fetchRemoteConfig: () => fetchAndCacheRemoteConfig(),\n };\n}\n\n/**\n * One-line server-side bootstrap for Next.js. Customers' `instrumentation.ts`\n * becomes:\n *\n * ```ts\n * export { register } from \"@interfere/next/instrumentation\";\n * ```\n *\n * Or, with overrides:\n *\n * ```ts\n * import { register as base } from \"@interfere/next/instrumentation\";\n * export const register = () => base({ serviceName: \"@my-org/api\" });\n * ```\n *\n * Customers who already run another OTel bootstrap (`@vercel/otel`,\n * DataDog `dd-trace-js`, custom `NodeTracerProvider`) should use\n * `buildInterfereOtelKit({...})` instead and compose the returned\n * processors / readers / propagators into their existing setup —\n * see the kit's JSDoc for examples.\n *\n * Constructs private `NodeTracerProvider` / `LoggerProvider` /\n * `MeterProvider` and registers them on the global slots. Last writer\n * wins on the globals, so customers running another OTel bootstrap\n * alongside this one will end up with whichever booted last; the kit\n * path avoids that fight entirely.\n *\n * Wired against the Node OTel SDK (`NodeTracerProvider`,\n * `AsyncLocalStorageContextManager`) — so this entry only ships into the\n * `nodejs` runtime bundle. The Edge runtime gets the no-op stub at\n * `instrumentation.edge.ts` via `package.json`'s `edge-light` /\n * `workerd` export conditions.\n *\n * Idempotent — repeat calls (e.g. HMR) short-circuit on the\n * module-scoped `registered` flag.\n *\n * Bails silently when `INTERFERE_PUBLIC_KEY` is unset so dev runs without\n * the SDK configured don't crash.\n */\nexport async function register(\n opts: ServerInstrumentationOptions = {}\n): Promise<void> {\n if (registered) {\n return;\n }\n\n const kit = buildInterfereOtelKit(opts);\n if (!kit) {\n return;\n }\n\n const resource = resourceFromAttributes(kit.resourceAttributes);\n\n const tracerProvider = new NodeTracerProvider({\n resource,\n spanProcessors: kit.spanProcessors,\n idGenerator: new PrerenderSafeIdGenerator(),\n });\n\n if (propagation.fields().length === 0) {\n propagation.setGlobalPropagator(\n new CompositePropagator({\n propagators: [new W3CTraceContextPropagator(), ...kit.propagators],\n })\n );\n }\n\n tracerProvider.register({\n contextManager: new AsyncLocalStorageContextManager().enable(),\n });\n\n const loggerProvider = new LoggerProvider({\n resource,\n processors: kit.logRecordProcessors,\n });\n\n const { logs: logsApi } = await import(\"@opentelemetry/api-logs\");\n logsApi.setGlobalLoggerProvider(loggerProvider);\n\n const meterProvider = new MeterProvider({\n resource,\n readers: kit.metricReaders,\n });\n\n metrics.setGlobalMeterProvider(meterProvider);\n\n registerInstrumentations({\n tracerProvider,\n meterProvider,\n instrumentations: kit.instrumentations,\n });\n\n if (opts.consoleBridge !== false) {\n kit.enableConsoleBridge();\n }\n\n registered = true;\n\n await kit.fetchRemoteConfig();\n}\n"],"mappings":"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"}
@@ -2,6 +2,7 @@ import { NextConfig } from "next";
2
2
 
3
3
  //#region src/internal/build/configure-build.d.ts
4
4
  interface InterfereInjectedValues {
5
+ readonly __INTERFERE_ENVIRONMENT__?: string;
5
6
  readonly __INTERFERE_FORCE_ENABLE__?: boolean;
6
7
  readonly __INTERFERE_PROXY_MODE__?: boolean;
7
8
  readonly __INTERFERE_PUBLIC_KEY__?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"configure-build.d.mts","names":[],"sources":["../../../src/internal/build/configure-build.ts"],"mappings":";;;UAKiB,uBAAA;EAAA,SACN,0BAAA;EAAA,SACA,wBAAA;EAAA,SACA,wBAAA;EAAA,SACA,0BAAA;AAAA;AAAA,UAqBD,mBAAA;EAAA,SACC,iBAAA,EAAmB,UAAA;EAAA,SACnB,eAAA,EAAiB,UAAA;EAAA,SACjB,UAAA;EAAA,SACA,MAAA,EAAQ,uBAAA;AAAA;AAAA,UAGT,oBAAA;EAAA,SACC,SAAA,EAAW,UAAA;EAAA,SACX,OAAA,EAAS,UAAU;AAAA;AAAA,iBAGd,cAAA,CAAA;EACd,eAAA;EACA,iBAAA;EACA,UAAA;EACA;AAAA,GACC,mBAAA,GAAsB,oBAAA"}
1
+ {"version":3,"file":"configure-build.d.mts","names":[],"sources":["../../../src/internal/build/configure-build.ts"],"mappings":";;;UAKiB,uBAAA;EAAA,SACN,yBAAA;EAAA,SACA,0BAAA;EAAA,SACA,wBAAA;EAAA,SACA,wBAAA;EAAA,SACA,0BAAA;AAAA;AAAA,UAqBD,mBAAA;EAAA,SACC,iBAAA,EAAmB,UAAA;EAAA,SACnB,eAAA,EAAiB,UAAA;EAAA,SACjB,UAAA;EAAA,SACA,MAAA,EAAQ,uBAAA;AAAA;AAAA,UAGT,oBAAA;EAAA,SACC,SAAA,EAAW,UAAA;EAAA,SACX,OAAA,EAAS,UAAU;AAAA;AAAA,iBAGd,cAAA;EACd,eAAA;EACA,iBAAA;EACA,UAAA;EACA;AAAA,GACC,mBAAA,GAAsB,oBAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"configure-build.mjs","names":[],"sources":["../../../src/internal/build/configure-build.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { basename, dirname, join, normalize, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { NextConfig } from \"next\";\n\nexport interface InterfereInjectedValues {\n readonly __INTERFERE_FORCE_ENABLE__?: boolean;\n readonly __INTERFERE_PROXY_MODE__?: boolean;\n readonly __INTERFERE_PUBLIC_KEY__?: string;\n readonly __INTERFERE_RELEASE_SLUG__: string | null;\n}\n\nconst INSTRUMENTATION_CLIENT_FILES = [\n \"instrumentation-client.ts\",\n \"instrumentation-client.tsx\",\n \"instrumentation-client.js\",\n \"instrumentation-client.jsx\",\n \"src/instrumentation-client.ts\",\n \"src/instrumentation-client.tsx\",\n \"src/instrumentation-client.js\",\n \"src/instrumentation-client.jsx\",\n] as const;\n\nconst LOADER_FILE_CANDIDATES = [\n \"value-injection-loader.ts\",\n \"value-injection-loader.js\",\n \"value-injection-loader.mjs\",\n \"value-injection-loader.cjs\",\n] as const;\n\ninterface ConfigureBuildInput {\n readonly existingTurbopack: NextConfig[\"turbopack\"] | undefined;\n readonly existingWebpack: NextConfig[\"webpack\"] | undefined;\n readonly projectDir: string;\n readonly values: InterfereInjectedValues;\n}\n\ninterface ConfigureBuildOutput {\n readonly turbopack: NextConfig[\"turbopack\"] | undefined;\n readonly webpack: NextConfig[\"webpack\"] | undefined;\n}\n\nexport function configureBuild({\n existingWebpack,\n existingTurbopack,\n projectDir,\n values,\n}: ConfigureBuildInput): ConfigureBuildOutput {\n const instrumentationClientPath =\n resolveInstrumentationClientPath(projectDir);\n\n return {\n webpack: buildWebpackHook(\n existingWebpack,\n instrumentationClientPath,\n values\n ),\n turbopack: buildTurbopackConfig(\n existingTurbopack,\n instrumentationClientPath,\n values\n ),\n };\n}\n\nfunction hasInjectedMetadata(metadata: InterfereInjectedValues): boolean {\n return (\n metadata.__INTERFERE_RELEASE_SLUG__ !== null ||\n metadata.__INTERFERE_PUBLIC_KEY__ !== undefined\n );\n}\n\nfunction buildWebpackHook(\n existingWebpack: NextConfig[\"webpack\"] | undefined,\n instrumentationClientPath: string | null,\n values: InterfereInjectedValues\n): NextConfig[\"webpack\"] | undefined {\n if (!(instrumentationClientPath && hasInjectedMetadata(values))) {\n return existingWebpack;\n }\n\n const normalizedPath = normalize(instrumentationClientPath);\n const loaderPath = resolveLoaderPath();\n\n return (webpackConfig, options) => {\n const outputConfig = existingWebpack\n ? existingWebpack(webpackConfig, options)\n : webpackConfig;\n const mutableConfig = outputConfig as {\n module?: { rules?: unknown[] };\n };\n\n const injectionRule = {\n test: (resource: string): boolean =>\n Boolean(resource) && normalize(resource) === normalizedPath,\n use: [\n {\n loader: loaderPath,\n options: { values },\n },\n ],\n };\n\n mutableConfig.module ??= {};\n mutableConfig.module.rules ??= [];\n mutableConfig.module.rules.push(injectionRule);\n\n return outputConfig;\n };\n}\n\nfunction buildTurbopackConfig(\n existingTurbopack: NextConfig[\"turbopack\"] | undefined,\n instrumentationClientPath: string | null,\n values: InterfereInjectedValues\n): NextConfig[\"turbopack\"] | undefined {\n // Turbopack natively pairs JS chunks to source maps via debug IDs (it\n // injects matching `//# debugId=…` into the chunk and a `\"debugId\"` field\n // into the map). We rely on this in `discoverTurbopack`, because the\n // `//# sourceMappingURL=…` URL in the chunk is the source map's content\n // hash — not the `.js.map` filename on disk — so URL-based pairing always\n // fails for turbopack output.\n //\n // We force `debugIds: true` unless the user opted out explicitly. The\n // user keeps full control: any user-provided value (including `false`)\n // wins via `??`.\n const baseConfig = (existingTurbopack ?? {}) as {\n rules?: Record<string, unknown>;\n debugIds?: boolean;\n };\n const debugIds = baseConfig.debugIds ?? true;\n\n // No instrumentation client → only the debugIds patch is needed, and only\n // if the user hasn't already declared turbopack config (in which case we\n // leave their object untouched but layered with our default).\n if (!(instrumentationClientPath && hasInjectedMetadata(values))) {\n if (existingTurbopack === undefined && debugIds === baseConfig.debugIds) {\n return existingTurbopack;\n }\n \n return { ...baseConfig, debugIds } as NextConfig[\"turbopack\"];\n }\n\n const ruleKey = `**/${basename(instrumentationClientPath)}`;\n const existingRule = (baseConfig.rules?.[ruleKey] ?? {}) as {\n loaders?: unknown[];\n };\n const existingLoaders = Array.isArray(existingRule.loaders)\n ? existingRule.loaders\n : [];\n\n return {\n ...baseConfig,\n debugIds,\n rules: {\n ...baseConfig.rules,\n [ruleKey]: {\n ...existingRule,\n loaders: [\n ...existingLoaders,\n {\n loader: resolveLoaderPath(),\n options: {\n serializedValues: JSON.stringify(values),\n },\n },\n ],\n },\n },\n } as NextConfig[\"turbopack\"];\n}\n\nfunction resolveInstrumentationClientPath(projectDir: string): string | null {\n for (const candidate of INSTRUMENTATION_CLIENT_FILES) {\n const filePath = resolve(projectDir, candidate);\n if (existsSync(filePath)) {\n return filePath;\n }\n }\n\n return null;\n}\n\nfunction resolveLoaderPath(): string {\n const directory = resolve(dirname(fileURLToPath(import.meta.url)));\n\n for (const candidate of LOADER_FILE_CANDIDATES) {\n const filePath = join(directory, candidate);\n if (existsSync(filePath)) {\n return filePath;\n }\n }\n\n return join(directory, \"value-injection-loader.js\");\n}\n"],"mappings":"mIAYA,MAAM,6BAA+B,CACnC,4BACA,6BACA,4BACA,6BACA,gCACA,iCACA,gCACA,gCACF,EAEM,uBAAyB,CAC7B,4BACA,4BACA,6BACA,4BACF,EAcA,SAAgB,eAAe,CAC7B,gBACA,kBACA,WACA,QAC4C,CAC5C,IAAM,0BACJ,iCAAiC,UAAU,EAE7C,MAAO,CACL,QAAS,iBACP,gBACA,0BACA,MACF,EACA,UAAW,qBACT,kBACA,0BACA,MACF,CACF,CACF,CAEA,SAAS,oBAAoB,SAA4C,CACvE,OACE,SAAS,6BAA+B,MACxC,SAAS,2BAA6B,IAAA,EAE1C,CAEA,SAAS,iBACP,gBACA,0BACA,OACmC,CACnC,GAAI,EAAE,2BAA6B,oBAAoB,MAAM,GAC3D,OAAO,gBAGT,IAAM,eAAiB,UAAU,yBAAyB,EACpD,WAAa,kBAAkB,EAErC,OAAQ,cAAe,UAAY,CACjC,IAAM,aAAe,gBACjB,gBAAgB,cAAe,OAAO,EACtC,cACE,cAAgB,aAIhB,cAAgB,CACpB,KAAO,UACL,EAAQ,UAAa,UAAU,QAAQ,IAAM,eAC/C,IAAK,CACH,CACE,OAAQ,WACR,QAAS,CAAE,MAAO,CACpB,CACF,CACF,EAMA,MAJA,eAAc,SAAW,CAAC,EAC1B,cAAc,OAAO,QAAU,CAAC,EAChC,cAAc,OAAO,MAAM,KAAK,aAAa,EAEtC,YACT,CACF,CAEA,SAAS,qBACP,kBACA,0BACA,OACqC,CAWrC,IAAM,WAAc,mBAAqB,CAAC,EAIpC,SAAW,WAAW,UAAY,GAKxC,GAAI,EAAE,2BAA6B,oBAAoB,MAAM,GAK3D,OAJI,oBAAsB,IAAA,IAAa,WAAa,WAAW,SACtD,kBAGF,CAAE,GAAG,WAAY,QAAS,EAGnC,IAAM,QAAU,MAAM,SAAS,yBAAyB,IAClD,aAAgB,WAAW,QAAQ,UAAY,CAAC,EAGhD,gBAAkB,MAAM,QAAQ,aAAa,OAAO,EACtD,aAAa,QACb,CAAC,EAEL,MAAO,CACL,GAAG,WACH,SACA,MAAO,CACL,GAAG,WAAW,OACb,SAAU,CACT,GAAG,aACH,QAAS,CACP,GAAG,gBACH,CACE,OAAQ,kBAAkB,EAC1B,QAAS,CACP,iBAAkB,KAAK,UAAU,MAAM,CACzC,CACF,CACF,CACF,CACF,CACF,CACF,CAEA,SAAS,iCAAiC,WAAmC,CAC3E,IAAK,IAAM,aAAa,6BAA8B,CACpD,IAAM,SAAW,QAAQ,WAAY,SAAS,EAC9C,GAAI,WAAW,QAAQ,EACrB,OAAO,QAEX,CAEA,OAAO,IACT,CAEA,SAAS,mBAA4B,CACnC,IAAM,UAAY,QAAQ,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC,CAAC,EAEjE,IAAK,IAAM,aAAa,uBAAwB,CAC9C,IAAM,SAAW,KAAK,UAAW,SAAS,EAC1C,GAAI,WAAW,QAAQ,EACrB,OAAO,QAEX,CAEA,OAAO,KAAK,UAAW,2BAA2B,CACpD"}
1
+ {"version":3,"file":"configure-build.mjs","names":[],"sources":["../../../src/internal/build/configure-build.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { basename, dirname, join, normalize, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { NextConfig } from \"next\";\n\nexport interface InterfereInjectedValues {\n readonly __INTERFERE_ENVIRONMENT__?: string;\n readonly __INTERFERE_FORCE_ENABLE__?: boolean;\n readonly __INTERFERE_PROXY_MODE__?: boolean;\n readonly __INTERFERE_PUBLIC_KEY__?: string;\n readonly __INTERFERE_RELEASE_SLUG__: string | null;\n}\n\nconst INSTRUMENTATION_CLIENT_FILES = [\n \"instrumentation-client.ts\",\n \"instrumentation-client.tsx\",\n \"instrumentation-client.js\",\n \"instrumentation-client.jsx\",\n \"src/instrumentation-client.ts\",\n \"src/instrumentation-client.tsx\",\n \"src/instrumentation-client.js\",\n \"src/instrumentation-client.jsx\",\n] as const;\n\nconst LOADER_FILE_CANDIDATES = [\n \"value-injection-loader.ts\",\n \"value-injection-loader.js\",\n \"value-injection-loader.mjs\",\n \"value-injection-loader.cjs\",\n] as const;\n\ninterface ConfigureBuildInput {\n readonly existingTurbopack: NextConfig[\"turbopack\"] | undefined;\n readonly existingWebpack: NextConfig[\"webpack\"] | undefined;\n readonly projectDir: string;\n readonly values: InterfereInjectedValues;\n}\n\ninterface ConfigureBuildOutput {\n readonly turbopack: NextConfig[\"turbopack\"] | undefined;\n readonly webpack: NextConfig[\"webpack\"] | undefined;\n}\n\nexport function configureBuild({\n existingWebpack,\n existingTurbopack,\n projectDir,\n values,\n}: ConfigureBuildInput): ConfigureBuildOutput {\n const instrumentationClientPath =\n resolveInstrumentationClientPath(projectDir);\n\n return {\n webpack: buildWebpackHook(\n existingWebpack,\n instrumentationClientPath,\n values\n ),\n turbopack: buildTurbopackConfig(\n existingTurbopack,\n instrumentationClientPath,\n values\n ),\n };\n}\n\nfunction hasInjectedMetadata(metadata: InterfereInjectedValues): boolean {\n return (\n metadata.__INTERFERE_RELEASE_SLUG__ !== null ||\n metadata.__INTERFERE_PUBLIC_KEY__ !== undefined\n );\n}\n\nfunction buildWebpackHook(\n existingWebpack: NextConfig[\"webpack\"] | undefined,\n instrumentationClientPath: string | null,\n values: InterfereInjectedValues\n): NextConfig[\"webpack\"] | undefined {\n if (!(instrumentationClientPath && hasInjectedMetadata(values))) {\n return existingWebpack;\n }\n\n const normalizedPath = normalize(instrumentationClientPath);\n const loaderPath = resolveLoaderPath();\n\n return (webpackConfig, options) => {\n const outputConfig = existingWebpack\n ? existingWebpack(webpackConfig, options)\n : webpackConfig;\n const mutableConfig = outputConfig as {\n module?: { rules?: unknown[] };\n };\n\n const injectionRule = {\n test: (resource: string): boolean =>\n Boolean(resource) && normalize(resource) === normalizedPath,\n use: [\n {\n loader: loaderPath,\n options: { values },\n },\n ],\n };\n\n mutableConfig.module ??= {};\n mutableConfig.module.rules ??= [];\n mutableConfig.module.rules.push(injectionRule);\n\n return outputConfig;\n };\n}\n\nfunction buildTurbopackConfig(\n existingTurbopack: NextConfig[\"turbopack\"] | undefined,\n instrumentationClientPath: string | null,\n values: InterfereInjectedValues\n): NextConfig[\"turbopack\"] | undefined {\n \n \n \n \n \n \n \n \n \n \n const baseConfig = (existingTurbopack ?? {}) as {\n rules?: Record<string, unknown>;\n debugIds?: boolean;\n };\n const debugIds = baseConfig.debugIds ?? true;\n\n \n \n \n if (!(instrumentationClientPath && hasInjectedMetadata(values))) {\n if (existingTurbopack === undefined && debugIds === baseConfig.debugIds) {\n return existingTurbopack;\n }\n \n return { ...baseConfig, debugIds } as NextConfig[\"turbopack\"];\n }\n\n const ruleKey = `**/${basename(instrumentationClientPath)}`;\n const existingRule = (baseConfig.rules?.[ruleKey] ?? {}) as {\n loaders?: unknown[];\n };\n const existingLoaders = Array.isArray(existingRule.loaders)\n ? existingRule.loaders\n : [];\n\n return {\n ...baseConfig,\n debugIds,\n rules: {\n ...baseConfig.rules,\n [ruleKey]: {\n ...existingRule,\n loaders: [\n ...existingLoaders,\n {\n loader: resolveLoaderPath(),\n options: {\n serializedValues: JSON.stringify(values),\n },\n },\n ],\n },\n },\n } as NextConfig[\"turbopack\"];\n}\n\nfunction resolveInstrumentationClientPath(projectDir: string): string | null {\n for (const candidate of INSTRUMENTATION_CLIENT_FILES) {\n const filePath = resolve(projectDir, candidate);\n if (existsSync(filePath)) {\n return filePath;\n }\n }\n\n return null;\n}\n\nfunction resolveLoaderPath(): string {\n const directory = resolve(dirname(fileURLToPath(import.meta.url)));\n\n for (const candidate of LOADER_FILE_CANDIDATES) {\n const filePath = join(directory, candidate);\n if (existsSync(filePath)) {\n return filePath;\n }\n }\n\n return join(directory, \"value-injection-loader.js\");\n}\n"],"mappings":"mIAaA,MAAM,6BAA+B,CACnC,4BACA,6BACA,4BACA,6BACA,gCACA,iCACA,gCACA,gCACF,EAEM,uBAAyB,CAC7B,4BACA,4BACA,6BACA,4BACF,EAcA,SAAgB,eAAe,CAC7B,gBACA,kBACA,WACA,QAC4C,CAC5C,IAAM,0BACJ,iCAAiC,UAAU,EAE7C,MAAO,CACL,QAAS,iBACP,gBACA,0BACA,MACF,EACA,UAAW,qBACT,kBACA,0BACA,MACF,CACF,CACF,CAEA,SAAS,oBAAoB,SAA4C,CACvE,OACE,SAAS,6BAA+B,MACxC,SAAS,2BAA6B,IAAA,EAE1C,CAEA,SAAS,iBACP,gBACA,0BACA,OACmC,CACnC,GAAI,EAAE,2BAA6B,oBAAoB,MAAM,GAC3D,OAAO,gBAGT,IAAM,eAAiB,UAAU,yBAAyB,EACpD,WAAa,kBAAkB,EAErC,OAAQ,cAAe,UAAY,CACjC,IAAM,aAAe,gBACjB,gBAAgB,cAAe,OAAO,EACtC,cACE,cAAgB,aAIhB,cAAgB,CACpB,KAAO,UACL,EAAQ,UAAa,UAAU,QAAQ,IAAM,eAC/C,IAAK,CACH,CACE,OAAQ,WACR,QAAS,CAAE,MAAO,CACpB,CACF,CACF,EAMA,MAJA,eAAc,SAAW,CAAC,EAC1B,cAAc,OAAO,QAAU,CAAC,EAChC,cAAc,OAAO,MAAM,KAAK,aAAa,EAEtC,YACT,CACF,CAEA,SAAS,qBACP,kBACA,0BACA,OACqC,CAWrC,IAAM,WAAc,mBAAqB,CAAC,EAIpC,SAAW,WAAW,UAAY,GAKxC,GAAI,EAAE,2BAA6B,oBAAoB,MAAM,GAK3D,OAJI,oBAAsB,IAAA,IAAa,WAAa,WAAW,SACtD,kBAGF,CAAE,GAAG,WAAY,QAAS,EAGnC,IAAM,QAAU,MAAM,SAAS,yBAAyB,IAClD,aAAgB,WAAW,QAAQ,UAAY,CAAC,EAGhD,gBAAkB,MAAM,QAAQ,aAAa,OAAO,EACtD,aAAa,QACb,CAAC,EAEL,MAAO,CACL,GAAG,WACH,SACA,MAAO,CACL,GAAG,WAAW,OACb,SAAU,CACT,GAAG,aACH,QAAS,CACP,GAAG,gBACH,CACE,OAAQ,kBAAkB,EAC1B,QAAS,CACP,iBAAkB,KAAK,UAAU,MAAM,CACzC,CACF,CACF,CACF,CACF,CACF,CACF,CAEA,SAAS,iCAAiC,WAAmC,CAC3E,IAAK,IAAM,aAAa,6BAA8B,CACpD,IAAM,SAAW,QAAQ,WAAY,SAAS,EAC9C,GAAI,WAAW,QAAQ,EACrB,OAAO,QAEX,CAEA,OAAO,IACT,CAEA,SAAS,mBAA4B,CACnC,IAAM,UAAY,QAAQ,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC,CAAC,EAEjE,IAAK,IAAM,aAAa,uBAAwB,CAC9C,IAAM,SAAW,KAAK,UAAW,SAAS,EAC1C,GAAI,WAAW,QAAQ,EACrB,OAAO,QAEX,CAEA,OAAO,KAAK,UAAW,2BAA2B,CACpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.mts","names":[],"sources":["../../../src/internal/build/pipeline.ts"],"mappings":";;;;;;UAciB,WAAA;EACf,OAAA;EACA,aAAA;EACA,QAAA;EACA,SAAA;EACA,SAAA;EACA,KAAA;EACA,UAAA;EACA,MAAA;AAAA;AAAA,KAGU,cAAA;EACN,KAAA;EAAc,MAAA;EAA0B,SAAA;AAAA;EAExC,KAAA;EACA,SAAA;EACA,OAAA,EAAS,qBAAA;EACT,MAAA,EAAQ,sBAAA;EACR,OAAA;EACA,MAAA,EAAQ,WAAA;AAAA;AAAA,iBASQ,gBAAA,CACpB,OAAA,EAAS,wBAAA,EACT,QAAA,EAAU,mBAAA;EACR,MAAA;EACA,OAAA;EACA,WAAA,EAAa,WAAA;AAAA,IACd,OAAA"}
1
+ {"version":3,"file":"pipeline.d.mts","names":[],"sources":["../../../src/internal/build/pipeline.ts"],"mappings":";;;;;;UAciB,WAAA;EACf,OAAA;EACA,aAAA;EACA,QAAA;EACA,SAAA;EACA,SAAA;EACA,KAAA;EACA,UAAA;EACA,MAAA;AAAA;AAAA,KAGU,cAAA;EACN,KAAA;EAAc,MAAA;EAA0B,SAAA;AAAA;EAExC,KAAA;EACA,SAAA;EACA,OAAA,EAAS,qBAAA;EACT,MAAA,EAAQ,sBAAA;EACR,OAAA;EACA,MAAA,EAAQ,WAAA;AAAA;AAAA,iBAWQ,gBAAA,CACpB,OAAA,EAAS,wBAAA,EACT,QAAA,EAAU,mBAAA;EACR,MAAA;EACA,OAAA;EACA,WAAA,EAAa,WAAA;AAAA,IACd,OAAA"}
@@ -1 +1 @@
1
- import{detectActiveBundler}from"./detect-bundler.mjs";import{resolveReleaseRequest}from"./release/index.mjs";import{discoverSourceMaps}from"./source-maps/discover.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}),start=performance.now(),bundler=detectActiveBundler(),[{discovered,config},discoverMs]=await timed(async()=>{let[discovered,config]=await Promise.all([discoverSourceMaps({bundler,projectDir:context.projectDir,distDir:context.distDir}),sdk.releases.getConfig()]);return{discovered,config}});if(discovered.files.length===0)return{ready:!1,reason:`no_source_maps`,fileCount:0};let releaseRequest=resolveReleaseRequest(metadata.buildId,metadata.releaseSlug,config),[release,createReleaseMs]=await timed(()=>sdk.releases.create(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({releaseSlug})),[,cleanupMs]=await timed(()=>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{detectActiveBundler}from"./detect-bundler.mjs";import{resolveReleaseRequest}from"./release/index.mjs";import{discoverSourceMaps}from"./source-maps/discover.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(),[{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};let releaseRequest=resolveReleaseRequest(metadata.buildId,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(()=>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 +1 @@
1
- {"version":3,"file":"pipeline.mjs","names":[],"sources":["../../../src/internal/build/pipeline.ts"],"sourcesContent":["import { HTTPClient, Interfere } from \"@interfere/sdk\";\nimport type { ReleasesConfigResponse } from \"@interfere/sdk/models/releases-config-response.js\";\nimport type { CreateReleaseResponse } from \"@interfere/types/releases/definition\";\nimport { type ReleaseSlug, releaseSlugSchema } from \"@interfere/types/releases/slug\";\n\nimport type {\n ProductionCompileContext,\n ResolvedBuildConfig,\n} from \"../../config.js\";\nimport { detectActiveBundler } from \"./detect-bundler.js\";\nimport { resolveReleaseRequest } from \"./release/index.js\";\nimport { discoverSourceMaps } from \"./source-maps/discover.js\";\nimport { cleanupSourceMaps, uploadSourceMaps } from \"./source-maps/index.js\";\n\nexport interface BuildTiming {\n cleanup: number;\n createRelease: number;\n discover: number;\n fileCount: number;\n preflight: number;\n total: number;\n totalBytes: number;\n upload: number;\n}\n\nexport type PipelineResult =\n | { ready: false; reason: \"no_source_maps\"; fileCount: 0 }\n | {\n ready: true;\n fileCount: number;\n release: CreateReleaseResponse;\n config: ReleasesConfigResponse;\n buildId: string;\n timing: BuildTiming;\n };\n\nasync function timed<T>(fn: () => Promise<T>): Promise<[T, number]> {\n const start = performance.now();\n const result = await fn();\n return [result, Math.round(performance.now() - start)];\n}\n\nexport async function runBuildPipeline(\n context: ProductionCompileContext,\n metadata: ResolvedBuildConfig & {\n apiKey: string;\n buildId: string;\n releaseSlug: ReleaseSlug;\n }\n) {\n const { apiUrl, apiKey } = metadata;\n\n const httpClient = new HTTPClient();\n\n httpClient.addHook(\"beforeRequest\", (request) => {\n const nextRequest = new Request(request);\n\n nextRequest.headers.set(\"Authorization\", `Bearer ${apiKey}`);\n\n return nextRequest;\n });\n\n const sdk = new Interfere({ serverURL: apiUrl, httpClient });\n\n const start = performance.now();\n\n const bundler = detectActiveBundler();\n\n const [{ discovered, config }, discoverMs] = await timed(async () => {\n const [discovered, config] = await Promise.all([\n discoverSourceMaps({\n bundler,\n projectDir: context.projectDir,\n distDir: context.distDir,\n }),\n sdk.releases.getConfig(),\n ]);\n return { discovered, config };\n });\n\n if (discovered.files.length === 0) {\n return { ready: false, reason: \"no_source_maps\", fileCount: 0 };\n }\n\n const releaseRequest = resolveReleaseRequest(\n metadata.buildId,\n metadata.releaseSlug,\n config\n );\n\n const [release, createReleaseMs] = await timed(() =>\n sdk.releases.create(releaseRequest)\n );\n\n // The SDK response carries the slug as a plain `string`. Brand it\n // here at the API boundary so every downstream caller (upload,\n // preflight, cleanup) reads a `ReleaseSlug`.\n const releaseSlug = releaseSlugSchema.parse(release.destination.slug);\n const buildId = release.build.hash ?? metadata.buildId;\n\n const [{ totalBytes }, uploadMs] = await timed(() =>\n uploadSourceMaps({ apiUrl, bundler, discovered, httpClient, releaseSlug: releaseSlug })\n );\n\n // Cleanup runs *after* preflight, not before (PRD draft had it inverted).\n // Preflight only depends on the upload having succeeded; if local cleanup\n // fails we still want the release marked confirmed and the build to fail\n // loudly via the cleanup error, rather than leaving a confirmed-but-uncleaned\n // workspace and a release that needs a redeploy to recover.\n const [, preflightMs] = await timed(() =>\n sdk.releases.preflight({ releaseSlug })\n );\n\n const [, cleanupMs] = await timed(() => cleanupSourceMaps(discovered.files));\n\n const timing: BuildTiming = {\n discover: discoverMs,\n createRelease: createReleaseMs,\n upload: uploadMs,\n preflight: preflightMs,\n cleanup: cleanupMs,\n total: Math.round(performance.now() - start),\n fileCount: discovered.files.length,\n totalBytes,\n };\n\n return {\n ready: true,\n fileCount: discovered.files.length,\n release: { ...release, destination: { ...release.destination, slug: releaseSlug } },\n config,\n buildId,\n timing,\n } satisfies PipelineResult;\n}\n"],"mappings":"qYAoCA,eAAe,MAAS,GAA4C,CAClE,IAAM,MAAQ,YAAY,IAAI,EAE9B,MAAO,CAAC,MADa,GAAG,EACR,KAAK,MAAM,YAAY,IAAI,EAAI,KAAK,CAAC,CACvD,CAEA,eAAsB,iBACpB,QACA,SAKA,CACA,GAAM,CAAE,OAAQ,QAAW,SAErB,WAAa,IAAI,WAEvB,WAAW,QAAQ,gBAAkB,SAAY,CAC/C,IAAM,YAAc,IAAI,QAAQ,OAAO,EAIvC,OAFA,YAAY,QAAQ,IAAI,gBAAiB,UAAU,QAAQ,EAEpD,WACT,CAAC,EAED,IAAM,IAAM,IAAI,UAAU,CAAE,UAAW,OAAQ,UAAW,CAAC,EAErD,MAAQ,YAAY,IAAI,EAExB,QAAU,oBAAoB,EAE9B,CAAC,CAAE,WAAY,QAAU,YAAc,MAAM,MAAM,SAAY,CACnE,GAAM,CAAC,WAAY,QAAU,MAAM,QAAQ,IAAI,CAC7C,mBAAmB,CACjB,QACA,WAAY,QAAQ,WACpB,QAAS,QAAQ,OACnB,CAAC,EACD,IAAI,SAAS,UAAU,CACzB,CAAC,EACD,MAAO,CAAE,WAAY,MAAO,CAC9B,CAAC,EAED,GAAI,WAAW,MAAM,SAAW,EAC9B,MAAO,CAAE,MAAO,GAAO,OAAQ,iBAAkB,UAAW,CAAE,EAGhE,IAAM,eAAiB,sBACrB,SAAS,QACT,SAAS,YACT,MACF,EAEM,CAAC,QAAS,iBAAmB,MAAM,UACvC,IAAI,SAAS,OAAO,cAAc,CACpC,EAKM,YAAc,kBAAkB,MAAM,QAAQ,YAAY,IAAI,EAC9D,QAAU,QAAQ,MAAM,MAAQ,SAAS,QAEzC,CAAC,CAAE,YAAc,UAAY,MAAM,UACvC,iBAAiB,CAAE,OAAQ,QAAS,WAAY,WAAyB,WAAY,CAAC,CACxF,EAOM,EAAG,aAAe,MAAM,UAC5B,IAAI,SAAS,UAAU,CAAE,WAAY,CAAC,CACxC,EAEM,EAAG,WAAa,MAAM,UAAY,kBAAkB,WAAW,KAAK,CAAC,EAErE,OAAsB,CAC1B,SAAU,WACV,cAAe,gBACf,OAAQ,SACR,UAAW,YACX,QAAS,UACT,MAAO,KAAK,MAAM,YAAY,IAAI,EAAI,KAAK,EAC3C,UAAW,WAAW,MAAM,OAC5B,UACF,EAEA,MAAO,CACL,MAAO,GACP,UAAW,WAAW,MAAM,OAC5B,QAAS,CAAE,GAAG,QAAS,YAAa,CAAE,GAAG,QAAQ,YAAa,KAAM,WAAY,CAAE,EAClF,OACA,QACA,MACF,CACF"}
1
+ {"version":3,"file":"pipeline.mjs","names":[],"sources":["../../../src/internal/build/pipeline.ts"],"sourcesContent":["import { HTTPClient, Interfere } from \"@interfere/sdk\";\nimport type { ReleasesConfigResponse } from \"@interfere/sdk/models/releases-config-response.js\";\nimport type { CreateReleaseResponse } from \"@interfere/types/releases/definition\";\nimport { type ReleaseSlug, releaseSlugSchema } from \"@interfere/types/releases/slug\";\n\nimport type {\n ProductionCompileContext,\n ResolvedBuildConfig,\n} from \"../../config.js\";\nimport { detectActiveBundler } from \"./detect-bundler.js\";\nimport { resolveReleaseRequest } from \"./release/index.js\";\nimport { discoverSourceMaps } from \"./source-maps/discover.js\";\nimport { cleanupSourceMaps, uploadSourceMaps } from \"./source-maps/index.js\";\n\nexport interface BuildTiming {\n cleanup: number;\n createRelease: number;\n discover: number;\n fileCount: number;\n preflight: number;\n total: number;\n totalBytes: number;\n upload: number;\n}\n\nexport type PipelineResult =\n | { ready: false; reason: \"no_source_maps\"; fileCount: 0 }\n | {\n ready: true;\n fileCount: number;\n release: CreateReleaseResponse;\n config: ReleasesConfigResponse;\n buildId: string;\n timing: BuildTiming;\n };\n\nasync function timed<T>(\n fn: () => Promise<T>\n): Promise<[result: T, durationMs: number]> {\n const start = performance.now();\n const result = await fn();\n return [result, Math.round(performance.now() - start)];\n}\n\nexport async function runBuildPipeline(\n context: ProductionCompileContext,\n metadata: ResolvedBuildConfig & {\n apiKey: string;\n buildId: string;\n releaseSlug: ReleaseSlug;\n }\n) {\n const { apiUrl, apiKey } = metadata;\n\n const httpClient = new HTTPClient();\n\n httpClient.addHook(\"beforeRequest\", (request) => {\n const nextRequest = new Request(request);\n\n nextRequest.headers.set(\"Authorization\", `Bearer ${apiKey}`);\n\n return nextRequest;\n });\n\n const sdk = new Interfere({ serverURL: apiUrl, httpClient });\n const security = { apiKey };\n\n const start = performance.now();\n\n const bundler = detectActiveBundler();\n\n const [{ discovered, config }, discoverMs] = await timed(async () => {\n const [discovered, config] = await Promise.all([\n discoverSourceMaps({\n bundler,\n projectDir: context.projectDir,\n distDir: context.distDir,\n }),\n sdk.releases.getConfig(security),\n ]);\n return { discovered, config };\n });\n\n if (discovered.files.length === 0) {\n return { ready: false, reason: \"no_source_maps\", fileCount: 0 };\n }\n\n const releaseRequest = resolveReleaseRequest(\n metadata.buildId,\n metadata.releaseSlug,\n config\n );\n\n const [release, createReleaseMs] = await timed(() =>\n sdk.releases.create(security, releaseRequest)\n );\n\n \n \n \n const releaseSlug = releaseSlugSchema.parse(release.destination.slug);\n const buildId = release.build.hash ?? metadata.buildId;\n\n const [{ totalBytes }, uploadMs] = await timed(() =>\n uploadSourceMaps({ apiUrl, bundler, discovered, httpClient, releaseSlug: releaseSlug })\n );\n\n \n \n \n \n \n const [, preflightMs] = await timed(() =>\n sdk.releases.preflight(security, { releaseSlug })\n );\n\n const [, cleanupMs] = await timed(() => cleanupSourceMaps(discovered.files));\n\n const timing: BuildTiming = {\n discover: discoverMs,\n createRelease: createReleaseMs,\n upload: uploadMs,\n preflight: preflightMs,\n cleanup: cleanupMs,\n total: Math.round(performance.now() - start),\n fileCount: discovered.files.length,\n totalBytes,\n };\n\n return {\n ready: true,\n fileCount: discovered.files.length,\n release: { ...release, destination: { ...release.destination, slug: releaseSlug } },\n config,\n buildId,\n timing,\n } satisfies PipelineResult;\n}\n"],"mappings":"qYAoCA,eAAe,MACb,GAC0C,CAC1C,IAAM,MAAQ,YAAY,IAAI,EAE9B,MAAO,CAAC,MADa,GAAG,EACR,KAAK,MAAM,YAAY,IAAI,EAAI,KAAK,CAAC,CACvD,CAEA,eAAsB,iBACpB,QACA,SAKA,CACA,GAAM,CAAE,OAAQ,QAAW,SAErB,WAAa,IAAI,WAEvB,WAAW,QAAQ,gBAAkB,SAAY,CAC/C,IAAM,YAAc,IAAI,QAAQ,OAAO,EAIvC,OAFA,YAAY,QAAQ,IAAI,gBAAiB,UAAU,QAAQ,EAEpD,WACT,CAAC,EAED,IAAM,IAAM,IAAI,UAAU,CAAE,UAAW,OAAQ,UAAW,CAAC,EACrD,SAAW,CAAE,MAAO,EAEpB,MAAQ,YAAY,IAAI,EAExB,QAAU,oBAAoB,EAE9B,CAAC,CAAE,WAAY,QAAU,YAAc,MAAM,MAAM,SAAY,CACnE,GAAM,CAAC,WAAY,QAAU,MAAM,QAAQ,IAAI,CAC7C,mBAAmB,CACjB,QACA,WAAY,QAAQ,WACpB,QAAS,QAAQ,OACnB,CAAC,EACD,IAAI,SAAS,UAAU,QAAQ,CACjC,CAAC,EACD,MAAO,CAAE,WAAY,MAAO,CAC9B,CAAC,EAED,GAAI,WAAW,MAAM,SAAW,EAC9B,MAAO,CAAE,MAAO,GAAO,OAAQ,iBAAkB,UAAW,CAAE,EAGhE,IAAM,eAAiB,sBACrB,SAAS,QACT,SAAS,YACT,MACF,EAEM,CAAC,QAAS,iBAAmB,MAAM,UACvC,IAAI,SAAS,OAAO,SAAU,cAAc,CAC9C,EAKM,YAAc,kBAAkB,MAAM,QAAQ,YAAY,IAAI,EAC9D,QAAU,QAAQ,MAAM,MAAQ,SAAS,QAEzC,CAAC,CAAE,YAAc,UAAY,MAAM,UACvC,iBAAiB,CAAE,OAAQ,QAAS,WAAY,WAAyB,WAAY,CAAC,CACxF,EAOM,EAAG,aAAe,MAAM,UAC5B,IAAI,SAAS,UAAU,SAAU,CAAE,WAAY,CAAC,CAClD,EAEM,EAAG,WAAa,MAAM,UAAY,kBAAkB,WAAW,KAAK,CAAC,EAErE,OAAsB,CAC1B,SAAU,WACV,cAAe,gBACf,OAAQ,SACR,UAAW,YACX,QAAS,UACT,MAAO,KAAK,MAAM,YAAY,IAAI,EAAI,KAAK,EAC3C,UAAW,WAAW,MAAM,OAC5B,UACF,EAEA,MAAO,CACL,MAAO,GACP,UAAW,WAAW,MAAM,OAC5B,QAAS,CAAE,GAAG,QAAS,YAAa,CAAE,GAAG,QAAQ,YAAa,KAAM,WAAY,CAAE,EAClF,OACA,QACA,MACF,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"vercel.d.mts","names":[],"sources":["../../../../../src/internal/build/release/destinations/vercel.ts"],"mappings":";;;iBAGgB,OAAA,CAAA,GAAW,0BAA0B"}
1
+ {"version":3,"file":"vercel.d.mts","names":[],"sources":["../../../../../src/internal/build/release/destinations/vercel.ts"],"mappings":";;;iBAGgB,OAAA,IAAW,0BAA0B"}
@@ -1,6 +1,9 @@
1
1
  import { ReleaseSourceMetadata } from "@interfere/types/integrations";
2
2
 
3
3
  //#region src/internal/build/release/sources/github.d.ts
4
- declare function resolve(): ReleaseSourceMetadata;
4
+ type GithubReleaseSource = Extract<ReleaseSourceMetadata, {
5
+ provider: "github";
6
+ }>;
7
+ declare function resolve(): GithubReleaseSource;
5
8
  //#endregion
6
9
  export { resolve };
@@ -1 +1 @@
1
- {"version":3,"file":"github.d.mts","names":[],"sources":["../../../../../src/internal/build/release/sources/github.ts"],"mappings":";;;iBAKgB,OAAA,CAAA,GAAW,qBAAqB"}
1
+ {"version":3,"file":"github.d.mts","names":[],"sources":["../../../../../src/internal/build/release/sources/github.ts"],"mappings":";;;KAKK,mBAAA,GAAsB,OAAO,CAChC,qBAAA;EACE,QAAA;AAAA;AAAA,iBAGY,OAAA,IAAW,mBAAmB"}
@@ -1 +1 @@
1
- {"version":3,"file":"github.mjs","names":[],"sources":["../../../../../src/internal/build/release/sources/github.ts"],"sourcesContent":["import type { ReleaseSourceMetadata } from \"@interfere/types/integrations\";\nimport { parseEnvValue } from \"@interfere/types/sdk/env\";\n\nimport { runGitCommand } from \"../git.js\";\n\nexport function resolve(): ReleaseSourceMetadata {\n return {\n provider: \"github\",\n branch:\n parseEnvValue(process.env[\"VERCEL_GIT_COMMIT_REF\"]) ??\n parseEnvValue(process.env[\"GITHUB_REF_NAME\"]) ??\n parseEnvValue(process.env[\"GITHUB_HEAD_REF\"]) ??\n runGitCommand(\"git rev-parse --abbrev-ref HEAD\") ??\n \"unknown\",\n commitMessage:\n parseEnvValue(process.env[\"VERCEL_GIT_COMMIT_MESSAGE\"]) ??\n runGitCommand(\"git log -1 --pretty=%B\") ??\n \"\",\n commitSha:\n parseEnvValue(process.env[\"VERCEL_GIT_COMMIT_SHA\"]) ??\n parseEnvValue(process.env[\"GITHUB_SHA\"]) ??\n runGitCommand(\"git rev-parse HEAD\"),\n };\n}\n"],"mappings":"0FAKA,SAAgB,SAAiC,CAC/C,MAAO,CACL,SAAU,SACV,OACE,cAAc,QAAQ,IAAI,qBAAwB,GAClD,cAAc,QAAQ,IAAI,eAAkB,GAC5C,cAAc,QAAQ,IAAI,eAAkB,GAC5C,cAAc,iCAAiC,GAC/C,UACF,cACE,cAAc,QAAQ,IAAI,yBAA4B,GACtD,cAAc,wBAAwB,GACtC,GACF,UACE,cAAc,QAAQ,IAAI,qBAAwB,GAClD,cAAc,QAAQ,IAAI,UAAa,GACvC,cAAc,oBAAoB,CACtC,CACF"}
1
+ {"version":3,"file":"github.mjs","names":[],"sources":["../../../../../src/internal/build/release/sources/github.ts"],"sourcesContent":["import type { ReleaseSourceMetadata } from \"@interfere/types/integrations\";\nimport { parseEnvValue } from \"@interfere/types/sdk/env\";\n\nimport { runGitCommand } from \"../git.js\";\n\ntype GithubReleaseSource = Extract<\n ReleaseSourceMetadata,\n { provider: \"github\" }\n>;\n\nexport function resolve(): GithubReleaseSource {\n return {\n provider: \"github\",\n branch:\n parseEnvValue(process.env[\"VERCEL_GIT_COMMIT_REF\"]) ??\n parseEnvValue(process.env[\"GITHUB_REF_NAME\"]) ??\n parseEnvValue(process.env[\"GITHUB_HEAD_REF\"]) ??\n runGitCommand(\"git rev-parse --abbrev-ref HEAD\") ??\n \"unknown\",\n commitMessage:\n parseEnvValue(process.env[\"VERCEL_GIT_COMMIT_MESSAGE\"]) ??\n runGitCommand(\"git log -1 --pretty=%B\") ??\n \"\",\n commitSha:\n parseEnvValue(process.env[\"VERCEL_GIT_COMMIT_SHA\"]) ??\n parseEnvValue(process.env[\"GITHUB_SHA\"]) ??\n runGitCommand(\"git rev-parse HEAD\"),\n };\n}\n"],"mappings":"0FAUA,SAAgB,SAA+B,CAC7C,MAAO,CACL,SAAU,SACV,OACE,cAAc,QAAQ,IAAI,qBAAwB,GAClD,cAAc,QAAQ,IAAI,eAAkB,GAC5C,cAAc,QAAQ,IAAI,eAAkB,GAC5C,cAAc,iCAAiC,GAC/C,UACF,cACE,cAAc,QAAQ,IAAI,yBAA4B,GACtD,cAAc,wBAAwB,GACtC,GACF,UACE,cAAc,QAAQ,IAAI,qBAAwB,GAClD,cAAc,QAAQ,IAAI,UAAa,GACvC,cAAc,oBAAoB,CACtC,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"discover-turbopack.mjs","names":[],"sources":["../../../../src/internal/build/source-maps/discover-turbopack.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport { relative } from \"node:path\";\n\nimport type { SourceMapFile } from \"./discover.js\";\nimport { resolveDistDir, toPublicPath, walkDistTrees } from \"./paths.js\";\n\nconst DEBUG_ID_RE = /\\/\\/[#@]\\s*debugId=([0-9a-f-]+)\\s*$/m;\n\n/**\n * Turbopack-mode discovery.\n *\n * Turbopack hashes the JS chunk and its source map independently, so the\n * `//# sourceMappingURL=…` URL embedded in the chunk never matches the\n * on-disk `.js.map` filename. When `experimental.turbo.debugIds` is on\n * (we set it for the user in `configure-build.ts`), turbopack natively\n * injects matching `debugId` values into both the chunk\n * (`//# debugId=…`) and the source map (`\"debugId\": \"…\"`).\n *\n * We pair JS chunks ↔ source maps by `debugId` (turbopack's natural key)\n * and additionally record each chunk's public URL as `chunkUrl` so the\n * server-side suffix-match resolver has a uniform field to look up by\n * `frame.fileName` regardless of bundler. The webpack discoverer\n * populates the same field directly from the chunk's URL; the turbopack\n * discoverer derives it from the JS chunk's on-disk path.\n *\n * Maps without a debugId, or whose debugId never appears in any chunk,\n * are silently skipped — they have no way to resolve at runtime.\n */\nexport async function discoverTurbopack(opts: {\n projectDir: string;\n distDir: string;\n}): Promise<{ files: SourceMapFile[]; sourceFileCount: number }> {\n const absDistDir = resolveDistDir(opts.projectDir, opts.distDir);\n const relDistDir = relative(opts.projectDir, absDistDir);\n\n const [mapPaths, jsPaths] = await Promise.all([\n walkDistTrees(absDistDir, \".js.map\"),\n walkDistTrees(absDistDir, \".js\"),\n ]);\n\n // Walk JS chunks once and build `debugId → publicUrl` so a single\n // pass over the maps can attach `chunkUrl` per entry.\n const debugIdToChunkUrl = new Map<string, string>();\n await Promise.all(\n jsPaths.map(async (jsAbs) => {\n const content = await readFile(jsAbs, \"utf8\").catch(() => null);\n if (content === null) {\n return;\n }\n const match = DEBUG_ID_RE.exec(content);\n const debugId = match?.[1];\n if (!debugId) {\n return;\n }\n const chunkUrl = toPublicPath(\n relative(opts.projectDir, jsAbs),\n relDistDir\n );\n // First write wins — collisions across .js shouldn't happen with\n // content-hashed chunk filenames, but if they do, the first walked\n // entry is the deterministic choice.\n if (!debugIdToChunkUrl.has(debugId)) {\n debugIdToChunkUrl.set(debugId, chunkUrl);\n }\n })\n );\n\n const files = await Promise.all(\n mapPaths.map(async (mapAbs): Promise<SourceMapFile | null> => {\n const content = await readFile(mapAbs, \"utf8\").catch(() => null);\n if (content === null) {\n return null;\n }\n\n let debugId: string | undefined;\n try {\n const parsed = JSON.parse(content) as { debugId?: unknown };\n if (typeof parsed.debugId === \"string\" && parsed.debugId.length > 0) {\n debugId = parsed.debugId;\n }\n } catch {\n return null;\n }\n\n if (!debugId) {\n return null;\n }\n\n const chunkUrl = debugIdToChunkUrl.get(debugId);\n if (!chunkUrl) {\n // No JS chunk in the dist tree carries this debugId — the map\n // can't pair to a runtime URL, so it's unresolvable. Drop it\n // rather than emitting an unusable manifest entry.\n return null;\n }\n\n return {\n absolute: mapAbs,\n path: toPublicPath(relative(opts.projectDir, mapAbs), relDistDir),\n content,\n hash: createHash(\"sha256\").update(content).digest(\"hex\"),\n debugId,\n chunkUrl,\n } satisfies SourceMapFile;\n })\n );\n\n const loaded = files.filter((f): f is SourceMapFile => f !== null);\n\n return {\n files: loaded,\n sourceFileCount: loaded.length,\n };\n}\n"],"mappings":"8KAOA,MAAM,YAAc,uCAsBpB,eAAsB,kBAAkB,KAGyB,CAC/D,IAAM,WAAa,eAAe,KAAK,WAAY,KAAK,OAAO,EACzD,WAAa,SAAS,KAAK,WAAY,UAAU,EAEjD,CAAC,SAAU,SAAW,MAAM,QAAQ,IAAI,CAC5C,cAAc,WAAY,SAAS,EACnC,cAAc,WAAY,KAAK,CACjC,CAAC,EAIK,kBAAoB,IAAI,IAC9B,MAAM,QAAQ,IACZ,QAAQ,IAAI,KAAO,QAAU,CAC3B,IAAM,QAAU,MAAM,SAAS,MAAO,MAAM,EAAE,UAAY,IAAI,EAC9D,GAAI,UAAY,KACd,OAGF,IAAM,QADQ,YAAY,KAAK,OACX,IAAI,GACxB,GAAI,CAAC,QACH,OAEF,IAAM,SAAW,aACf,SAAS,KAAK,WAAY,KAAK,EAC/B,UACF,EAIK,kBAAkB,IAAI,OAAO,GAChC,kBAAkB,IAAI,QAAS,QAAQ,CAE3C,CAAC,CACH,EA0CA,IAAM,QAAS,MAxCK,QAAQ,IAC1B,SAAS,IAAI,KAAO,SAA0C,CAC5D,IAAM,QAAU,MAAM,SAAS,OAAQ,MAAM,EAAE,UAAY,IAAI,EAC/D,GAAI,UAAY,KACd,OAAO,KAGT,IAAI,QACJ,GAAI,CACF,IAAM,OAAS,KAAK,MAAM,OAAO,EAC7B,OAAO,OAAO,SAAY,UAAY,OAAO,QAAQ,OAAS,IAChE,QAAU,OAAO,QAErB,MAAQ,CACN,OAAO,IACT,CAEA,GAAI,CAAC,QACH,OAAO,KAGT,IAAM,SAAW,kBAAkB,IAAI,OAAO,EAQ9C,OAPK,SAOE,CACL,SAAU,OACV,KAAM,aAAa,SAAS,KAAK,WAAY,MAAM,EAAG,UAAU,EAChE,QACA,KAAM,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK,EACvD,QACA,QACF,EAVS,IAWX,CAAC,CACH,GAEqB,OAAQ,GAA0B,IAAM,IAAI,EAEjE,MAAO,CACL,MAAO,OACP,gBAAiB,OAAO,MAC1B,CACF"}
1
+ {"version":3,"file":"discover-turbopack.mjs","names":[],"sources":["../../../../src/internal/build/source-maps/discover-turbopack.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport { relative } from \"node:path\";\n\nimport type { SourceMapFile } from \"./discover.js\";\nimport { resolveDistDir, toPublicPath, walkDistTrees } from \"./paths.js\";\n\nconst DEBUG_ID_RE = /\\/\\/[#@]\\s*debugId=([0-9a-f-]+)\\s*$/m;\n\n/**\n * Turbopack-mode discovery.\n *\n * Turbopack hashes the JS chunk and its source map independently, so the\n * `//# sourceMappingURL=…` URL embedded in the chunk never matches the\n * on-disk `.js.map` filename. When `experimental.turbo.debugIds` is on\n * (we set it for the user in `configure-build.ts`), turbopack natively\n * injects matching `debugId` values into both the chunk\n * (`//# debugId=…`) and the source map (`\"debugId\": \"…\"`).\n *\n * We pair JS chunks ↔ source maps by `debugId` (turbopack's natural key)\n * and additionally record each chunk's public URL as `chunkUrl` so the\n * server-side suffix-match resolver has a uniform field to look up by\n * `frame.fileName` regardless of bundler. The webpack discoverer\n * populates the same field directly from the chunk's URL; the turbopack\n * discoverer derives it from the JS chunk's on-disk path.\n *\n * Maps without a debugId, or whose debugId never appears in any chunk,\n * are silently skipped — they have no way to resolve at runtime.\n */\nexport async function discoverTurbopack(opts: {\n projectDir: string;\n distDir: string;\n}): Promise<{ files: SourceMapFile[]; sourceFileCount: number }> {\n const absDistDir = resolveDistDir(opts.projectDir, opts.distDir);\n const relDistDir = relative(opts.projectDir, absDistDir);\n\n const [mapPaths, jsPaths] = await Promise.all([\n walkDistTrees(absDistDir, \".js.map\"),\n walkDistTrees(absDistDir, \".js\"),\n ]);\n\n \n \n const debugIdToChunkUrl = new Map<string, string>();\n await Promise.all(\n jsPaths.map(async (jsAbs) => {\n const content = await readFile(jsAbs, \"utf8\").catch(() => null);\n if (content === null) {\n return;\n }\n const match = DEBUG_ID_RE.exec(content);\n const debugId = match?.[1];\n if (!debugId) {\n return;\n }\n const chunkUrl = toPublicPath(\n relative(opts.projectDir, jsAbs),\n relDistDir\n );\n \n \n \n if (!debugIdToChunkUrl.has(debugId)) {\n debugIdToChunkUrl.set(debugId, chunkUrl);\n }\n })\n );\n\n const files = await Promise.all(\n mapPaths.map(async (mapAbs): Promise<SourceMapFile | null> => {\n const content = await readFile(mapAbs, \"utf8\").catch(() => null);\n if (content === null) {\n return null;\n }\n\n let debugId: string | undefined;\n try {\n const parsed = JSON.parse(content) as { debugId?: unknown };\n if (typeof parsed.debugId === \"string\" && parsed.debugId.length > 0) {\n debugId = parsed.debugId;\n }\n } catch {\n return null;\n }\n\n if (!debugId) {\n return null;\n }\n\n const chunkUrl = debugIdToChunkUrl.get(debugId);\n if (!chunkUrl) {\n \n \n \n return null;\n }\n\n return {\n absolute: mapAbs,\n path: toPublicPath(relative(opts.projectDir, mapAbs), relDistDir),\n content,\n hash: createHash(\"sha256\").update(content).digest(\"hex\"),\n debugId,\n chunkUrl,\n } satisfies SourceMapFile;\n })\n );\n\n const loaded = files.filter((f): f is SourceMapFile => f !== null);\n\n return {\n files: loaded,\n sourceFileCount: loaded.length,\n };\n}\n"],"mappings":"8KAOA,MAAM,YAAc,uCAsBpB,eAAsB,kBAAkB,KAGyB,CAC/D,IAAM,WAAa,eAAe,KAAK,WAAY,KAAK,OAAO,EACzD,WAAa,SAAS,KAAK,WAAY,UAAU,EAEjD,CAAC,SAAU,SAAW,MAAM,QAAQ,IAAI,CAC5C,cAAc,WAAY,SAAS,EACnC,cAAc,WAAY,KAAK,CACjC,CAAC,EAIK,kBAAoB,IAAI,IAC9B,MAAM,QAAQ,IACZ,QAAQ,IAAI,KAAO,QAAU,CAC3B,IAAM,QAAU,MAAM,SAAS,MAAO,MAAM,EAAE,UAAY,IAAI,EAC9D,GAAI,UAAY,KACd,OAGF,IAAM,QADQ,YAAY,KAAK,OACX,IAAI,GACxB,GAAI,CAAC,QACH,OAEF,IAAM,SAAW,aACf,SAAS,KAAK,WAAY,KAAK,EAC/B,UACF,EAIK,kBAAkB,IAAI,OAAO,GAChC,kBAAkB,IAAI,QAAS,QAAQ,CAE3C,CAAC,CACH,EA0CA,IAAM,QAAS,MAxCK,QAAQ,IAC1B,SAAS,IAAI,KAAO,SAA0C,CAC5D,IAAM,QAAU,MAAM,SAAS,OAAQ,MAAM,EAAE,UAAY,IAAI,EAC/D,GAAI,UAAY,KACd,OAAO,KAGT,IAAI,QACJ,GAAI,CACF,IAAM,OAAS,KAAK,MAAM,OAAO,EAC7B,OAAO,OAAO,SAAY,UAAY,OAAO,QAAQ,OAAS,IAChE,QAAU,OAAO,QAErB,MAAQ,CACN,OAAO,IACT,CAEA,GAAI,CAAC,QACH,OAAO,KAGT,IAAM,SAAW,kBAAkB,IAAI,OAAO,EAQ9C,OAPK,SAOE,CACL,SAAU,OACV,KAAM,aAAa,SAAS,KAAK,WAAY,MAAM,EAAG,UAAU,EAChE,QACA,KAAM,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK,EACvD,QACA,QACF,EAVS,IAWX,CAAC,CACH,GAEqB,OAAQ,GAA0B,IAAM,IAAI,EAEjE,MAAO,CACL,MAAO,OACP,gBAAiB,OAAO,MAC1B,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"discover-webpack.mjs","names":[],"sources":["../../../../src/internal/build/source-maps/discover-webpack.ts"],"sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { relative } from \"node:path\";\n\nimport type { SourceMapFile } from \"./discover.js\";\nimport { resolveDistDir, toPublicPath, walkDistTrees } from \"./paths.js\";\n\nconst SOURCEMAPPING_RE = /\\/\\/[#@]\\s*sourceMappingURL=(\\S+)\\s*$/;\n\n/**\n * Per the TC39 sourcemap-debug-id proposal, the LAST `//# debugId=…`\n * comment in the bundle is the canonical one. Same regex Sentry uses\n * to detect spec-compliant debug IDs already injected by another tool\n * (webpack 5.104+ `SourceMapDevToolPlugin({ debugIds: true })`,\n * turbopack `experimental.turbopack.debugIds`, rollup, vite, etc.).\n */\nconst SPEC_LAST_DEBUG_ID_RE =\n /\\/\\/# debugId=([a-fA-F0-9-]+)(?![\\s\\S]*\\/\\/# debugId=)/m;\n\n/**\n * Reads an existing `//# debugId=…` comment from the bundle source.\n * Returns `null` when no debug ID comment is present — the caller is\n * expected to generate and inject one.\n */\nexport function readDebugIdFromJs(content: string): string | null {\n const match = SPEC_LAST_DEBUG_ID_RE.exec(content);\n return match?.[1] ?? null;\n}\n\n/**\n * Inserts a `//# debugId=…` comment into the chunk. Idempotent: if a\n * spec-compliant comment is already present (e.g. webpack 5.104+ with\n * `SourceMapDevToolPlugin({ debugIds: true })`, turbopack with\n * `experimental.turbopack.debugIds`, or any other tool) it's\n * overwritten with the supplied debugId — callers that read the\n * existing id via `readDebugIdFromJs` first get an idempotent no-op.\n *\n * Otherwise the comment is appended just before the existing\n * `//# sourceMappingURL=…` line so the browser still resolves the map.\n */\nexport function injectDebugIdIntoJs(content: string, debugId: string): string {\n const debugIdComment = `//# debugId=${debugId}`;\n\n if (SPEC_LAST_DEBUG_ID_RE.test(content)) {\n return content.replace(SPEC_LAST_DEBUG_ID_RE, debugIdComment);\n }\n\n const match = SOURCEMAPPING_RE.exec(content);\n if (match) {\n const before = content.slice(0, match.index).trimEnd();\n const sourceMappingLine = match[0];\n return `${before}\\n${debugIdComment}\\n${sourceMappingLine}`;\n }\n\n return `${content}\\n${debugIdComment}`;\n}\n\n/**\n * Adds `debugId` to the source-map JSON. Idempotent: writes both\n * `debugId` (TC39 spec field) and `debug_id` (legacy variant some\n * older Sentry tooling emits) so manifests stay readable across the\n * ecosystem. Re-running with the same id produces identical output.\n */\nexport function injectDebugIdIntoMap(content: string, debugId: string): string {\n const json = JSON.parse(content) as Record<string, unknown>;\n json[\"debugId\"] = debugId;\n return JSON.stringify(json);\n}\n\n/**\n * Webpack-mode discovery: walks the dist tree, parses each `.js`\n * chunk's `//# sourceMappingURL=…` comment to find its paired `.map`,\n * and ensures both carry the same `debugId`.\n *\n * Idempotent w.r.t. debug-id state on disk:\n * - If the chunk already carries a `//# debugId=…` comment (webpack\n * 5.104+ with `SourceMapDevToolPlugin({ debugIds: true })`, or a\n * third-party tool that already injected one) we reuse it.\n * - Otherwise we generate a fresh UUID and inject it into both files.\n *\n * The \"reuse if present\" branch matches the strategy in Sentry's\n * webpack plugin (`addDebugIdToBundleSource` in\n * `bundler-plugin-core/src/debug-id-upload.ts`) and means our\n * discovery works transparently when bundlers natively emit debugIds.\n */\nexport async function discoverWebpack(opts: {\n projectDir: string;\n distDir: string;\n}): Promise<{ files: SourceMapFile[]; sourceFileCount: number }> {\n const absDistDir = resolveDistDir(opts.projectDir, opts.distDir);\n const relDistDir = relative(opts.projectDir, absDistDir);\n\n const jsPaths = await walkDistTrees(absDistDir, \".js\");\n\n const pairs = new Map<\n string,\n {\n mapAbs: string;\n jsAbs: string;\n jsPublic: string;\n jsContent: string;\n existingDebugId: string | null;\n }\n >();\n\n for (const jsAbs of jsPaths) {\n const content = await readFile(jsAbs, \"utf8\");\n const match = SOURCEMAPPING_RE.exec(content);\n if (!match?.[1]) {\n continue;\n }\n\n const jsDir = jsAbs.slice(0, jsAbs.lastIndexOf(\"/\") + 1);\n const mapRef = decodeURIComponent(match[1]);\n const mapAbs = `${jsDir}${mapRef}`;\n const jsPublic = toPublicPath(relative(opts.projectDir, jsAbs), relDistDir);\n const mapPublic = toPublicPath(\n relative(opts.projectDir, mapAbs),\n relDistDir\n );\n\n pairs.set(mapPublic, {\n mapAbs,\n jsAbs,\n jsPublic,\n jsContent: content,\n existingDebugId: readDebugIdFromJs(content),\n });\n }\n\n const files = await Promise.all(\n Array.from(\n pairs.entries(),\n async ([mapPublic, pair]): Promise<SourceMapFile | null> => {\n const mapContent = await readFile(pair.mapAbs, \"utf8\").catch(\n () => null\n );\n if (mapContent === null) {\n return null;\n }\n\n const debugId = pair.existingDebugId ?? randomUUID();\n\n // Skip the rewrite when the bundler already injected the\n // canonical debug-id comment + source-map field (webpack\n // native debugIds, turbopack, etc.). Re-running with the\n // same id is a no-op anyway, but skipping the I/O is faster\n // and keeps file mtimes stable for incremental builds.\n if (!pair.existingDebugId) {\n const injectedJs = injectDebugIdIntoJs(pair.jsContent, debugId);\n await writeFile(pair.jsAbs, injectedJs, \"utf8\");\n }\n\n const mapJson = JSON.parse(mapContent) as Record<string, unknown>;\n const finalMap =\n mapJson[\"debugId\"] === debugId\n ? mapContent\n : injectDebugIdIntoMap(mapContent, debugId);\n if (finalMap !== mapContent) {\n await writeFile(pair.mapAbs, finalMap, \"utf8\");\n }\n\n return {\n absolute: pair.mapAbs,\n path: mapPublic,\n content: finalMap,\n hash: createHash(\"sha256\").update(finalMap).digest(\"hex\"),\n debugId,\n chunkUrl: pair.jsPublic,\n };\n }\n )\n );\n\n const loaded = files.filter((f): f is SourceMapFile => f !== null);\n\n return {\n files: loaded,\n sourceFileCount: pairs.size,\n };\n}\n"],"mappings":"mMAOA,MAAM,iBAAmB,wCASnB,sBACJ,0DAOF,SAAgB,kBAAkB,QAAgC,CAEhE,OADc,sBAAsB,KAAK,OAC9B,IAAI,IAAM,IACvB,CAaA,SAAgB,oBAAoB,QAAiB,QAAyB,CAC5E,IAAM,eAAiB,eAAe,UAEtC,GAAI,sBAAsB,KAAK,OAAO,EACpC,OAAO,QAAQ,QAAQ,sBAAuB,cAAc,EAG9D,IAAM,MAAQ,iBAAiB,KAAK,OAAO,EAO3C,OANI,MAGK,GAFQ,QAAQ,MAAM,EAAG,MAAM,KAAK,EAAE,QAE9B,EAAE,IAAI,eAAe,IADV,MAAM,KAI3B,GAAG,QAAQ,IAAI,gBACxB,CAQA,SAAgB,qBAAqB,QAAiB,QAAyB,CAC7E,IAAM,KAAO,KAAK,MAAM,OAAO,EAE/B,MADA,MAAK,QAAa,QACX,KAAK,UAAU,IAAI,CAC5B,CAkBA,eAAsB,gBAAgB,KAG2B,CAC/D,IAAM,WAAa,eAAe,KAAK,WAAY,KAAK,OAAO,EACzD,WAAa,SAAS,KAAK,WAAY,UAAU,EAEjD,QAAU,MAAM,cAAc,WAAY,KAAK,EAE/C,MAAQ,IAAI,IAWlB,IAAK,IAAM,SAAS,QAAS,CAC3B,IAAM,QAAU,MAAM,SAAS,MAAO,MAAM,EACtC,MAAQ,iBAAiB,KAAK,OAAO,EAC3C,GAAI,CAAC,QAAQ,GACX,SAKF,IAAM,OAAS,GAFD,MAAM,MAAM,EAAG,MAAM,YAAY,GAAG,EAAI,CAEhC,IADP,mBAAmB,MAAM,EACT,IACzB,SAAW,aAAa,SAAS,KAAK,WAAY,KAAK,EAAG,UAAU,EACpE,UAAY,aAChB,SAAS,KAAK,WAAY,MAAM,EAChC,UACF,EAEA,MAAM,IAAI,UAAW,CACnB,OACA,MACA,SACA,UAAW,QACX,gBAAiB,kBAAkB,OAAO,CAC5C,CAAC,CACH,CAgDA,MAAO,CACL,OAHa,MA5CK,QAAQ,IAC1B,MAAM,KACJ,MAAM,QAAQ,EACd,MAAO,CAAC,UAAW,QAAyC,CAC1D,IAAM,WAAa,MAAM,SAAS,KAAK,OAAQ,MAAM,EAAE,UAC/C,IACR,EACA,GAAI,aAAe,KACjB,OAAO,KAGT,IAAM,QAAU,KAAK,iBAAmB,WAAW,EAOnD,GAAI,CAAC,KAAK,gBAAiB,CACzB,IAAM,WAAa,oBAAoB,KAAK,UAAW,OAAO,EAC9D,MAAM,UAAU,KAAK,MAAO,WAAY,MAAM,CAChD,CAGA,IAAM,SADU,KAAK,MAAM,UAEnB,EAAE,UAAe,QACnB,WACA,qBAAqB,WAAY,OAAO,EAK9C,OAJI,WAAa,YACf,MAAM,UAAU,KAAK,OAAQ,SAAU,MAAM,EAGxC,CACL,SAAU,KAAK,OACf,KAAM,UACN,QAAS,SACT,KAAM,WAAW,QAAQ,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAK,EACxD,QACA,SAAU,KAAK,QACjB,CACF,CACF,CACF,GAEqB,OAAQ,GAA0B,IAAM,IAG/C,EACZ,gBAAiB,MAAM,IACzB,CACF"}
1
+ {"version":3,"file":"discover-webpack.mjs","names":[],"sources":["../../../../src/internal/build/source-maps/discover-webpack.ts"],"sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { relative } from \"node:path\";\n\nimport type { SourceMapFile } from \"./discover.js\";\nimport { resolveDistDir, toPublicPath, walkDistTrees } from \"./paths.js\";\n\nconst SOURCEMAPPING_RE = /\\/\\/[#@]\\s*sourceMappingURL=(\\S+)\\s*$/;\n\n/**\n * Per the TC39 sourcemap-debug-id proposal, the LAST `//# debugId=…`\n * comment in the bundle is the canonical one. Same regex Sentry uses\n * to detect spec-compliant debug IDs already injected by another tool\n * (webpack 5.104+ `SourceMapDevToolPlugin({ debugIds: true })`,\n * turbopack `experimental.turbopack.debugIds`, rollup, vite, etc.).\n */\nconst SPEC_LAST_DEBUG_ID_RE =\n /\\/\\/# debugId=([a-fA-F0-9-]+)(?![\\s\\S]*\\/\\/# debugId=)/m;\n\n/**\n * Reads an existing `//# debugId=…` comment from the bundle source.\n * Returns `null` when no debug ID comment is present — the caller is\n * expected to generate and inject one.\n */\nexport function readDebugIdFromJs(content: string): string | null {\n const match = SPEC_LAST_DEBUG_ID_RE.exec(content);\n return match?.[1] ?? null;\n}\n\n/**\n * Inserts a `//# debugId=…` comment into the chunk. Idempotent: if a\n * spec-compliant comment is already present (e.g. webpack 5.104+ with\n * `SourceMapDevToolPlugin({ debugIds: true })`, turbopack with\n * `experimental.turbopack.debugIds`, or any other tool) it's\n * overwritten with the supplied debugId — callers that read the\n * existing id via `readDebugIdFromJs` first get an idempotent no-op.\n *\n * Otherwise the comment is appended just before the existing\n * `//# sourceMappingURL=…` line so the browser still resolves the map.\n */\nexport function injectDebugIdIntoJs(content: string, debugId: string): string {\n const debugIdComment = `//# debugId=${debugId}`;\n\n if (SPEC_LAST_DEBUG_ID_RE.test(content)) {\n return content.replace(SPEC_LAST_DEBUG_ID_RE, debugIdComment);\n }\n\n const match = SOURCEMAPPING_RE.exec(content);\n if (match) {\n const before = content.slice(0, match.index).trimEnd();\n const sourceMappingLine = match[0];\n return `${before}\\n${debugIdComment}\\n${sourceMappingLine}`;\n }\n\n return `${content}\\n${debugIdComment}`;\n}\n\n/**\n * Adds `debugId` to the source-map JSON. Idempotent: writes both\n * `debugId` (TC39 spec field) and `debug_id` (legacy variant some\n * older Sentry tooling emits) so manifests stay readable across the\n * ecosystem. Re-running with the same id produces identical output.\n */\nexport function injectDebugIdIntoMap(content: string, debugId: string): string {\n const json = JSON.parse(content) as Record<string, unknown>;\n json[\"debugId\"] = debugId;\n return JSON.stringify(json);\n}\n\n/**\n * Webpack-mode discovery: walks the dist tree, parses each `.js`\n * chunk's `//# sourceMappingURL=…` comment to find its paired `.map`,\n * and ensures both carry the same `debugId`.\n *\n * Idempotent w.r.t. debug-id state on disk:\n * - If the chunk already carries a `//# debugId=…` comment (webpack\n * 5.104+ with `SourceMapDevToolPlugin({ debugIds: true })`, or a\n * third-party tool that already injected one) we reuse it.\n * - Otherwise we generate a fresh UUID and inject it into both files.\n *\n * The \"reuse if present\" branch matches the strategy in Sentry's\n * webpack plugin (`addDebugIdToBundleSource` in\n * `bundler-plugin-core/src/debug-id-upload.ts`) and means our\n * discovery works transparently when bundlers natively emit debugIds.\n */\nexport async function discoverWebpack(opts: {\n projectDir: string;\n distDir: string;\n}): Promise<{ files: SourceMapFile[]; sourceFileCount: number }> {\n const absDistDir = resolveDistDir(opts.projectDir, opts.distDir);\n const relDistDir = relative(opts.projectDir, absDistDir);\n\n const jsPaths = await walkDistTrees(absDistDir, \".js\");\n\n const pairs = new Map<\n string,\n {\n mapAbs: string;\n jsAbs: string;\n jsPublic: string;\n jsContent: string;\n existingDebugId: string | null;\n }\n >();\n\n for (const jsAbs of jsPaths) {\n const content = await readFile(jsAbs, \"utf8\");\n const match = SOURCEMAPPING_RE.exec(content);\n if (!match?.[1]) {\n continue;\n }\n\n const jsDir = jsAbs.slice(0, jsAbs.lastIndexOf(\"/\") + 1);\n const mapRef = decodeURIComponent(match[1]);\n const mapAbs = `${jsDir}${mapRef}`;\n const jsPublic = toPublicPath(relative(opts.projectDir, jsAbs), relDistDir);\n const mapPublic = toPublicPath(\n relative(opts.projectDir, mapAbs),\n relDistDir\n );\n\n pairs.set(mapPublic, {\n mapAbs,\n jsAbs,\n jsPublic,\n jsContent: content,\n existingDebugId: readDebugIdFromJs(content),\n });\n }\n\n const files = await Promise.all(\n Array.from(\n pairs.entries(),\n async ([mapPublic, pair]): Promise<SourceMapFile | null> => {\n const mapContent = await readFile(pair.mapAbs, \"utf8\").catch(\n () => null\n );\n if (mapContent === null) {\n return null;\n }\n\n const debugId = pair.existingDebugId ?? randomUUID();\n\n \n \n \n \n \n if (!pair.existingDebugId) {\n const injectedJs = injectDebugIdIntoJs(pair.jsContent, debugId);\n await writeFile(pair.jsAbs, injectedJs, \"utf8\");\n }\n\n const mapJson = JSON.parse(mapContent) as Record<string, unknown>;\n const finalMap =\n mapJson[\"debugId\"] === debugId\n ? mapContent\n : injectDebugIdIntoMap(mapContent, debugId);\n if (finalMap !== mapContent) {\n await writeFile(pair.mapAbs, finalMap, \"utf8\");\n }\n\n return {\n absolute: pair.mapAbs,\n path: mapPublic,\n content: finalMap,\n hash: createHash(\"sha256\").update(finalMap).digest(\"hex\"),\n debugId,\n chunkUrl: pair.jsPublic,\n };\n }\n )\n );\n\n const loaded = files.filter((f): f is SourceMapFile => f !== null);\n\n return {\n files: loaded,\n sourceFileCount: pairs.size,\n };\n}\n"],"mappings":"mMAOA,MAAM,iBAAmB,wCASnB,sBACJ,0DAOF,SAAgB,kBAAkB,QAAgC,CAEhE,OADc,sBAAsB,KAAK,OAC9B,IAAI,IAAM,IACvB,CAaA,SAAgB,oBAAoB,QAAiB,QAAyB,CAC5E,IAAM,eAAiB,eAAe,UAEtC,GAAI,sBAAsB,KAAK,OAAO,EACpC,OAAO,QAAQ,QAAQ,sBAAuB,cAAc,EAG9D,IAAM,MAAQ,iBAAiB,KAAK,OAAO,EAO3C,OANI,MAGK,GAFQ,QAAQ,MAAM,EAAG,MAAM,KAAK,EAAE,QAE9B,EAAE,IAAI,eAAe,IADV,MAAM,KAI3B,GAAG,QAAQ,IAAI,gBACxB,CAQA,SAAgB,qBAAqB,QAAiB,QAAyB,CAC7E,IAAM,KAAO,KAAK,MAAM,OAAO,EAE/B,MADA,MAAK,QAAa,QACX,KAAK,UAAU,IAAI,CAC5B,CAkBA,eAAsB,gBAAgB,KAG2B,CAC/D,IAAM,WAAa,eAAe,KAAK,WAAY,KAAK,OAAO,EACzD,WAAa,SAAS,KAAK,WAAY,UAAU,EAEjD,QAAU,MAAM,cAAc,WAAY,KAAK,EAE/C,MAAQ,IAAI,IAWlB,IAAK,IAAM,SAAS,QAAS,CAC3B,IAAM,QAAU,MAAM,SAAS,MAAO,MAAM,EACtC,MAAQ,iBAAiB,KAAK,OAAO,EAC3C,GAAI,CAAC,QAAQ,GACX,SAKF,IAAM,OAAS,GAFD,MAAM,MAAM,EAAG,MAAM,YAAY,GAAG,EAAI,CAEhC,IADP,mBAAmB,MAAM,EACT,IACzB,SAAW,aAAa,SAAS,KAAK,WAAY,KAAK,EAAG,UAAU,EACpE,UAAY,aAChB,SAAS,KAAK,WAAY,MAAM,EAChC,UACF,EAEA,MAAM,IAAI,UAAW,CACnB,OACA,MACA,SACA,UAAW,QACX,gBAAiB,kBAAkB,OAAO,CAC5C,CAAC,CACH,CAgDA,MAAO,CACL,OAHa,MA5CK,QAAQ,IAC1B,MAAM,KACJ,MAAM,QAAQ,EACd,MAAO,CAAC,UAAW,QAAyC,CAC1D,IAAM,WAAa,MAAM,SAAS,KAAK,OAAQ,MAAM,EAAE,UAC/C,IACR,EACA,GAAI,aAAe,KACjB,OAAO,KAGT,IAAM,QAAU,KAAK,iBAAmB,WAAW,EAOnD,GAAI,CAAC,KAAK,gBAAiB,CACzB,IAAM,WAAa,oBAAoB,KAAK,UAAW,OAAO,EAC9D,MAAM,UAAU,KAAK,MAAO,WAAY,MAAM,CAChD,CAGA,IAAM,SADU,KAAK,MAAM,UAEnB,EAAE,UAAe,QACnB,WACA,qBAAqB,WAAY,OAAO,EAK9C,OAJI,WAAa,YACf,MAAM,UAAU,KAAK,OAAQ,SAAU,MAAM,EAGxC,CACL,SAAU,KAAK,OACf,KAAM,UACN,QAAS,SACT,KAAM,WAAW,QAAQ,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAK,EACxD,QACA,SAAU,KAAK,QACjB,CACF,CACF,CACF,GAEqB,OAAQ,GAA0B,IAAM,IAG/C,EACZ,gBAAiB,MAAM,IACzB,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/internal/build/source-maps/index.ts"],"sourcesContent":["import { unlink } from \"node:fs/promises\";\n\nimport { log } from \"../../logger.js\";\nimport type { SourceMapFile } from \"./discover.js\";\n\nexport async function cleanupSourceMaps(files: SourceMapFile[]): Promise<void> {\n // ENOENT is the only failure we silently absorb — a missing file just\n // means the upload-then-cleanup race lost. Anything else (perm, IO,\n // EBUSY) is a real signal: the source map is now stranded on the\n // customer's CDN and we surface it via a single aggregated warning so\n // an operator can investigate without the build failing outright.\n const results = await Promise.allSettled(files.map((f) => unlink(f.absolute)));\n const failures: string[] = [];\n for (const [i, r] of results.entries()) {\n if (r.status !== \"rejected\") {\n continue;\n }\n const err = r.reason as NodeJS.ErrnoException;\n if (err.code === \"ENOENT\") {\n continue;\n }\n const file = files[i];\n failures.push(\n `${file?.absolute ?? \"<unknown>\"}: ${err.code ?? err.message ?? String(err)}`\n );\n }\n if (failures.length > 0) {\n log.warn(\"Source-map cleanup left files on disk\", failures);\n }\n}\n\nexport { uploadSourceMaps } from \"./upload.js\";\n"],"mappings":"kHAKA,eAAsB,kBAAkB,MAAuC,CAM7E,IAAM,QAAU,MAAM,QAAQ,WAAW,MAAM,IAAK,GAAM,OAAO,EAAE,QAAQ,CAAC,CAAC,EACvE,SAAqB,CAAC,EAC5B,IAAK,GAAM,CAAC,EAAG,KAAM,QAAQ,QAAQ,EAAG,CACtC,GAAI,EAAE,SAAW,WACf,SAEF,IAAM,IAAM,EAAE,OACd,GAAI,IAAI,OAAS,SACf,SAEF,IAAM,KAAO,MAAM,GACnB,SAAS,KACP,GAAG,MAAM,UAAY,YAAY,IAAI,IAAI,MAAQ,IAAI,SAAW,OAAO,GAAG,GAC5E,CACF,CACI,SAAS,OAAS,GACpB,IAAI,KAAK,wCAAyC,QAAQ,CAE9D"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/internal/build/source-maps/index.ts"],"sourcesContent":["import { unlink } from \"node:fs/promises\";\n\nimport { log } from \"../../logger.js\";\nimport type { SourceMapFile } from \"./discover.js\";\n\nexport async function cleanupSourceMaps(files: SourceMapFile[]): Promise<void> {\n \n \n \n \n \n const results = await Promise.allSettled(files.map((f) => unlink(f.absolute)));\n const failures: string[] = [];\n for (const [i, r] of results.entries()) {\n if (r.status !== \"rejected\") {\n continue;\n }\n const err = r.reason as NodeJS.ErrnoException;\n if (err.code === \"ENOENT\") {\n continue;\n }\n const file = files[i];\n failures.push(\n `${file?.absolute ?? \"<unknown>\"}: ${err.code ?? err.message ?? String(err)}`\n );\n }\n if (failures.length > 0) {\n log.warn(\"Source-map cleanup left files on disk\", failures);\n }\n}\n\nexport { uploadSourceMaps } from \"./upload.js\";\n"],"mappings":"kHAKA,eAAsB,kBAAkB,MAAuC,CAM7E,IAAM,QAAU,MAAM,QAAQ,WAAW,MAAM,IAAK,GAAM,OAAO,EAAE,QAAQ,CAAC,CAAC,EACvE,SAAqB,CAAC,EAC5B,IAAK,GAAM,CAAC,EAAG,KAAM,QAAQ,QAAQ,EAAG,CACtC,GAAI,EAAE,SAAW,WACf,SAEF,IAAM,IAAM,EAAE,OACd,GAAI,IAAI,OAAS,SACf,SAEF,IAAM,KAAO,MAAM,GACnB,SAAS,KACP,GAAG,MAAM,UAAY,YAAY,IAAI,IAAI,MAAQ,IAAI,SAAW,OAAO,GAAG,GAC5E,CACF,CACI,SAAS,OAAS,GACpB,IAAI,KAAK,wCAAyC,QAAQ,CAE9D"}
@@ -1 +1 @@
1
- {"version":3,"file":"upload.d.mts","names":[],"sources":["../../../../src/internal/build/source-maps/upload.ts"],"mappings":";;;;;;UAciB,YAAA;EACf,SAAA;EACA,UAAU;AAAA;AAAA,UAGF,YAAA;EACR,MAAA;EACA,OAAA,EAAS,eAAA;EACT,UAAA,EAAY,eAAA;EACZ,UAAA,EAAY,UAAA;EACZ,WAAA,EAAa,WAAA;AAAA;;;;;;;;;;;;;;iBAgBO,gBAAA,CAAA;EACpB,MAAA;EACA,OAAA;EACA,UAAA;EACA,UAAA;EACA;AAAA,GACC,YAAA,GAAe,OAAA,CAAQ,YAAA;AAAA,UAoEhB,iBAAA;EACR,iBAAA;EACA,QAAA;EACA,OAAA;EACA,eAAA;AAAA;AAAA,iBAyBc,eAAA,CAAgB,OAAA,WAAkB,iBAAiB"}
1
+ {"version":3,"file":"upload.d.mts","names":[],"sources":["../../../../src/internal/build/source-maps/upload.ts"],"mappings":";;;;;;UAciB,YAAA;EACf,SAAA;EACA,UAAU;AAAA;AAAA,UAGF,YAAA;EACR,MAAA;EACA,OAAA,EAAS,eAAA;EACT,UAAA,EAAY,eAAA;EACZ,UAAA,EAAY,UAAA;EACZ,WAAA,EAAa,WAAA;AAAA;;;;;;;;;;;;;;iBAgBO,gBAAA;EACpB,MAAA;EACA,OAAA;EACA,UAAA;EACA,UAAA;EACA;AAAA,GACC,YAAA,GAAe,OAAA,CAAQ,YAAA;AAAA,UAoEhB,iBAAA;EACR,iBAAA;EACA,QAAA;EACA,OAAA;EACA,eAAA;AAAA;AAAA,iBAyBc,eAAA,CAAgB,OAAA,WAAkB,iBAAiB"}
@@ -1 +1 @@
1
- {"version":3,"file":"upload.mjs","names":[],"sources":["../../../../src/internal/build/source-maps/upload.ts"],"sourcesContent":["import type { HTTPClient } from \"@interfere/sdk\";\nimport type { ReleaseSlug } from \"@interfere/types/releases/slug\";\nimport type {\n CompleteSourceMapsRequest,\n CompleteSourceMapsResponse,\n ManifestBundler,\n SignSourceMapsRequest,\n SignSourceMapsResponse,\n} from \"@interfere/types/data/source-maps\";\n\nimport type { DiscoveryResult } from \"./discover.js\";\n\nconst PUT_CONCURRENCY = 12;\n\nexport interface UploadResult {\n fileCount: number;\n totalBytes: number;\n}\n\ninterface UploadParams {\n apiUrl: string;\n bundler: ManifestBundler;\n discovered: DiscoveryResult;\n httpClient: HTTPClient;\n releaseSlug: ReleaseSlug;\n}\n\n/**\n * Drives the three-step presigned-URL flow:\n *\n * 1. POST `/v1/releases/:slug/source-maps/sign` with per-file metadata.\n * 2. PUT each file's content directly to R2 in parallel (concurrency\n * bounded so a build with 1000+ chunks doesn't open 1000 sockets).\n * 3. POST `/v1/releases/:slug/source-maps/complete` to materialize the\n * manifest and update the release row.\n *\n * The collector never sees source-map bytes — uploads scale with R2\n * limits, not Cloudflare worker / load-balancer body-size caps. The\n * legacy multipart endpoint is still wired for older SDKs.\n */\nexport async function uploadSourceMaps({\n apiUrl,\n bundler,\n discovered,\n httpClient,\n releaseSlug,\n}: UploadParams): Promise<UploadResult> {\n const filesByPath = new Map(discovered.files.map((f) => [f.path, f]));\n\n const signRequest = {\n files: discovered.files.map((file) => ({\n path: file.path,\n sizeBytes: byteLengthOf(file.content),\n ...extractRichness(file.content),\n })),\n } satisfies SignSourceMapsRequest;\n\n const signResponse = await postJson<SignSourceMapsResponse>(\n httpClient,\n new URL(\n `/v1/releases/${encodeURIComponent(releaseSlug)}/source-maps/sign`,\n apiUrl\n ),\n signRequest\n );\n\n let totalBytes = 0;\n\n await mapWithConcurrency(\n signResponse.uploads,\n PUT_CONCURRENCY,\n async (upload) => {\n const file = filesByPath.get(upload.path);\n if (!file) {\n throw new Error(\n `Sign response referenced unknown path \"${upload.path}\"`\n );\n }\n\n totalBytes += byteLengthOf(file.content);\n await putToR2(upload.presignedUrl, file.content);\n }\n );\n\n const completeRequest = {\n files: discovered.files.map((file) => ({\n path: file.path,\n hash: file.hash,\n debugId: file.debugId,\n chunkUrl: file.chunkUrl,\n })),\n sourceFileCount: discovered.sourceFileCount,\n bundler,\n } satisfies CompleteSourceMapsRequest;\n\n const completeResponse = await postJson<CompleteSourceMapsResponse>(\n httpClient,\n new URL(\n `/v1/releases/${encodeURIComponent(releaseSlug)}/source-maps/complete`,\n apiUrl\n ),\n completeRequest\n );\n\n return {\n fileCount: completeResponse.fileCount,\n totalBytes,\n };\n}\n\nfunction byteLengthOf(content: string) {\n return new TextEncoder().encode(content).byteLength;\n}\n\ninterface SourceMapRichness {\n hasSourcesContent: boolean;\n hasNames: boolean;\n hasFile: boolean;\n mappingsPresent: boolean;\n}\n\nconst EMPTY_RICHNESS: SourceMapRichness = {\n hasSourcesContent: false,\n hasNames: false,\n hasFile: false,\n mappingsPresent: false,\n};\n\nfunction leafRichness(map: Record<string, unknown>): SourceMapRichness {\n const sourcesContent = map[\"sourcesContent\"];\n const names = map[\"names\"];\n const file = map[\"file\"];\n const mappings = map[\"mappings\"];\n\n return {\n hasSourcesContent:\n Array.isArray(sourcesContent) && sourcesContent.length > 0,\n hasNames: Array.isArray(names) && names.length > 0,\n hasFile: typeof file === \"string\" && file.length > 0,\n mappingsPresent: typeof mappings === \"string\" && mappings.length > 0,\n };\n}\n\nexport function extractRichness(content: string): SourceMapRichness {\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch {\n return EMPTY_RICHNESS;\n }\n\n if (!(parsed && typeof parsed === \"object\")) {\n return EMPTY_RICHNESS;\n }\n\n const top = parsed as Record<string, unknown>;\n const sections = top[\"sections\"];\n\n if (!Array.isArray(sections)) {\n return leafRichness(top);\n }\n\n const merged: SourceMapRichness = {\n hasSourcesContent: false,\n hasNames: false,\n hasFile: typeof top[\"file\"] === \"string\" && (top[\"file\"] as string).length > 0,\n mappingsPresent: false,\n };\n\n for (const section of sections) {\n const inner = (section as { map?: unknown } | null)?.map;\n if (!(inner && typeof inner === \"object\")) {\n continue;\n }\n const r = leafRichness(inner as Record<string, unknown>);\n merged.hasSourcesContent ||= r.hasSourcesContent;\n merged.hasNames ||= r.hasNames;\n merged.mappingsPresent ||= r.mappingsPresent;\n }\n\n return merged;\n}\n\nasync function postJson<T>(\n httpClient: HTTPClient,\n url: URL,\n body: unknown\n): Promise<T> {\n const response = await httpClient.request(\n new Request(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n },\n body: JSON.stringify(body),\n })\n );\n\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n throw new Error(\n `Source-map API request failed: POST ${url.pathname} -> ${response.status} ${detail}`\n );\n }\n\n return (await response.json()) as T;\n}\n\nasync function putToR2(presignedUrl: string, content: string) {\n const response = await fetch(presignedUrl, {\n method: \"PUT\",\n headers: { \"content-type\": \"application/json\" },\n body: content,\n });\n\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n throw new Error(\n `Release metadata file upload failed: ${response.status} ${detail}`\n );\n }\n}\n\nasync function mapWithConcurrency<T>(\n items: T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<void>\n): Promise<void> {\n let cursor = 0;\n\n async function worker() {\n while (true) {\n const i = cursor++;\n if (i >= items.length) {\n return;\n }\n const item = items[i];\n // Index is bounded by `items.length` above; the assertion is\n // narrowing for noUncheckedIndexedAccess, not a runtime check.\n if (item === undefined) {\n return;\n }\n await fn(item, i);\n }\n }\n\n await Promise.all(\n Array.from({ length: Math.min(concurrency, items.length) }, () => worker())\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"upload.mjs","names":[],"sources":["../../../../src/internal/build/source-maps/upload.ts"],"sourcesContent":["import type { HTTPClient } from \"@interfere/sdk\";\nimport type { ReleaseSlug } from \"@interfere/types/releases/slug\";\nimport type {\n CompleteSourceMapsRequest,\n CompleteSourceMapsResponse,\n ManifestBundler,\n SignSourceMapsRequest,\n SignSourceMapsResponse,\n} from \"@interfere/types/data/source-maps\";\n\nimport type { DiscoveryResult } from \"./discover.js\";\n\nconst PUT_CONCURRENCY = 12;\n\nexport interface UploadResult {\n fileCount: number;\n totalBytes: number;\n}\n\ninterface UploadParams {\n apiUrl: string;\n bundler: ManifestBundler;\n discovered: DiscoveryResult;\n httpClient: HTTPClient;\n releaseSlug: ReleaseSlug;\n}\n\n/**\n * Drives the three-step presigned-URL flow:\n *\n * 1. POST `/v1/releases/:slug/source-maps/sign` with per-file metadata.\n * 2. PUT each file's content directly to R2 in parallel (concurrency\n * bounded so a build with 1000+ chunks doesn't open 1000 sockets).\n * 3. POST `/v1/releases/:slug/source-maps/complete` to materialize the\n * manifest and update the release row.\n *\n * The collector never sees source-map bytes — uploads scale with R2\n * limits, not Cloudflare worker / load-balancer body-size caps. The\n * legacy multipart endpoint is still wired for older SDKs.\n */\nexport async function uploadSourceMaps({\n apiUrl,\n bundler,\n discovered,\n httpClient,\n releaseSlug,\n}: UploadParams): Promise<UploadResult> {\n const filesByPath = new Map(discovered.files.map((f) => [f.path, f]));\n\n const signRequest = {\n files: discovered.files.map((file) => ({\n path: file.path,\n sizeBytes: byteLengthOf(file.content),\n ...extractRichness(file.content),\n })),\n } satisfies SignSourceMapsRequest;\n\n const signResponse = await postJson<SignSourceMapsResponse>(\n httpClient,\n new URL(\n `/v1/releases/${encodeURIComponent(releaseSlug)}/source-maps/sign`,\n apiUrl\n ),\n signRequest\n );\n\n let totalBytes = 0;\n\n await mapWithConcurrency(\n signResponse.uploads,\n PUT_CONCURRENCY,\n async (upload) => {\n const file = filesByPath.get(upload.path);\n if (!file) {\n throw new Error(\n `Sign response referenced unknown path \"${upload.path}\"`\n );\n }\n\n totalBytes += byteLengthOf(file.content);\n await putToR2(upload.presignedUrl, file.content);\n }\n );\n\n const completeRequest = {\n files: discovered.files.map((file) => ({\n path: file.path,\n hash: file.hash,\n debugId: file.debugId,\n chunkUrl: file.chunkUrl,\n })),\n sourceFileCount: discovered.sourceFileCount,\n bundler,\n } satisfies CompleteSourceMapsRequest;\n\n const completeResponse = await postJson<CompleteSourceMapsResponse>(\n httpClient,\n new URL(\n `/v1/releases/${encodeURIComponent(releaseSlug)}/source-maps/complete`,\n apiUrl\n ),\n completeRequest\n );\n\n return {\n fileCount: completeResponse.fileCount,\n totalBytes,\n };\n}\n\nfunction byteLengthOf(content: string) {\n return new TextEncoder().encode(content).byteLength;\n}\n\ninterface SourceMapRichness {\n hasSourcesContent: boolean;\n hasNames: boolean;\n hasFile: boolean;\n mappingsPresent: boolean;\n}\n\nconst EMPTY_RICHNESS: SourceMapRichness = {\n hasSourcesContent: false,\n hasNames: false,\n hasFile: false,\n mappingsPresent: false,\n};\n\nfunction leafRichness(map: Record<string, unknown>): SourceMapRichness {\n const sourcesContent = map[\"sourcesContent\"];\n const names = map[\"names\"];\n const file = map[\"file\"];\n const mappings = map[\"mappings\"];\n\n return {\n hasSourcesContent:\n Array.isArray(sourcesContent) && sourcesContent.length > 0,\n hasNames: Array.isArray(names) && names.length > 0,\n hasFile: typeof file === \"string\" && file.length > 0,\n mappingsPresent: typeof mappings === \"string\" && mappings.length > 0,\n };\n}\n\nexport function extractRichness(content: string): SourceMapRichness {\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch {\n return EMPTY_RICHNESS;\n }\n\n if (!(parsed && typeof parsed === \"object\")) {\n return EMPTY_RICHNESS;\n }\n\n const top = parsed as Record<string, unknown>;\n const sections = top[\"sections\"];\n\n if (!Array.isArray(sections)) {\n return leafRichness(top);\n }\n\n const merged: SourceMapRichness = {\n hasSourcesContent: false,\n hasNames: false,\n hasFile: typeof top[\"file\"] === \"string\" && (top[\"file\"] as string).length > 0,\n mappingsPresent: false,\n };\n\n for (const section of sections) {\n const inner = (section as { map?: unknown } | null)?.map;\n if (!(inner && typeof inner === \"object\")) {\n continue;\n }\n const r = leafRichness(inner as Record<string, unknown>);\n merged.hasSourcesContent ||= r.hasSourcesContent;\n merged.hasNames ||= r.hasNames;\n merged.mappingsPresent ||= r.mappingsPresent;\n }\n\n return merged;\n}\n\nasync function postJson<T>(\n httpClient: HTTPClient,\n url: URL,\n body: unknown\n): Promise<T> {\n const response = await httpClient.request(\n new Request(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n },\n body: JSON.stringify(body),\n })\n );\n\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n throw new Error(\n `Source-map API request failed: POST ${url.pathname} -> ${response.status} ${detail}`\n );\n }\n\n return (await response.json()) as T;\n}\n\nasync function putToR2(presignedUrl: string, content: string) {\n const response = await fetch(presignedUrl, {\n method: \"PUT\",\n headers: { \"content-type\": \"application/json\" },\n body: content,\n });\n\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n throw new Error(\n `Release metadata file upload failed: ${response.status} ${detail}`\n );\n }\n}\n\nasync function mapWithConcurrency<T>(\n items: T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<void>\n): Promise<void> {\n let cursor = 0;\n\n async function worker() {\n while (true) {\n const i = cursor++;\n if (i >= items.length) {\n return;\n }\n const item = items[i];\n \n \n if (item === undefined) {\n return;\n }\n await fn(item, i);\n }\n }\n\n await Promise.all(\n Array.from({ length: Math.min(concurrency, items.length) }, () => worker())\n );\n}\n"],"mappings":"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"}
@@ -1,11 +1,9 @@
1
- import { Env } from "@interfere/types/sdk/runtime";
2
-
3
1
  //#region src/internal/env.d.ts
4
2
  interface InterfereEnv {
5
3
  readonly apiKey: string | null;
6
4
  readonly apiUrl: string;
7
5
  readonly nextRuntime: string | null;
8
- readonly nodeEnvironment: Env;
6
+ readonly nodeEnvironment: string;
9
7
  readonly publicKey: string | null;
10
8
  readonly release: {
11
9
  readonly sourceId: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.mts","names":[],"sources":["../../src/internal/env.ts"],"mappings":";;;UAKiB,YAAA;EAAA,SACN,MAAA;EAAA,SACA,MAAA;EAAA,SACA,WAAA;EAAA,SACA,eAAA,EAAiB,GAAG;EAAA,SACpB,SAAA;EAAA,SACA,OAAA;IAAA,SACE,QAAA;IAAA,SACA,aAAA;EAAA;AAAA;;;;;;AAAa;AAa1B;;;iBAAgB,iBAAA,CAAA;AAAA,iBAOA,gBAAA,CAAA,GAAoB,YAAY"}
1
+ {"version":3,"file":"env.d.mts","names":[],"sources":["../../src/internal/env.ts"],"mappings":";UAIiB,YAAA;EAAA,SACN,MAAA;EAAA,SACA,MAAA;EAAA,SACA,WAAA;EAAA,SACA,eAAA;EAAA,SACA,SAAA;EAAA,SACA,OAAA;IAAA,SACE,QAAA;IAAA,SACA,aAAA;EAAA;AAAA;;;;AAAa;AAa1B;;;;AAAiC;iBAAjB,iBAAA;AAAA,iBAOA,gBAAA,IAAoB,YAAY"}
@@ -1 +1 @@
1
- import{API_URL}from"@interfere/constants/api";import{parseEnvValue}from"@interfere/types/sdk/env";import{normalizeEnv}from"@interfere/types/sdk/runtime";function isEnabledOnServer(){return process.env.NODE_ENV===`production`?!0:!!process.env.NEXT_PUBLIC_INTERFERE_FORCE_ENABLE}function readInterfereEnv(){let nodeEnvironment=normalizeEnv(process.env.NODE_ENV);return{apiKey:parseEnvValue(process.env.INTERFERE_API_KEY),apiUrl:parseEnvValue(process.env.INTERFERE_API_URL)??API_URL,nextRuntime:parseEnvValue(process.env.NEXT_RUNTIME),nodeEnvironment,publicKey:parseEnvValue(process.env.INTERFERE_PUBLIC_KEY),release:{sourceId:parseEnvValue(process.env.NEXT_PUBLIC_INTERFERE_BUILD_ID),destinationId:parseEnvValue(process.env.NEXT_PUBLIC_INTERFERE_RELEASE_ID)}}}export{isEnabledOnServer,readInterfereEnv};
1
+ import{parseEnvValue}from"@interfere/types/sdk/env";import{API_URL}from"@interfere/constants/api";import{resolveEnvironment}from"@interfere/types/sdk/runtime";function isEnabledOnServer(){return process.env.NODE_ENV===`production`?!0:!!process.env.NEXT_PUBLIC_INTERFERE_FORCE_ENABLE}function readInterfereEnv(){let nodeEnvironment=resolveEnvironment({env:process.env});return{apiKey:parseEnvValue(process.env.INTERFERE_API_KEY),apiUrl:parseEnvValue(process.env.INTERFERE_API_URL)??API_URL,nextRuntime:parseEnvValue(process.env.NEXT_RUNTIME),nodeEnvironment,publicKey:parseEnvValue(process.env.INTERFERE_PUBLIC_KEY),release:{sourceId:parseEnvValue(process.env.NEXT_PUBLIC_INTERFERE_BUILD_ID),destinationId:parseEnvValue(process.env.NEXT_PUBLIC_INTERFERE_RELEASE_ID)}}}export{isEnabledOnServer,readInterfereEnv};
@@ -1 +1 @@
1
- {"version":3,"file":"env.mjs","names":[],"sources":["../../src/internal/env.ts"],"sourcesContent":["import { API_URL } from \"@interfere/constants/api\";\nimport { parseEnvValue } from \"@interfere/types/sdk/env\";\nimport type { Env } from \"@interfere/types/sdk/runtime\";\nimport { normalizeEnv } from \"@interfere/types/sdk/runtime\";\n\nexport interface InterfereEnv {\n readonly apiKey: string | null;\n readonly apiUrl: string;\n readonly nextRuntime: string | null;\n readonly nodeEnvironment: Env;\n readonly publicKey: string | null;\n readonly release: {\n readonly sourceId: string | null;\n readonly destinationId: string | null;\n };\n}\n\n/**\n * Server-side gate for the proxy route handler, the `captureError` /\n * `onRequestError` helpers, and the remote-config fetcher. Distinct\n * from the browser-side `isEnabledByEnvironment` (in\n * `@interfere/react/internal/kernel`) because the server has different\n * env conventions: `NEXT_PUBLIC_INTERFERE_FORCE_ENABLE` is the dev\n * opt-in, and there's no \"unknown runtime, default enabled\" case the\n * way the browser SDK has for plain-browser / Vite hosts.\n */\nexport function isEnabledOnServer(): boolean {\n if (process.env[\"NODE_ENV\"] === \"production\") {\n return true;\n }\n return !!process.env[\"NEXT_PUBLIC_INTERFERE_FORCE_ENABLE\"];\n}\n\nexport function readInterfereEnv(): InterfereEnv {\n const nodeEnvironment = normalizeEnv(process.env[\"NODE_ENV\"]);\n\n return {\n apiKey: parseEnvValue(process.env[\"INTERFERE_API_KEY\"]),\n apiUrl: parseEnvValue(process.env[\"INTERFERE_API_URL\"]) ?? API_URL,\n nextRuntime: parseEnvValue(process.env[\"NEXT_RUNTIME\"]),\n nodeEnvironment,\n publicKey: parseEnvValue(process.env[\"INTERFERE_PUBLIC_KEY\"]),\n release: {\n sourceId: parseEnvValue(process.env[\"NEXT_PUBLIC_INTERFERE_BUILD_ID\"]),\n destinationId: parseEnvValue(\n process.env[\"NEXT_PUBLIC_INTERFERE_RELEASE_ID\"]\n ),\n },\n };\n}\n"],"mappings":"yJA0BA,SAAgB,mBAA6B,CAI3C,OAHI,QAAQ,IAAI,WAAgB,aACvB,GAEF,CAAC,CAAC,QAAQ,IAAI,kCACvB,CAEA,SAAgB,kBAAiC,CAC/C,IAAM,gBAAkB,aAAa,QAAQ,IAAI,QAAW,EAE5D,MAAO,CACL,OAAQ,cAAc,QAAQ,IAAI,iBAAoB,EACtD,OAAQ,cAAc,QAAQ,IAAI,iBAAoB,GAAK,QAC3D,YAAa,cAAc,QAAQ,IAAI,YAAe,EACtD,gBACA,UAAW,cAAc,QAAQ,IAAI,oBAAuB,EAC5D,QAAS,CACP,SAAU,cAAc,QAAQ,IAAI,8BAAiC,EACrE,cAAe,cACb,QAAQ,IAAI,gCACd,CACF,CACF,CACF"}
1
+ {"version":3,"file":"env.mjs","names":[],"sources":["../../src/internal/env.ts"],"sourcesContent":["import { API_URL } from \"@interfere/constants/api\";\nimport { parseEnvValue } from \"@interfere/types/sdk/env\";\nimport { resolveEnvironment } from \"@interfere/types/sdk/runtime\";\n\nexport interface InterfereEnv {\n readonly apiKey: string | null;\n readonly apiUrl: string;\n readonly nextRuntime: string | null;\n readonly nodeEnvironment: string;\n readonly publicKey: string | null;\n readonly release: {\n readonly sourceId: string | null;\n readonly destinationId: string | null;\n };\n}\n\n/**\n * Server-side gate for the proxy route handler, the `captureError` /\n * `onRequestError` helpers, and the remote-config fetcher. Distinct\n * from the browser-side `isEnabledByEnvironment` (in\n * `@interfere/react/internal/kernel`) because the server has different\n * env conventions: `NEXT_PUBLIC_INTERFERE_FORCE_ENABLE` is the dev\n * opt-in, and there's no \"unknown runtime, default enabled\" case the\n * way the browser SDK has for plain-browser / Vite hosts.\n */\nexport function isEnabledOnServer(): boolean {\n if (process.env[\"NODE_ENV\"] === \"production\") {\n return true;\n }\n return !!process.env[\"NEXT_PUBLIC_INTERFERE_FORCE_ENABLE\"];\n}\n\nexport function readInterfereEnv(): InterfereEnv {\n const nodeEnvironment = resolveEnvironment({ env: process.env });\n\n return {\n apiKey: parseEnvValue(process.env[\"INTERFERE_API_KEY\"]),\n apiUrl: parseEnvValue(process.env[\"INTERFERE_API_URL\"]) ?? API_URL,\n nextRuntime: parseEnvValue(process.env[\"NEXT_RUNTIME\"]),\n nodeEnvironment,\n publicKey: parseEnvValue(process.env[\"INTERFERE_PUBLIC_KEY\"]),\n release: {\n sourceId: parseEnvValue(process.env[\"NEXT_PUBLIC_INTERFERE_BUILD_ID\"]),\n destinationId: parseEnvValue(\n process.env[\"NEXT_PUBLIC_INTERFERE_RELEASE_ID\"]\n ),\n },\n };\n}\n"],"mappings":"+JAyBA,SAAgB,mBAA6B,CAI3C,OAHI,QAAQ,IAAI,WAAgB,aACvB,GAEF,CAAC,CAAC,QAAQ,IAAI,kCACvB,CAEA,SAAgB,kBAAiC,CAC/C,IAAM,gBAAkB,mBAAmB,CAAE,IAAK,QAAQ,GAAI,CAAC,EAE/D,MAAO,CACL,OAAQ,cAAc,QAAQ,IAAI,iBAAoB,EACtD,OAAQ,cAAc,QAAQ,IAAI,iBAAoB,GAAK,QAC3D,YAAa,cAAc,QAAQ,IAAI,YAAe,EACtD,gBACA,UAAW,cAAc,QAAQ,IAAI,oBAAuB,EAC5D,QAAS,CACP,SAAU,cAAc,QAAQ,IAAI,8BAAiC,EACrE,cAAe,cACb,QAAQ,IAAI,gCACd,CACF,CACF,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"release-slug.d.mts","names":[],"sources":["../../src/internal/release-slug.ts"],"mappings":";;;;;AAwBA;;;;AAAgC;AAOhC;;;;;;;;iBAPgB,gBAAA,CAAA;AAAA,iBAOA,kBAAA,CAAA;EACd,SAAA;EACA,IAAA,EAAM,WAAW;AAAA"}
1
+ {"version":3,"file":"release-slug.d.mts","names":[],"sources":["../../src/internal/release-slug.ts"],"mappings":";;;;;AAwBA;;;;AAAgC;AAOhC;;;;;;;;iBAPgB,gBAAA;AAAA,iBAOA,kBAAA;EACd,SAAA;EACA,IAAA,EAAM,WAAW;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"handle-get.d.mts","names":[],"sources":["../../../src/internal/route/handle-get.ts"],"mappings":";;AAqCA;;;;;;;;;;;;iBAAsB,SAAA,CAAU,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,QAAA"}
1
+ {"version":3,"file":"handle-get.d.mts","names":[],"sources":["../../../src/internal/route/handle-get.ts"],"mappings":";;AAiCA;;;;;;;;;;;;iBAAsB,SAAA,CAAU,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,QAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"handle-get.mjs","names":[],"sources":["../../../src/internal/route/handle-get.ts"],"sourcesContent":["import { SW_SCRIPT } from \"@interfere/react/sw\";\n\nimport { log } from \"../logger.js\";\nimport {\n extractSubPath,\n formatProxyError,\n forwardToCollector,\n hasPublicKeyCredential,\n notConfiguredResponse,\n resolveAuthenticatedEnv,\n} from \"./proxy.js\";\n\nconst SW_HEADERS: Record<string, string> = {\n \"content-type\": \"application/javascript; charset=utf-8\",\n // Allow the SW to control the whole origin, not just `/api/interfere/`.\n // The SDK still scopes its `register()` call narrowly; this header just\n // unlocks the option without re-issuing a new SW URL.\n \"service-worker-allowed\": \"/\",\n // Customers' edges (Vercel, Cloudflare) cache aggressively by default;\n // a 1-hour TTL balances \"pick up SW updates within a deploy cycle\"\n // against \"don't refetch on every navigation\".\n \"cache-control\": \"public, max-age=3600\",\n};\n\n/**\n * Generic GET proxy. SDK 10.x routes `GET /v2/config` (and any future\n * GET-based endpoints) through here; SDK 9.x clients still hit\n * `GET /v1/config` and get the same delegated response on the collector\n * side.\n *\n * `/sw` is special-cased to serve the SDK's bundled service-worker\n * script directly out of the customer's Next.js process. `SW_SCRIPT`\n * is a string export emitted at @interfere/react build time\n * (`scripts/build-sw.ts`); on Node runtime the customer resolves it\n * via `node_modules`, on Edge runtime the customer's Next.js bundler\n * inlines it into the Edge worker. Either way, no `fs.readFileSync`.\n */\nexport async function handleGet(request: Request): Promise<Response> {\n const subPath = extractSubPath(request);\n\n if (subPath === \"/sw\") {\n return new Response(SW_SCRIPT, { status: 200, headers: SW_HEADERS });\n }\n\n const env = resolveAuthenticatedEnv();\n if (!hasPublicKeyCredential(request, env.publicKey)) {\n return notConfiguredResponse();\n }\n try {\n return await forwardToCollector(request, env, subPath);\n } catch (error) {\n const detail = formatProxyError(error);\n log.error(`Proxy ${request.method} ${subPath} failed`, detail.lines);\n return Response.json(\n { code: \"INTERFERE_PROXY_ERROR\", message: detail.message },\n { status: 502 }\n );\n }\n}\n"],"mappings":"2NAYA,MAAM,WAAqC,CACzC,eAAgB,wCAIhB,yBAA0B,IAI1B,gBAAiB,sBACnB,EAeA,eAAsB,UAAU,QAAqC,CACnE,IAAM,QAAU,eAAe,OAAO,EAEtC,GAAI,UAAY,MACd,OAAO,IAAI,SAAS,UAAW,CAAE,OAAQ,IAAK,QAAS,UAAW,CAAC,EAGrE,IAAM,IAAM,wBAAwB,EACpC,GAAI,CAAC,uBAAuB,QAAS,IAAI,SAAS,EAChD,OAAO,sBAAsB,EAE/B,GAAI,CACF,OAAO,MAAM,mBAAmB,QAAS,IAAK,OAAO,CACvD,OAAS,MAAO,CACd,IAAM,OAAS,iBAAiB,KAAK,EAErC,OADA,IAAI,MAAM,SAAS,QAAQ,OAAO,GAAG,QAAQ,SAAU,OAAO,KAAK,EAC5D,SAAS,KACd,CAAE,KAAM,wBAAyB,QAAS,OAAO,OAAQ,EACzD,CAAE,OAAQ,GAAI,CAChB,CACF,CACF"}
1
+ {"version":3,"file":"handle-get.mjs","names":[],"sources":["../../../src/internal/route/handle-get.ts"],"sourcesContent":["import { SW_SCRIPT } from \"@interfere/react/sw\";\n\nimport { log } from \"../logger.js\";\nimport {\n extractSubPath,\n formatProxyError,\n forwardToCollector,\n hasPublicKeyCredential,\n notConfiguredResponse,\n resolveAuthenticatedEnv,\n} from \"./proxy.js\";\n\nconst SW_HEADERS: Record<string, string> = {\n \"content-type\": \"application/javascript; charset=utf-8\",\n\n \"service-worker-allowed\": \"/\",\n\n \"cache-control\": \"public, max-age=3600\",\n};\n\n/**\n * Generic GET proxy. SDK 10.x routes `GET /v2/config` (and any future\n * GET-based endpoints) through here; SDK 9.x clients still hit\n * `GET /v1/config` and get the same delegated response on the collector\n * side.\n *\n * `/sw` is special-cased to serve the SDK's bundled service-worker\n * script directly out of the customer's Next.js process. `SW_SCRIPT`\n * is a string export emitted at @interfere/react build time\n * (`scripts/build-sw.ts`); on Node runtime the customer resolves it\n * via `node_modules`, on Edge runtime the customer's Next.js bundler\n * inlines it into the Edge worker. Either way, no `fs.readFileSync`.\n */\nexport async function handleGet(request: Request): Promise<Response> {\n const subPath = extractSubPath(request);\n\n if (subPath === \"/sw\") {\n return new Response(SW_SCRIPT, { status: 200, headers: SW_HEADERS });\n }\n\n const env = resolveAuthenticatedEnv();\n if (!hasPublicKeyCredential(request, env.publicKey)) {\n return notConfiguredResponse();\n }\n try {\n return await forwardToCollector(request, env, subPath);\n } catch (error) {\n const detail = formatProxyError(error);\n log.error(`Proxy ${request.method} ${subPath} failed`, detail.lines);\n return Response.json(\n { code: \"INTERFERE_PROXY_ERROR\", message: detail.message },\n { status: 502 }\n );\n }\n}\n"],"mappings":"2NAYA,MAAM,WAAqC,CACzC,eAAgB,wCAEhB,yBAA0B,IAE1B,gBAAiB,sBACnB,EAeA,eAAsB,UAAU,QAAqC,CACnE,IAAM,QAAU,eAAe,OAAO,EAEtC,GAAI,UAAY,MACd,OAAO,IAAI,SAAS,UAAW,CAAE,OAAQ,IAAK,QAAS,UAAW,CAAC,EAGrE,IAAM,IAAM,wBAAwB,EACpC,GAAI,CAAC,uBAAuB,QAAS,IAAI,SAAS,EAChD,OAAO,sBAAsB,EAE/B,GAAI,CACF,OAAO,MAAM,mBAAmB,QAAS,IAAK,OAAO,CACvD,OAAS,MAAO,CACd,IAAM,OAAS,iBAAiB,KAAK,EAErC,OADA,IAAI,MAAM,SAAS,QAAQ,OAAO,GAAG,QAAQ,SAAU,OAAO,KAAK,EAC5D,SAAS,KACd,CAAE,KAAM,wBAAyB,QAAS,OAAO,OAAQ,EACzD,CAAE,OAAQ,GAAI,CAChB,CACF,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.d.mts","names":[],"sources":["../../../src/internal/route/proxy.ts"],"mappings":";;;;;AAuDA;;;;;iBAAgB,gBAAA,CAAiB,OAAA,EAAS,OAAA,GAAU,OAAO;AAAA,UAW1C,gBAAA;EACf,MAAA;EACA,SAAA;EACA,OAAA,EAAS,YAAY;AAAA;AAAA,iBAGP,uBAAA,CAAA,GAA2B,gBAAgB;;iBAU3C,sBAAA,CACd,OAAA,EAAS,OAAO,EAChB,YAAA;AAAA,iBAQc,qBAAA,CAAA,GAAyB,QAAQ;AAAA,iBAajC,cAAA,CAAe,OAAgB,EAAP,OAAO;;;AApCxB;AAGvB;;;;AAA2D;AAU3D;;;iBAgDgB,aAAA,CAAc,OAAgB,EAAP,OAAO;AAAA,iBAI9B,gBAAA,CAAiB,KAAA;EAC/B,OAAA;EACA,KAAA;AAAA;AAAA,iBAqDoB,kBAAA,CACpB,OAAA,EAAS,OAAA,EACT,GAAA,EAAK,gBAAA,EACL,OAAA,WACC,OAAA,CAAQ,QAAA"}
1
+ {"version":3,"file":"proxy.d.mts","names":[],"sources":["../../../src/internal/route/proxy.ts"],"mappings":";;;;;AAiCA;;;;;iBAAgB,gBAAA,CAAiB,OAAA,EAAS,OAAA,GAAU,OAAO;AAAA,UAW1C,gBAAA;EACf,MAAA;EACA,SAAA;EACA,OAAA,EAAS,YAAY;AAAA;AAAA,iBAGP,uBAAA,IAA2B,gBAAgB;;iBAU3C,sBAAA,CACd,OAAA,EAAS,OAAO,EAChB,YAAA;AAAA,iBAQc,qBAAA,IAAyB,QAAQ;AAAA,iBAajC,cAAA,CAAe,OAAgB,EAAP,OAAO;;;AApCxB;AAGvB;;;;AAA2D;AAU3D;;;iBAgDgB,aAAA,CAAc,OAAgB,EAAP,OAAO;AAAA,iBAI9B,gBAAA,CAAiB,KAAA;EAC/B,OAAA;EACA,KAAA;AAAA;AAAA,iBAqDoB,kBAAA,CACpB,OAAA,EAAS,OAAA,EACT,GAAA,EAAK,gBAAA,EACL,OAAA,WACC,OAAA,CAAQ,QAAA"}
@@ -1 +1 @@
1
- import{readInterfereEnv}from"../env.mjs";import{log}from"../logger.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{resolveRoutePrefix}from"@interfere/constants/route-prefix";import{extractCountryHeader}from"@interfere/types/sdk/geo";const STRIPPED_AUTH_HEADERS=[`authorization`,`x-api-key`,`x-interfere-api-key`],STRIPPED_TRANSPORT_HEADERS=[`host`,`content-length`,`connection`,`keep-alive`,`transfer-encoding`,`te`,`trailer`,`upgrade`,`proxy-authenticate`,`proxy-authorization`];function forwardedHeaders(request){let headers=new Headers(request.headers);for(let name of STRIPPED_AUTH_HEADERS)headers.delete(name);for(let name of STRIPPED_TRANSPORT_HEADERS)headers.delete(name);return headers}function resolveAuthenticatedEnv(){let env=readInterfereEnv();return{apiUrl:env.apiUrl,publicKey:env.publicKey,release:env.release}}function hasPublicKeyCredential(request,envPublicKey){return envPublicKey?!0:new URL(request.url).searchParams.has(`pk`)}function notConfiguredResponse(){return log.warn(`Not configured`,[`INTERFERE_PUBLIC_KEY is not set and the request has no ?pk= query param. The proxy route will return 503.`]),Response.json({code:`INTERFERE_NOT_CONFIGURED`,message:`INTERFERE_PUBLIC_KEY is required.`},{status:503})}function extractSubPath(request){let prefix=resolveRoutePrefix(),url=new URL(request.url),idx=url.pathname.indexOf(prefix);if(idx===-1)return`/`;let remainder=url.pathname.slice(idx+prefix.length);return remainder===``?`/`:remainder.startsWith(`/`)?remainder:`/${remainder}`}function extractSearch(request){return new URL(request.url).search}function formatProxyError(error){if(!(error instanceof Error))return{message:String(error),lines:[String(error)]};let lines=[`${error.name}: ${error.message}`];if(`cause`in error&&error.cause){let cause=error.cause instanceof Error?error.cause.message:String(error.cause);lines.push(`cause: ${cause}`)}return`code`in error&&typeof error.code==`string`&&lines.push(`code: ${error.code}`),{message:`${error.name}: ${error.message}`,lines}}function buildUpstreamUrl(apiUrl,subPath,request,publicKey){let incoming=new URL(request.url),upstream=new URL(`${apiUrl}${subPath}`);for(let[key,value]of incoming.searchParams)upstream.searchParams.append(key,value);return publicKey&&!upstream.searchParams.has(`pk`)&&upstream.searchParams.set(`pk`,publicKey),upstream.toString()}async function forwardToCollector(request,env,subPath){let url=buildUpstreamUrl(env.apiUrl,subPath,request,env.publicKey),body=request.method!==`GET`&&request.method!==`HEAD`?await request.text():void 0,headers=forwardedHeaders(request);headers.has(`content-type`)||headers.set(`content-type`,`application/json`);let country=extractCountryHeader(request);country!==null&&headers.set(`x-country-code`,country);let upstream=await fetch(url,{...omitUndefined({body}),method:request.method,headers,signal:AbortSignal.timeout(1e4)});if(!upstream.ok){let text=await upstream.text().catch(()=>``);return upstream.status>=500?(log.error(`Upstream ${upstream.status} for ${request.method} ${subPath}`,[text]),Response.json({code:`INTERFERE_UPSTREAM_ERROR`,message:text},{status:upstream.status})):upstream.status>=400?(log.warn(`Upstream ${upstream.status} for ${request.method} ${subPath}`,[text]),Response.json({code:`INTERFERE_UPSTREAM_WARNING`,message:text},{status:upstream.status})):(log.error(`Upstream ${upstream.status} for ${request.method} ${subPath}`,[text]),Response.json({code:`INTERFERE_UPSTREAM_ERROR`,message:text},{status:upstream.status}))}return new Response(upstream.body,{status:upstream.status,headers:{"content-type":upstream.headers.get(`content-type`)??`application/json`}})}export{extractSearch,extractSubPath,formatProxyError,forwardToCollector,forwardedHeaders,hasPublicKeyCredential,notConfiguredResponse,resolveAuthenticatedEnv};
1
+ import{readInterfereEnv}from"../env.mjs";import{log}from"../logger.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{resolveRoutePrefix}from"@interfere/constants/route-prefix";import{extractCountryHeader}from"@interfere/types/sdk/geo";const STRIPPED_AUTH_HEADERS=[`authorization`,`x-api-key`,`x-interfere-api-key`],STRIPPED_TRANSPORT_HEADERS=[`host`,`content-length`,`connection`,`keep-alive`,`transfer-encoding`,`te`,`trailer`,`upgrade`,`proxy-authenticate`,`proxy-authorization`];function forwardedHeaders(request){let headers=new Headers(request.headers);for(let name of STRIPPED_AUTH_HEADERS)headers.delete(name);for(let name of STRIPPED_TRANSPORT_HEADERS)headers.delete(name);return headers}function resolveAuthenticatedEnv(){let env=readInterfereEnv();return{apiUrl:env.apiUrl,publicKey:env.publicKey,release:env.release}}function hasPublicKeyCredential(request,envPublicKey){return envPublicKey?!0:new URL(request.url).searchParams.has(`pk`)}function notConfiguredResponse(){return log.warn(`Not configured`,[`INTERFERE_PUBLIC_KEY is not set and the request has no ?pk= query param. The proxy route will return 503.`]),Response.json({code:`INTERFERE_NOT_CONFIGURED`,message:`INTERFERE_PUBLIC_KEY is required.`},{status:503})}function extractSubPath(request){let prefix=resolveRoutePrefix(),url=new URL(request.url),idx=url.pathname.indexOf(prefix);if(idx===-1)return`/`;let remainder=url.pathname.slice(idx+prefix.length);return remainder===``?`/`:remainder.startsWith(`/`)?remainder:`/${remainder}`}function extractSearch(request){return new URL(request.url).search}function formatProxyError(error){if(!(error instanceof Error))return{message:String(error),lines:[String(error)]};let lines=[`${error.name}: ${error.message}`];if(`cause`in error&&error.cause){let cause=error.cause instanceof Error?error.cause.message:String(error.cause);lines.push(`cause: ${cause}`)}return`code`in error&&typeof error.code==`string`&&lines.push(`code: ${error.code}`),{message:`${error.name}: ${error.message}`,lines}}function buildUpstreamUrl(apiUrl,subPath,request,publicKey){let incoming=new URL(request.url),upstream=new URL(`${apiUrl}${subPath}`);for(let[key,value]of incoming.searchParams)upstream.searchParams.append(key,value);return publicKey&&!upstream.searchParams.has(`pk`)&&upstream.searchParams.set(`pk`,publicKey),upstream.toString()}async function forwardToCollector(request,env,subPath){let url=buildUpstreamUrl(env.apiUrl,subPath,request,env.publicKey),body=request.method!==`GET`&&request.method!==`HEAD`?new Uint8Array(await request.arrayBuffer()):void 0,headers=forwardedHeaders(request);headers.has(`content-type`)||headers.set(`content-type`,`application/json`);let country=extractCountryHeader(request);country!==null&&headers.set(`x-country-code`,country);let upstream=await fetch(url,{...omitUndefined({body}),method:request.method,headers,signal:AbortSignal.timeout(1e4)});if(!upstream.ok){let text=await upstream.text().catch(()=>``);return upstream.status>=500?(log.error(`Upstream ${upstream.status} for ${request.method} ${subPath}`,[text]),Response.json({code:`INTERFERE_UPSTREAM_ERROR`,message:text},{status:upstream.status})):upstream.status>=400?(log.warn(`Upstream ${upstream.status} for ${request.method} ${subPath}`,[text]),Response.json({code:`INTERFERE_UPSTREAM_WARNING`,message:text},{status:upstream.status})):(log.error(`Upstream ${upstream.status} for ${request.method} ${subPath}`,[text]),Response.json({code:`INTERFERE_UPSTREAM_ERROR`,message:text},{status:upstream.status}))}return new Response(upstream.body,{status:upstream.status,headers:{"content-type":upstream.headers.get(`content-type`)??`application/json`}})}export{extractSearch,extractSubPath,formatProxyError,forwardToCollector,forwardedHeaders,hasPublicKeyCredential,notConfiguredResponse,resolveAuthenticatedEnv};
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.mjs","names":[],"sources":["../../../src/internal/route/proxy.ts"],"sourcesContent":["import { resolveRoutePrefix } from \"@interfere/constants/route-prefix\";\nimport { omitUndefined } from \"@interfere/helpers/omit-undefined\";\nimport { extractCountryHeader } from \"@interfere/types/sdk/geo\";\n\nimport { type InterfereEnv, readInterfereEnv } from \"../env.js\";\nimport { log } from \"../logger.js\";\n\n// Never forward customer API keys from browser-originated ingestion requests.\nconst STRIPPED_AUTH_HEADERS = [\n \"authorization\",\n \"x-api-key\",\n \"x-interfere-api-key\",\n] as const;\n\n// Headers tied to the *inbound* HTTP transaction. These must not leak onto\n// the *outbound* `fetch` because the runtime computes them fresh for the new\n// body / connection, and forwarding them stale either silently corrupts the\n// request or hangs it until a timeout fires.\n//\n// Specifically `content-length`: `handleIngest` parses the incoming JSON,\n// injects build/release metadata, then re-`JSON.stringify`s the result. The\n// new body almost always has a different byte length to the original, so\n// forwarding the inbound `content-length` makes undici stall writing the\n// body until our 10s `AbortSignal.timeout` fires with\n// `Request body length does not match content-length header`. This was the\n// 2026-05-01 prod symptom (Vercel proxy 502s on `POST /v1/ingest` from\n// dashboard + homepage).\n//\n// `host` would route the upstream fetch back at the proxy hostname. fetch\n// sets it correctly from the URL automatically.\n//\n// The remainder are RFC 7230 §6.1 hop-by-hop headers — meaningful only on\n// the connection that received them. There's no canonical stdlib list for\n// any of this; every proxy in the Node ecosystem hand-rolls the same set\n// (`http-proxy`, Cloudflare Workers docs, Hono, undici's ProxyAgent, …).\nconst STRIPPED_TRANSPORT_HEADERS = [\n \"host\",\n \"content-length\",\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n \"te\",\n \"trailer\",\n \"upgrade\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n] as const;\n\n/**\n * Clone the inbound request's headers, removing entries that should never\n * cross the proxy boundary. Returns a mutable `Headers` so callers can\n * `.set()`/`.delete()` upstream-specific values without having to merge\n * record literals (which lose case-insensitivity and create the precedence\n * footguns that masked the 2026-05-01 incident in code review).\n */\nexport function forwardedHeaders(request: Request): Headers {\n const headers = new Headers(request.headers);\n for (const name of STRIPPED_AUTH_HEADERS) {\n headers.delete(name);\n }\n for (const name of STRIPPED_TRANSPORT_HEADERS) {\n headers.delete(name);\n }\n return headers;\n}\n\nexport interface AuthenticatedEnv {\n apiUrl: string;\n publicKey: string | null;\n release: InterfereEnv[\"release\"];\n}\n\nexport function resolveAuthenticatedEnv(): AuthenticatedEnv {\n const env = readInterfereEnv();\n return {\n apiUrl: env.apiUrl,\n publicKey: env.publicKey,\n release: env.release,\n };\n}\n\n/** True when the proxy can attach a publishable key from env or the request. */\nexport function hasPublicKeyCredential(\n request: Request,\n envPublicKey: string | null\n): boolean {\n if (envPublicKey) {\n return true;\n }\n return new URL(request.url).searchParams.has(\"pk\");\n}\n\nexport function notConfiguredResponse(): Response {\n log.warn(\"Not configured\", [\n \"INTERFERE_PUBLIC_KEY is not set and the request has no ?pk= query param. The proxy route will return 503.\",\n ]);\n return Response.json(\n {\n code: \"INTERFERE_NOT_CONFIGURED\",\n message: \"INTERFERE_PUBLIC_KEY is required.\",\n },\n { status: 503 }\n );\n}\n\nexport function extractSubPath(request: Request): string {\n const prefix = resolveRoutePrefix();\n const url = new URL(request.url);\n const idx = url.pathname.indexOf(prefix);\n if (idx === -1) {\n return \"/\";\n }\n const remainder = url.pathname.slice(idx + prefix.length);\n if (remainder === \"\") {\n return \"/\";\n }\n return remainder.startsWith(\"/\") ? remainder : `/${remainder}`;\n}\n\n/**\n * Preserve any query string verbatim when forwarding upstream.\n *\n * The browser SDK uses `?_pv=…` and `?pk=…` (and `?_pt=…` in direct\n * mode) as a `navigator.sendBeacon` fallback when `visibilitychange→\n * hidden` aborts the keepalive fetch path. The collector's\n * `otlpProducerGuard` and `surfacePkAuth` accept those values verbatim\n * — but only if the proxy actually forwards them. Passing\n * `request.url`'s `search` through (`\"\"` when absent, leading `?`\n * when present) keeps the rewrite transparent.\n */\nexport function extractSearch(request: Request): string {\n return new URL(request.url).search;\n}\n\nexport function formatProxyError(error: unknown): {\n message: string;\n lines: string[];\n} {\n if (!(error instanceof Error)) {\n return { message: String(error), lines: [String(error)] };\n }\n\n const lines = [`${error.name}: ${error.message}`];\n\n if (\"cause\" in error && error.cause) {\n const cause =\n error.cause instanceof Error ? error.cause.message : String(error.cause);\n lines.push(`cause: ${cause}`);\n }\n\n if (\n \"code\" in error &&\n typeof (error as NodeJS.ErrnoException).code === \"string\"\n ) {\n lines.push(`code: ${(error as NodeJS.ErrnoException).code}`);\n }\n\n return { message: `${error.name}: ${error.message}`, lines };\n}\n\n/**\n * Append `?pk=` to the outbound URL from the server env. Proxy mode\n * means the browser never sees `INTERFERE_PUBLIC_KEY` (it's not\n * `NEXT_PUBLIC_`-exposed), so the proxy is the only place the\n * credential can attach to the request. Preserves any existing query\n * params (e.g. `_pv=` debug flag). If the inbound request already\n * carries `?pk=`, that value wins — a misconfigured server-env pk\n * shouldn't clobber an explicit one from the SDK.\n */\nfunction buildUpstreamUrl(\n apiUrl: string,\n subPath: string,\n request: Request,\n publicKey: string | null\n): string {\n const incoming = new URL(request.url);\n const upstream = new URL(`${apiUrl}${subPath}`);\n\n for (const [key, value] of incoming.searchParams) {\n upstream.searchParams.append(key, value);\n }\n\n if (publicKey && !upstream.searchParams.has(\"pk\")) {\n upstream.searchParams.set(\"pk\", publicKey);\n }\n\n return upstream.toString();\n}\n\nexport async function forwardToCollector(\n request: Request,\n env: AuthenticatedEnv,\n subPath: string\n): Promise<Response> {\n const url = buildUpstreamUrl(env.apiUrl, subPath, request, env.publicKey);\n const hasBody = request.method !== \"GET\" && request.method !== \"HEAD\";\n const body = hasBody ? await request.text() : undefined;\n\n const headers = forwardedHeaders(request);\n // Default to JSON only when the inbound request didn't pin a content-type\n // — non-`/v1/ingest` paths relay the body bytes verbatim, so honour\n // whatever the caller said it was sending.\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n const country = extractCountryHeader(request);\n if (country !== null) {\n headers.set(\"x-country-code\", country);\n }\n\n const upstream = await fetch(url, {\n ...omitUndefined({ body }),\n method: request.method,\n headers,\n signal: AbortSignal.timeout(10_000),\n });\n\n if (!upstream.ok) {\n const text = await upstream.text().catch(() => \"\");\n\n if (upstream.status >= 500) {\n log.error(\n `Upstream ${upstream.status} for ${request.method} ${subPath}`,\n [text]\n );\n return Response.json(\n { code: \"INTERFERE_UPSTREAM_ERROR\", message: text },\n { status: upstream.status }\n );\n }\n if (upstream.status >= 400) {\n log.warn(`Upstream ${upstream.status} for ${request.method} ${subPath}`, [\n text,\n ]);\n return Response.json(\n { code: \"INTERFERE_UPSTREAM_WARNING\", message: text },\n { status: upstream.status }\n );\n }\n\n log.error(`Upstream ${upstream.status} for ${request.method} ${subPath}`, [\n text,\n ]);\n return Response.json(\n { code: \"INTERFERE_UPSTREAM_ERROR\", message: text },\n { status: upstream.status }\n );\n }\n\n return new Response(upstream.body, {\n status: upstream.status,\n headers: {\n \"content-type\":\n upstream.headers.get(\"content-type\") ?? \"application/json\",\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"proxy.mjs","names":[],"sources":["../../../src/internal/route/proxy.ts"],"sourcesContent":["import { resolveRoutePrefix } from \"@interfere/constants/route-prefix\";\nimport { omitUndefined } from \"@interfere/helpers/omit-undefined\";\nimport { extractCountryHeader } from \"@interfere/types/sdk/geo\";\n\nimport { type InterfereEnv, readInterfereEnv } from \"../env.js\";\nimport { log } from \"../logger.js\";\n\nconst STRIPPED_AUTH_HEADERS = [\n \"authorization\",\n \"x-api-key\",\n \"x-interfere-api-key\",\n] as const;\n\nconst STRIPPED_TRANSPORT_HEADERS = [\n \"host\",\n \"content-length\",\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n \"te\",\n \"trailer\",\n \"upgrade\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n] as const;\n\n/**\n * Clone the inbound request's headers, removing entries that should never\n * cross the proxy boundary. Returns a mutable `Headers` so callers can\n * `.set()`/`.delete()` upstream-specific values without having to merge\n * record literals (which lose case-insensitivity and create the precedence\n * footguns that masked the 2026-05-01 incident in code review).\n */\nexport function forwardedHeaders(request: Request): Headers {\n const headers = new Headers(request.headers);\n for (const name of STRIPPED_AUTH_HEADERS) {\n headers.delete(name);\n }\n for (const name of STRIPPED_TRANSPORT_HEADERS) {\n headers.delete(name);\n }\n return headers;\n}\n\nexport interface AuthenticatedEnv {\n apiUrl: string;\n publicKey: string | null;\n release: InterfereEnv[\"release\"];\n}\n\nexport function resolveAuthenticatedEnv(): AuthenticatedEnv {\n const env = readInterfereEnv();\n return {\n apiUrl: env.apiUrl,\n publicKey: env.publicKey,\n release: env.release,\n };\n}\n\n/** True when the proxy can attach a publishable key from env or the request. */\nexport function hasPublicKeyCredential(\n request: Request,\n envPublicKey: string | null\n): boolean {\n if (envPublicKey) {\n return true;\n }\n return new URL(request.url).searchParams.has(\"pk\");\n}\n\nexport function notConfiguredResponse(): Response {\n log.warn(\"Not configured\", [\n \"INTERFERE_PUBLIC_KEY is not set and the request has no ?pk= query param. The proxy route will return 503.\",\n ]);\n return Response.json(\n {\n code: \"INTERFERE_NOT_CONFIGURED\",\n message: \"INTERFERE_PUBLIC_KEY is required.\",\n },\n { status: 503 }\n );\n}\n\nexport function extractSubPath(request: Request): string {\n const prefix = resolveRoutePrefix();\n const url = new URL(request.url);\n const idx = url.pathname.indexOf(prefix);\n if (idx === -1) {\n return \"/\";\n }\n const remainder = url.pathname.slice(idx + prefix.length);\n if (remainder === \"\") {\n return \"/\";\n }\n return remainder.startsWith(\"/\") ? remainder : `/${remainder}`;\n}\n\n/**\n * Preserve any query string verbatim when forwarding upstream.\n *\n * The browser SDK uses `?_pv=…` and `?pk=…` (and `?_pt=…` in direct\n * mode) as a `navigator.sendBeacon` fallback when `visibilitychange→\n * hidden` aborts the keepalive fetch path. The collector's\n * `otlpProducerGuard` and `surfacePkAuth` accept those values verbatim\n * — but only if the proxy actually forwards them. Passing\n * `request.url`'s `search` through (`\"\"` when absent, leading `?`\n * when present) keeps the rewrite transparent.\n */\nexport function extractSearch(request: Request): string {\n return new URL(request.url).search;\n}\n\nexport function formatProxyError(error: unknown): {\n message: string;\n lines: string[];\n} {\n if (!(error instanceof Error)) {\n return { message: String(error), lines: [String(error)] };\n }\n\n const lines = [`${error.name}: ${error.message}`];\n\n if (\"cause\" in error && error.cause) {\n const cause =\n error.cause instanceof Error ? error.cause.message : String(error.cause);\n lines.push(`cause: ${cause}`);\n }\n\n if (\n \"code\" in error &&\n typeof (error as NodeJS.ErrnoException).code === \"string\"\n ) {\n lines.push(`code: ${(error as NodeJS.ErrnoException).code}`);\n }\n\n return { message: `${error.name}: ${error.message}`, lines };\n}\n\n/**\n * Append `?pk=` to the outbound URL from the server env. Proxy mode\n * means the browser never sees `INTERFERE_PUBLIC_KEY` (it's not\n * `NEXT_PUBLIC_`-exposed), so the proxy is the only place the\n * credential can attach to the request. Preserves any existing query\n * params (e.g. `_pv=` debug flag). If the inbound request already\n * carries `?pk=`, that value wins — a misconfigured server-env pk\n * shouldn't clobber an explicit one from the SDK.\n */\nfunction buildUpstreamUrl(\n apiUrl: string,\n subPath: string,\n request: Request,\n publicKey: string | null\n): string {\n const incoming = new URL(request.url);\n const upstream = new URL(`${apiUrl}${subPath}`);\n\n for (const [key, value] of incoming.searchParams) {\n upstream.searchParams.append(key, value);\n }\n\n if (publicKey && !upstream.searchParams.has(\"pk\")) {\n upstream.searchParams.set(\"pk\", publicKey);\n }\n\n return upstream.toString();\n}\n\nexport async function forwardToCollector(\n request: Request,\n env: AuthenticatedEnv,\n subPath: string\n): Promise<Response> {\n const url = buildUpstreamUrl(env.apiUrl, subPath, request, env.publicKey);\n const hasBody = request.method !== \"GET\" && request.method !== \"HEAD\";\n const body = hasBody\n ? new Uint8Array(await request.arrayBuffer())\n : undefined;\n\n const headers = forwardedHeaders(request);\n\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n const country = extractCountryHeader(request);\n if (country !== null) {\n headers.set(\"x-country-code\", country);\n }\n\n const upstream = await fetch(url, {\n ...omitUndefined({ body }),\n method: request.method,\n headers,\n signal: AbortSignal.timeout(10_000),\n });\n\n if (!upstream.ok) {\n const text = await upstream.text().catch(() => \"\");\n\n if (upstream.status >= 500) {\n log.error(\n `Upstream ${upstream.status} for ${request.method} ${subPath}`,\n [text]\n );\n return Response.json(\n { code: \"INTERFERE_UPSTREAM_ERROR\", message: text },\n { status: upstream.status }\n );\n }\n if (upstream.status >= 400) {\n log.warn(`Upstream ${upstream.status} for ${request.method} ${subPath}`, [\n text,\n ]);\n return Response.json(\n { code: \"INTERFERE_UPSTREAM_WARNING\", message: text },\n { status: upstream.status }\n );\n }\n\n log.error(`Upstream ${upstream.status} for ${request.method} ${subPath}`, [\n text,\n ]);\n return Response.json(\n { code: \"INTERFERE_UPSTREAM_ERROR\", message: text },\n { status: upstream.status }\n );\n }\n\n return new Response(upstream.body, {\n status: upstream.status,\n headers: {\n \"content-type\":\n upstream.headers.get(\"content-type\") ?? \"application/json\",\n },\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"capture.d.mts","names":[],"sources":["../../../src/internal/server/capture.ts"],"mappings":";;;iBA+GgB,YAAA,CACd,KAAA,WACA,QAAA,YACA,OAAA,GAAU,mBAAmB;AAAA,iBAwBf,cAAA,CACd,KAAA,EAAO,KAAA;EAAU,MAAA;AAAA,GACjB,QAAA,WACA,OAAA,EAAS,qBAAqB"}
1
+ {"version":3,"file":"capture.d.mts","names":[],"sources":["../../../src/internal/server/capture.ts"],"mappings":";;;iBAsGgB,YAAA,CACd,KAAA,WACA,QAAA,YACA,OAAA,GAAU,mBAAmB;AAAA,iBAwBf,cAAA,CACd,KAAA,EAAO,KAAA;EAAU,MAAA;AAAA,GACjB,QAAA,WACA,OAAA,EAAS,qBAAqB"}
@@ -1 +1 @@
1
- {"version":3,"file":"capture.mjs","names":[],"sources":["../../../src/internal/server/capture.ts"],"sourcesContent":["import {\n isNonErrorException,\n MECHANISM_TYPE,\n type NonErrorException,\n toException,\n} from \"@interfere/types/sdk/errors\";\nimport type { ErrorMechanism } from \"@interfere/types/sdk/plugins/payload/errors\";\n\nimport { SpanKind, SpanStatusCode, trace } from \"@opentelemetry/api\";\n\nimport { isEnabledOnServer } from \"../env.js\";\nimport { isPluginEnabled } from \"./remote-config.js\";\nimport type { CaptureErrorContext, OnRequestErrorContext } from \"./types.js\";\n\nconst TRACER_NAME = \"@interfere/next/server\";\n\nconst ON_REQUEST_ERROR_MECHANISM: ErrorMechanism = {\n type: MECHANISM_TYPE.nextjs.onRequestError,\n handled: false,\n synthetic: false,\n};\n\nconst DEFAULT_CAPTURE_ERROR_MECHANISM: ErrorMechanism = {\n type: MECHANISM_TYPE.nextjs.captureError,\n handled: true,\n};\n\n// Per-process dedupe so a thrown route-handler error captured by both the\n// caller's `try { } catch { captureError(e) }` and Next's own\n// `onRequestError` hook only emits one exception event. WeakSet so GC'd\n// errors don't pin memory.\nconst seen = new WeakSet<Error>();\n\ninterface RecordExceptionInput {\n context?: CaptureErrorContext | undefined;\n error: unknown;\n mechanism: ErrorMechanism;\n}\n\nfunction buildAttrs(\n value: Error | NonErrorException,\n mechanism: ErrorMechanism,\n context: CaptureErrorContext | undefined\n): Record<string, string> {\n const attrs: Record<string, string> = {\n \"interfere.exception.mechanism\": mechanism.type,\n \"interfere.exception.handled\": String(mechanism.handled),\n };\n const digest = context?.nextjs?.errorDigest;\n if (digest) {\n attrs[\"interfere.error.digest\"] = digest;\n }\n if (context?.nextjs?.requestMethod) {\n attrs[\"http.request.method\"] = context.nextjs.requestMethod;\n }\n if (context?.nextjs?.requestPath) {\n attrs[\"url.path\"] = context.nextjs.requestPath;\n }\n if (isNonErrorException(value)) {\n attrs[\"exception.type\"] = value.type;\n attrs[\"exception.message\"] = value.value;\n attrs[\"interfere.exception.kind\"] = \"non-error\";\n return attrs;\n }\n attrs[\"exception.type\"] = value.name;\n attrs[\"exception.message\"] = value.message;\n if (value.stack) {\n attrs[\"exception.stacktrace\"] = value.stack;\n }\n return attrs;\n}\n\nfunction recordException({\n error,\n mechanism,\n context,\n}: RecordExceptionInput): void {\n const value = toException(error);\n const attrs = buildAttrs(value, mechanism, context);\n\n // Prefer the active span (route handler / Next request span). Fall\n // back to a fresh single-event span when no active span is available\n // — Next sometimes calls `onRequestError` from background contexts\n // (background revalidation, failed prerender) where the request span\n // is no longer current.\n const tracer = trace.getTracer(TRACER_NAME);\n const active = trace.getActiveSpan();\n if (active) {\n active.addEvent(\"exception\", attrs);\n if (!mechanism.handled) {\n active.setStatus({\n code: SpanStatusCode.ERROR,\n message: isNonErrorException(value) ? value.value : value.message,\n });\n }\n return;\n }\n\n const span = tracer.startSpan(\"interfere.captureError\", {\n kind: SpanKind.INTERNAL,\n });\n span.addEvent(\"exception\", attrs);\n if (!mechanism.handled) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: isNonErrorException(value) ? value.value : value.message,\n });\n }\n span.end();\n}\n\nexport function captureError(\n error: unknown,\n _request?: unknown,\n context?: CaptureErrorContext\n): void {\n if (!isEnabledOnServer()) {\n return;\n }\n\n if (!isPluginEnabled(\"errors\")) {\n return;\n }\n\n if (error instanceof Error) {\n if (seen.has(error)) {\n return;\n }\n seen.add(error);\n }\n\n recordException({\n error,\n mechanism: context?.mechanism ?? DEFAULT_CAPTURE_ERROR_MECHANISM,\n context,\n });\n}\n\nexport function onRequestError(\n error: Error & { digest?: string },\n _request: unknown,\n context: OnRequestErrorContext\n): void {\n if (seen.has(error)) {\n return;\n }\n seen.add(error);\n\n if (!isEnabledOnServer()) {\n return;\n }\n if (!isPluginEnabled(\"errors\")) {\n return;\n }\n\n recordException({\n error,\n mechanism: ON_REQUEST_ERROR_MECHANISM,\n context: {\n mechanism: ON_REQUEST_ERROR_MECHANISM,\n nextjs: {\n ...context,\n ...(error.digest ? { errorDigest: error.digest } : {}),\n },\n },\n });\n}\n"],"mappings":"iPAcA,MAEM,2BAA6C,CACjD,KAAM,eAAe,OAAO,eAC5B,QAAS,GACT,UAAW,EACb,EAEM,gCAAkD,CACtD,KAAM,eAAe,OAAO,aAC5B,QAAS,EACX,EAMM,KAAO,IAAI,QAQjB,SAAS,WACP,MACA,UACA,QACwB,CACxB,IAAM,MAAgC,CACpC,gCAAiC,UAAU,KAC3C,8BAA+B,OAAO,UAAU,OAAO,CACzD,EACM,OAAS,SAAS,QAAQ,YAqBhC,OApBI,SACF,MAAM,0BAA4B,QAEhC,SAAS,QAAQ,gBACnB,MAAM,uBAAyB,QAAQ,OAAO,eAE5C,SAAS,QAAQ,cACnB,MAAM,YAAc,QAAQ,OAAO,aAEjC,oBAAoB,KAAK,GAC3B,MAAM,kBAAoB,MAAM,KAChC,MAAM,qBAAuB,MAAM,MACnC,MAAM,4BAA8B,YAC7B,QAET,MAAM,kBAAoB,MAAM,KAChC,MAAM,qBAAuB,MAAM,QAC/B,MAAM,QACR,MAAM,wBAA0B,MAAM,OAEjC,MACT,CAEA,SAAS,gBAAgB,CACvB,MACA,UACA,SAC6B,CAC7B,IAAM,MAAQ,YAAY,KAAK,EACzB,MAAQ,WAAW,MAAO,UAAW,OAAO,EAO5C,OAAS,MAAM,UAAU,wBAAW,EACpC,OAAS,MAAM,cAAc,EACnC,GAAI,OAAQ,CACV,OAAO,SAAS,YAAa,KAAK,EAC7B,UAAU,SACb,OAAO,UAAU,CACf,KAAM,eAAe,MACrB,QAAS,oBAAoB,KAAK,EAAI,MAAM,MAAQ,MAAM,OAC5D,CAAC,EAEH,MACF,CAEA,IAAM,KAAO,OAAO,UAAU,yBAA0B,CACtD,KAAM,SAAS,QACjB,CAAC,EACD,KAAK,SAAS,YAAa,KAAK,EAC3B,UAAU,SACb,KAAK,UAAU,CACb,KAAM,eAAe,MACrB,QAAS,oBAAoB,KAAK,EAAI,MAAM,MAAQ,MAAM,OAC5D,CAAC,EAEH,KAAK,IAAI,CACX,CAEA,SAAgB,aACd,MACA,SACA,QACM,CACD,qBAAkB,GAIlB,gBAAgB,QAAQ,EAI7B,IAAI,iBAAiB,MAAO,CAC1B,GAAI,KAAK,IAAI,KAAK,EAChB,OAEF,KAAK,IAAI,KAAK,CAChB,CAEA,gBAAgB,CACd,MACA,UAAW,SAAS,WAAa,gCACjC,OACF,CAAC,CAND,CAOF,CAEA,SAAgB,eACd,MACA,SACA,QACM,CACF,KAAK,IAAI,KAAK,IAGlB,KAAK,IAAI,KAAK,EAET,kBAAkB,GAGlB,gBAAgB,QAAQ,GAI7B,gBAAgB,CACd,MACA,UAAW,2BACX,QAAS,CACP,UAAW,2BACX,OAAQ,CACN,GAAG,QACH,GAAI,MAAM,OAAS,CAAE,YAAa,MAAM,MAAO,EAAI,CAAC,CACtD,CACF,CACF,CAAC,EACH"}
1
+ {"version":3,"file":"capture.mjs","names":[],"sources":["../../../src/internal/server/capture.ts"],"sourcesContent":["import {\n isNonErrorException,\n MECHANISM_TYPE,\n type NonErrorException,\n toException,\n} from \"@interfere/types/sdk/errors\";\nimport type { ErrorMechanism } from \"@interfere/types/sdk/plugins/payload/errors\";\n\nimport { SpanKind, SpanStatusCode, trace } from \"@opentelemetry/api\";\n\nimport { isEnabledOnServer } from \"../env.js\";\nimport { isPluginEnabled } from \"./remote-config.js\";\nimport type { CaptureErrorContext, OnRequestErrorContext } from \"./types.js\";\n\nconst TRACER_NAME = \"@interfere/next/server\";\n\nconst ON_REQUEST_ERROR_MECHANISM: ErrorMechanism = {\n type: MECHANISM_TYPE.nextjs.onRequestError,\n handled: false,\n synthetic: false,\n};\n\nconst DEFAULT_CAPTURE_ERROR_MECHANISM: ErrorMechanism = {\n type: MECHANISM_TYPE.nextjs.captureError,\n handled: true,\n};\n\nconst seen = new WeakSet<Error>();\n\ninterface RecordExceptionInput {\n context?: CaptureErrorContext | undefined;\n error: unknown;\n mechanism: ErrorMechanism;\n}\n\nfunction buildAttrs(\n value: Error | NonErrorException,\n mechanism: ErrorMechanism,\n context: CaptureErrorContext | undefined\n): Record<string, string> {\n const attrs: Record<string, string> = {\n \"interfere.exception.mechanism\": mechanism.type,\n \"interfere.exception.handled\": String(mechanism.handled),\n };\n const digest = context?.nextjs?.errorDigest;\n if (digest) {\n attrs[\"interfere.error.digest\"] = digest;\n }\n if (context?.nextjs?.requestMethod) {\n attrs[\"http.request.method\"] = context.nextjs.requestMethod;\n }\n if (context?.nextjs?.requestPath) {\n attrs[\"url.path\"] = context.nextjs.requestPath;\n }\n if (isNonErrorException(value)) {\n attrs[\"exception.type\"] = value.type;\n attrs[\"exception.message\"] = value.value;\n attrs[\"interfere.exception.kind\"] = \"non-error\";\n return attrs;\n }\n attrs[\"exception.type\"] = value.name;\n attrs[\"exception.message\"] = value.message;\n if (value.stack) {\n attrs[\"exception.stacktrace\"] = value.stack;\n }\n return attrs;\n}\n\nfunction recordException({\n error,\n mechanism,\n context,\n}: RecordExceptionInput): void {\n const value = toException(error);\n const attrs = buildAttrs(value, mechanism, context);\n\n const tracer = trace.getTracer(TRACER_NAME);\n const active = trace.getActiveSpan();\n if (active) {\n active.addEvent(\"exception\", attrs);\n if (!mechanism.handled) {\n active.setStatus({\n code: SpanStatusCode.ERROR,\n message: isNonErrorException(value) ? value.value : value.message,\n });\n }\n return;\n }\n\n const span = tracer.startSpan(\"interfere.captureError\", {\n kind: SpanKind.INTERNAL,\n });\n span.addEvent(\"exception\", attrs);\n if (!mechanism.handled) {\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: isNonErrorException(value) ? value.value : value.message,\n });\n }\n span.end();\n}\n\nexport function captureError(\n error: unknown,\n _request?: unknown,\n context?: CaptureErrorContext\n): void {\n if (!isEnabledOnServer()) {\n return;\n }\n\n if (!isPluginEnabled(\"errors\")) {\n return;\n }\n\n if (error instanceof Error) {\n if (seen.has(error)) {\n return;\n }\n seen.add(error);\n }\n\n recordException({\n error,\n mechanism: context?.mechanism ?? DEFAULT_CAPTURE_ERROR_MECHANISM,\n context,\n });\n}\n\nexport function onRequestError(\n error: Error & { digest?: string },\n _request: unknown,\n context: OnRequestErrorContext\n): void {\n if (seen.has(error)) {\n return;\n }\n seen.add(error);\n\n if (!isEnabledOnServer()) {\n return;\n }\n if (!isPluginEnabled(\"errors\")) {\n return;\n }\n\n recordException({\n error,\n mechanism: ON_REQUEST_ERROR_MECHANISM,\n context: {\n mechanism: ON_REQUEST_ERROR_MECHANISM,\n nextjs: {\n ...context,\n ...(error.digest ? { errorDigest: error.digest } : {}),\n },\n },\n });\n}\n"],"mappings":"iPAcA,MAEM,2BAA6C,CACjD,KAAM,eAAe,OAAO,eAC5B,QAAS,GACT,UAAW,EACb,EAEM,gCAAkD,CACtD,KAAM,eAAe,OAAO,aAC5B,QAAS,EACX,EAEM,KAAO,IAAI,QAQjB,SAAS,WACP,MACA,UACA,QACwB,CACxB,IAAM,MAAgC,CACpC,gCAAiC,UAAU,KAC3C,8BAA+B,OAAO,UAAU,OAAO,CACzD,EACM,OAAS,SAAS,QAAQ,YAqBhC,OApBI,SACF,MAAM,0BAA4B,QAEhC,SAAS,QAAQ,gBACnB,MAAM,uBAAyB,QAAQ,OAAO,eAE5C,SAAS,QAAQ,cACnB,MAAM,YAAc,QAAQ,OAAO,aAEjC,oBAAoB,KAAK,GAC3B,MAAM,kBAAoB,MAAM,KAChC,MAAM,qBAAuB,MAAM,MACnC,MAAM,4BAA8B,YAC7B,QAET,MAAM,kBAAoB,MAAM,KAChC,MAAM,qBAAuB,MAAM,QAC/B,MAAM,QACR,MAAM,wBAA0B,MAAM,OAEjC,MACT,CAEA,SAAS,gBAAgB,CACvB,MACA,UACA,SAC6B,CAC7B,IAAM,MAAQ,YAAY,KAAK,EACzB,MAAQ,WAAW,MAAO,UAAW,OAAO,EAE5C,OAAS,MAAM,UAAU,wBAAW,EACpC,OAAS,MAAM,cAAc,EACnC,GAAI,OAAQ,CACV,OAAO,SAAS,YAAa,KAAK,EAC7B,UAAU,SACb,OAAO,UAAU,CACf,KAAM,eAAe,MACrB,QAAS,oBAAoB,KAAK,EAAI,MAAM,MAAQ,MAAM,OAC5D,CAAC,EAEH,MACF,CAEA,IAAM,KAAO,OAAO,UAAU,yBAA0B,CACtD,KAAM,SAAS,QACjB,CAAC,EACD,KAAK,SAAS,YAAa,KAAK,EAC3B,UAAU,SACb,KAAK,UAAU,CACb,KAAM,eAAe,MACrB,QAAS,oBAAoB,KAAK,EAAI,MAAM,MAAQ,MAAM,OAC5D,CAAC,EAEH,KAAK,IAAI,CACX,CAEA,SAAgB,aACd,MACA,SACA,QACM,CACD,qBAAkB,GAIlB,gBAAgB,QAAQ,EAI7B,IAAI,iBAAiB,MAAO,CAC1B,GAAI,KAAK,IAAI,KAAK,EAChB,OAEF,KAAK,IAAI,KAAK,CAChB,CAEA,gBAAgB,CACd,MACA,UAAW,SAAS,WAAa,gCACjC,OACF,CAAC,CAND,CAOF,CAEA,SAAgB,eACd,MACA,SACA,QACM,CACF,KAAK,IAAI,KAAK,IAGlB,KAAK,IAAI,KAAK,EAET,kBAAkB,GAGlB,gBAAgB,QAAQ,GAI7B,gBAAgB,CACd,MACA,UAAW,2BACX,QAAS,CACP,UAAW,2BACX,OAAQ,CACN,GAAG,QACH,GAAI,MAAM,OAAS,CAAE,YAAa,MAAM,MAAO,EAAI,CAAC,CACtD,CACF,CACF,CAAC,EACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"console-bridge.d.mts","names":[],"sources":["../../../src/internal/server/console-bridge.ts"],"mappings":";;AAgFA;;;;AAAmC;;;;;;;;;;iBAAnB,mBAAA,CAAA"}
1
+ {"version":3,"file":"console-bridge.d.mts","names":[],"sources":["../../../src/internal/server/console-bridge.ts"],"mappings":";;AAgFA;;;;AAAmC;;;;;;;;;;iBAAnB,mBAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"console-bridge.mjs","names":[],"sources":["../../../src/internal/server/console-bridge.ts"],"sourcesContent":["import { context, trace } from \"@opentelemetry/api\";\nimport { logs, type SeverityNumber } from \"@opentelemetry/api-logs\";\n\nconst ATTR_EXCEPTION_TYPE = \"exception.type\" as const;\nconst ATTR_EXCEPTION_MESSAGE = \"exception.message\" as const;\nconst ATTR_EXCEPTION_STACKTRACE = \"exception.stacktrace\" as const;\n\nconst CONSOLE_METHODS = [\"debug\", \"log\", \"info\", \"warn\", \"error\"] as const;\ntype ConsoleMethod = (typeof CONSOLE_METHODS)[number];\n\nconst SEVERITY: Record<ConsoleMethod, SeverityNumber> = {\n debug: 5,\n log: 9,\n info: 9,\n warn: 13,\n error: 17,\n};\n\n/**\n * Bound at first failure log; subsequent failures only re-log every Nth\n * occurrence so a misconfigured logger backend doesn't itself spam the\n * console with bridge errors at line rate.\n */\nconst BRIDGE_FAILURE_LOG_INTERVAL = 100;\n\nfunction stringify(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n if (value instanceof Error) {\n return value.stack ?? `${value.name}: ${value.message}`;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nfunction extractException(args: unknown[]): Error | undefined {\n for (const arg of args) {\n if (arg instanceof Error) {\n return arg;\n }\n }\n return;\n}\n\nfunction buildExceptionAttributes(\n args: unknown[]\n): Record<string, string> | undefined {\n const exception = extractException(args);\n if (!exception) {\n return;\n }\n const attrs: Record<string, string> = {\n [ATTR_EXCEPTION_TYPE]: exception.name,\n [ATTR_EXCEPTION_MESSAGE]: exception.message,\n };\n if (exception.stack) {\n attrs[ATTR_EXCEPTION_STACKTRACE] = exception.stack;\n }\n return attrs;\n}\n\n/**\n * Patches `console.{debug,log,info,warn,error}` to additionally emit OTel\n * `LogRecord`s alongside the original output. Every log record carries\n * the active span's context (so trace ↔ log correlation works in\n * dashboards) and any `Error` arg gets unpacked into semconv exception\n * attrs.\n *\n * Returns a disposer that restores the original `console.*` methods.\n *\n * Cycle protection: `emitting` guards against the bridge re-entering\n * itself if a logger backend itself logs to console (which would\n * recursively emit forever). Failures are bounded-rate logged via the\n * original `console.error` so a broken backend doesn't drown the\n * customer's logs in bridge-failure messages.\n */\nexport function bridgeConsoleToOtel(): () => void {\n const logger = logs.getLogger(\"@interfere/next/console\");\n let emitting = false;\n let bridgeFailures = 0;\n\n const original: Record<ConsoleMethod, (...args: unknown[]) => void> = {\n debug: console.debug,\n log: console.log,\n info: console.info,\n warn: console.warn,\n error: console.error,\n };\n\n function emitLog(\n orig: (...args: unknown[]) => void,\n severity: SeverityNumber,\n method: ConsoleMethod,\n body: string,\n activeContext: ReturnType<typeof context.active>,\n activeSpan: ReturnType<typeof trace.getSpan>,\n attributes: Record<string, string> | undefined\n ): void {\n if (emitting) {\n return;\n }\n emitting = true;\n try {\n logger.emit({\n severityNumber: severity,\n severityText: method.toUpperCase(),\n body,\n ...(activeSpan ? { context: activeContext } : {}),\n ...(attributes ? { attributes } : {}),\n });\n } catch (error) {\n bridgeFailures++;\n if (bridgeFailures % BRIDGE_FAILURE_LOG_INTERVAL === 1) {\n orig.call(\n console,\n `[interfere] console→OTel bridge emission failed (${bridgeFailures} total):`,\n error\n );\n }\n } finally {\n emitting = false;\n }\n }\n\n for (const method of CONSOLE_METHODS) {\n const orig = original[method];\n const severity = SEVERITY[method];\n console[method] = (...args: unknown[]) => {\n orig.apply(console, args);\n\n const body = args.map(stringify).join(\" \");\n const activeContext = context.active();\n const activeSpan = trace.getSpan(activeContext);\n const attributes = buildExceptionAttributes(args);\n\n // Defer emission via microtask so the synchronous return path\n // out of `console.X` doesn't include the LoggerProvider's batch\n // logic. Customer code's `console.error(...)` call should feel\n // synchronous; the OTel emission rides the next microtask.\n // `queueMicrotask` (not `setTimeout(0)`): macrotask-deferred\n // emissions risk being lost on hard process exit.\n queueMicrotask(() => {\n emitLog(\n orig,\n severity,\n method,\n body,\n activeContext,\n activeSpan,\n attributes\n );\n });\n };\n }\n\n return () => {\n for (const method of CONSOLE_METHODS) {\n console[method] = original[method];\n }\n };\n}\n"],"mappings":"wFAGA,MAIM,gBAAkB,CAAC,QAAS,MAAO,OAAQ,OAAQ,OAAO,EAG1D,SAAkD,CACtD,MAAO,EACP,IAAK,EACL,KAAM,EACN,KAAM,GACN,MAAO,EACT,EASA,SAAS,UAAU,MAAwB,CACzC,GAAI,OAAO,OAAU,SACnB,OAAO,MAET,GAAI,iBAAiB,MACnB,OAAO,MAAM,OAAS,GAAG,MAAM,KAAK,IAAI,MAAM,UAEhD,GAAI,CACF,OAAO,KAAK,UAAU,KAAK,CAC7B,MAAQ,CACN,OAAO,OAAO,KAAK,CACrB,CACF,CAEA,SAAS,iBAAiB,KAAoC,CAC5D,IAAK,IAAM,OAAO,KAChB,GAAI,eAAe,MACjB,OAAO,GAIb,CAEA,SAAS,yBACP,KACoC,CACpC,IAAM,UAAY,iBAAiB,IAAI,EACvC,GAAI,CAAC,UACH,OAEF,IAAM,MAAgC,CACnC,iBAAsB,UAAU,KAChC,oBAAyB,UAAU,OACtC,EAIA,OAHI,UAAU,QACZ,MAAM,wBAA6B,UAAU,OAExC,KACT,CAiBA,SAAgB,qBAAkC,CAChD,IAAM,OAAS,KAAK,UAAU,yBAAyB,EACnD,SAAW,GACX,eAAiB,EAEf,SAAgE,CACpE,MAAO,QAAQ,MACf,IAAK,QAAQ,IACb,KAAM,QAAQ,KACd,KAAM,QAAQ,KACd,MAAO,QAAQ,KACjB,EAEA,SAAS,QACP,KACA,SACA,OACA,KACA,cACA,WACA,WACM,CACF,aAGJ,UAAW,GACX,GAAI,CACF,OAAO,KAAK,CACV,eAAgB,SAChB,aAAc,OAAO,YAAY,EACjC,KACA,GAAI,WAAa,CAAE,QAAS,aAAc,EAAI,CAAC,EAC/C,GAAI,WAAa,CAAE,UAAW,EAAI,CAAC,CACrC,CAAC,CACH,OAAS,MAAO,CACd,iBACI,eAAiB,KAAgC,GACnD,KAAK,KACH,QACA,oDAAoD,eAAe,UACnE,KACF,CAEJ,QAAU,CACR,SAAW,EACb,CApBW,CAqBb,CAEA,IAAK,IAAM,UAAU,gBAAiB,CACpC,IAAM,KAAO,SAAS,QAChB,SAAW,SAAS,QAC1B,QAAQ,SAAW,GAAG,OAAoB,CACxC,KAAK,MAAM,QAAS,IAAI,EAExB,IAAM,KAAO,KAAK,IAAI,SAAS,EAAE,KAAK,GAAG,EACnC,cAAgB,QAAQ,OAAO,EAC/B,WAAa,MAAM,QAAQ,aAAa,EACxC,WAAa,yBAAyB,IAAI,EAQhD,mBAAqB,CACnB,QACE,KACA,SACA,OACA,KACA,cACA,WACA,UACF,CACF,CAAC,CACH,CACF,CAEA,UAAa,CACX,IAAK,IAAM,UAAU,gBACnB,QAAQ,QAAU,SAAS,OAE/B,CACF"}
1
+ {"version":3,"file":"console-bridge.mjs","names":[],"sources":["../../../src/internal/server/console-bridge.ts"],"sourcesContent":["import { context, trace } from \"@opentelemetry/api\";\nimport { logs, type SeverityNumber } from \"@opentelemetry/api-logs\";\n\nconst ATTR_EXCEPTION_TYPE = \"exception.type\" as const;\nconst ATTR_EXCEPTION_MESSAGE = \"exception.message\" as const;\nconst ATTR_EXCEPTION_STACKTRACE = \"exception.stacktrace\" as const;\n\nconst CONSOLE_METHODS = [\"debug\", \"log\", \"info\", \"warn\", \"error\"] as const;\ntype ConsoleMethod = (typeof CONSOLE_METHODS)[number];\n\nconst SEVERITY: Record<ConsoleMethod, SeverityNumber> = {\n debug: 5,\n log: 9,\n info: 9,\n warn: 13,\n error: 17,\n};\n\n/**\n * Bound at first failure log; subsequent failures only re-log every Nth\n * occurrence so a misconfigured logger backend doesn't itself spam the\n * console with bridge errors at line rate.\n */\nconst BRIDGE_FAILURE_LOG_INTERVAL = 100;\n\nfunction stringify(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n if (value instanceof Error) {\n return value.stack ?? `${value.name}: ${value.message}`;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nfunction extractException(args: unknown[]): Error | undefined {\n for (const arg of args) {\n if (arg instanceof Error) {\n return arg;\n }\n }\n return;\n}\n\nfunction buildExceptionAttributes(\n args: unknown[]\n): Record<string, string> | undefined {\n const exception = extractException(args);\n if (!exception) {\n return;\n }\n const attrs: Record<string, string> = {\n [ATTR_EXCEPTION_TYPE]: exception.name,\n [ATTR_EXCEPTION_MESSAGE]: exception.message,\n };\n if (exception.stack) {\n attrs[ATTR_EXCEPTION_STACKTRACE] = exception.stack;\n }\n return attrs;\n}\n\n/**\n * Patches `console.{debug,log,info,warn,error}` to additionally emit OTel\n * `LogRecord`s alongside the original output. Every log record carries\n * the active span's context (so trace ↔ log correlation works in\n * dashboards) and any `Error` arg gets unpacked into semconv exception\n * attrs.\n *\n * Returns a disposer that restores the original `console.*` methods.\n *\n * Cycle protection: `emitting` guards against the bridge re-entering\n * itself if a logger backend itself logs to console (which would\n * recursively emit forever). Failures are bounded-rate logged via the\n * original `console.error` so a broken backend doesn't drown the\n * customer's logs in bridge-failure messages.\n */\nexport function bridgeConsoleToOtel(): () => void {\n const logger = logs.getLogger(\"@interfere/next/console\");\n let emitting = false;\n let bridgeFailures = 0;\n\n const original: Record<ConsoleMethod, (...args: unknown[]) => void> = {\n debug: console.debug,\n log: console.log,\n info: console.info,\n warn: console.warn,\n error: console.error,\n };\n\n function emitLog(\n orig: (...args: unknown[]) => void,\n severity: SeverityNumber,\n method: ConsoleMethod,\n body: string,\n activeContext: ReturnType<typeof context.active>,\n activeSpan: ReturnType<typeof trace.getSpan>,\n attributes: Record<string, string> | undefined\n ): void {\n if (emitting) {\n return;\n }\n emitting = true;\n try {\n logger.emit({\n severityNumber: severity,\n severityText: method.toUpperCase(),\n body,\n ...(activeSpan ? { context: activeContext } : {}),\n ...(attributes ? { attributes } : {}),\n });\n } catch (error) {\n bridgeFailures++;\n if (bridgeFailures % BRIDGE_FAILURE_LOG_INTERVAL === 1) {\n orig.call(\n console,\n `[interfere] console→OTel bridge emission failed (${bridgeFailures} total):`,\n error\n );\n }\n } finally {\n emitting = false;\n }\n }\n\n for (const method of CONSOLE_METHODS) {\n const orig = original[method];\n const severity = SEVERITY[method];\n console[method] = (...args: unknown[]) => {\n orig.apply(console, args);\n\n const body = args.map(stringify).join(\" \");\n const activeContext = context.active();\n const activeSpan = trace.getSpan(activeContext);\n const attributes = buildExceptionAttributes(args);\n\n queueMicrotask(() => {\n emitLog(\n orig,\n severity,\n method,\n body,\n activeContext,\n activeSpan,\n attributes\n );\n });\n };\n }\n\n return () => {\n for (const method of CONSOLE_METHODS) {\n console[method] = original[method];\n }\n };\n}\n"],"mappings":"wFAGA,MAIM,gBAAkB,CAAC,QAAS,MAAO,OAAQ,OAAQ,OAAO,EAG1D,SAAkD,CACtD,MAAO,EACP,IAAK,EACL,KAAM,EACN,KAAM,GACN,MAAO,EACT,EASA,SAAS,UAAU,MAAwB,CACzC,GAAI,OAAO,OAAU,SACnB,OAAO,MAET,GAAI,iBAAiB,MACnB,OAAO,MAAM,OAAS,GAAG,MAAM,KAAK,IAAI,MAAM,UAEhD,GAAI,CACF,OAAO,KAAK,UAAU,KAAK,CAC7B,MAAQ,CACN,OAAO,OAAO,KAAK,CACrB,CACF,CAEA,SAAS,iBAAiB,KAAoC,CAC5D,IAAK,IAAM,OAAO,KAChB,GAAI,eAAe,MACjB,OAAO,GAIb,CAEA,SAAS,yBACP,KACoC,CACpC,IAAM,UAAY,iBAAiB,IAAI,EACvC,GAAI,CAAC,UACH,OAEF,IAAM,MAAgC,CACnC,iBAAsB,UAAU,KAChC,oBAAyB,UAAU,OACtC,EAIA,OAHI,UAAU,QACZ,MAAM,wBAA6B,UAAU,OAExC,KACT,CAiBA,SAAgB,qBAAkC,CAChD,IAAM,OAAS,KAAK,UAAU,yBAAyB,EACnD,SAAW,GACX,eAAiB,EAEf,SAAgE,CACpE,MAAO,QAAQ,MACf,IAAK,QAAQ,IACb,KAAM,QAAQ,KACd,KAAM,QAAQ,KACd,MAAO,QAAQ,KACjB,EAEA,SAAS,QACP,KACA,SACA,OACA,KACA,cACA,WACA,WACM,CACF,aAGJ,UAAW,GACX,GAAI,CACF,OAAO,KAAK,CACV,eAAgB,SAChB,aAAc,OAAO,YAAY,EACjC,KACA,GAAI,WAAa,CAAE,QAAS,aAAc,EAAI,CAAC,EAC/C,GAAI,WAAa,CAAE,UAAW,EAAI,CAAC,CACrC,CAAC,CACH,OAAS,MAAO,CACd,iBACI,eAAiB,KAAgC,GACnD,KAAK,KACH,QACA,oDAAoD,eAAe,UACnE,KACF,CAEJ,QAAU,CACR,SAAW,EACb,CApBW,CAqBb,CAEA,IAAK,IAAM,UAAU,gBAAiB,CACpC,IAAM,KAAO,SAAS,QAChB,SAAW,SAAS,QAC1B,QAAQ,SAAW,GAAG,OAAoB,CACxC,KAAK,MAAM,QAAS,IAAI,EAExB,IAAM,KAAO,KAAK,IAAI,SAAS,EAAE,KAAK,GAAG,EACnC,cAAgB,QAAQ,OAAO,EAC/B,WAAa,MAAM,QAAQ,aAAa,EACxC,WAAa,yBAAyB,IAAI,EAEhD,mBAAqB,CACnB,QACE,KACA,SACA,OACA,KACA,cACA,WACA,UACF,CACF,CAAC,CACH,CACF,CAEA,UAAa,CACX,IAAK,IAAM,UAAU,gBACnB,QAAQ,QAAU,SAAS,OAE/B,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"id-generator.d.mts","names":[],"sources":["../../../src/internal/server/id-generator.ts"],"mappings":";;;;;AAoCA;;;;;;;;;;;;;;;;AA0CwB;;;cA1CX,wBAAA,YAAoC,WAAW;EAAA,iBACzC,MAAA;EAAA,iBACA,WAAA;EAAA,iBACA,UAAA;EAAA,QACT,YAAA;EAAA,QACA,WAAA;;EAaR,eAAA,CAAA;EAQA,cAAA,CAAA;EAAA,QAQQ,eAAA;EAAA,QAQA,cAAA;AAAA"}
1
+ {"version":3,"file":"id-generator.d.mts","names":[],"sources":["../../../src/internal/server/id-generator.ts"],"mappings":";;;;;AAoCA;;;;;;;;;;;;;;;;AAuCwB;;;cAvCX,wBAAA,YAAoC,WAAW;EAAA,iBACzC,MAAA;EAAA,iBACA,WAAA;EAAA,iBACA,UAAA;EAAA,QACT,YAAA;EAAA,QACA,WAAA;;EAUR,eAAA;EAQA,cAAA;EAAA,QAQQ,eAAA;EAAA,QAQA,cAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"id-generator.mjs","names":[],"sources":["../../../src/internal/server/id-generator.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport {\n type IdGenerator,\n RandomIdGenerator,\n} from \"@opentelemetry/sdk-trace-base\";\n\nconst TRACE_ID_HEX_LEN = 32;\nconst SPAN_ID_HEX_LEN = 16;\nconst TRACE_PREFIX_HEX_LEN = 24;\nconst SPAN_PREFIX_HEX_LEN = 12;\nconst TRACE_COUNTER_HEX_LEN = TRACE_ID_HEX_LEN - TRACE_PREFIX_HEX_LEN;\nconst SPAN_COUNTER_HEX_LEN = SPAN_ID_HEX_LEN - SPAN_PREFIX_HEX_LEN;\nconst TRACE_COUNTER_MODULUS = 2 ** (TRACE_COUNTER_HEX_LEN * 4);\nconst SPAN_COUNTER_MODULUS = 2 ** (SPAN_COUNTER_HEX_LEN * 4);\n\n/**\n * IdGenerator hybrid that uses OTel's default `RandomIdGenerator` whenever\n * available and falls back to a counter-based generator inside Next 16's\n * prerender contexts.\n *\n * Why: Next 16's prerender extension throws synchronously on any\n * `Math.random()` / `crypto.getRandomValues()` call inside a Server\n * Component that hasn't first awaited Request data\n * (`next-prerender-random` / `next-prerender-crypto`). OTel's default\n * generator hits `Math.random()` on every span creation, so spans Next\n * opens around prerendered route renders blow up the build.\n *\n * The hybrid path keeps full 128-bit trace IDs / 64-bit span IDs for the\n * common case (SSR, runtime requests, fluid-compute invocations) and only\n * degrades inside prerender — where the alternative is build failure.\n *\n * Inside prerender, IDs are minted from a per-process random prefix\n * (seeded once at construction time, outside any prerender ALS frame) plus\n * a monotonic counter. Counter widths are masked to keep IDs at their\n * spec-mandated lengths even after wrap.\n */\nexport class PrerenderSafeIdGenerator implements IdGenerator {\n private readonly random = new RandomIdGenerator();\n private readonly tracePrefix: string;\n private readonly spanPrefix: string;\n private traceCounter = 0;\n private spanCounter = 0;\n\n constructor() {\n // Seeded once at `register()` time — module init runs outside any\n // prerender ALS frame, so `randomBytes` is permitted here even if\n // every later call from inside a render is not.\n const seed = randomBytes((TRACE_PREFIX_HEX_LEN + SPAN_PREFIX_HEX_LEN) / 2);\n this.tracePrefix = seed\n .subarray(0, TRACE_PREFIX_HEX_LEN / 2)\n .toString(\"hex\");\n this.spanPrefix = seed.subarray(TRACE_PREFIX_HEX_LEN / 2).toString(\"hex\");\n }\n\n generateTraceId(): string {\n try {\n return this.random.generateTraceId();\n } catch {\n return this.fallbackTraceId();\n }\n }\n\n generateSpanId(): string {\n try {\n return this.random.generateSpanId();\n } catch {\n return this.fallbackSpanId();\n }\n }\n\n private fallbackTraceId(): string {\n this.traceCounter = (this.traceCounter + 1) % TRACE_COUNTER_MODULUS;\n return (\n this.tracePrefix +\n this.traceCounter.toString(16).padStart(TRACE_COUNTER_HEX_LEN, \"0\")\n );\n }\n\n private fallbackSpanId(): string {\n this.spanCounter = (this.spanCounter + 1) % SPAN_COUNTER_MODULUS;\n return (\n this.spanPrefix +\n this.spanCounter.toString(16).padStart(SPAN_COUNTER_HEX_LEN, \"0\")\n );\n }\n}\n"],"mappings":"kGAMA,MAMM,sBAAwB,GAAM,GAC9B,qBAAuB,GAAM,GAuBnC,IAAa,yBAAb,KAA6D,CAC3D,OAA0B,IAAI,kBAC9B,YACA,WACA,aAAuB,EACvB,YAAsB,EAEtB,aAAc,CAIZ,IAAM,KAAO,YAAa,GAA8C,CAAC,EACzE,KAAK,YAAc,KAChB,SAAS,EAAG,GAAuB,CAAC,EACpC,SAAS,KAAK,EACjB,KAAK,WAAa,KAAK,SAAS,GAAuB,CAAC,EAAE,SAAS,KAAK,CAC1E,CAEA,iBAA0B,CACxB,GAAI,CACF,OAAO,KAAK,OAAO,gBAAgB,CACrC,MAAQ,CACN,OAAO,KAAK,gBAAgB,CAC9B,CACF,CAEA,gBAAyB,CACvB,GAAI,CACF,OAAO,KAAK,OAAO,eAAe,CACpC,MAAQ,CACN,OAAO,KAAK,eAAe,CAC7B,CACF,CAEA,iBAAkC,CAEhC,MADA,MAAK,cAAgB,KAAK,aAAe,GAAK,sBAE5C,KAAK,YACL,KAAK,aAAa,SAAS,EAAE,EAAE,SAAS,EAAuB,GAAG,CAEtE,CAEA,gBAAiC,CAE/B,MADA,MAAK,aAAe,KAAK,YAAc,GAAK,qBAE1C,KAAK,WACL,KAAK,YAAY,SAAS,EAAE,EAAE,SAAS,EAAsB,GAAG,CAEpE,CACF"}
1
+ {"version":3,"file":"id-generator.mjs","names":[],"sources":["../../../src/internal/server/id-generator.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport {\n type IdGenerator,\n RandomIdGenerator,\n} from \"@opentelemetry/sdk-trace-base\";\n\nconst TRACE_ID_HEX_LEN = 32;\nconst SPAN_ID_HEX_LEN = 16;\nconst TRACE_PREFIX_HEX_LEN = 24;\nconst SPAN_PREFIX_HEX_LEN = 12;\nconst TRACE_COUNTER_HEX_LEN = TRACE_ID_HEX_LEN - TRACE_PREFIX_HEX_LEN;\nconst SPAN_COUNTER_HEX_LEN = SPAN_ID_HEX_LEN - SPAN_PREFIX_HEX_LEN;\nconst TRACE_COUNTER_MODULUS = 2 ** (TRACE_COUNTER_HEX_LEN * 4);\nconst SPAN_COUNTER_MODULUS = 2 ** (SPAN_COUNTER_HEX_LEN * 4);\n\n/**\n * IdGenerator hybrid that uses OTel's default `RandomIdGenerator` whenever\n * available and falls back to a counter-based generator inside Next 16's\n * prerender contexts.\n *\n * Why: Next 16's prerender extension throws synchronously on any\n * `Math.random()` / `crypto.getRandomValues()` call inside a Server\n * Component that hasn't first awaited Request data\n * (`next-prerender-random` / `next-prerender-crypto`). OTel's default\n * generator hits `Math.random()` on every span creation, so spans Next\n * opens around prerendered route renders blow up the build.\n *\n * The hybrid path keeps full 128-bit trace IDs / 64-bit span IDs for the\n * common case (SSR, runtime requests, fluid-compute invocations) and only\n * degrades inside prerender — where the alternative is build failure.\n *\n * Inside prerender, IDs are minted from a per-process random prefix\n * (seeded once at construction time, outside any prerender ALS frame) plus\n * a monotonic counter. Counter widths are masked to keep IDs at their\n * spec-mandated lengths even after wrap.\n */\nexport class PrerenderSafeIdGenerator implements IdGenerator {\n private readonly random = new RandomIdGenerator();\n private readonly tracePrefix: string;\n private readonly spanPrefix: string;\n private traceCounter = 0;\n private spanCounter = 0;\n\n constructor() {\n const seed = randomBytes((TRACE_PREFIX_HEX_LEN + SPAN_PREFIX_HEX_LEN) / 2);\n this.tracePrefix = seed\n .subarray(0, TRACE_PREFIX_HEX_LEN / 2)\n .toString(\"hex\");\n this.spanPrefix = seed.subarray(TRACE_PREFIX_HEX_LEN / 2).toString(\"hex\");\n }\n\n generateTraceId(): string {\n try {\n return this.random.generateTraceId();\n } catch {\n return this.fallbackTraceId();\n }\n }\n\n generateSpanId(): string {\n try {\n return this.random.generateSpanId();\n } catch {\n return this.fallbackSpanId();\n }\n }\n\n private fallbackTraceId(): string {\n this.traceCounter = (this.traceCounter + 1) % TRACE_COUNTER_MODULUS;\n return (\n this.tracePrefix +\n this.traceCounter.toString(16).padStart(TRACE_COUNTER_HEX_LEN, \"0\")\n );\n }\n\n private fallbackSpanId(): string {\n this.spanCounter = (this.spanCounter + 1) % SPAN_COUNTER_MODULUS;\n return (\n this.spanPrefix +\n this.spanCounter.toString(16).padStart(SPAN_COUNTER_HEX_LEN, \"0\")\n );\n }\n}\n"],"mappings":"kGAMA,MAMM,sBAAwB,GAAM,GAC9B,qBAAuB,GAAM,GAuBnC,IAAa,yBAAb,KAA6D,CAC3D,OAA0B,IAAI,kBAC9B,YACA,WACA,aAAuB,EACvB,YAAsB,EAEtB,aAAc,CACZ,IAAM,KAAO,YAAa,GAA8C,CAAC,EACzE,KAAK,YAAc,KAChB,SAAS,EAAG,GAAuB,CAAC,EACpC,SAAS,KAAK,EACjB,KAAK,WAAa,KAAK,SAAS,GAAuB,CAAC,EAAE,SAAS,KAAK,CAC1E,CAEA,iBAA0B,CACxB,GAAI,CACF,OAAO,KAAK,OAAO,gBAAgB,CACrC,MAAQ,CACN,OAAO,KAAK,gBAAgB,CAC9B,CACF,CAEA,gBAAyB,CACvB,GAAI,CACF,OAAO,KAAK,OAAO,eAAe,CACpC,MAAQ,CACN,OAAO,KAAK,eAAe,CAC7B,CACF,CAEA,iBAAkC,CAEhC,MADA,MAAK,cAAgB,KAAK,aAAe,GAAK,sBAE5C,KAAK,YACL,KAAK,aAAa,SAAS,EAAE,EAAE,SAAS,EAAuB,GAAG,CAEtE,CAEA,gBAAiC,CAE/B,MADA,MAAK,aAAe,KAAK,YAAc,GAAK,qBAE1C,KAAK,WACL,KAAK,YAAY,SAAS,EAAE,EAAE,SAAS,EAAsB,GAAG,CAEpE,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"remote-config.d.mts","names":[],"sources":["../../../src/internal/server/remote-config.ts"],"mappings":";iBAWsB,yBAAA,CAAA,GAA6B,OAAO;AAAA,iBAoC1C,eAAA,CAAgB,MAAc"}
1
+ {"version":3,"file":"remote-config.d.mts","names":[],"sources":["../../../src/internal/server/remote-config.ts"],"mappings":";iBAWsB,yBAAA,IAA6B,OAAO;AAAA,iBAoC1C,eAAA,CAAgB,MAAc"}
@@ -1 +1 @@
1
- import{isEnabledOnServer,readInterfereEnv}from"../env.mjs";import{withPublicKey}from"../url.mjs";import{API_PATHS}from"@interfere/constants/api";let cachedConfig=null;async function fetchAndCacheRemoteConfig(){if(!isEnabledOnServer())return;let env=readInterfereEnv();if(env.publicKey!==null)try{let url=withPublicKey(`${env.apiUrl}${API_PATHS.CONFIG}`,env.publicKey),response=await fetch(url,{method:`GET`,headers:{"content-type":`application/json`},signal:AbortSignal.timeout(1e4)});if(!response.ok)return;let config=await response.json();config?.plugins&&(cachedConfig=config.plugins)}catch{}}function isPluginEnabled(plugin){return cachedConfig?cachedConfig[plugin]!==!1:!0}export{fetchAndCacheRemoteConfig,isPluginEnabled};
1
+ import{isEnabledOnServer,readInterfereEnv}from"../env.mjs";import{withPublicKey}from"../url.mjs";import{API_PATHS}from"@interfere/constants/api";let cachedConfig=null;async function fetchAndCacheRemoteConfig(){if(!isEnabledOnServer())return;let env=readInterfereEnv();if(env.publicKey!==null)try{let url=withPublicKey(`${env.apiUrl}${API_PATHS.CONFIG}`,env.publicKey),response=await fetch(url,{method:`GET`,headers:{"content-type":`application/json`},signal:AbortSignal.timeout(1e4)});if(!response.ok)return;let config=await response.json();config?.plugins&&(cachedConfig=config.plugins)}catch{return}}function isPluginEnabled(plugin){return cachedConfig?cachedConfig[plugin]!==!1:!0}export{fetchAndCacheRemoteConfig,isPluginEnabled};
@@ -1 +1 @@
1
- {"version":3,"file":"remote-config.mjs","names":[],"sources":["../../../src/internal/server/remote-config.ts"],"sourcesContent":["import { API_PATHS } from \"@interfere/constants/api\";\nimport type {\n RemoteConfig,\n RemotePluginConfig,\n} from \"@interfere/types/sdk/remote-config\";\n\nimport { isEnabledOnServer, readInterfereEnv } from \"../env.js\";\nimport { withPublicKey } from \"../url.js\";\n\nlet cachedConfig: RemotePluginConfig | null = null;\n\nexport async function fetchAndCacheRemoteConfig(): Promise<void> {\n if (!isEnabledOnServer()) {\n return;\n }\n\n const env = readInterfereEnv();\n if (env.publicKey === null) {\n return;\n }\n\n try {\n const url = withPublicKey(\n `${env.apiUrl}${API_PATHS.CONFIG}`,\n env.publicKey\n );\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n \"content-type\": \"application/json\",\n },\n signal: AbortSignal.timeout(10_000),\n });\n\n if (!response.ok) {\n return;\n }\n\n const config = (await response.json()) as RemoteConfig;\n if (config?.plugins) {\n cachedConfig = config.plugins;\n }\n } catch {\n // Fail silently — all plugins remain enabled\n }\n}\n\nexport function isPluginEnabled(plugin: string): boolean {\n if (!cachedConfig) {\n return true;\n }\n return cachedConfig[plugin as keyof RemotePluginConfig] !== false;\n}\n"],"mappings":"iJASA,IAAI,aAA0C,KAE9C,eAAsB,2BAA2C,CAC/D,GAAI,CAAC,kBAAkB,EACrB,OAGF,IAAM,IAAM,iBAAiB,EACzB,OAAI,YAAc,KAItB,GAAI,CACF,IAAM,IAAM,cACV,GAAG,IAAI,SAAS,UAAU,SAC1B,IAAI,SACN,EACM,SAAW,MAAM,MAAM,IAAK,CAChC,OAAQ,MACR,QAAS,CACP,eAAgB,kBAClB,EACA,OAAQ,YAAY,QAAQ,GAAM,CACpC,CAAC,EAED,GAAI,CAAC,SAAS,GACZ,OAGF,IAAM,OAAU,MAAM,SAAS,KAAK,EAChC,QAAQ,UACV,aAAe,OAAO,QAE1B,MAAQ,CAER,CACF,CAEA,SAAgB,gBAAgB,OAAyB,CAIvD,OAHK,aAGE,aAAa,UAAwC,GAFnD,EAGX"}
1
+ {"version":3,"file":"remote-config.mjs","names":[],"sources":["../../../src/internal/server/remote-config.ts"],"sourcesContent":["import { API_PATHS } from \"@interfere/constants/api\";\nimport type {\n RemoteConfig,\n RemotePluginConfig,\n} from \"@interfere/types/sdk/remote-config\";\n\nimport { isEnabledOnServer, readInterfereEnv } from \"../env.js\";\nimport { withPublicKey } from \"../url.js\";\n\nlet cachedConfig: RemotePluginConfig | null = null;\n\nexport async function fetchAndCacheRemoteConfig(): Promise<void> {\n if (!isEnabledOnServer()) {\n return;\n }\n\n const env = readInterfereEnv();\n if (env.publicKey === null) {\n return;\n }\n\n try {\n const url = withPublicKey(\n `${env.apiUrl}${API_PATHS.CONFIG}`,\n env.publicKey\n );\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n \"content-type\": \"application/json\",\n },\n signal: AbortSignal.timeout(10_000),\n });\n\n if (!response.ok) {\n return;\n }\n\n const config = (await response.json()) as RemoteConfig;\n if (config?.plugins) {\n cachedConfig = config.plugins;\n }\n } catch {\n return;\n }\n}\n\nexport function isPluginEnabled(plugin: string): boolean {\n if (!cachedConfig) {\n return true;\n }\n return cachedConfig[plugin as keyof RemotePluginConfig] !== false;\n}\n"],"mappings":"iJASA,IAAI,aAA0C,KAE9C,eAAsB,2BAA2C,CAC/D,GAAI,CAAC,kBAAkB,EACrB,OAGF,IAAM,IAAM,iBAAiB,EACzB,OAAI,YAAc,KAItB,GAAI,CACF,IAAM,IAAM,cACV,GAAG,IAAI,SAAS,UAAU,SAC1B,IAAI,SACN,EACM,SAAW,MAAM,MAAM,IAAK,CAChC,OAAQ,MACR,QAAS,CACP,eAAgB,kBAClB,EACA,OAAQ,YAAY,QAAQ,GAAM,CACpC,CAAC,EAED,GAAI,CAAC,SAAS,GACZ,OAGF,IAAM,OAAU,MAAM,SAAS,KAAK,EAChC,QAAQ,UACV,aAAe,OAAO,QAE1B,MAAQ,CACN,MACF,CACF,CAEA,SAAgB,gBAAgB,OAAyB,CAIvD,OAHK,aAGE,aAAa,UAAwC,GAFnD,EAGX"}
@@ -1 +1 @@
1
- {"version":3,"file":"trace-meta.d.mts","names":[],"sources":["../../../src/internal/server/trace-meta.tsx"],"mappings":";;AA8BA;;;;AAAyB;;;;;;;;;;;;;;;;;;;;;;;iBAAT,SAAA,CAAA,+BAAS,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"trace-meta.d.mts","names":[],"sources":["../../../src/internal/server/trace-meta.tsx"],"mappings":";;AA8BA;;;;AAAyB;;;;;;;;;;;;;;;;;;;;;;;iBAAT,SAAA,gCAAS,GAAA,CAAA,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"traceparent.d.mts","names":[],"sources":["../../../src/internal/server/traceparent.ts"],"mappings":";;AAiBA;;;;AAAiC;;;;;;;iBAAjB,iBAAA,CAAA"}
1
+ {"version":3,"file":"traceparent.d.mts","names":[],"sources":["../../../src/internal/server/traceparent.ts"],"mappings":";;AAiBA;;;;AAAiC;;;;;;;iBAAjB,iBAAA"}
package/dist/package.mjs CHANGED
@@ -1 +1 @@
1
- var name=`@interfere/next`,version=`10.0.1-canary.1`;export{name,version};
1
+ var name=`@interfere/next`,version=`11.0.0-canary.2`;export{name,version};
@@ -1 +1 @@
1
- {"version":3,"file":"provider.d.mts","names":[],"sources":["../src/provider.tsx"],"mappings":";;;;;UAeU,sBAAA,SAA+B,iBAAiB;EACxD,OAAA,GAAU,cAAA;EACV,aAAA;AAAA;;;;;;;;;iBAWc,iBAAA,CAAA;EACd,QAAA;EACA,OAAA;EACA;AAAA,GACC,sBAAA,GAAyB,SAAA"}
1
+ {"version":3,"file":"provider.d.mts","names":[],"sources":["../src/provider.tsx"],"mappings":";;;;;UAeU,sBAAA,SAA+B,iBAAiB;EACxD,OAAA,GAAU,cAAA;EACV,aAAA;AAAA;;;;;;;;;iBAWc,iBAAA;EACd,QAAA;EACA,OAAA;EACA;AAAA,GACC,sBAAA,GAAyB,SAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"provider.mjs","names":["CoreInterfereProvider"],"sources":["../src/provider.tsx"],"sourcesContent":["\"use client\";\n\nimport { InterfereProvider as CoreInterfereProvider } from \"@interfere/react/provider\";\nimport type { ConsentState } from \"@interfere/types/sdk/plugins/manifest\";\n\nimport {\n type PropsWithChildren,\n type ReactNode,\n useSyncExternalStore,\n} from \"react\";\n\nimport { getKernelOrNull, subscribeToKernel } from \"./instrument-client.js\";\n\nconst nullSnapshot = () => null;\n\ninterface InterfereProviderProps extends PropsWithChildren {\n consent?: ConsentState | undefined;\n errorBoundary?: boolean;\n}\n\n/**\n * Next.js wrapper around `@interfere/react`'s provider that resolves the\n * kernel from module scope (set by the matching `init()` call). Customer\n * code never passes the kernel explicitly. Uses `getKernelOrNull()` so the\n * provider is safe to render during SSR/SSG when `init()` hasn't run\n * (`init()` is client-only); the core provider mounts with null-safe\n * accessors and switches to the real kernel on client hydration.\n */\nexport function InterfereProvider({\n children,\n consent,\n errorBoundary,\n}: InterfereProviderProps): ReactNode {\n // Subscribe so the provider re-renders when `init()` resolves on the\n // client (customers commonly fire-and-forget `init()` from\n // `instrumentation-client.ts`, so the kernel may not be available\n // when the provider first mounts). Server renders see `null` via\n // `nullSnapshot` and the core provider mounts with null-safe accessors.\n const kernel = useSyncExternalStore(\n subscribeToKernel,\n getKernelOrNull,\n nullSnapshot\n );\n return (\n <CoreInterfereProvider\n kernel={kernel}\n {...(consent === undefined ? {} : { consent })}\n {...(errorBoundary === undefined ? {} : { errorBoundary })}\n >\n {children}\n </CoreInterfereProvider>\n );\n}\n\n// biome-ignore lint/performance/noBarrelFile: Next.js provider entrypoint re-exports the React SDK hooks for one-line customer setup.\nexport { useInterfere, useSession } from \"@interfere/react/provider\";\nexport type {\n ConsentCategory,\n ConsentState,\n GateableCategory,\n} from \"@interfere/types/sdk/plugins/manifest\";\n"],"mappings":"uQAaA,MAAM,iBAAqB,KAe3B,SAAgB,kBAAkB,CAChC,SACA,QACA,eACoC,CAWpC,OACE,IAACA,oBAAD,CACE,OAPW,qBACb,kBACA,gBACA,YAIe,EACb,GAAK,UAAY,IAAA,GAAY,CAAC,EAAI,CAAE,OAAQ,EAC5C,GAAK,gBAAkB,IAAA,GAAY,CAAC,EAAI,CAAE,aAAc,EAEvD,QACoB,CAAA,CAE3B"}
1
+ {"version":3,"file":"provider.mjs","names":["CoreInterfereProvider"],"sources":["../src/provider.tsx"],"sourcesContent":["\"use client\";\n\nimport { InterfereProvider as CoreInterfereProvider } from \"@interfere/react/provider\";\nimport type { ConsentState } from \"@interfere/types/sdk/plugins/manifest\";\n\nimport {\n type PropsWithChildren,\n type ReactNode,\n useSyncExternalStore,\n} from \"react\";\n\nimport { getKernelOrNull, subscribeToKernel } from \"./instrument-client.js\";\n\nconst nullSnapshot = () => null;\n\ninterface InterfereProviderProps extends PropsWithChildren {\n consent?: ConsentState | undefined;\n errorBoundary?: boolean;\n}\n\n/**\n * Next.js wrapper around `@interfere/react`'s provider that resolves the\n * kernel from module scope (set by the matching `init()` call). Customer\n * code never passes the kernel explicitly. Uses `getKernelOrNull()` so the\n * provider is safe to render during SSR/SSG when `init()` hasn't run\n * (`init()` is client-only); the core provider mounts with null-safe\n * accessors and switches to the real kernel on client hydration.\n */\nexport function InterfereProvider({\n children,\n consent,\n errorBoundary,\n}: InterfereProviderProps): ReactNode {\n const kernel = useSyncExternalStore(\n subscribeToKernel,\n getKernelOrNull,\n nullSnapshot\n );\n return (\n <CoreInterfereProvider\n kernel={kernel}\n {...(consent === undefined ? {} : { consent })}\n {...(errorBoundary === undefined ? {} : { errorBoundary })}\n >\n {children}\n </CoreInterfereProvider>\n );\n}\n\n// biome-ignore lint/performance/noBarrelFile: Next.js provider entrypoint re-exports the React SDK hooks for one-line customer setup.\nexport { useInterfere, useSession } from \"@interfere/react/provider\";\nexport type {\n ConsentCategory,\n ConsentState,\n GateableCategory,\n} from \"@interfere/types/sdk/plugins/manifest\";\n"],"mappings":"uQAaA,MAAM,iBAAqB,KAe3B,SAAgB,kBAAkB,CAChC,SACA,QACA,eACoC,CAMpC,OACE,IAACA,oBAAD,CACE,OAPW,qBACb,kBACA,gBACA,YAIe,EACb,GAAK,UAAY,IAAA,GAAY,CAAC,EAAI,CAAE,OAAQ,EAC5C,GAAK,gBAAkB,IAAA,GAAY,CAAC,EAAI,CAAE,aAAc,EAEvD,QACoB,CAAA,CAE3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"route-handler.d.mts","names":[],"sources":["../src/route-handler.ts"],"mappings":";;AAaA;;;;;iBAAsB,GAAA,CAAI,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,QAAA;AAAA,iBAOrC,IAAA,CAAK,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,QAAA;AAAA,iBAchC,OAAA,CAAA,GAAW,QAAQ"}
1
+ {"version":3,"file":"route-handler.d.mts","names":[],"sources":["../src/route-handler.ts"],"mappings":";;AAaA;;;;;iBAAsB,GAAA,CAAI,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,QAAA;AAAA,iBAOrC,IAAA,CAAK,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,QAAA;AAAA,iBAchC,OAAA,IAAW,QAAQ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interfere/next",
3
- "version": "10.0.1-canary.1",
3
+ "version": "11.0.0-canary.2",
4
4
  "license": "MIT",
5
5
  "description": "Build software that never breaks.",
6
6
  "keywords": [
@@ -17,7 +17,7 @@
17
17
  "author": "Interfere <support@interfere.com> (https://interfere.com)",
18
18
  "repository": {
19
19
  "type": "git",
20
- "url": "git+https://github.com/interfere-inc/interfere.git",
20
+ "url": "git+https://interfere.ghe.com/interfere/interfere.git",
21
21
  "directory": "src/packages/public/next"
22
22
  },
23
23
  "files": [
@@ -95,14 +95,14 @@
95
95
  "scripts": {
96
96
  "build": "tsdown",
97
97
  "test": "vitest run",
98
- "typecheck": "tsgo --noEmit --incremental"
98
+ "typecheck": "tsgo --noEmit"
99
99
  },
100
100
  "dependencies": {
101
101
  "@interfere/constants": "^9.0.2-canary.0",
102
102
  "@interfere/helpers": "^9.0.0",
103
- "@interfere/react": "^10.0.1-canary.1",
104
- "@interfere/sdk": "^10.0.1-canary.1",
105
- "@interfere/types": "^9.0.3-canary.0",
103
+ "@interfere/react": "^11.0.0-canary.2",
104
+ "@interfere/sdk": "^11.0.0-canary.2",
105
+ "@interfere/types": "^9.0.3-canary.1",
106
106
  "@opentelemetry/api": "^1.9.1",
107
107
  "@opentelemetry/api-logs": "^0.218.0",
108
108
  "@opentelemetry/baggage-span-processor": "^0.5.0",
@@ -122,9 +122,9 @@
122
122
  "uuid": "^14.0.0"
123
123
  },
124
124
  "peerDependencies": {
125
- "next": "^16.2.4",
126
- "react": "^19.2.6",
127
- "react-dom": "^19.2.6"
125
+ "next": "^15.3.0 || ^16.0.0",
126
+ "react": "^19.0.0",
127
+ "react-dom": "^19.0.0"
128
128
  },
129
129
  "peerDependenciesMeta": {
130
130
  "next": {
@@ -143,16 +143,16 @@
143
143
  "@types/node": "^24.12.0",
144
144
  "@types/react": "19.2.15",
145
145
  "@types/react-dom": "19.2.3",
146
- "@typescript/native-preview": "7.0.0-dev.20260522.1",
147
- "@vitest/browser": "4.1.7",
148
- "@vitest/browser-playwright": "4.1.7",
149
- "@vitest/coverage-v8": "^4.1.6",
146
+ "@typescript/native-preview": "7.0.0-dev.20260601.1",
147
+ "@vitest/browser": "4.1.8",
148
+ "@vitest/browser-playwright": "4.1.8",
149
+ "@vitest/coverage-v8": "4.1.8",
150
150
  "next": "^16.2.4",
151
151
  "playwright": "^1.60.0",
152
152
  "react": "^19.2.6",
153
153
  "react-dom": "^19.2.6",
154
- "tsdown": "^0.22.0",
155
- "vitest": "^4.1.6",
154
+ "tsdown": "0.22.1",
155
+ "vitest": "4.1.8",
156
156
  "vitest-browser-react": "2.2.0",
157
157
  "webpack": "^5.106.2"
158
158
  }