@interfere/vite 12.0.4 → 12.0.5

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
@@ -135,14 +135,15 @@ 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. |
141
- | `INTERFERE_API_KEY` | Yes for release metadata | Interfere secret key in the `interfere_secret_us_*` or `interfere_secret_eu_*` format. Used by the plugin at build time. Without it, release metadata is not published and the collector rejects events as `COLLECTOR_RELEASE_NOT_FOUND`. |
142
- | `INTERFERE_API_URL` | No | Override the collector URL the build pipeline targets. Defaults to `https://in.interfere.com`. |
143
- | `VITE_INTERFERE_API_URL` | No | Override the collector URL the browser kernel targets in direct mode. Proxy mode uses `INTERFERE_API_URL` server-side. |
144
- | `VITE_INTERFERE_COMMIT_SHA` | No | Override the auto-detected commit SHA used to derive the release slug. Defaults to `git rev-parse HEAD`. |
145
- | `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. |
141
+ | `INTERFERE_API_KEY` | Yes for release metadata | Interfere secret key in the `interfere_secret_us_*` or `interfere_secret_eu_*` format. Used by the plugin at build time. Without it, release metadata is not published and the collector rejects events as `COLLECTOR_RELEASE_NOT_FOUND`. |
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://in.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. |
146
147
 
147
148
  ## Plugin Options
148
149
 
@@ -1 +1 @@
1
- import{readPublicationStatus}from"./read-publication-status.mjs";import{uploadSourceMapChunks}from"@interfere/build/upload-source-map-chunks";import{completeSourceMapsResponse}from"@interfere/types/data/source-maps";import{sourceMapPublicationStatus}from"@interfere/types/data/source-map-status";import{extractRichness}from"@interfere/types/sdk/source-map-richness";async function transferSourceMaps(input){let{client,bundler,releaseSlug}=input,{files,sourceFileCount}=input.discovery,start=performance.now(),signFiles=files.map(file=>({path:file.path,sizeBytes:new TextEncoder().encode(file.content).byteLength,...extractRichness(file.content)})),environment=input.environment??void 0,signed=await client.releases.signSourceMapUploads({releaseSlug,environment,files:signFiles},{abortSignal:AbortSignal.timeout(6e4)}),signedAt=performance.now(),session=null;if(input.readinessTimeoutMs===void 0?input.capturePublication&&(session=await readPublicationStatus(input)):session=sourceMapPublicationStatus.parse(await client.releases.getSourceMapPublicationStatus({releaseSlug,environment},{abortSignal:AbortSignal.timeout(6e4)})),input.readinessTimeoutMs!==void 0&&session&&(!session.sessionId||session.releaseSlug!==releaseSlug||session.environment!==input.environment))throw Error(`Source-map publication requires an identified upload session for this environment`);let sessionReadAt=performance.now(),totalBytes=await uploadSourceMapChunks({client,environment,releaseSlug,files,signed}),uploadedAt=performance.now(),completion={bundler,sourceFileCount,files:files.map(({path,hash,debugId,chunkUrl})=>({path,hash,debugId,chunkUrl}))},accepted=completeSourceMapsResponse.safeParse(await client.releases.finalizeSourceMaps({releaseSlug,environment,...completion},{abortSignal:AbortSignal.timeout(Math.max(0,signed.expiresAt*1e3-Date.now()))}));if(!accepted.success||!accepted.data.ok||accepted.data.fileCount!==files.length)throw Error(`Source-map server did not accept the complete batch`);let acceptedAt=performance.now();return{completion,session,acceptedAt,timing:{sign:Math.round(signedAt-start),session:Math.round(sessionReadAt-signedAt),upload:Math.round(uploadedAt-sessionReadAt),finalize:Math.round(acceptedAt-uploadedAt),totalBytes}}}export{transferSourceMaps};
1
+ import{readPublicationStatus}from"./read-publication-status.mjs";import{uploadSourceMapChunks}from"@interfere/build/upload-source-map-chunks";import{completeSourceMapsResponse}from"@interfere/types/data/source-maps";import{sourceMapPublicationStatus}from"@interfere/types/data/source-map-status";import{extractRichness}from"@interfere/types/sdk/source-map-richness";async function transferSourceMaps(input){let{client,bundler,releaseSlug}=input,{files,sourceFileCount}=input.discovery,start=performance.now(),signFiles=files.map(file=>({path:file.path,sizeBytes:new TextEncoder().encode(file.content).byteLength,...extractRichness(file.content)})),environment=input.environment??void 0,signed=await client.releases.signSourceMapUploads({releaseSlug,environment,files:signFiles},{abortSignal:AbortSignal.timeout(6e4)}),signedAt=performance.now(),session=null;if(input.readinessTimeoutMs===void 0?input.capturePublication&&(session=await readPublicationStatus(input)):session=sourceMapPublicationStatus.parse(await client.releases.getSourceMapPublicationStatus({releaseSlug,environment},{abortSignal:AbortSignal.timeout(6e4)})),input.readinessTimeoutMs!==void 0&&session&&(!session.sessionId||session.releaseSlug!==releaseSlug||session.environment!==input.environment))throw Error(`Source-map publication requires an identified upload session for this environment`);let sessionReadAt=performance.now(),totalBytes=await uploadSourceMapChunks({compression:`gzip`,client,environment,releaseSlug,files,signed}),uploadedAt=performance.now(),completion={bundler,sourceFileCount,files:files.map(({path,hash,debugId,chunkUrl})=>({path,hash,debugId,chunkUrl}))},accepted=completeSourceMapsResponse.safeParse(await client.releases.finalizeSourceMaps({releaseSlug,environment,...completion},{abortSignal:AbortSignal.timeout(Math.max(0,signed.expiresAt*1e3-Date.now()))}));if(!accepted.success||!accepted.data.ok||accepted.data.fileCount!==files.length)throw Error(`Source-map server did not accept the complete batch`);let acceptedAt=performance.now();return{completion,session,acceptedAt,timing:{sign:Math.round(signedAt-start),session:Math.round(sessionReadAt-signedAt),upload:Math.round(uploadedAt-sessionReadAt),finalize:Math.round(acceptedAt-uploadedAt),totalBytes}}}export{transferSourceMaps};
package/dist/package.mjs CHANGED
@@ -1 +1 @@
1
- var name=`@interfere/vite`,version=`12.0.4`;export{name,version};
1
+ var name=`@interfere/vite`,version=`12.0.5`;export{name,version};
package/dist/plugin.mjs CHANGED
@@ -1,3 +1,3 @@
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{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{classifyReleasePipelineError,diagnoseRelease,isInterfereApiKey}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`};function reportReleaseDiagnostic(logger,code,input){let diagnostic=diagnoseRelease(code,DIAGNOSTIC_CONTEXT,input);logger.warn(`[interfere] ${diagnostic.title}`);for(let line of diagnostic.details)logger.warn(`[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 failMissingPublicKey(){let lines=[`interfere() requires VITE_INTERFERE_PUBLIC_KEY to identify your project at runtime.`,`Set the VITE_INTERFERE_PUBLIC_KEY environment variable, or remove interfere() from your Vite config.`,`See: https://interfere.com/docs/sdk/vite`];if(isTestEnv())throw Error(`Interfere: Public key not set\n${lines.join(`
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{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{classifyReleasePipelineError,diagnoseRelease,isInterfereApiKey}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`};function reportReleaseDiagnostic(logger,code,input){let diagnostic=diagnoseRelease(code,DIAGNOSTIC_CONTEXT,input);logger.warn(`[interfere] ${diagnostic.title}`);for(let line of diagnostic.details)logger.warn(`[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 failMissingPublicKey(){let lines=[`interfere() requires VITE_INTERFERE_PUBLIC_KEY to identify your project at runtime.`,`Set the VITE_INTERFERE_PUBLIC_KEY environment variable, or remove interfere() from your Vite config.`,`See: https://interfere.com/docs/sdk/vite`];if(isTestEnv())throw Error(`Interfere: Public key not set\n${lines.join(`
2
2
  `)}`);console.error(`
3
- ⨯ Interfere → Public key not set`);for(let i=0;i<lines.length;i+=1){let line=lines[i];if(!line)continue;let connector=i===lines.length-1?`└`:`├`;console.error(` ${connector} ${line}`)}process.exit(1)}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 isUploadReady(state){return!!(state.isProductionBuild&&state.enableUpload&&state.apiKey&&state.releaseSlug&&state.commitSha&&!state.uploadStarted)}function shouldWaitForServerOutput(state){return 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}/surfaces/${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}/surfaces/${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 uploadSourceMaps(state){let{apiKey,apiUrl,releaseSlug,commitSha,logger,projectDir,uploadOutDir}=state;if(!isInterfereApiKey(apiKey)){if(reportReleaseDiagnostic(logger,`API_KEY_WRONG_PREFIX`,{apiKey}),state.requireUpload)throw Error(`Required source-map publication needs a valid INTERFERE_API_KEY`);return}logger.info(`[interfere] uploading source maps for release ${releaseSlug}…`);try{let client=new InterfereClient({baseUrl:apiUrl,apiKey:{token:apiKey},headers:{"user-agent":PRODUCER_VERSION},maxRetries:4,timeoutInSeconds:10}),[discovered,releasesConfig]=await Promise.all([discoverByPairing({distDir:uploadOutDir,projectDir,publicPathRewrites:[{from:`public`,to:``}],subtrees:[`.`]}),client.releases.getReleasesConfig({abortSignal:AbortSignal.timeout(6e4)})]);if(discovered.files.length===0){if(state.requireUpload)throw Error(`No source maps found for required publication`);logger.warn(`[interfere] No source maps found; the release remains unready. Enable source-map generation and upload them during the build.`);return}if(state.requireUpload&&discovered.files.length!==discovered.sourceFileCount)throw Error(`Required source-map publication is incomplete: loaded ${discovered.files.length} of ${discovered.sourceFileCount} referenced maps`);let result=await runReleasePipeline({client,buildId:commitSha,bundler:`rollup`,discovery:discovered,environment:state.environment,producerVersion:PRODUCER_VERSION,releaseSlug,releasesConfig,readinessTimeoutMs:state.readinessTimeoutMs,capturePublication:state.receipt!==void 0});if(!result.ok){if(logSkippedRelease(logger,result.failure),state.requireUpload)throw Error(`Required source-map publication failed: ${result.failure.kind}`);return}logCleanupFailures(logger,result.data.cleanupFailures),result.data.publication&&state.receipt?.recordPublication(result.data.publication),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}, readiness=${result.data.timing.readiness}, 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.`)}catch(error){let message=error instanceof Error?error.message:String(error);if(reportReleaseDiagnostic(logger,classifyReleasePipelineError(error),{errorMessage:message}),state.requireUpload)throw error}}async function uploadSourceMapsWhenReady(state){return isUploadReady(state)?shouldWaitForServerOutput(state)?(state.logger.info(`[interfere] waiting for server build output in ${path.relative(state.projectDir,state.serverSourcemapsDir)} before uploading source maps.`),!1):(state.uploadStarted=!0,await uploadSourceMaps(state),state.uploadComplete):state.uploadComplete}function emitSetupWarnings(state){let{logger,apiKey,releaseSlug,sourceMapsExplicit,isProductionBuild}=state;sourceMapsExplicit!==!1&&apiKey&&!releaseSlug&&logger.warn(`[interfere] No commit SHA available. Release metadata publishing is disabled. Set INTERFERE_SOURCE_ID, expose VERCEL_GIT_COMMIT_SHA / GITHUB_SHA, or run inside a git checkout.`),isProductionBuild&&sourceMapsExplicit!==!1&&!apiKey&&logger.warn("[interfere] INTERFERE_API_KEY is not set. Release metadata will not be published. Create an Interfere secret key in the dashboard and set it in your build environment, or pass `interfere({ sourceMaps: false })` to suppress this warning if release metadata is intentionally handled out-of-band.")}function resolveReleaseEnv(config){return{...loadEnv(config.mode,config.envDir,``),...process.env}}function shouldEnableUpload({apiKey,isProductionBuild,releaseSlug,sourceMaps}){return isProductionBuild?sourceMaps===`required`||(sourceMaps??!!(apiKey&&releaseSlug)):!1}function buildPluginState(options,config,browser){let{logger}=config,isProductionBuild=config.command===`build`;isProductionBuild&&!browser.publicKey&&failMissingPublicKey();let releaseEnv=resolveReleaseEnv(config),apiKey=pickEnvValue(releaseEnv,`INTERFERE_API_KEY`),apiUrl=pickEnvValue(releaseEnv,`INTERFERE_API_URL`)??API_URL,requireUpload=isProductionBuild&&options.sourceMaps===`required`,readinessTimeoutMs=options.sourceMapReadinessTimeoutMs;if(readinessTimeoutMs!==void 0&&(options.sourceMaps!==`required`||!Number.isSafeInteger(readinessTimeoutMs)||readinessTimeoutMs<1||readinessTimeoutMs>6e5))throw Error(`sourceMapReadinessTimeoutMs requires sourceMaps: required and an integer between 1 and 600000 milliseconds`);if(requireUpload&&!apiKey)throw Error(`Required source-map publication needs INTERFERE_API_KEY`);if(requireUpload&&!browser.commitSha)throw Error(`Required source-map publication needs a commit SHA`);let 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,readinessTimeoutMs,apiKey,apiUrl,buildStartedAt:Math.floor(Date.now()/1e3)*1e3,commitSha:browser.commitSha,enableUpload:shouldEnableUpload({apiKey,isProductionBuild,releaseSlug:browser.releaseSlug,sourceMaps:options.sourceMaps}),environment:browser.environment,isProductionBuild,logger,projectDir:config.root,publicKey:browser.publicKey,requireUpload,releaseSlug:browser.releaseSlug,serverBuild,serverSourcemapsDir,uploadOutDir,uploadStarted:!1,uploadComplete:!1}}function announceSetup(state,options){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.`),emitSetupWarnings({apiKey:state.apiKey,isProductionBuild:state.isProductionBuild,logger:state.logger,releaseSlug:state.releaseSlug,sourceMapsExplicit:options.sourceMaps})}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!==!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.uploadStarted=previous.uploadStarted,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: false }) to opt out.`)}function interfere(options={}){let receipt=options.buildReceipt?createBuildReceipt(options.buildReceipt):void 0,resolveBuildConfig=createBuildConfigResolver(receipt?.observeConfig),state=null,overriddenSourcemap,announced=!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?.requireUpload)return;let environments=Object.values(builder.environments);if(environments.every(environment=>!environment.isBuilt))for(let environment of environments)await builder.build(environment);if(!await uploadSourceMapsWhenReady(state))throw Error(`Required publication is waiting for fresh server source maps`);await writeReceipt()}},buildStart(){state&&!announced&&(announceSetup(state,options),warnForcedSourcemap(state.logger,overriddenSourcemap),announced=!0)},async closeBundle(){state&&await uploadSourceMapsWhenReady(state),await writeReceipt()},async writeBundle(output,bundle){output.dir&&await receipt?.recordOutput(output.dir,Object.keys(bundle))},outputOptions(output){return state?.enableUpload?{...output,sourcemapDebugIds:!1}:null},renderChunk:{order:`post`,handler(code){return state?.enableUpload?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},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};
3
+ ⨯ Interfere → Public key not set`);for(let i=0;i<lines.length;i+=1){let line=lines[i];if(!line)continue;let connector=i===lines.length-1?`└`:`├`;console.error(` ${connector} ${line}`)}process.exit(1)}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 isUploadReady(state){return!!(state.isProductionBuild&&state.enableUpload&&state.apiKey&&state.releaseSlug&&state.commitSha&&!state.uploadStarted)}function shouldWaitForServerOutput(state){return 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}/surfaces/${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}/surfaces/${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 uploadSourceMaps(state){let{apiKey,apiUrl,releaseSlug,commitSha,logger,projectDir,uploadOutDir}=state;if(!isInterfereApiKey(apiKey)){if(reportReleaseDiagnostic(logger,`API_KEY_WRONG_PREFIX`,{apiKey}),state.requireUpload)throw Error(`Required source-map publication needs a valid INTERFERE_API_KEY`);return}logger.info(`[interfere] uploading source maps for release ${releaseSlug}…`);try{let client=new InterfereClient({fetch:sourceMapUploadFetch,baseUrl:apiUrl,apiKey:{token:apiKey},headers:{"user-agent":PRODUCER_VERSION},maxRetries:4,timeoutInSeconds:10}),[discovered,releasesConfig]=await Promise.all([discoverByPairing({distDir:uploadOutDir,projectDir,publicPathRewrites:[{from:`public`,to:``}],subtrees:[`.`]}),client.releases.getReleasesConfig({abortSignal:AbortSignal.timeout(6e4)})]);if(discovered.files.length===0){if(state.requireUpload)throw Error(`No source maps found for required publication`);logger.warn(`[interfere] No source maps found; the release remains unready. Enable source-map generation and upload them during the build.`);return}if(state.requireUpload&&discovered.files.length!==discovered.sourceFileCount)throw Error(`Required source-map publication is incomplete: loaded ${discovered.files.length} of ${discovered.sourceFileCount} referenced maps`);let result=await runReleasePipeline({client,buildId:commitSha,bundler:`rollup`,discovery:discovered,environment:state.environment,producerVersion:PRODUCER_VERSION,releaseSlug,releasesConfig,readinessTimeoutMs:state.readinessTimeoutMs,capturePublication:state.receipt!==void 0});if(!result.ok){if(logSkippedRelease(logger,result.failure),state.requireUpload)throw Error(`Required source-map publication failed: ${result.failure.kind}`);return}logCleanupFailures(logger,result.data.cleanupFailures),result.data.publication&&state.receipt?.recordPublication(result.data.publication),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}, readiness=${result.data.timing.readiness}, 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.`)}catch(error){let message=error instanceof Error?error.message:String(error);if(reportReleaseDiagnostic(logger,classifyReleasePipelineError(error),{errorMessage:message}),state.requireUpload)throw error}}async function uploadSourceMapsWhenReady(state){return isUploadReady(state)?shouldWaitForServerOutput(state)?(state.logger.info(`[interfere] waiting for server build output in ${path.relative(state.projectDir,state.serverSourcemapsDir)} before uploading source maps.`),!1):(state.uploadStarted=!0,await uploadSourceMaps(state),state.uploadComplete):state.uploadComplete}function emitSetupWarnings(state){let{logger,apiKey,releaseSlug,sourceMapsExplicit,isProductionBuild}=state;sourceMapsExplicit!==!1&&apiKey&&!releaseSlug&&logger.warn(`[interfere] No commit SHA available. Release metadata publishing is disabled. Set INTERFERE_SOURCE_ID, expose VERCEL_GIT_COMMIT_SHA / GITHUB_SHA, or run inside a git checkout.`),isProductionBuild&&sourceMapsExplicit!==!1&&!apiKey&&logger.warn("[interfere] INTERFERE_API_KEY is not set. Release metadata will not be published. Create an Interfere secret key in the dashboard and set it in your build environment, or pass `interfere({ sourceMaps: false })` to suppress this warning if release metadata is intentionally handled out-of-band.")}function resolveReleaseEnv(config){return{...loadEnv(config.mode,config.envDir,``),...process.env}}function shouldEnableUpload({apiKey,isProductionBuild,releaseSlug,sourceMaps}){return isProductionBuild?sourceMaps===`required`||(sourceMaps??!!(apiKey&&releaseSlug)):!1}function buildPluginState(options,config,browser){let{logger}=config,isProductionBuild=config.command===`build`;isProductionBuild&&!browser.publicKey&&failMissingPublicKey();let releaseEnv=resolveReleaseEnv(config),apiKey=pickEnvValue(releaseEnv,`INTERFERE_API_KEY`),apiUrl=pickEnvValue(releaseEnv,`INTERFERE_API_URL`)??API_URL,requireUpload=isProductionBuild&&options.sourceMaps===`required`,readinessTimeoutMs=options.sourceMapReadinessTimeoutMs;if(readinessTimeoutMs!==void 0&&(options.sourceMaps!==`required`||!Number.isSafeInteger(readinessTimeoutMs)||readinessTimeoutMs<1||readinessTimeoutMs>6e5))throw Error(`sourceMapReadinessTimeoutMs requires sourceMaps: required and an integer between 1 and 600000 milliseconds`);if(requireUpload&&!apiKey)throw Error(`Required source-map publication needs INTERFERE_API_KEY`);if(requireUpload&&!browser.commitSha)throw Error(`Required source-map publication needs a commit SHA`);let 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,readinessTimeoutMs,apiKey,apiUrl,buildStartedAt:Math.floor(Date.now()/1e3)*1e3,commitSha:browser.commitSha,enableUpload:shouldEnableUpload({apiKey,isProductionBuild,releaseSlug:browser.releaseSlug,sourceMaps:options.sourceMaps}),environment:browser.environment,isProductionBuild,logger,projectDir:config.root,publicKey:browser.publicKey,requireUpload,releaseSlug:browser.releaseSlug,serverBuild,serverSourcemapsDir,uploadOutDir,uploadStarted:!1,uploadComplete:!1}}function announceSetup(state,options){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.`),emitSetupWarnings({apiKey:state.apiKey,isProductionBuild:state.isProductionBuild,logger:state.logger,releaseSlug:state.releaseSlug,sourceMapsExplicit:options.sourceMaps})}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!==!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.uploadStarted=previous.uploadStarted,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: false }) to opt out.`)}function interfere(options={}){let receipt=options.buildReceipt?createBuildReceipt(options.buildReceipt):void 0,resolveBuildConfig=createBuildConfigResolver(receipt?.observeConfig),state=null,overriddenSourcemap,announced=!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?.requireUpload)return;let environments=Object.values(builder.environments);if(environments.every(environment=>!environment.isBuilt))for(let environment of environments)await builder.build(environment);if(!await uploadSourceMapsWhenReady(state))throw Error(`Required publication is waiting for fresh server source maps`);await writeReceipt()}},buildStart(){state&&!announced&&(announceSetup(state,options),warnForcedSourcemap(state.logger,overriddenSourcemap),announced=!0)},async closeBundle(){state&&await uploadSourceMapsWhenReady(state),await writeReceipt()},async writeBundle(output,bundle){output.dir&&await receipt?.recordOutput(output.dir,Object.keys(bundle))},outputOptions(output){return state?.enableUpload?{...output,sourcemapDebugIds:!1}:null},renderChunk:{order:`post`,handler(code){return state?.enableUpload?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},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.0.4",
3
+ "version": "12.0.5",
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.2.2",
65
+ "@interfere/build": "^0.3.0",
66
66
  "@interfere/constants": "^9.2.0",
67
67
  "@interfere/helpers": "^9.2.0",
68
68
  "@interfere/javascript": "^0.2.1",
69
- "@interfere/react": "^12.0.4",
70
- "@interfere/types": "^10.2.1",
69
+ "@interfere/react": "^12.0.5",
70
+ "@interfere/types": "^10.2.2",
71
71
  "@opentelemetry/api": "^1.9.1",
72
72
  "@opentelemetry/api-logs": "^0.222.0",
73
73
  "@opentelemetry/baggage-span-processor": "^0.5.0",