@interfere/vite 12.1.1 → 12.1.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Interfere, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -140,7 +140,7 @@ For **Vercel preview / production**, set `INTERFERE_API_KEY` in your `.env.local
140
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
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
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`. |
143
+ | `INTERFERE_API_URL` | No | Override the collector URL the build pipeline targets. Defaults to `https://api.interfere.com`. |
144
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
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
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. |
@@ -0,0 +1,90 @@
1
+ # Third-party notices
2
+
3
+ The MIT license in LICENSE applies to code authored by Interfere. Third-party components retain their respective licenses.
4
+
5
+ ## UAParser.js PRO Enterprise
6
+
7
+ - Package: `@ua-parser-js/pro-enterprise`
8
+ - Version: 2.0.10
9
+ - Project: https://uaparser.dev/
10
+ - License: UAParser.js PRO Enterprise, Version 2, July 2024
11
+
12
+ The React SDK uses this package for browser, device, and bot detection. The Next.js and Vite SDKs depend on the React SDK. UAParser is not covered by the Interfere SDKs' MIT license.
13
+
14
+ The license below restricts client use to the original functionality provided by the license holder and prohibits extracting, reproducing, or reusing UAParser independently. It also requires written consent for the open-source distribution described in its restrictions. This notice does not grant additional rights or replace any required permission.
15
+
16
+ ### Upstream license
17
+
18
+ ```text
19
+ # UAPARSER.JS PRO ENTERPRISE LICENSE
20
+
21
+ Version 2, July 2024
22
+
23
+ Copyright (C) 2023-2024 Faisal Salman <f+ua-parser-js@faisalman.com>
24
+
25
+ ---
26
+
27
+ ## Definitions
28
+
29
+ "We" are the team behind UAParser.js.
30
+
31
+ "You" are the individual, organization, or company who is responsible for purchasing this license.
32
+
33
+ "The Code" is UAParser.js.
34
+
35
+ "Project" is what you built with The Code.
36
+
37
+ ---
38
+
39
+ ## License
40
+
41
+ We retain all title, intellectual property, and ownership rights to The Code.
42
+
43
+ The Code is licensed, not sold, to You for use solely subject to the terms and conditions detailed here.
44
+
45
+ We grant You (and only You) a limited, non-exclusive, non-transferable, non-sublicensable, royalty-free right to use, copy, and modify The Code.
46
+
47
+ This license is only valid for You as one (1) individual, organization, or company and cannot be transferred to another individual, organization, or company.
48
+
49
+ ---
50
+
51
+ ## Rights
52
+
53
+ You may use, copy, and modify The Code for an indefinite number of Projects.
54
+
55
+ You have the right to receive lifetime updates and one (1) year of support, starting from the time you make the purchase.
56
+
57
+ ---
58
+
59
+ ## Restrictions
60
+
61
+ You may not redistribute The Code, as-is or modified, except as a part of a Project that you made.
62
+
63
+ If You transfer a Project to a client, the use of The Code must be limited to the original functionality that You created for them. The Code must not be extracted, reproduced, or used in any other way. You must inform your client of this condition.
64
+
65
+ You may not deliver a Project that contains The Code as an open-source Project that might be used for commercial purposes by the general public, except with our written consent.
66
+
67
+ You may not use The Code for unlawful, inappropriate, illegal, or offensive purposes.
68
+
69
+ ---
70
+
71
+ ## Limitations
72
+
73
+ The Code is provided 'as is' without warranty of any kind, expressed or implied.
74
+
75
+ We shall not be liable for any damages, including but not limited to, direct, indirect, special, incidental, or consequential damages or losses that occur by the use of The Code.
76
+
77
+ We reserve the right to discontinue the lifetime updates for The Code at any time, with or without notice.
78
+
79
+ We offer support only for questions within the scope of The Code's functionality or related issues at our sole discretion.
80
+
81
+ ---
82
+
83
+ ## Termination
84
+
85
+ This license is effective indefinitely but can be revoked at any time if there is a violation of any of the terms.
86
+
87
+ ---
88
+
89
+ END OF TERMS AND CONDITIONS
90
+ ```
package/dist/package.mjs CHANGED
@@ -1 +1 @@
1
- var name=`@interfere/vite`,version=`12.1.1`;export{name,version};
1
+ var name=`@interfere/vite`,version=`12.1.3`;export{name,version};
package/dist/plugin.mjs CHANGED
@@ -1,2 +1,2 @@
1
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}/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 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};
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interfere/vite",
3
- "version": "12.1.1",
3
+ "version": "12.1.3",
4
4
  "description": "Vite build plugin and client + server initialization for Interfere.",
5
5
  "keywords": [
6
6
  "error-tracking",
@@ -12,9 +12,7 @@
12
12
  "vite"
13
13
  ],
14
14
  "homepage": "https://interfere.com",
15
- "bugs": {
16
- "url": "mailto:support@interfere.com"
17
- },
15
+ "bugs": "mailto:support@interfere.com",
18
16
  "license": "MIT",
19
17
  "author": "Interfere <support@interfere.com> (https://interfere.com)",
20
18
  "repository": {
@@ -23,7 +21,9 @@
23
21
  "directory": "src/packages/public/vite"
24
22
  },
25
23
  "files": [
26
- "dist"
24
+ "dist",
25
+ "LICENSE",
26
+ "THIRD_PARTY_NOTICES.md"
27
27
  ],
28
28
  "type": "module",
29
29
  "exports": {
@@ -58,7 +58,7 @@
58
58
  },
59
59
  "scripts": {
60
60
  "build": "tsdown",
61
- "test": "vitest run --config ../../../../vitest.config.ts",
61
+ "test": "node ../../private/vitest-config/src/cli.ts run --config ../../../../vitest.config.ts",
62
62
  "typecheck": "bun check"
63
63
  },
64
64
  "dependencies": {
@@ -66,49 +66,49 @@
66
66
  "@interfere/constants": "^9.2.0",
67
67
  "@interfere/helpers": "^9.2.1",
68
68
  "@interfere/javascript": "^0.2.1",
69
- "@interfere/react": "^12.1.1",
69
+ "@interfere/react": "^12.1.3",
70
70
  "@interfere/types": "^10.3.0",
71
- "@opentelemetry/api": "^1.9.1",
72
- "@opentelemetry/api-logs": "^0.222.0",
73
- "@opentelemetry/baggage-span-processor": "^0.5.0",
74
- "@opentelemetry/context-async-hooks": "^2.11.0",
75
- "@opentelemetry/core": "^2.11.0",
76
- "@opentelemetry/exporter-logs-otlp-http": "^0.222.0",
77
- "@opentelemetry/exporter-metrics-otlp-http": "^0.222.0",
78
- "@opentelemetry/exporter-trace-otlp-http": "^0.222.0",
79
- "@opentelemetry/instrumentation": "^0.222.0",
80
- "@opentelemetry/instrumentation-undici": "^0.32.0",
81
- "@opentelemetry/resources": "^2.11.0",
82
- "@opentelemetry/sdk-logs": "^0.222.0",
83
- "@opentelemetry/sdk-metrics": "^2.11.0",
84
- "@opentelemetry/sdk-trace-base": "^2.11.0",
85
- "@opentelemetry/sdk-trace-node": "^2.11.0",
86
- "zod": "^4.5.4"
71
+ "@opentelemetry/api": "1.9.1",
72
+ "@opentelemetry/api-logs": "0.222.0",
73
+ "@opentelemetry/baggage-span-processor": "0.5.0",
74
+ "@opentelemetry/context-async-hooks": "2.11.0",
75
+ "@opentelemetry/core": "2.11.0",
76
+ "@opentelemetry/exporter-logs-otlp-http": "0.222.0",
77
+ "@opentelemetry/exporter-metrics-otlp-http": "0.222.0",
78
+ "@opentelemetry/exporter-trace-otlp-http": "0.222.0",
79
+ "@opentelemetry/instrumentation": "0.222.0",
80
+ "@opentelemetry/instrumentation-undici": "0.32.0",
81
+ "@opentelemetry/resources": "2.11.0",
82
+ "@opentelemetry/sdk-logs": "0.222.0",
83
+ "@opentelemetry/sdk-metrics": "2.11.0",
84
+ "@opentelemetry/sdk-trace-base": "2.11.0",
85
+ "@opentelemetry/sdk-trace-node": "2.11.0",
86
+ "zod": "4.6.6"
87
87
  },
88
88
  "devDependencies": {
89
89
  "@interfere/test-utils": "^10.0.1",
90
90
  "@interfere/typescript-config": "^9.0.2",
91
91
  "@interfere/vitest-config": "0.0.0",
92
- "@jridgewell/trace-mapping": "^0.3.31",
93
- "@playwright/test": "^1.63.0",
92
+ "@jridgewell/trace-mapping": "0.3.31",
93
+ "@playwright/test": "1.63.0",
94
94
  "@tanstack/react-start": "1.168.60",
95
- "@types/node": "^26.4.1",
95
+ "@types/node": "26.4.1",
96
96
  "@types/react": "19.2.18",
97
97
  "@types/react-dom": "19.2.7",
98
98
  "@vitest/coverage-v8": "5.0.5",
99
- "msw": "^2.14.6",
99
+ "msw": "2.15.0",
100
100
  "react": "19.3.0",
101
101
  "react-dom": "19.3.0",
102
- "tsdown": "^0.23.0",
103
- "typescript": "^7.0.2",
104
- "vite": "^8.2.2",
102
+ "tsdown": "0.23.0",
103
+ "typescript": "7.0.2",
104
+ "vite": "8.2.4",
105
105
  "vitest": "5.0.5"
106
106
  },
107
107
  "peerDependencies": {
108
108
  "@tanstack/react-start": "1.168.60",
109
109
  "react": "^19.0.0",
110
110
  "react-dom": "^19.0.0",
111
- "vite": "^8.2.2"
111
+ "vite": "8.2.4"
112
112
  },
113
113
  "peerDependenciesMeta": {
114
114
  "@tanstack/react-start": {