@interfere/vite 11.1.1 → 11.2.1

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.
@@ -0,0 +1,11 @@
1
+ //#region src/internal/build/git.d.ts
2
+ /**
3
+ * Runs a git command and returns its stdout, or `null` when git isn't
4
+ * available. Build-time only — inject it into the shared resolvers
5
+ * (`resolveReleaseSlug`, `resolveGithubReleaseSource`) rather than
6
+ * importing it from runtime server code, which must never spawn
7
+ * subprocesses.
8
+ */
9
+ declare function runGitCommand(command: string): string | null;
10
+ //#endregion
11
+ export { runGitCommand };
@@ -0,0 +1 @@
1
+ import{execSync}from"node:child_process";function runGitCommand(command){try{let output=execSync(command,{encoding:`utf-8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim();return output.length>0?output:null}catch{return null}}export{runGitCommand};
@@ -1 +1 @@
1
- import{cleanupSourceMaps,uploadSourceMaps}from"./source-maps.mjs";import{releaseSlugSchema}from"@interfere/types/releases/slug";import{applyDeployEnvironment}from"@interfere/types/sdk/deploy-environment";import{execSync}from"node:child_process";import{HTTPClient,Interfere}from"@interfere/sdk";import{configuredSourceProviderSchema,releaseDestinationProviderSchema}from"@interfere/types/integrations";import{resolveCliReleaseDestination}from"@interfere/types/sdk/cli-destination";import{parseEnvValue}from"@interfere/types/sdk/env";import{resolveGithubReleaseSource}from"@interfere/types/sdk/github-source";const sources={github:resolveGithubSource,ghe_cloud:resolveGithubSource,ghe_server:resolveGithubSource},destinations={vercel:resolveVercelDestination,cloudflare:resolveCloudflareDestination,cli:resolveCliDestination};async function timed(fn){let start=performance.now();return[await fn(),Math.round(performance.now()-start)]}function createSdkClient(opts){let httpClient=new HTTPClient;return httpClient.addHook(`beforeRequest`,request=>{let next=new Request(request);return next.headers.set(`Authorization`,`Bearer ${opts.apiKey}`),next}),{httpClient,sdk:new Interfere({serverURL:opts.apiUrl,httpClient}),security:{apiKey:opts.apiKey}}}function resolveReleaseRequest(buildId,environment,releaseSlug,producerVersion,config){let sourceProvider=configuredSourceProviderSchema.safeParse(config.surface.sourceProvider);if(!sourceProvider.success)return{ok:!1,reason:`missing_source_provider`};let destinationProvider=releaseDestinationProviderSchema.safeParse(config.surface.destinationProvider);return destinationProvider.success?{ok:!0,request:{source:sources[sourceProvider.data](),destination:applyDeployEnvironment(destinations[destinationProvider.data](releaseSlug),environment),buildId,slug:releaseSlug,producerVersion}}:{ok:!1,reason:`missing_destination_provider`}}async function runReleasePipeline(input){let{apiKey,apiUrl,bundler,discovery,releasesConfig}=input,releaseRequest=resolveReleaseRequest(input.buildId,input.environment,input.releaseSlug,input.producerVersion,releasesConfig);if(!releaseRequest.ok)return{ok:!1,failure:{kind:releaseRequest.reason,config:releasesConfig}};let start=performance.now(),{httpClient,sdk,security}=createSdkClient({apiKey,apiUrl}),[release,createReleaseMs]=await timed(()=>sdk.releases.create(security,releaseRequest.request)),releaseSlug=releaseSlugSchema.parse(release.destination.slug),buildId=release.build.hash??input.buildId,[{totalBytes},uploadMs]=await timed(()=>uploadSourceMaps({apiUrl,bundler,discovered:discovery,httpClient,releaseSlug})),[,preflightMs]=await timed(()=>sdk.releases.preflight(security,{releaseSlug})),[{failures},cleanupMs]=await timed(()=>cleanupSourceMaps(discovery.files));return{ok:!0,data:{buildId,cleanupFailures:failures,config:releasesConfig,fileCount:discovery.files.length,release:{...release,destination:{...release.destination,slug:releaseSlug}},releaseSlug,timing:{createRelease:createReleaseMs,upload:uploadMs,preflight:preflightMs,cleanup:cleanupMs,total:Math.round(performance.now()-start),fileCount:discovery.files.length,totalBytes}}}}function runGitCommand(command){try{let output=execSync(command,{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim();return output.length>0?output:null}catch{return null}}function resolveGithubSource(){return resolveGithubReleaseSource(process.env,runGitCommand)}function resolveVercelDestination(){let environment=parseEnvValue(process.env.VERCEL_ENV??process.env.VERCEL_TARGET_ENV),deploymentId=parseEnvValue(process.env.VERCEL_DEPLOYMENT_ID);return{provider:`vercel`,destinationReleaseId:deploymentId,environment,deploymentId,deploymentUrl:resolveDeploymentUrl(),environmentName:environment,environmentTarget:environment}}function resolveDeploymentUrl(){let deploymentUrl=parseEnvValue(process.env.VERCEL_URL);return deploymentUrl===null?null:deploymentUrl.startsWith(`https://`)||deploymentUrl.startsWith(`http://`)?deploymentUrl:`https://${deploymentUrl}`}function resolveCloudflareDestination(){let raw=parseEnvValue(process.env.CF_VERSION_METADATA),versionMetadata=null;if(raw)try{versionMetadata=JSON.parse(raw)}catch{versionMetadata=null}let versionId=parseEnvValue(process.env.CF_VERSION_ID)??versionMetadata?.id??parseEnvValue(process.env.WORKERS_CI_BUILD_UUID)??null,scriptName=parseEnvValue(process.env.CF_WORKER_NAME)??parseEnvValue(process.env.WORKERS_CI_SCRIPT_NAME)??null,accountId=parseEnvValue(process.env.CF_ACCOUNT_ID)??parseEnvValue(process.env.CLOUDFLARE_ACCOUNT_ID)??null,environment=parseEnvValue(process.env.WORKERS_CI_BRANCH)??versionMetadata?.tag??null;return{provider:`cloudflare`,destinationReleaseId:versionId,environment,versionId,scriptName,accountId,deploymentUrl:resolveCloudflareDeploymentUrl(),environmentName:environment,environmentTarget:environment}}function resolveCloudflareDeploymentUrl(){let candidate=parseEnvValue(process.env.CF_PAGES_URL)??parseEnvValue(process.env.WORKERS_CI_DEPLOY_URL);return candidate===null?null:candidate.startsWith(`https://`)||candidate.startsWith(`http://`)?candidate:`https://${candidate}`}function resolveCliDestination(releaseSlug){return resolveCliReleaseDestination(process.env,releaseSlug)}export{createSdkClient,runReleasePipeline};
1
+ import{runGitCommand}from"./git.mjs";import{cleanupSourceMaps,uploadSourceMaps}from"./source-maps.mjs";import{releaseSlugSchema}from"@interfere/types/releases/slug";import{applyDeployEnvironment}from"@interfere/types/sdk/deploy-environment";import{HTTPClient,Interfere}from"@interfere/sdk";import{configuredSourceProviderSchema,releaseDestinationProviderSchema}from"@interfere/types/integrations";import{resolveCliReleaseDestination}from"@interfere/types/sdk/cli-destination";import{parseEnvValue}from"@interfere/types/sdk/env";import{resolveGithubReleaseSource}from"@interfere/types/sdk/github-source";const sources={github:resolveGithubSource,ghe_cloud:resolveGithubSource,ghe_server:resolveGithubSource},destinations={vercel:resolveVercelDestination,cloudflare:resolveCloudflareDestination,cli:resolveCliDestination};async function timed(fn){let start=performance.now();return[await fn(),Math.round(performance.now()-start)]}function createSdkClient(opts){let httpClient=new HTTPClient;return httpClient.addHook(`beforeRequest`,request=>{let next=new Request(request);return next.headers.set(`Authorization`,`Bearer ${opts.apiKey}`),next}),{httpClient,sdk:new Interfere({serverURL:opts.apiUrl,httpClient}),security:{apiKey:opts.apiKey}}}function resolveReleaseRequest(buildId,environment,releaseSlug,producerVersion,config){let sourceProvider=configuredSourceProviderSchema.safeParse(config.surface.sourceProvider);if(!sourceProvider.success)return{ok:!1,reason:`missing_source_provider`};let destinationProvider=releaseDestinationProviderSchema.safeParse(config.surface.destinationProvider);return destinationProvider.success?{ok:!0,request:{source:sources[sourceProvider.data](),destination:applyDeployEnvironment(destinations[destinationProvider.data](releaseSlug),environment),buildId,slug:releaseSlug,producerVersion}}:{ok:!1,reason:`missing_destination_provider`}}async function runReleasePipeline(input){let{apiKey,apiUrl,bundler,discovery,releasesConfig}=input,releaseRequest=resolveReleaseRequest(input.buildId,input.environment,input.releaseSlug,input.producerVersion,releasesConfig);if(!releaseRequest.ok)return{ok:!1,failure:{kind:releaseRequest.reason,config:releasesConfig}};let start=performance.now(),{httpClient,sdk,security}=createSdkClient({apiKey,apiUrl}),[release,createReleaseMs]=await timed(()=>sdk.releases.create(security,releaseRequest.request)),releaseSlug=releaseSlugSchema.parse(release.destination.slug),buildId=release.build.hash??input.buildId,[{totalBytes},uploadMs]=await timed(()=>uploadSourceMaps({apiUrl,bundler,discovered:discovery,httpClient,releaseSlug})),[,preflightMs]=await timed(()=>sdk.releases.preflight(security,{releaseSlug})),[{failures},cleanupMs]=await timed(()=>cleanupSourceMaps(discovery.files));return{ok:!0,data:{buildId,cleanupFailures:failures,config:releasesConfig,fileCount:discovery.files.length,release:{...release,destination:{...release.destination,slug:releaseSlug}},releaseSlug,timing:{createRelease:createReleaseMs,upload:uploadMs,preflight:preflightMs,cleanup:cleanupMs,total:Math.round(performance.now()-start),fileCount:discovery.files.length,totalBytes}}}}function resolveGithubSource(){return resolveGithubReleaseSource(process.env,runGitCommand)}function resolveVercelDestination(){let environment=parseEnvValue(process.env.VERCEL_ENV??process.env.VERCEL_TARGET_ENV),deploymentId=parseEnvValue(process.env.VERCEL_DEPLOYMENT_ID);return{provider:`vercel`,destinationReleaseId:deploymentId,environment,deploymentId,deploymentUrl:resolveDeploymentUrl(),environmentName:environment,environmentTarget:environment}}function resolveDeploymentUrl(){let deploymentUrl=parseEnvValue(process.env.VERCEL_URL);return deploymentUrl===null?null:deploymentUrl.startsWith(`https://`)||deploymentUrl.startsWith(`http://`)?deploymentUrl:`https://${deploymentUrl}`}function resolveCloudflareDestination(){let raw=parseEnvValue(process.env.CF_VERSION_METADATA),versionMetadata=null;if(raw)try{versionMetadata=JSON.parse(raw)}catch{versionMetadata=null}let versionId=parseEnvValue(process.env.CF_VERSION_ID)??versionMetadata?.id??parseEnvValue(process.env.WORKERS_CI_BUILD_UUID)??null,scriptName=parseEnvValue(process.env.CF_WORKER_NAME)??parseEnvValue(process.env.WORKERS_CI_SCRIPT_NAME)??null,accountId=parseEnvValue(process.env.CF_ACCOUNT_ID)??parseEnvValue(process.env.CLOUDFLARE_ACCOUNT_ID)??null,environment=parseEnvValue(process.env.WORKERS_CI_BRANCH)??versionMetadata?.tag??null;return{provider:`cloudflare`,destinationReleaseId:versionId,environment,versionId,scriptName,accountId,deploymentUrl:resolveCloudflareDeploymentUrl(),environmentName:environment,environmentTarget:environment}}function resolveCloudflareDeploymentUrl(){let candidate=parseEnvValue(process.env.CF_PAGES_URL)??parseEnvValue(process.env.WORKERS_CI_DEPLOY_URL);return candidate===null?null:candidate.startsWith(`https://`)||candidate.startsWith(`http://`)?candidate:`https://${candidate}`}function resolveCliDestination(releaseSlug){return resolveCliReleaseDestination(process.env,releaseSlug)}export{createSdkClient,runReleasePipeline};
@@ -1,7 +1,8 @@
1
1
  import { ReleaseSlug } from "@interfere/types/releases/slug";
2
+ import { RunGitCommand } from "@interfere/types/sdk/github-source";
2
3
  //#region src/internal/release-slug.d.ts
3
- declare function resolveCommitSha(): string | null;
4
- declare function resolveReleaseSlug(): {
4
+ declare function resolveCommitSha(runGit?: RunGitCommand): string | null;
5
+ declare function resolveReleaseSlug(runGit?: RunGitCommand): {
5
6
  commitSha: string | null;
6
7
  slug: ReleaseSlug | null;
7
8
  };
@@ -1 +1 @@
1
- import{deriveReleaseSlug}from"@interfere/types/releases/slug";import{execSync}from"node:child_process";import{resolveReleaseCommitSha}from"@interfere/types/sdk/github-source";function runGitCommand(command){try{let output=execSync(command,{encoding:`utf-8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim();return output.length>0?output:null}catch{return null}}function resolveCommitSha(){return resolveReleaseCommitSha(process.env,runGitCommand)}function resolveReleaseSlug(){let commitSha=resolveCommitSha();return{commitSha,slug:commitSha?deriveReleaseSlug(commitSha):null}}export{resolveCommitSha,resolveReleaseSlug};
1
+ import{deriveReleaseSlug}from"@interfere/types/releases/slug";import{resolveReleaseCommitSha}from"@interfere/types/sdk/github-source";const noGit=()=>null;function resolveCommitSha(runGit=noGit){return resolveReleaseCommitSha(process.env,runGit)}function resolveReleaseSlug(runGit=noGit){let commitSha=resolveCommitSha(runGit);return{commitSha,slug:commitSha?deriveReleaseSlug(commitSha):null}}export{resolveCommitSha,resolveReleaseSlug};
@@ -14,6 +14,11 @@
14
14
  * the original `console.error` so a broken backend doesn't drown
15
15
  * the customer's logs in bridge-failure messages.
16
16
  *
17
+ * Serialization is deferred to a microtask and bodies are capped at
18
+ * {@link MAX_BODY_CHARS} characters, so the synchronous `console.*`
19
+ * call only captures the active context; an arg mutated before the
20
+ * microtask runs is serialized in its mutated state.
21
+ *
17
22
  * Sibling of `@interfere/next/internal/server/console-bridge` — same
18
23
  * bridge, same trade-offs, intentionally kept in the Vite adapter's
19
24
  * source tree so SDKs do not share build/runtime internals.
@@ -1 +1 @@
1
- import{context,trace}from"@opentelemetry/api";import{logs}from"@opentelemetry/api-logs";const CONSOLE_METHODS=[`debug`,`log`,`info`,`warn`,`error`],SEVERITY={debug:5,log:9,info:9,warn:13,error:17};function stringify(value){if(typeof value==`string`)return value;if(value instanceof Error)return value.stack??`${value.name}: ${value.message}`;try{return JSON.stringify(value)}catch{return String(value)}}function extractException(args){for(let arg of args)if(arg instanceof Error)return arg}function buildExceptionAttributes(args){let exception=extractException(args);if(!exception)return;let attrs={"exception.type":exception.name,"exception.message":exception.message};return exception.stack&&(attrs[`exception.stacktrace`]=exception.stack),attrs}function bridgeConsoleToOtel(){let logger=logs.getLogger(`@interfere/vite/console`),emitting=!1,bridgeFailures=0,original={debug:console.debug,log:console.log,info:console.info,warn:console.warn,error:console.error};function emitLog(orig,severity,method,body,activeContext,activeSpan,attributes){if(!emitting){emitting=!0;try{logger.emit({severityNumber:severity,severityText:method.toUpperCase(),body,...activeSpan?{context:activeContext}:{},...attributes?{attributes}:{}})}catch(error){bridgeFailures+=1,bridgeFailures%100==1&&orig.call(console,`[interfere] console→OTel bridge emission failed (${bridgeFailures} total):`,error)}finally{emitting=!1}}}for(let method of CONSOLE_METHODS){let orig=original[method],severity=SEVERITY[method];console[method]=(...args)=>{orig.apply(console,args);let body=args.map(stringify).join(` `),activeContext=context.active(),activeSpan=trace.getSpan(activeContext),attributes=buildExceptionAttributes(args);queueMicrotask(()=>{emitLog(orig,severity,method,body,activeContext,activeSpan,attributes)})}}return()=>{for(let method of CONSOLE_METHODS)console[method]=original[method]}}export{bridgeConsoleToOtel};
1
+ import{context,trace}from"@opentelemetry/api";import{logs}from"@opentelemetry/api-logs";const CONSOLE_METHODS=[`debug`,`log`,`info`,`warn`,`error`],SEVERITY={debug:5,log:9,info:9,warn:13,error:17},MAX_BODY_CHARS=8192;function buildBody(args){let body=args.map(stringify).join(` `);return body.length>MAX_BODY_CHARS?`${body.slice(0,MAX_BODY_CHARS)}…`:body}function stringify(value){if(typeof value==`string`)return value;if(value instanceof Error)return value.stack??`${value.name}: ${value.message}`;try{return JSON.stringify(value)}catch{return String(value)}}function extractException(args){for(let arg of args)if(arg instanceof Error)return arg}function buildExceptionAttributes(args){let exception=extractException(args);if(!exception)return;let attrs={"exception.type":exception.name,"exception.message":exception.message};return exception.stack&&(attrs[`exception.stacktrace`]=exception.stack),attrs}function bridgeConsoleToOtel(){let logger=logs.getLogger(`@interfere/vite/console`),emitting=!1,bridgeFailures=0,original={debug:console.debug,log:console.log,info:console.info,warn:console.warn,error:console.error};function emitLog(orig,severity,method,args,activeContext,activeSpan){if(!emitting){emitting=!0;try{let attributes=buildExceptionAttributes(args);logger.emit({severityNumber:severity,severityText:method.toUpperCase(),body:buildBody(args),...activeSpan?{context:activeContext}:{},...attributes?{attributes}:{}})}catch(error){bridgeFailures+=1,bridgeFailures%100==1&&orig.call(console,`[interfere] console→OTel bridge emission failed (${bridgeFailures} total):`,error)}finally{emitting=!1}}}for(let method of CONSOLE_METHODS){let orig=original[method],severity=SEVERITY[method];console[method]=(...args)=>{orig.apply(console,args);let activeContext=context.active(),activeSpan=trace.getSpan(activeContext);queueMicrotask(()=>{emitLog(orig,severity,method,args,activeContext,activeSpan)})}}return()=>{for(let method of CONSOLE_METHODS)console[method]=original[method]}}export{bridgeConsoleToOtel};
package/dist/package.mjs CHANGED
@@ -1 +1 @@
1
- var name=`@interfere/vite`,version=`11.1.1`;export{name,version};
1
+ var name=`@interfere/vite`,version=`11.2.1`;export{name,version};
package/dist/plugin.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import{PRODUCER_VERSION}from"./version.mjs";import{resolveBakedConfig}from"./internal/build/baked-config.mjs";import{discoverByPairing}from"./internal/build/source-maps.mjs";import{createSdkClient,runReleasePipeline}from"./internal/build/pipeline.mjs";import{resolveCommitSha}from"./internal/release-slug.mjs";import{API_URL}from"@interfere/constants/api";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{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{resolveBakedConfig}from"./internal/build/baked-config.mjs";import{runGitCommand}from"./internal/build/git.mjs";import{discoverByPairing}from"./internal/build/source-maps.mjs";import{createSdkClient,runReleasePipeline}from"./internal/build/pipeline.mjs";import{resolveCommitSha}from"./internal/release-slug.mjs";import{API_URL}from"@interfere/constants/api";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{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();return{apiHost:resolveApiHost(options,env),commitSha,environment:command===`build`?resolveDeployEnvironment({...env,...process.env}):null,publicKey:env.VITE_INTERFERE_PUBLIC_KEY,releaseSlug:commitSha?deriveReleaseSlug(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)){reportReleaseDiagnostic(logger,`API_KEY_WRONG_PREFIX`,{apiKey});return}logger.info(`[interfere] uploading source maps for release ${releaseSlug}…`);try{let{sdk,security}=createSdkClient({apiKey,apiUrl}),[discovered,releasesConfig]=await Promise.all([discoverByPairing({projectDir,distDir:uploadOutDir,subtrees:[`.`],publicPathRewrites:[{from:`public`,to:``}]}),sdk.releases.getConfig(security)]);if(discovered.files.length===0){logger.info(`[interfere] no source maps found, skipping upload.`);return}let result=await runReleasePipeline({apiKey,apiUrl,bundler:`rollup`,buildId:commitSha,discovery:discovered,environment:state.environment,producerVersion:PRODUCER_VERSION,releasesConfig,releaseSlug});if(!result.ok){logSkippedRelease(logger,result.failure);return}logCleanupFailures(logger,result.data.cleanupFailures),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);reportReleaseDiagnostic(logger,classifyReleasePipelineError(error),{errorMessage:message})}}async function uploadSourceMapsWhenReady(state){if(isUploadReady(state)){if(shouldWaitForServerOutput(state)){state.logger.info(`[interfere] waiting for Nitro server output before uploading source maps.`);return}state.uploadStarted=!0,await uploadSourceMaps(state)}}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??!!(apiKey&&releaseSlug):!1}function buildPluginState(options,config,browser,announce){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,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 announce&&(logger.info(`[interfere] ingest → ${browser.apiHost??`Interfere (default)`}`),!browser.apiHost&&serverBuild&&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,isProductionBuild,logger,releaseSlug:browser.releaseSlug,sourceMapsExplicit:options.sourceMaps})),{apiKey,apiUrl,commitSha:browser.commitSha,enableUpload:shouldEnableUpload({apiKey,isProductionBuild,releaseSlug:browser.releaseSlug,sourceMaps:options.sourceMaps}),environment:browser.environment,isProductionBuild,logger,serverBuild,projectDir:config.root,publicKey:browser.publicKey,releaseSlug:browser.releaseSlug,serverSourcemapsDir,uploadOutDir,uploadStarted:!1,buildStartedAt:Math.floor(Date.now()/1e3)*1e3}}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?path.resolve(root,userConfig.envDir):root}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.uploadStarted=previous.uploadStarted,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 state=null,overriddenSourcemap,announced=!1;return{name:`interfere`,enforce:`pre`,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),define=browserDefine(browser);if(command===`build`&&browser.publicKey&&!isTestEnv()){let baked=await resolveBakedConfig(browser.apiHost??API_URL,browser.publicKey);baked?.ingestUrl&&(define[`import.meta.env.VITE_INTERFERE_INGEST_URL`]=JSON.stringify(baked.ingestUrl)),baked?.plugins&&(define[`import.meta.env.VITE_INTERFERE_PLUGINS`]=JSON.stringify(JSON.stringify(baked.plugins)))}return{define}},configResolved(config){let next=buildPluginState(options,config,resolveBrowserConfig(options,config.env,config.command),!announced);announced=!0,carryOverUploadProgress(state,next),warnForcedSourcemap(next.logger,overriddenSourcemap),overriddenSourcemap=void 0,state=next},async closeBundle(){state&&await uploadSourceMapsWhenReady(state)}}}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(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)){reportReleaseDiagnostic(logger,`API_KEY_WRONG_PREFIX`,{apiKey});return}logger.info(`[interfere] uploading source maps for release ${releaseSlug}…`);try{let{sdk,security}=createSdkClient({apiKey,apiUrl}),[discovered,releasesConfig]=await Promise.all([discoverByPairing({projectDir,distDir:uploadOutDir,subtrees:[`.`],publicPathRewrites:[{from:`public`,to:``}]}),sdk.releases.getConfig(security)]);if(discovered.files.length===0){logger.info(`[interfere] no source maps found, skipping upload.`);return}let result=await runReleasePipeline({apiKey,apiUrl,bundler:`rollup`,buildId:commitSha,discovery:discovered,environment:state.environment,producerVersion:PRODUCER_VERSION,releasesConfig,releaseSlug});if(!result.ok){logSkippedRelease(logger,result.failure);return}logCleanupFailures(logger,result.data.cleanupFailures),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);reportReleaseDiagnostic(logger,classifyReleasePipelineError(error),{errorMessage:message})}}async function uploadSourceMapsWhenReady(state){if(isUploadReady(state)){if(shouldWaitForServerOutput(state)){state.logger.info(`[interfere] waiting for Nitro server output before uploading source maps.`);return}state.uploadStarted=!0,await uploadSourceMaps(state)}}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??!!(apiKey&&releaseSlug):!1}function buildPluginState(options,config,browser,announce){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,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 announce&&(logger.info(`[interfere] ingest → ${browser.apiHost??`Interfere (default)`}`),!browser.apiHost&&serverBuild&&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,isProductionBuild,logger,releaseSlug:browser.releaseSlug,sourceMapsExplicit:options.sourceMaps})),{apiKey,apiUrl,commitSha:browser.commitSha,enableUpload:shouldEnableUpload({apiKey,isProductionBuild,releaseSlug:browser.releaseSlug,sourceMaps:options.sourceMaps}),environment:browser.environment,isProductionBuild,logger,serverBuild,projectDir:config.root,publicKey:browser.publicKey,releaseSlug:browser.releaseSlug,serverSourcemapsDir,uploadOutDir,uploadStarted:!1,buildStartedAt:Math.floor(Date.now()/1e3)*1e3}}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?path.resolve(root,userConfig.envDir):root}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.uploadStarted=previous.uploadStarted,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 state=null,overriddenSourcemap,announced=!1;return{name:`interfere`,enforce:`pre`,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),define=browserDefine(browser);if(command===`build`&&browser.publicKey&&!isTestEnv()){let baked=await resolveBakedConfig(browser.apiHost??API_URL,browser.publicKey);baked?.ingestUrl&&(define[`import.meta.env.VITE_INTERFERE_INGEST_URL`]=JSON.stringify(baked.ingestUrl)),baked?.plugins&&(define[`import.meta.env.VITE_INTERFERE_PLUGINS`]=JSON.stringify(JSON.stringify(baked.plugins)))}return{define}},configResolved(config){let next=buildPluginState(options,config,resolveBrowserConfig(options,config.env,config.command),!announced);announced=!0,carryOverUploadProgress(state,next),warnForcedSourcemap(next.logger,overriddenSourcemap),overriddenSourcemap=void 0,state=next},async closeBundle(){state&&await uploadSourceMapsWhenReady(state)}}}export{interfere};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interfere/vite",
3
- "version": "11.1.1",
3
+ "version": "11.2.1",
4
4
  "license": "MIT",
5
5
  "description": "Vite build plugin and client + server initialization for Interfere.",
6
6
  "keywords": [
@@ -62,11 +62,11 @@
62
62
  "typecheck": "tsc --noEmit"
63
63
  },
64
64
  "dependencies": {
65
- "@interfere/constants": "^9.0.5",
66
- "@interfere/helpers": "^9.1.1",
67
- "@interfere/react": "^11.1.1",
68
- "@interfere/sdk": "^11.1.1",
69
- "@interfere/types": "^10.1.1",
65
+ "@interfere/constants": "^9.0.6",
66
+ "@interfere/helpers": "^9.1.2",
67
+ "@interfere/react": "^11.2.1",
68
+ "@interfere/sdk": "^11.2.1",
69
+ "@interfere/types": "^10.1.2",
70
70
  "@opentelemetry/api": "^1.9.1",
71
71
  "@opentelemetry/api-logs": "^0.221.0",
72
72
  "@opentelemetry/baggage-span-processor": "^0.5.0",
@@ -104,7 +104,7 @@
104
104
  }
105
105
  },
106
106
  "devDependencies": {
107
- "@interfere/test-utils": "^9.1.1",
107
+ "@interfere/test-utils": "^9.1.2",
108
108
  "@interfere/typescript-config": "^9.0.2",
109
109
  "@tanstack/react-start": "1.168.42",
110
110
  "@types/node": "^26.2.0",