@interfere/vite 12.1.4 → 12.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -127,7 +127,7 @@ The SDK splits its credentials by concern: a **public key** travels with the bro
127
127
 
128
128
  1. **Install** `@interfere/vite` and add the plugin (above).
129
129
  2. **Public key** — copy the dashboard's regional public key (`interfere_public_us_*` or `interfere_public_eu_*`) into `.env.local` as `VITE_INTERFERE_PUBLIC_KEY=...`. Public by design; safe to commit to a public repo.
130
- 3. **Secret key** — create an Interfere secret key and put the regional value (`interfere_secret_us_*` or `interfere_secret_eu_*`) in `.env.local` as `INTERFERE_API_KEY=...`. This key is what the build pipeline uses to publish release metadata. **Server-side; never commit.**
130
+ 3. **Secret key** — create a workspace secret key with the **Publish releases** (`release:write`) permission and put the regional value (`interfere_secret_us_*` or `interfere_secret_eu_*`) in `.env.local` as `INTERFERE_API_KEY=...`. One key publishes releases for every app in the workspace; the plugin names this app with `VITE_INTERFERE_PUBLIC_KEY`. **Server-side; never commit.**
131
131
  4. **First build** — run `bun run build`. The plugin's `closeBundle` hook publishes release metadata and preflight-confirms the release. After this the commit's slug is accepted at ingest.
132
132
  5. **Dev cycle** — `bun run dev` then exercises the bundle in your browser. Events flow with the commit's preflight-confirmed slug (the build did the work). New commit? Re-run `bun run build` so the new commit's slug is preflighted.
133
133
 
@@ -135,15 +135,16 @@ For **Vercel preview / production**, set `INTERFERE_API_KEY` in your `.env.local
135
135
 
136
136
  ## Environment Variables
137
137
 
138
- | Variable | Required | Description |
139
- | ---------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
140
- | `VITE_INTERFERE_PUBLIC_KEY` | Yes | Surface public key in the `interfere_public_us_*` or `interfere_public_eu_*` format. Stamped into the page by the plugin at build time, and reused server-side (`register()`, request middleware) — the plugin mirrors it into the dev server's process, so no separate server variable is needed. A production build without it logs a warning and sends no events. |
141
- | `INTERFERE_API_KEY` | Yes for deploy builds | Interfere secret key in the `interfere_secret_us_*` or `interfere_secret_eu_*` format. Used by the plugin at build time to register the release and upload source maps. A production build without it logs a warning and skips the release. A key with the wrong prefix fails the build. If Interfere rejects the key, the build logs an error and continues without a release. |
142
- | `INTERFERE_SOURCEMAP_UPLOAD_CONCURRENCY` | No | Maximum concurrent source-map uploads. Defaults to 20; must be a positive integer. |
143
- | `INTERFERE_API_URL` | No | Override the collector URL the build pipeline targets. Defaults to `https://api.interfere.com`. |
144
- | `VITE_INTERFERE_API_URL` | No | Override the collector URL the browser kernel targets in direct mode. Proxy mode uses `INTERFERE_API_URL` server-side. |
145
- | `VITE_INTERFERE_COMMIT_SHA` | No | Override the auto-detected commit SHA used to derive the release slug. Defaults to `git rev-parse HEAD`. |
146
- | `VITE_INTERFERE_FORCE_ENABLE` | No | `"1"` / `"true"` flips the SDK into force-enable mode so `vite dev` (which sets `NODE_ENV=development`) emits events. Production collectors silently 202-drop force-enabled batches; safe to ship by mistake but useless. Use only in dev. |
138
+ | Variable | Required | Description |
139
+ | ---------------------------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
140
+ | `VITE_INTERFERE_PUBLIC_KEY` | Yes | Surface public key in the `interfere_public_us_*` or `interfere_public_eu_*` format. Stamped into the page by the plugin at build time, and reused server-side (`register()`, request middleware) — the plugin mirrors it into the dev server's process, so no separate server variable is needed. A production build without it logs a warning and sends no events. |
141
+ | `INTERFERE_API_KEY` | Yes for deploy builds | Workspace secret key in the `interfere_secret_us_*` or `interfere_secret_eu_*` format, with the **Publish releases** (`release:write`) permission. Used by the plugin at build time to register the release and upload source maps for the app that `VITE_INTERFERE_PUBLIC_KEY` names. A production build without it logs a warning and skips the release. A key with the wrong prefix fails the build. If Interfere rejects the key, the build logs an error and continues without a release. |
142
+ | `INTERFERE_SOURCEMAP_UPLOAD_CONCURRENCY` | No | Maximum concurrent source-map uploads. Defaults to 20; must be a positive integer. |
143
+ | `INTERFERE_API_URL` | No | Override the collector URL the build pipeline targets. Defaults to `https://api.interfere.com`. |
144
+ | `VITE_INTERFERE_API_URL` | No | Override the collector URL the browser kernel targets in direct mode. Proxy mode uses `INTERFERE_API_URL` server-side. |
145
+ | `VITE_INTERFERE_COMMIT_SHA` | No | Override the auto-detected commit SHA used to derive the release slug. Defaults to `git rev-parse HEAD`. |
146
+ | `VITE_INTERFERE_FORCE_ENABLE` | No | `"1"` / `"true"` flips the SDK into force-enable mode so `vite dev` (which sets `NODE_ENV=development`) emits events. Production collectors silently 202-drop force-enabled batches; safe to ship by mistake but useless. Use only in dev. |
147
+ | `VITE_INTERFERE_SERVICE_NAME` | No | The OTel `service.name` the browser SDK reports when `init()` is called without one. Defaults to the project package name without its scope (`@acme/storefront` → `storefront`). The `serviceName` plugin option overrides it; an explicit `init({ serviceName })` always wins. |
147
148
 
148
149
  ## Plugin Options
149
150
 
@@ -159,6 +160,7 @@ interfere({
159
160
  | `commitSha` | `VITE_INTERFERE_COMMIT_SHA` → `git rev-parse HEAD` | Commit SHA used to derive the deterministic release slug. |
160
161
  | `sourceMaps` | `{ upload: true }` | `{ upload: false }` registers the release without uploading source maps. Interfere API failures, including a rejected secret key, timeouts, network failures and HTTP error responses, never fail the build: they are logged, local source maps are deleted and the build finishes. |
161
162
  | `disable` | `false` | Turns Interfere off for this build: no release, no source-map upload, no build checks, and the browser and server SDKs send no events. |
163
+ | `serviceName` | `VITE_INTERFERE_SERVICE_NAME` → package name | The OTel `service.name` stamped into the page for `init()` calls that pass none, so the Services page never shows `unknown_service`. Falls back to the project package name without its scope. An explicit `init({ serviceName })` still wins. |
162
164
  | `buildReceipt` | disabled | Private JSON path inside the project and outside public/build output directories. Records consumed SDK config and fetch status, original release/build identity, source publication status when available, and hashes of native Vite output files. Keep this artifact private; missing or fallback evidence cannot establish safe deployment reuse. |
163
165
 
164
166
  Build logs report release creation, signing, session lookup, upload, finalization, receipt capture and cleanup in milliseconds, plus transferred bytes. Finalization confirms durable acceptance. When Vite builds several environments, the plugin uploads once after all of them finish. It does not replace runtime stack/source canaries or continually rehash every previously published map.
@@ -169,6 +171,8 @@ Ingest requests never carry cookies. `apiHost` posts to your own site's subdomai
169
171
 
170
172
  Upgrade all `@interfere/*` SDK packages together. Keep the env var names, but replace old `int_pub_*` / `interfere_pk_*` values with the dashboard's regional public key, and replace old `ak_*` / `interfere_ak_*` build keys with the regional Interfere secret key.
171
173
 
174
+ Secret keys that were created for one app keep working. New secret keys belong to the workspace, so set `VITE_INTERFERE_PUBLIC_KEY` on the build machine as well.
175
+
172
176
  ## Identity Management
173
177
 
174
178
  Link sessions to your authenticated users with `identity.set()`:
@@ -1 +1 @@
1
- import{API_URL_GLOBAL,ENVIRONMENT_GLOBAL,FORCE_ENABLE_GLOBAL,INGEST_URL_GLOBAL,PUBLIC_KEY_GLOBAL,RELEASE_SLUG_GLOBAL}from"@interfere/types/sdk/injected-globals";import{PLUGINS_GLOBAL}from"@interfere/types/sdk/remote-config";function applyBrowserGlobals(){let g=globalThis;import.meta.env.VITE_INTERFERE_PUBLIC_KEY&&(g[PUBLIC_KEY_GLOBAL]??=import.meta.env.VITE_INTERFERE_PUBLIC_KEY),import.meta.env.VITE_INTERFERE_API_HOST&&(g[API_URL_GLOBAL]??=import.meta.env.VITE_INTERFERE_API_HOST),import.meta.env.VITE_INTERFERE_ENVIRONMENT&&(g[ENVIRONMENT_GLOBAL]??=import.meta.env.VITE_INTERFERE_ENVIRONMENT),import.meta.env.VITE_INTERFERE_RELEASE_SLUG&&(g[RELEASE_SLUG_GLOBAL]??=import.meta.env.VITE_INTERFERE_RELEASE_SLUG),import.meta.env.VITE_INTERFERE_INGEST_URL&&(g[INGEST_URL_GLOBAL]??=import.meta.env.VITE_INTERFERE_INGEST_URL);let bakedPlugins=parseBakedPlugins(import.meta.env.VITE_INTERFERE_PLUGINS);bakedPlugins&&(g[PLUGINS_GLOBAL]??=bakedPlugins);let forceEnable=import.meta.env.VITE_INTERFERE_FORCE_ENABLE;(forceEnable===`1`||forceEnable===`true`)&&(g[FORCE_ENABLE_GLOBAL]??=!0)}function parseBakedPlugins(raw){if(raw)try{let parsed=JSON.parse(raw);return typeof parsed==`object`&&parsed&&!Array.isArray(parsed)?parsed:void 0}catch{return}}export{applyBrowserGlobals};
1
+ import{API_URL_GLOBAL,ENVIRONMENT_GLOBAL,FORCE_ENABLE_GLOBAL,INGEST_URL_GLOBAL,PUBLIC_KEY_GLOBAL,RELEASE_SLUG_GLOBAL}from"@interfere/types/sdk/injected-globals";import{PLUGINS_GLOBAL}from"@interfere/types/sdk/remote-config";import{SERVICE_NAME_GLOBAL}from"@interfere/types/sdk/service-name";function applyBrowserGlobals(){let g=globalThis;import.meta.env.VITE_INTERFERE_PUBLIC_KEY&&(g[PUBLIC_KEY_GLOBAL]??=import.meta.env.VITE_INTERFERE_PUBLIC_KEY),import.meta.env.VITE_INTERFERE_API_HOST&&(g[API_URL_GLOBAL]??=import.meta.env.VITE_INTERFERE_API_HOST),import.meta.env.VITE_INTERFERE_ENVIRONMENT&&(g[ENVIRONMENT_GLOBAL]??=import.meta.env.VITE_INTERFERE_ENVIRONMENT),import.meta.env.VITE_INTERFERE_RELEASE_SLUG&&(g[RELEASE_SLUG_GLOBAL]??=import.meta.env.VITE_INTERFERE_RELEASE_SLUG),import.meta.env.VITE_INTERFERE_INGEST_URL&&(g[INGEST_URL_GLOBAL]??=import.meta.env.VITE_INTERFERE_INGEST_URL),import.meta.env.VITE_INTERFERE_SERVICE_NAME&&(g[SERVICE_NAME_GLOBAL]??=import.meta.env.VITE_INTERFERE_SERVICE_NAME);let bakedPlugins=parseBakedPlugins(import.meta.env.VITE_INTERFERE_PLUGINS);bakedPlugins&&(g[PLUGINS_GLOBAL]??=bakedPlugins);let forceEnable=import.meta.env.VITE_INTERFERE_FORCE_ENABLE;(forceEnable===`1`||forceEnable===`true`)&&(g[FORCE_ENABLE_GLOBAL]??=!0)}function parseBakedPlugins(raw){if(raw)try{let parsed=JSON.parse(raw);return typeof parsed==`object`&&parsed&&!Array.isArray(parsed)?parsed:void 0}catch{return}}export{applyBrowserGlobals};
@@ -1 +1 @@
1
- import{isEnabledOnServer}from"./env.mjs";import{isPluginEnabled}from"./remote-config.mjs";import{MECHANISM_TYPE,isNonErrorException,toException}from"@interfere/types/sdk/errors";import{SpanKind,SpanStatusCode,context,trace}from"@opentelemetry/api";const DEFAULT_CAPTURE_ERROR_MECHANISM={handled:!0,type:MECHANISM_TYPE.node.captureError},seen=new WeakSet;function buildAttrs(value,mechanism,ctx){let attrs={"interfere.exception.handled":String(mechanism.handled),"interfere.exception.mechanism":mechanism.type};return ctx?.request?.method&&(attrs[`http.request.method`]=ctx.request.method),ctx?.request?.path&&(attrs[`url.path`]=ctx.request.path),isNonErrorException(value)?(attrs[`exception.type`]=value.type,attrs[`exception.message`]=value.value,attrs[`interfere.exception.kind`]=`non-error`,attrs):(attrs[`exception.type`]=value.name,attrs[`exception.message`]=value.message,value.stack&&(attrs[`exception.stacktrace`]=value.stack),attrs)}function recordException(error,mechanism,ctx){let value=toException(error),attrs=buildAttrs(value,mechanism,ctx),tracer=trace.getTracer(`@interfere/vite/server`),active=trace.getActiveSpan();if(active){active.addEvent(`exception`,attrs),mechanism.handled||active.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message});return}let span=tracer.startSpan(`interfere.captureError`,{kind:SpanKind.INTERNAL},context.active());span.addEvent(`exception`,attrs),mechanism.handled||span.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message}),span.end()}function captureError(error,ctx){if(isEnabledOnServer()&&isPluginEnabled(`errors`)){if(error instanceof Error){if(seen.has(error))return;seen.add(error)}recordException(error,ctx?.mechanism??DEFAULT_CAPTURE_ERROR_MECHANISM,ctx)}}export{captureError};
1
+ import{isEnabledOnServer}from"./env.mjs";import{isPluginEnabled}from"./remote-config.mjs";import{MECHANISM_TYPE,isNonErrorException,toException}from"@interfere/types/sdk/errors";import{exceptionSpanName}from"@interfere/types/sdk/throw-site";import{SpanKind,SpanStatusCode,context,trace}from"@opentelemetry/api";const DEFAULT_CAPTURE_ERROR_MECHANISM={handled:!0,type:MECHANISM_TYPE.node.captureError},seen=new WeakSet;function buildAttrs(value,mechanism,ctx){let attrs={"interfere.exception.handled":String(mechanism.handled),"interfere.exception.mechanism":mechanism.type};return ctx?.request?.method&&(attrs[`http.request.method`]=ctx.request.method),ctx?.request?.path&&(attrs[`url.path`]=ctx.request.path),isNonErrorException(value)?(attrs[`exception.type`]=value.type,attrs[`exception.message`]=value.value,attrs[`interfere.exception.kind`]=`non-error`,attrs):(attrs[`exception.type`]=value.name,attrs[`exception.message`]=value.message,value.stack&&(attrs[`exception.stacktrace`]=value.stack),attrs)}function recordException(error,mechanism,ctx){let value=toException(error),attrs=buildAttrs(value,mechanism,ctx),tracer=trace.getTracer(`@interfere/vite/server`),active=trace.getActiveSpan();if(active){active.addEvent(`exception`,attrs),mechanism.handled||active.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message});return}let span=tracer.startSpan(exceptionSpanName(value),{attributes:{"interfere.capture.kind":`exception`},kind:SpanKind.INTERNAL},context.active());span.addEvent(`exception`,attrs),mechanism.handled||span.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message}),span.end()}function captureError(error,ctx){if(isEnabledOnServer()&&isPluginEnabled(`errors`)){if(error instanceof Error){if(seen.has(error))return;seen.add(error)}recordException(error,ctx?.mechanism??DEFAULT_CAPTURE_ERROR_MECHANISM,ctx)}}export{captureError};
@@ -0,0 +1,12 @@
1
+ //#region src/internal/service-name.d.ts
2
+ /**
3
+ * The `service.name` a build stamps into the page when `init()` is called
4
+ * without one: the plugin option, then `VITE_INTERFERE_SERVICE_NAME`, then
5
+ * the project's package name without its scope.
6
+ */
7
+ export declare function resolveServiceName(input: {
8
+ option: string | undefined;
9
+ env: Record<string, string | undefined>;
10
+ root: string;
11
+ }): string | undefined;
12
+ //#endregion
@@ -0,0 +1 @@
1
+ import{existsSync,readFileSync}from"node:fs";import path from"node:path";const SCOPE_PREFIX=/^@[^/]+\//u;function nonEmpty(value){return value?.trim()||void 0}function unscopedPackageName(name){return nonEmpty(name.replace(SCOPE_PREFIX,``))}function packageServiceName(root){let manifest=path.join(root,`package.json`);if(!existsSync(manifest))return;let parsed;try{parsed=JSON.parse(readFileSync(manifest,`utf8`))}catch{return}if(typeof parsed!=`object`||!parsed||!(`name`in parsed))return;let{name}=parsed;return typeof name==`string`?unscopedPackageName(name):void 0}function resolveServiceName(input){return nonEmpty(input.option)??nonEmpty(input.env.VITE_INTERFERE_SERVICE_NAME)??packageServiceName(input.root)}export{resolveServiceName};
package/dist/package.mjs CHANGED
@@ -1 +1 @@
1
- var name=`@interfere/vite`,version=`12.1.4`;export{name,version};
1
+ var name=`@interfere/vite`,version=`12.2.0`;export{name,version};
package/dist/plugin.d.mts CHANGED
@@ -20,6 +20,13 @@ export interface InterfereViteOptions {
20
20
  commitSha?: string;
21
21
  /** Separates source maps for distinct bundles built from the same commit. */
22
22
  buildVariant?: string;
23
+ /**
24
+ * The OTel `service.name` the browser SDK reports when `init()` is called
25
+ * without one. Defaults to `VITE_INTERFERE_SERVICE_NAME`, then to the
26
+ * project package name without its scope (`@acme/storefront` →
27
+ * `storefront`). An explicit `init({ serviceName })` still wins.
28
+ */
29
+ serviceName?: string;
23
30
  /**
24
31
  * Directory (relative to the project root) where the SSR/server build
25
32
  * writes its `.js.map` files, when that isn't `<build.outDir>/server`.
package/dist/plugin.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{PRODUCER_VERSION}from"./version.mjs";import{runGitCommand}from"./internal/build/git.mjs";import{createBuildReceipt}from"./internal/build/build-receipt.mjs";import{runReleasePipeline}from"./internal/build/pipeline.mjs";import{renderDebugId}from"./internal/build/render-debug-id.mjs";import{discoverByPairing}from"./internal/build/source-maps.mjs";import{resolveCommitSha}from"./internal/release-slug.mjs";import{cleanupSourceMaps}from"@interfere/build/cleanup-source-maps";import{sourceMapUploadFetch}from"@interfere/build/source-map-upload-fetch";import{InterfereClient}from"@interfere/javascript";import{createBuildConfigResolver}from"@interfere/build/config";import{API_URL}from"@interfere/constants/api";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{deriveReleaseSlug}from"@interfere/types/releases/slug";import{checkReleaseConfig,diagnoseRelease}from"@interfere/types/sdk/build-diagnostics";import{resolveDeployEnvironment}from"@interfere/types/sdk/deploy-environment";import{createHash}from"node:crypto";import{existsSync,readdirSync,statSync}from"node:fs";import path from"node:path";import{loadEnv}from"vite";const SERVER_RUNTIME_PLUGIN_HINTS=[`tanstack-start`,`nitro`,`@tanstack/react-start`],DIAGNOSTIC_CONTEXT={publicKeyEnvVar:`VITE_INTERFERE_PUBLIC_KEY`,docsUrl:`https://interfere.com/docs/sdk/vite`};function buildError(title,details){return Error([`Interfere: ${title}`,...details].join(`
2
- `))}function logDiagnostic(logger,diagnostic){for(let line of[diagnostic.title,...diagnostic.details])logger[diagnostic.severity](`[interfere] ${line}`)}function isTestEnv(){return!!(process.env.VITEST||process.env.VITEST_WORKER_ID)}function pickEnvValue(env,key){let value=env[key];return value&&value.length>0?value:void 0}function hasServerRuntimePlugin(pluginNames){return pluginNames.some(name=>SERVER_RUNTIME_PLUGIN_HINTS.some(hint=>name.includes(hint)))}function normalizeOrigin(host){try{return new URL(host).origin}catch{return null}}function resolveApiHost(options,env){let host=options.apiHost??env.VITE_INTERFERE_API_URL;if(!host)return;let origin=normalizeOrigin(host);if(!origin)throw Error(`interfere({ apiHost }) requires a full URL, e.g. apiHost: "https://in.yourapp.com"`);return origin}function resolveBrowserConfig(options,env,command){let commitSha=options.commitSha??env.VITE_INTERFERE_COMMIT_SHA??resolveCommitSha(runGitCommand);return{apiHost:resolveApiHost(options,env),commitSha,environment:command===`build`?resolveDeployEnvironment({...env,...process.env}):null,publicKey:env.VITE_INTERFERE_PUBLIC_KEY,releaseSlug:commitSha?deriveReleaseSlug(options.buildVariant?createHash(`sha256`).update(JSON.stringify([commitSha,options.buildVariant])).digest(`hex`):commitSha):null}}function browserDefine(browser){return{"import.meta.env.VITE_INTERFERE_API_HOST":JSON.stringify(browser.apiHost??null),"import.meta.env.VITE_INTERFERE_ENVIRONMENT":JSON.stringify(browser.environment),"import.meta.env.VITE_INTERFERE_RELEASE_SLUG":JSON.stringify(browser.releaseSlug)}}function hasFreshServerSourceMaps(root,sinceMs){if(!existsSync(root))return!1;let entries=readdirSync(root,{withFileTypes:!0});for(let entry of entries){let absolute=path.join(root,entry.name);if(entry.isDirectory()){if(hasFreshServerSourceMaps(absolute,sinceMs))return!0;continue}if(entry.name.endsWith(`.map`)&&statSync(absolute).mtimeMs>=sinceMs)return!0}return!1}function isReleaseReady(state){return!!(state.releaseEnabled&&state.apiKey&&state.releaseSlug&&state.commitSha&&!state.releaseStarted)}function shouldWaitForServerOutput(state){return state.uploadSourceMaps&&state.serverBuild&&!hasFreshServerSourceMaps(state.serverSourcemapsDir,state.buildStartedAt)}function logSkippedRelease(logger,failure){if(failure.kind===`missing_source_provider`){logger.warn(`[interfere] release skipped: this surface has no version control provider configured. Configure one at https://interfere.com/~/${failure.config.org.slug}/apps/${failure.config.surface.slug}.`);return}logger.warn(`[interfere] release skipped: this surface has no deployment target configured. Configure one at https://interfere.com/~/${failure.config.org.slug}/apps/${failure.config.surface.slug}.`)}function logCleanupFailures(logger,failures){for(let failure of failures)logger.warn(`[interfere] cleanup left ${failure.absolute} on disk: ${failure.code??failure.message}`)}async function discoverUploadableSourceMaps(state){let discovered;try{discovered=await discoverByPairing({distDir:state.uploadOutDir,projectDir:state.projectDir,publicPathRewrites:[{from:`public`,to:``}],subtrees:[`.`]})}catch(error){return logDiagnostic(state.logger,diagnoseRelease(DIAGNOSTIC_CONTEXT,error)),null}return discovered.missingSourceMapCount>0&&state.logger.warn(`[interfere] ${discovered.missingSourceMapCount} referenced source map(s) not found. Errors in those chunks will not resolve to source.`),discovered.files.length===0?(state.logger.warn(`[interfere] No source maps found; the release remains unready. Enable source-map generation and upload them during the build.`),null):discovered}async function publishRelease(state,discovered){let{apiKey,apiUrl,releaseSlug,commitSha,logger}=state,client=new InterfereClient({fetch:sourceMapUploadFetch,baseUrl:apiUrl,apiKey:{token:apiKey},headers:{"user-agent":PRODUCER_VERSION},maxRetries:4,timeoutInSeconds:10}),releasesConfig=await client.releases.getReleasesConfig({abortSignal:AbortSignal.timeout(6e4)}),result=await runReleasePipeline({client,buildId:commitSha,bundler:`rollup`,discovery:discovered,environment:state.environment,producerVersion:PRODUCER_VERSION,releaseSlug,releasesConfig,capturePublication:state.receipt!==void 0,uploadSourceMaps:state.uploadSourceMaps});if(!result.ok){logSkippedRelease(logger,result.failure);return}if(logCleanupFailures(logger,result.data.cleanupFailures),result.data.publication&&state.receipt?.recordPublication(result.data.publication),!result.data.timing){logger.info(`[interfere] release ${result.data.releaseSlug} registered without source maps.`);return}logger.info(`[interfere] source-map stages (ms): create=${result.data.timing.createRelease}, sign=${result.data.timing.sign}, session=${result.data.timing.session}, upload=${result.data.timing.upload}, finalize=${result.data.timing.finalize}, receipt=${result.data.timing.receipt}, cleanup=${result.data.timing.cleanup}; bytes=${result.data.timing.totalBytes}.`),state.uploadComplete=!0,logger.info(`[interfere] release ${result.data.releaseSlug} confirmed — ${result.data.fileCount} maps, ${result.data.timing.total}ms.`)}async function releaseBuild(state){let{logger,releaseSlug}=state;logger.info(state.uploadSourceMaps?`[interfere] uploading source maps for release ${releaseSlug}…`:`[interfere] registering release ${releaseSlug} without source maps…`);let discovered=state.uploadSourceMaps?await discoverUploadableSourceMaps(state):{files:[],missingSourceMapCount:0,sourceFileCount:0};if(discovered){try{await publishRelease(state,discovered)}catch(error){logDiagnostic(logger,diagnoseRelease(DIAGNOSTIC_CONTEXT,error))}state.uploadComplete||logCleanupFailures(logger,await cleanupSourceMaps(discovered.files))}}async function releaseBuildWhenReady(state){if(isReleaseReady(state)){if(shouldWaitForServerOutput(state)){state.logger.info(`[interfere] waiting for server build output in ${path.relative(state.projectDir,state.serverSourcemapsDir)} before uploading source maps.`);return}state.releaseStarted=!0,await releaseBuild(state)}}function resolveReleaseEnv(config){return{...loadEnv(config.mode,config.envDir,``),...process.env}}function buildPluginState(options,config,browser){let{logger}=config,isProductionBuild=config.command===`build`,releaseEnv=resolveReleaseEnv(config),apiKey=pickEnvValue(releaseEnv,`INTERFERE_API_KEY`),apiUrl=pickEnvValue(releaseEnv,`INTERFERE_API_URL`)??API_URL,release=isProductionBuild?checkReleaseConfig(DIAGNOSTIC_CONTEXT,{apiKey,commitSha:browser.commitSha,releaseSlug:browser.releaseSlug}):null;if(release?.status===`fail`)throw buildError(release.title,release.details);let releaseEnabled=release?.status===`ok`,uploadOutDir=path.resolve(config.root,config.build.outDir),serverSourcemapsDir=options.serverSourcemapsDir?path.resolve(config.root,options.serverSourcemapsDir):path.join(uploadOutDir,`server`),serverBuild=hasServerRuntimePlugin(config.plugins.map(p=>p.name))||!!config.build.ssr;return{apiHost:browser.apiHost,apiKey,apiUrl,buildStartedAt:Math.floor(Date.now()/1e3)*1e3,commitSha:browser.commitSha,releaseEnabled,skippedRelease:release?.status===`skip`?release:null,uploadSourceMaps:releaseEnabled&&options.sourceMaps?.upload!==!1,environment:browser.environment,isProductionBuild,logger,projectDir:config.root,publicKey:browser.publicKey,releaseSlug:browser.releaseSlug,serverBuild,serverSourcemapsDir,uploadOutDir,releaseStarted:!1,uploadComplete:!1}}function announceSetup(state){if(state.logger.info(`[interfere] ingest → ${state.apiHost??`Interfere (default)`}`),!state.apiHost&&state.serverBuild&&state.logger.warn(`[interfere] Telemetry posts directly to Interfere. On a server-rendered app, set interfere({ apiHost }) to a subdomain of your own site to survive ad-blockers.`),state.isProductionBuild&&!state.publicKey&&state.logger.warn(`[interfere] VITE_INTERFERE_PUBLIC_KEY is not set, so this build sends no events. Set it on builds that you deploy.`),state.skippedRelease)for(let line of[state.skippedRelease.title,...state.skippedRelease.details])state.logger.warn(`[interfere] ${line}`)}function mirrorPublicKeyForDevServer(command,env){if(command!==`serve`)return;let publicKey=env.VITE_INTERFERE_PUBLIC_KEY;publicKey&&!process.env.VITE_INTERFERE_PUBLIC_KEY&&(process.env.VITE_INTERFERE_PUBLIC_KEY=publicKey)}function resolveEnvDir(userConfig){let root=userConfig.root?path.resolve(process.cwd(),userConfig.root):process.cwd();return userConfig.envDir===!1||userConfig.envDir===void 0||userConfig.envDir===``?root:path.resolve(root,userConfig.envDir)}function forceSourcemapForUpload(userConfig,options,command,env){if(!(command===`build`&&options.sourceMaps?.upload!==!1&&pickEnvValue(env,`INTERFERE_API_KEY`)))return;let previous=userConfig.build?.sourcemap;return userConfig.build={...userConfig.build,sourcemap:!0},previous===void 0||previous===!0?void 0:previous}function carryOverUploadProgress(previous,next){previous?.serverBuild&&next.serverBuild&&(next.uploadOutDir=previous.uploadOutDir,next.serverSourcemapsDir=previous.serverSourcemapsDir,next.releaseStarted=previous.releaseStarted,next.uploadComplete=previous.uploadComplete,next.buildStartedAt=previous.buildStartedAt)}function warnForcedSourcemap(logger,overridden){overridden!==void 0&&logger.warn(`[interfere] Forced build.sourcemap to true (was ${JSON.stringify(overridden)}). Source maps are required to symbolicate errors; they are uploaded then deleted from the build output, so they are never served. Pass interfere({ sourceMaps: { upload: false } }) to opt out.`)}function disabledPlugin(){return{name:`interfere`,config(){return{define:{"import.meta.env.VITE_INTERFERE_DISABLED":JSON.stringify(`true`)},ssr:{noExternal:[`@interfere/vite`]}}}}}function rejectRemovedOptions(options){let sourceMaps=options.sourceMaps;if(sourceMaps!==void 0&&(typeof sourceMaps!=`object`||!sourceMaps))throw buildError(`Removed: interfere({ sourceMaps })`,['`sourceMaps: true`, `sourceMaps: false` and `sourceMaps: "required"` were removed.',"Use `sourceMaps: { upload: false }` to register the release without source maps, or `disable: true` to turn Interfere off for the build.",`See: ${DIAGNOSTIC_CONTEXT.docsUrl}`]);if(`sourceMapReadinessTimeoutMs`in options)throw buildError(`Removed: interfere({ sourceMapReadinessTimeoutMs })`,[`The build no longer waits for source-map processing. Remove the option.`,`See: ${DIAGNOSTIC_CONTEXT.docsUrl}`])}function interfere(options={}){if(rejectRemovedOptions(options),options.disable)return disabledPlugin();let receipt=options.buildReceipt?createBuildReceipt(options.buildReceipt):void 0,remoteConfigFallback=!1,resolveBuildConfig=createBuildConfigResolver(result=>{receipt?.observeConfig(result),remoteConfigFallback||=result.status===`fallback`}),state=null,overriddenSourcemap,announced=!1,uploadAfterAppBuild=!1,writeReceipt=async()=>{try{await receipt?.write()}catch{state?.logger.warn(`[interfere] Private build receipt could not be written.`)}};return{buildApp:{order:`post`,async handler(builder){if(!state)return;uploadAfterAppBuild=!0;let environments=Object.values(builder.environments);if(environments.every(environment=>!environment.isBuilt))for(let environment of environments)await builder.build(environment);isReleaseReady(state)&&(state.releaseStarted=!0,await releaseBuild(state)),await writeReceipt()}},buildStart(){state&&!announced&&(announceSetup(state),warnForcedSourcemap(state.logger,overriddenSourcemap),remoteConfigFallback&&state.logger.warn(`[interfere] Could not load SDK settings from Interfere. The build uses the default settings.`),announced=!0)},async closeBundle(){state&&!uploadAfterAppBuild&&await releaseBuildWhenReady(state),await writeReceipt()},async writeBundle(output,bundle){output.dir&&await receipt?.recordOutput(output.dir,Object.keys(bundle))},outputOptions(output){return state?.uploadSourceMaps?{...output,sourcemapDebugIds:!1}:null},renderChunk:{order:`post`,handler(code){return state?.uploadSourceMaps?renderDebugId(code):null}},async config(userConfig,{command,mode}){let env=loadEnv(mode,resolveEnvDir(userConfig),``);mirrorPublicKeyForDevServer(command,env),overriddenSourcemap=forceSourcemapForUpload(userConfig,options,command,env);let browser=resolveBrowserConfig(options,env,command),baked=command===`build`&&browser.publicKey&&!isTestEnv()?await resolveBuildConfig(browser.apiHost??API_URL,browser.publicKey):null;return{define:omitUndefined({...browserDefine(browser),"import.meta.env.VITE_INTERFERE_INGEST_URL":baked?.ingestUrl?JSON.stringify(baked.ingestUrl):void 0,"import.meta.env.VITE_INTERFERE_SERVER_TRACING":baked?.serverTracing?JSON.stringify(baked.serverTracing):void 0,"import.meta.env.VITE_INTERFERE_LOG_SOURCING":baked?.logSourcing?JSON.stringify(baked.logSourcing):void 0,"import.meta.env.VITE_INTERFERE_PLUGINS":baked?.plugins?JSON.stringify(JSON.stringify(baked.plugins)):void 0}),ssr:{noExternal:[`@interfere/vite`]}}},async configResolved(config){let browser=resolveBrowserConfig(options,config.env,config.command),next=buildPluginState(options,config,browser);carryOverUploadProgress(state,next),state={...next,receipt},uploadAfterAppBuild||=config.builder!==void 0,config.command===`build`&&await receipt?.configure(config,{commitSha:browser.commitSha,releaseSlug:browser.releaseSlug,environment:browser.environment,publicKey:browser.publicKey,apiOrigin:browser.apiHost??API_URL,producerVersion:PRODUCER_VERSION})},enforce:`pre`,name:`interfere`,sharedDuringBuild:!0}}export{interfere};
1
+ import{PRODUCER_VERSION}from"./version.mjs";import{runGitCommand}from"./internal/build/git.mjs";import{createBuildReceipt}from"./internal/build/build-receipt.mjs";import{runReleasePipeline}from"./internal/build/pipeline.mjs";import{renderDebugId}from"./internal/build/render-debug-id.mjs";import{discoverByPairing}from"./internal/build/source-maps.mjs";import{resolveCommitSha}from"./internal/release-slug.mjs";import{resolveServiceName}from"./internal/service-name.mjs";import{cleanupSourceMaps}from"@interfere/build/cleanup-source-maps";import{sourceMapUploadFetch}from"@interfere/build/source-map-upload-fetch";import{InterfereClient}from"@interfere/javascript";import{createBuildConfigResolver}from"@interfere/build/config";import{API_URL,PUBLIC_KEY_HEADER}from"@interfere/constants/api";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{deriveReleaseSlug}from"@interfere/types/releases/slug";import{checkReleaseConfig,diagnoseRelease}from"@interfere/types/sdk/build-diagnostics";import{resolveDeployEnvironment}from"@interfere/types/sdk/deploy-environment";import{createHash}from"node:crypto";import{existsSync,readdirSync,statSync}from"node:fs";import path from"node:path";import{loadEnv}from"vite";const SERVER_RUNTIME_PLUGIN_HINTS=[`tanstack-start`,`nitro`,`@tanstack/react-start`],DIAGNOSTIC_CONTEXT={publicKeyEnvVar:`VITE_INTERFERE_PUBLIC_KEY`,docsUrl:`https://interfere.com/docs/sdk/vite`};function buildError(title,details){return Error([`Interfere: ${title}`,...details].join(`
2
+ `))}function logDiagnostic(logger,diagnostic){for(let line of[diagnostic.title,...diagnostic.details])logger[diagnostic.severity](`[interfere] ${line}`)}function isTestEnv(){return!!(process.env.VITEST||process.env.VITEST_WORKER_ID)}function pickEnvValue(env,key){let value=env[key];return value&&value.length>0?value:void 0}function hasServerRuntimePlugin(pluginNames){return pluginNames.some(name=>SERVER_RUNTIME_PLUGIN_HINTS.some(hint=>name.includes(hint)))}function normalizeOrigin(host){try{return new URL(host).origin}catch{return null}}function resolveApiHost(options,env){let host=options.apiHost??env.VITE_INTERFERE_API_URL;if(!host)return;let origin=normalizeOrigin(host);if(!origin)throw Error(`interfere({ apiHost }) requires a full URL, e.g. apiHost: "https://in.yourapp.com"`);return origin}function resolveBrowserConfig(options,env,command,root){let commitSha=options.commitSha??env.VITE_INTERFERE_COMMIT_SHA??resolveCommitSha(runGitCommand);return{apiHost:resolveApiHost(options,env),commitSha,environment:command===`build`?resolveDeployEnvironment({...env,...process.env}):null,publicKey:env.VITE_INTERFERE_PUBLIC_KEY,serviceName:resolveServiceName({option:options.serviceName,env,root}),releaseSlug:commitSha?deriveReleaseSlug(options.buildVariant?createHash(`sha256`).update(JSON.stringify([commitSha,options.buildVariant])).digest(`hex`):commitSha):null}}function browserDefine(browser){return{"import.meta.env.VITE_INTERFERE_API_HOST":JSON.stringify(browser.apiHost??null),"import.meta.env.VITE_INTERFERE_ENVIRONMENT":JSON.stringify(browser.environment),"import.meta.env.VITE_INTERFERE_RELEASE_SLUG":JSON.stringify(browser.releaseSlug),"import.meta.env.VITE_INTERFERE_SERVICE_NAME":JSON.stringify(browser.serviceName??null)}}function hasFreshServerSourceMaps(root,sinceMs){if(!existsSync(root))return!1;let entries=readdirSync(root,{withFileTypes:!0});for(let entry of entries){let absolute=path.join(root,entry.name);if(entry.isDirectory()){if(hasFreshServerSourceMaps(absolute,sinceMs))return!0;continue}if(entry.name.endsWith(`.map`)&&statSync(absolute).mtimeMs>=sinceMs)return!0}return!1}function isReleaseReady(state){return!!(state.releaseEnabled&&state.apiKey&&state.releaseSlug&&state.commitSha&&!state.releaseStarted)}function shouldWaitForServerOutput(state){return state.uploadSourceMaps&&state.serverBuild&&!hasFreshServerSourceMaps(state.serverSourcemapsDir,state.buildStartedAt)}function logSkippedRelease(logger,failure){if(failure.kind===`missing_source_provider`){logger.warn(`[interfere] release skipped: this surface has no version control provider configured. Configure one at https://interfere.com/~/${failure.config.org.slug}/apps/${failure.config.surface.slug}.`);return}logger.warn(`[interfere] release skipped: this surface has no deployment target configured. Configure one at https://interfere.com/~/${failure.config.org.slug}/apps/${failure.config.surface.slug}.`)}function logCleanupFailures(logger,failures){for(let failure of failures)logger.warn(`[interfere] cleanup left ${failure.absolute} on disk: ${failure.code??failure.message}`)}async function discoverUploadableSourceMaps(state){let discovered;try{discovered=await discoverByPairing({distDir:state.uploadOutDir,projectDir:state.projectDir,publicPathRewrites:[{from:`public`,to:``}],subtrees:[`.`]})}catch(error){return logDiagnostic(state.logger,diagnoseRelease(DIAGNOSTIC_CONTEXT,error)),null}return discovered.missingSourceMapCount>0&&state.logger.warn(`[interfere] ${discovered.missingSourceMapCount} referenced source map(s) not found. Errors in those chunks will not resolve to source.`),discovered.files.length===0?(state.logger.warn(`[interfere] No source maps found; the release remains unready. Enable source-map generation and upload them during the build.`),null):discovered}async function publishRelease(state,discovered){let{apiKey,apiUrl,releaseSlug,commitSha,logger,publicKey}=state,client=new InterfereClient({fetch:sourceMapUploadFetch,baseUrl:apiUrl,apiKey:{token:apiKey},headers:{"user-agent":PRODUCER_VERSION,[PUBLIC_KEY_HEADER]:publicKey},maxRetries:4,timeoutInSeconds:10}),releasesConfig=await client.releases.getReleasesConfig({abortSignal:AbortSignal.timeout(6e4)}),result=await runReleasePipeline({client,buildId:commitSha,bundler:`rollup`,discovery:discovered,environment:state.environment,producerVersion:PRODUCER_VERSION,releaseSlug,releasesConfig,capturePublication:state.receipt!==void 0,uploadSourceMaps:state.uploadSourceMaps});if(!result.ok){logSkippedRelease(logger,result.failure);return}if(logCleanupFailures(logger,result.data.cleanupFailures),result.data.publication&&state.receipt?.recordPublication(result.data.publication),!result.data.timing){logger.info(`[interfere] release ${result.data.releaseSlug} registered without source maps.`);return}logger.info(`[interfere] source-map stages (ms): create=${result.data.timing.createRelease}, sign=${result.data.timing.sign}, session=${result.data.timing.session}, upload=${result.data.timing.upload}, finalize=${result.data.timing.finalize}, receipt=${result.data.timing.receipt}, cleanup=${result.data.timing.cleanup}; bytes=${result.data.timing.totalBytes}.`),state.uploadComplete=!0,logger.info(`[interfere] release ${result.data.releaseSlug} confirmed — ${result.data.fileCount} maps, ${result.data.timing.total}ms.`)}async function releaseBuild(state){let{logger,releaseSlug}=state;logger.info(state.uploadSourceMaps?`[interfere] uploading source maps for release ${releaseSlug}…`:`[interfere] registering release ${releaseSlug} without source maps…`);let discovered=state.uploadSourceMaps?await discoverUploadableSourceMaps(state):{files:[],missingSourceMapCount:0,sourceFileCount:0};if(discovered){try{await publishRelease(state,discovered)}catch(error){logDiagnostic(logger,diagnoseRelease(DIAGNOSTIC_CONTEXT,error))}state.uploadComplete||logCleanupFailures(logger,await cleanupSourceMaps(discovered.files))}}async function releaseBuildWhenReady(state){if(isReleaseReady(state)){if(shouldWaitForServerOutput(state)){state.logger.info(`[interfere] waiting for server build output in ${path.relative(state.projectDir,state.serverSourcemapsDir)} before uploading source maps.`);return}state.releaseStarted=!0,await releaseBuild(state)}}function resolveReleaseEnv(config){return{...loadEnv(config.mode,config.envDir,``),...process.env}}function buildPluginState(options,config,browser){let{logger}=config,isProductionBuild=config.command===`build`,releaseEnv=resolveReleaseEnv(config),apiKey=pickEnvValue(releaseEnv,`INTERFERE_API_KEY`),apiUrl=pickEnvValue(releaseEnv,`INTERFERE_API_URL`)??API_URL,release=isProductionBuild?checkReleaseConfig(DIAGNOSTIC_CONTEXT,{apiKey,commitSha:browser.commitSha,releaseSlug:browser.releaseSlug}):null;if(release?.status===`fail`)throw buildError(release.title,release.details);let releaseEnabled=release?.status===`ok`,uploadOutDir=path.resolve(config.root,config.build.outDir),serverSourcemapsDir=options.serverSourcemapsDir?path.resolve(config.root,options.serverSourcemapsDir):path.join(uploadOutDir,`server`),serverBuild=hasServerRuntimePlugin(config.plugins.map(p=>p.name))||!!config.build.ssr;return{apiHost:browser.apiHost,apiKey,apiUrl,buildStartedAt:Math.floor(Date.now()/1e3)*1e3,commitSha:browser.commitSha,releaseEnabled,skippedRelease:release?.status===`skip`?release:null,uploadSourceMaps:releaseEnabled&&options.sourceMaps?.upload!==!1,environment:browser.environment,isProductionBuild,logger,projectDir:config.root,publicKey:browser.publicKey,releaseSlug:browser.releaseSlug,serverBuild,serverSourcemapsDir,uploadOutDir,releaseStarted:!1,uploadComplete:!1}}function announceSetup(state){if(state.logger.info(`[interfere] ingest → ${state.apiHost??`Interfere (default)`}`),!state.apiHost&&state.serverBuild&&state.logger.warn(`[interfere] Telemetry posts directly to Interfere. On a server-rendered app, set interfere({ apiHost }) to a subdomain of your own site to survive ad-blockers.`),state.isProductionBuild&&!state.publicKey&&state.logger.warn(`[interfere] VITE_INTERFERE_PUBLIC_KEY is not set, so this build sends no events. Set it on builds that you deploy.`),state.skippedRelease)for(let line of[state.skippedRelease.title,...state.skippedRelease.details])state.logger.warn(`[interfere] ${line}`)}function mirrorPublicKeyForDevServer(command,env){if(command!==`serve`)return;let publicKey=env.VITE_INTERFERE_PUBLIC_KEY;publicKey&&!process.env.VITE_INTERFERE_PUBLIC_KEY&&(process.env.VITE_INTERFERE_PUBLIC_KEY=publicKey)}function resolveRoot(userConfig){return userConfig.root?path.resolve(process.cwd(),userConfig.root):process.cwd()}function resolveEnvDir(userConfig){let root=resolveRoot(userConfig);return userConfig.envDir===!1||userConfig.envDir===void 0||userConfig.envDir===``?root:path.resolve(root,userConfig.envDir)}function forceSourcemapForUpload(userConfig,options,command,env){if(!(command===`build`&&options.sourceMaps?.upload!==!1&&pickEnvValue(env,`INTERFERE_API_KEY`)))return;let previous=userConfig.build?.sourcemap;return userConfig.build={...userConfig.build,sourcemap:!0},previous===void 0||previous===!0?void 0:previous}function carryOverUploadProgress(previous,next){previous?.serverBuild&&next.serverBuild&&(next.uploadOutDir=previous.uploadOutDir,next.serverSourcemapsDir=previous.serverSourcemapsDir,next.releaseStarted=previous.releaseStarted,next.uploadComplete=previous.uploadComplete,next.buildStartedAt=previous.buildStartedAt)}function warnForcedSourcemap(logger,overridden){overridden!==void 0&&logger.warn(`[interfere] Forced build.sourcemap to true (was ${JSON.stringify(overridden)}). Source maps are required to symbolicate errors; they are uploaded then deleted from the build output, so they are never served. Pass interfere({ sourceMaps: { upload: false } }) to opt out.`)}function disabledPlugin(){return{name:`interfere`,config(){return{define:{"import.meta.env.VITE_INTERFERE_DISABLED":JSON.stringify(`true`)},ssr:{noExternal:[`@interfere/vite`]}}}}}function rejectRemovedOptions(options){let sourceMaps=options.sourceMaps;if(sourceMaps!==void 0&&(typeof sourceMaps!=`object`||!sourceMaps))throw buildError(`Removed: interfere({ sourceMaps })`,['`sourceMaps: true`, `sourceMaps: false` and `sourceMaps: "required"` were removed.',"Use `sourceMaps: { upload: false }` to register the release without source maps, or `disable: true` to turn Interfere off for the build.",`See: ${DIAGNOSTIC_CONTEXT.docsUrl}`]);if(`sourceMapReadinessTimeoutMs`in options)throw buildError(`Removed: interfere({ sourceMapReadinessTimeoutMs })`,[`The build no longer waits for source-map processing. Remove the option.`,`See: ${DIAGNOSTIC_CONTEXT.docsUrl}`])}function interfere(options={}){if(rejectRemovedOptions(options),options.disable)return disabledPlugin();let receipt=options.buildReceipt?createBuildReceipt(options.buildReceipt):void 0,remoteConfigFallback=!1,resolveBuildConfig=createBuildConfigResolver(result=>{receipt?.observeConfig(result),remoteConfigFallback||=result.status===`fallback`}),state=null,overriddenSourcemap,announced=!1,uploadAfterAppBuild=!1,writeReceipt=async()=>{try{await receipt?.write()}catch{state?.logger.warn(`[interfere] Private build receipt could not be written.`)}};return{buildApp:{order:`post`,async handler(builder){if(!state)return;uploadAfterAppBuild=!0;let environments=Object.values(builder.environments);if(environments.every(environment=>!environment.isBuilt))for(let environment of environments)await builder.build(environment);isReleaseReady(state)&&(state.releaseStarted=!0,await releaseBuild(state)),await writeReceipt()}},buildStart(){state&&!announced&&(announceSetup(state),warnForcedSourcemap(state.logger,overriddenSourcemap),remoteConfigFallback&&state.logger.warn(`[interfere] Could not load SDK settings from Interfere. The build uses the default settings.`),announced=!0)},async closeBundle(){state&&!uploadAfterAppBuild&&await releaseBuildWhenReady(state),await writeReceipt()},async writeBundle(output,bundle){output.dir&&await receipt?.recordOutput(output.dir,Object.keys(bundle))},outputOptions(output){return state?.uploadSourceMaps?{...output,sourcemapDebugIds:!1}:null},renderChunk:{order:`post`,handler(code){return state?.uploadSourceMaps?renderDebugId(code):null}},async config(userConfig,{command,mode}){let env=loadEnv(mode,resolveEnvDir(userConfig),``);mirrorPublicKeyForDevServer(command,env),overriddenSourcemap=forceSourcemapForUpload(userConfig,options,command,env);let browser=resolveBrowserConfig(options,env,command,resolveRoot(userConfig)),baked=command===`build`&&browser.publicKey&&!isTestEnv()?await resolveBuildConfig(browser.apiHost??API_URL,browser.publicKey):null;return{define:omitUndefined({...browserDefine(browser),"import.meta.env.VITE_INTERFERE_INGEST_URL":baked?.ingestUrl?JSON.stringify(baked.ingestUrl):void 0,"import.meta.env.VITE_INTERFERE_SERVER_TRACING":baked?.serverTracing?JSON.stringify(baked.serverTracing):void 0,"import.meta.env.VITE_INTERFERE_LOG_SOURCING":baked?.logSourcing?JSON.stringify(baked.logSourcing):void 0,"import.meta.env.VITE_INTERFERE_PLUGINS":baked?.plugins?JSON.stringify(JSON.stringify(baked.plugins)):void 0}),ssr:{noExternal:[`@interfere/vite`]}}},async configResolved(config){let browser=resolveBrowserConfig(options,config.env,config.command,config.root),next=buildPluginState(options,config,browser);carryOverUploadProgress(state,next),state={...next,receipt},uploadAfterAppBuild||=config.builder!==void 0,config.command===`build`&&await receipt?.configure(config,{commitSha:browser.commitSha,releaseSlug:browser.releaseSlug,environment:browser.environment,publicKey:browser.publicKey,apiOrigin:browser.apiHost??API_URL,producerVersion:PRODUCER_VERSION})},enforce:`pre`,name:`interfere`,sharedDuringBuild:!0}}export{interfere};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interfere/vite",
3
- "version": "12.1.4",
3
+ "version": "12.2.0",
4
4
  "description": "Vite build plugin and client + server initialization for Interfere.",
5
5
  "keywords": [
6
6
  "error-tracking",
@@ -62,12 +62,12 @@
62
62
  "typecheck": "bun check"
63
63
  },
64
64
  "dependencies": {
65
- "@interfere/build": "^0.3.2",
66
- "@interfere/constants": "^9.2.0",
67
- "@interfere/helpers": "^9.2.1",
65
+ "@interfere/build": "^0.3.3",
66
+ "@interfere/constants": "^9.3.0",
67
+ "@interfere/helpers": "^9.3.0",
68
68
  "@interfere/javascript": "^0.3.0",
69
- "@interfere/react": "^12.1.4",
70
- "@interfere/types": "^10.3.0",
69
+ "@interfere/react": "^12.2.0",
70
+ "@interfere/types": "^10.4.0",
71
71
  "@opentelemetry/api": "1.9.1",
72
72
  "@opentelemetry/api-logs": "0.222.0",
73
73
  "@opentelemetry/baggage-span-processor": "0.5.0",
@@ -86,7 +86,7 @@
86
86
  "zod": "4.6.6"
87
87
  },
88
88
  "devDependencies": {
89
- "@interfere/test-utils": "^10.0.1",
89
+ "@interfere/test-utils": "^10.0.2",
90
90
  "@interfere/typescript-config": "^9.0.2",
91
91
  "@interfere/vitest-config": "0.0.0",
92
92
  "@jridgewell/trace-mapping": "0.3.31",