@interfere/vite 10.0.1-canary.0 → 10.0.1-canary.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.
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.mts","names":[],"sources":["../../../src/internal/build/pipeline.ts"],"mappings":";;;;;;;;UA2BiB,WAAA;EACf,OAAA;EACA,aAAA;EACA,SAAA;EACA,SAAA;EACA,KAAA;EACA,UAAA;EACA,MAAA;AAAA;AAAA,KAGU,sBAAA;EACN,IAAA;EAAiC,MAAA,EAAQ,sBAAA;AAAA;EACzC,IAAA;EAAsC,MAAA,EAAQ,sBAAsB;AAAA;AAAA,KAE9D,qBAAA;EACN,EAAA;EAAU,IAAA,EAAM,sBAAA;AAAA;EAChB,EAAA;EAAW,OAAA,EAAS,sBAAsB;AAAA;AAAA,UAE/B,sBAAA;EACf,OAAA;EACA,eAAA;IACE,QAAA;IACA,IAAA;IACA,OAAA;EAAA;EAEF,MAAA,EAAQ,sBAAA;EACR,SAAA;EACA,OAAA,EAAS,qBAAA;EACT,WAAA,EAAa,WAAA;EACb,MAAA,EAAQ,WAAA;AAAA;AAAA,UAGO,oBAAA;EACf,MAAA;EACA,MAAA;EACA,OAAA;EACA,OAAA,EAAS,eAAA;EACT,SAAA,EAAW,eAAA;EACX,eAAA;EACA,WAAA,EAAa,WAAA;EACb,cAAA,EAAgB,sBAAA;AAAA;AAAA,iBA2BF,eAAA,CAAgB,IAAA;EAAQ,MAAA;EAAgB,MAAA;AAAA;EACtD,UAAA,EAAY,UAAA;EACZ,GAAA,EAAK,SAAS;AAAA;AAAA,iBAwCM,kBAAA,CACpB,KAAA,EAAO,oBAAA,GACN,OAAA,CAAQ,qBAAA"}
1
+ {"version":3,"file":"pipeline.d.mts","names":[],"sources":["../../../src/internal/build/pipeline.ts"],"mappings":";;;;;;;;UA4BiB,WAAA;EACf,OAAA;EACA,aAAA;EACA,SAAA;EACA,SAAA;EACA,KAAA;EACA,UAAA;EACA,MAAA;AAAA;AAAA,KAGU,sBAAA;EACN,IAAA;EAAiC,MAAA,EAAQ,sBAAA;AAAA;EACzC,IAAA;EAAsC,MAAA,EAAQ,sBAAsB;AAAA;AAAA,KAE9D,qBAAA;EACN,EAAA;EAAU,IAAA,EAAM,sBAAA;AAAA;EAChB,EAAA;EAAW,OAAA,EAAS,sBAAsB;AAAA;AAAA,UAE/B,sBAAA;EACf,OAAA;EACA,eAAA;IACE,QAAA;IACA,IAAA;IACA,OAAA;EAAA;EAEF,MAAA,EAAQ,sBAAA;EACR,SAAA;EACA,OAAA,EAAS,qBAAA;EACT,WAAA,EAAa,WAAA;EACb,MAAA,EAAQ,WAAA;AAAA;AAAA,UAGO,oBAAA;EACf,MAAA;EACA,MAAA;EACA,OAAA;EACA,OAAA,EAAS,eAAA;EACT,SAAA,EAAW,eAAA;EACX,eAAA;EACA,WAAA,EAAa,WAAA;EACb,cAAA,EAAgB,sBAAA;AAAA;AAAA,iBA2BF,eAAA,CAAgB,IAAA;EAAQ,MAAA;EAAgB,MAAA;AAAA;EACtD,UAAA,EAAY,UAAA;EACZ,GAAA,EAAK,SAAS;AAAA;AAAA,iBA2CM,kBAAA,CACpB,KAAA,EAAO,oBAAA,GACN,OAAA,CAAQ,qBAAA"}
@@ -1 +1 @@
1
- import{cleanupSourceMaps,uploadSourceMaps}from"./source-maps.mjs";import{parseEnvValue}from"@interfere/types/sdk/env";import{releaseSlugSchema}from"@interfere/types/releases/slug";import{execSync}from"node:child_process";import{HTTPClient,Interfere}from"@interfere/sdk";const sources={github:resolveGithubSource},destinations={vercel:resolveVercelDestination};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})}}function resolveReleaseRequest(buildId,releaseSlug,producerVersion,config){return config.surface.sourceProvider?config.surface.destinationProvider?{ok:!0,request:{source:sources[config.surface.sourceProvider](),destination:destinations[config.surface.destinationProvider](),buildId,slug:releaseSlug,producerVersion}}:{ok:!1,reason:`missing_destination_provider`}:{ok:!1,reason:`missing_source_provider`}}async function runReleasePipeline(input){let{apiKey,apiUrl,bundler,discovery,releasesConfig}=input,releaseRequest=resolveReleaseRequest(input.buildId,input.releaseSlug,input.producerVersion,releasesConfig);if(!releaseRequest.ok)return{ok:!1,failure:{kind:releaseRequest.reason,config:releasesConfig}};let start=performance.now(),{httpClient,sdk}=createSdkClient({apiKey,apiUrl}),[release,createReleaseMs]=await timed(()=>sdk.releases.create(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({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{provider:`github`,branch:parseEnvValue(process.env.VERCEL_GIT_COMMIT_REF)??parseEnvValue(process.env.GITHUB_REF_NAME)??parseEnvValue(process.env.GITHUB_HEAD_REF)??runGitCommand(`git rev-parse --abbrev-ref HEAD`)??`unknown`,commitMessage:parseEnvValue(process.env.VERCEL_GIT_COMMIT_MESSAGE)??runGitCommand(`git log -1 --pretty=%B`)??``,commitSha:parseEnvValue(process.env.VERCEL_GIT_COMMIT_SHA)??parseEnvValue(process.env.GITHUB_SHA)??runGitCommand(`git rev-parse HEAD`)}}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}`}export{createSdkClient,runReleasePipeline};
1
+ import{cleanupSourceMaps,uploadSourceMaps}from"./source-maps.mjs";import{parseEnvValue}from"@interfere/types/sdk/env";import{releaseSlugSchema}from"@interfere/types/releases/slug";import{execSync}from"node:child_process";import{HTTPClient,Interfere}from"@interfere/sdk";import{destinationProviderSchema}from"@interfere/types/integrations";const sources={github:resolveGithubSource},destinations={vercel:resolveVercelDestination};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})}}function resolveReleaseRequest(buildId,releaseSlug,producerVersion,config){if(!config.surface.sourceProvider)return{ok:!1,reason:`missing_source_provider`};let destinationProvider=destinationProviderSchema.safeParse(config.surface.destinationProvider);return destinationProvider.success?{ok:!0,request:{source:sources[config.surface.sourceProvider](),destination:destinations[destinationProvider.data](),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.releaseSlug,input.producerVersion,releasesConfig);if(!releaseRequest.ok)return{ok:!1,failure:{kind:releaseRequest.reason,config:releasesConfig}};let start=performance.now(),{httpClient,sdk}=createSdkClient({apiKey,apiUrl}),[release,createReleaseMs]=await timed(()=>sdk.releases.create(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({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{provider:`github`,branch:parseEnvValue(process.env.VERCEL_GIT_COMMIT_REF)??parseEnvValue(process.env.GITHUB_REF_NAME)??parseEnvValue(process.env.GITHUB_HEAD_REF)??runGitCommand(`git rev-parse --abbrev-ref HEAD`)??`unknown`,commitMessage:parseEnvValue(process.env.VERCEL_GIT_COMMIT_MESSAGE)??runGitCommand(`git log -1 --pretty=%B`)??``,commitSha:parseEnvValue(process.env.VERCEL_GIT_COMMIT_SHA)??parseEnvValue(process.env.GITHUB_SHA)??runGitCommand(`git rev-parse HEAD`)}}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}`}export{createSdkClient,runReleasePipeline};
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.mjs","names":[],"sources":["../../../src/internal/build/pipeline.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\n\nimport { HTTPClient, Interfere } from \"@interfere/sdk\";\nimport type { ReleasesConfigResponse } from \"@interfere/sdk/models/releases-config-response.js\";\nimport type { ManifestBundler } from \"@interfere/types/data/source-maps\";\nimport type {\n DestinationProvider,\n ReleaseDestinationMetadata,\n ReleaseSourceMetadata,\n SourceProvider,\n} from \"@interfere/types/integrations\";\nimport type {\n CreateReleaseRequest,\n CreateReleaseResponse,\n} from \"@interfere/types/releases/definition\";\nimport {\n type ReleaseSlug,\n releaseSlugSchema,\n} from \"@interfere/types/releases/slug\";\nimport { parseEnvValue } from \"@interfere/types/sdk/env\";\n\nimport {\n cleanupSourceMaps,\n type DiscoveryResult,\n uploadSourceMaps,\n} from \"./source-maps.js\";\n\nexport interface BuildTiming {\n cleanup: number;\n createRelease: number;\n fileCount: number;\n preflight: number;\n total: number;\n totalBytes: number;\n upload: number;\n}\n\nexport type ReleasePipelineFailure =\n | { kind: \"missing_source_provider\"; config: ReleasesConfigResponse }\n | { kind: \"missing_destination_provider\"; config: ReleasesConfigResponse };\n\nexport type ReleasePipelineResult =\n | { ok: true; data: ReleasePipelineSuccess }\n | { ok: false; failure: ReleasePipelineFailure };\n\nexport interface ReleasePipelineSuccess {\n buildId: string;\n cleanupFailures: {\n absolute: string;\n code: string | undefined;\n message: string;\n }[];\n config: ReleasesConfigResponse;\n fileCount: number;\n release: CreateReleaseResponse;\n releaseSlug: ReleaseSlug;\n timing: BuildTiming;\n}\n\nexport interface ReleasePipelineInput {\n apiKey: string;\n apiUrl: string;\n buildId: string;\n bundler: ManifestBundler;\n discovery: DiscoveryResult;\n producerVersion: string;\n releaseSlug: ReleaseSlug;\n releasesConfig: ReleasesConfigResponse;\n}\n\ntype ResolveReleaseRequestResult =\n | { ok: true; request: CreateReleaseRequest }\n | {\n ok: false;\n reason: \"missing_source_provider\" | \"missing_destination_provider\";\n };\n\nconst sources: Record<SourceProvider, () => ReleaseSourceMetadata> = {\n github: resolveGithubSource,\n};\n\nconst destinations: Record<\n DestinationProvider,\n () => ReleaseDestinationMetadata\n> = {\n vercel: resolveVercelDestination,\n};\n\nasync function timed<T>(fn: () => Promise<T>): Promise<[T, number]> {\n const start = performance.now();\n const result = await fn();\n return [result, Math.round(performance.now() - start)];\n}\n\nexport function createSdkClient(opts: { apiKey: string; apiUrl: string }): {\n httpClient: HTTPClient;\n sdk: Interfere;\n} {\n const httpClient = new HTTPClient();\n httpClient.addHook(\"beforeRequest\", (request) => {\n const next = new Request(request);\n next.headers.set(\"Authorization\", `Bearer ${opts.apiKey}`);\n return next;\n });\n return {\n httpClient,\n sdk: new Interfere({ serverURL: opts.apiUrl, httpClient }),\n };\n}\n\nfunction resolveReleaseRequest(\n buildId: string,\n releaseSlug: ReleaseSlug,\n producerVersion: string,\n config: ReleasesConfigResponse\n): ResolveReleaseRequestResult {\n if (!config.surface.sourceProvider) {\n return { ok: false, reason: \"missing_source_provider\" };\n }\n\n if (!config.surface.destinationProvider) {\n return { ok: false, reason: \"missing_destination_provider\" };\n }\n\n return {\n ok: true,\n request: {\n source: sources[config.surface.sourceProvider](),\n destination: destinations[config.surface.destinationProvider](),\n buildId,\n slug: releaseSlug,\n producerVersion,\n },\n };\n}\n\nexport async function runReleasePipeline(\n input: ReleasePipelineInput\n): Promise<ReleasePipelineResult> {\n const { apiKey, apiUrl, bundler, discovery, releasesConfig } = input;\n const releaseRequest = resolveReleaseRequest(\n input.buildId,\n input.releaseSlug,\n input.producerVersion,\n releasesConfig\n );\n\n if (!releaseRequest.ok) {\n return {\n ok: false,\n failure: { kind: releaseRequest.reason, config: releasesConfig },\n };\n }\n\n const start = performance.now();\n const { httpClient, sdk } = createSdkClient({ apiKey, apiUrl });\n const [release, createReleaseMs] = await timed(() =>\n sdk.releases.create(releaseRequest.request)\n );\n const releaseSlug = releaseSlugSchema.parse(release.destination.slug);\n const buildId = release.build.hash ?? input.buildId;\n const [{ totalBytes }, uploadMs] = await timed(() =>\n uploadSourceMaps({\n apiUrl,\n bundler,\n discovered: discovery,\n httpClient,\n releaseSlug,\n })\n );\n const [, preflightMs] = await timed(() =>\n sdk.releases.preflight({ releaseSlug })\n );\n const [{ failures }, cleanupMs] = await timed(() =>\n cleanupSourceMaps(discovery.files)\n );\n\n return {\n ok: true,\n data: {\n buildId,\n cleanupFailures: failures,\n config: releasesConfig,\n fileCount: discovery.files.length,\n release: {\n ...release,\n destination: { ...release.destination, slug: releaseSlug },\n },\n releaseSlug,\n timing: {\n createRelease: createReleaseMs,\n upload: uploadMs,\n preflight: preflightMs,\n cleanup: cleanupMs,\n total: Math.round(performance.now() - start),\n fileCount: discovery.files.length,\n totalBytes,\n },\n },\n };\n}\n\nfunction runGitCommand(command: string): string | null {\n try {\n const output = execSync(command, {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n return output.length > 0 ? output : null;\n } catch {\n return null;\n }\n}\n\nfunction resolveGithubSource(): ReleaseSourceMetadata {\n return {\n provider: \"github\",\n branch:\n parseEnvValue(process.env[\"VERCEL_GIT_COMMIT_REF\"]) ??\n parseEnvValue(process.env[\"GITHUB_REF_NAME\"]) ??\n parseEnvValue(process.env[\"GITHUB_HEAD_REF\"]) ??\n runGitCommand(\"git rev-parse --abbrev-ref HEAD\") ??\n \"unknown\",\n commitMessage:\n parseEnvValue(process.env[\"VERCEL_GIT_COMMIT_MESSAGE\"]) ??\n runGitCommand(\"git log -1 --pretty=%B\") ??\n \"\",\n commitSha:\n parseEnvValue(process.env[\"VERCEL_GIT_COMMIT_SHA\"]) ??\n parseEnvValue(process.env[\"GITHUB_SHA\"]) ??\n runGitCommand(\"git rev-parse HEAD\"),\n };\n}\n\nfunction resolveVercelDestination(): ReleaseDestinationMetadata {\n const environment = parseEnvValue(\n process.env[\"VERCEL_ENV\"] ?? process.env[\"VERCEL_TARGET_ENV\"]\n );\n const deploymentId = parseEnvValue(process.env[\"VERCEL_DEPLOYMENT_ID\"]);\n\n return {\n provider: \"vercel\",\n destinationReleaseId: deploymentId,\n environment,\n deploymentId,\n deploymentUrl: resolveDeploymentUrl(),\n environmentName: environment,\n environmentTarget: environment,\n };\n}\n\nfunction resolveDeploymentUrl(): string | null {\n const deploymentUrl = parseEnvValue(process.env[\"VERCEL_URL\"]);\n if (deploymentUrl === null) {\n return null;\n }\n if (\n deploymentUrl.startsWith(\"https://\") ||\n deploymentUrl.startsWith(\"http://\")\n ) {\n return deploymentUrl;\n }\n return `https://${deploymentUrl}`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.mjs","names":[],"sources":["../../../src/internal/build/pipeline.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\n\nimport { HTTPClient, Interfere } from \"@interfere/sdk\";\nimport type { ReleasesConfigResponse } from \"@interfere/sdk/models/releases-config-response.js\";\nimport type { ManifestBundler } from \"@interfere/types/data/source-maps\";\nimport {\n destinationProviderSchema,\n type DestinationProvider,\n type ReleaseDestinationMetadata,\n type ReleaseSourceMetadata,\n type SourceProvider,\n} from \"@interfere/types/integrations\";\nimport type {\n CreateReleaseRequest,\n CreateReleaseResponse,\n} from \"@interfere/types/releases/definition\";\nimport {\n type ReleaseSlug,\n releaseSlugSchema,\n} from \"@interfere/types/releases/slug\";\nimport { parseEnvValue } from \"@interfere/types/sdk/env\";\n\nimport {\n cleanupSourceMaps,\n type DiscoveryResult,\n uploadSourceMaps,\n} from \"./source-maps.js\";\n\nexport interface BuildTiming {\n cleanup: number;\n createRelease: number;\n fileCount: number;\n preflight: number;\n total: number;\n totalBytes: number;\n upload: number;\n}\n\nexport type ReleasePipelineFailure =\n | { kind: \"missing_source_provider\"; config: ReleasesConfigResponse }\n | { kind: \"missing_destination_provider\"; config: ReleasesConfigResponse };\n\nexport type ReleasePipelineResult =\n | { ok: true; data: ReleasePipelineSuccess }\n | { ok: false; failure: ReleasePipelineFailure };\n\nexport interface ReleasePipelineSuccess {\n buildId: string;\n cleanupFailures: {\n absolute: string;\n code: string | undefined;\n message: string;\n }[];\n config: ReleasesConfigResponse;\n fileCount: number;\n release: CreateReleaseResponse;\n releaseSlug: ReleaseSlug;\n timing: BuildTiming;\n}\n\nexport interface ReleasePipelineInput {\n apiKey: string;\n apiUrl: string;\n buildId: string;\n bundler: ManifestBundler;\n discovery: DiscoveryResult;\n producerVersion: string;\n releaseSlug: ReleaseSlug;\n releasesConfig: ReleasesConfigResponse;\n}\n\ntype ResolveReleaseRequestResult =\n | { ok: true; request: CreateReleaseRequest }\n | {\n ok: false;\n reason: \"missing_source_provider\" | \"missing_destination_provider\";\n };\n\nconst sources: Record<SourceProvider, () => ReleaseSourceMetadata> = {\n github: resolveGithubSource,\n};\n\nconst destinations: Record<\n DestinationProvider,\n () => ReleaseDestinationMetadata\n> = {\n vercel: resolveVercelDestination,\n};\n\nasync function timed<T>(fn: () => Promise<T>): Promise<[T, number]> {\n const start = performance.now();\n const result = await fn();\n return [result, Math.round(performance.now() - start)];\n}\n\nexport function createSdkClient(opts: { apiKey: string; apiUrl: string }): {\n httpClient: HTTPClient;\n sdk: Interfere;\n} {\n const httpClient = new HTTPClient();\n httpClient.addHook(\"beforeRequest\", (request) => {\n const next = new Request(request);\n next.headers.set(\"Authorization\", `Bearer ${opts.apiKey}`);\n return next;\n });\n return {\n httpClient,\n sdk: new Interfere({ serverURL: opts.apiUrl, httpClient }),\n };\n}\n\nfunction resolveReleaseRequest(\n buildId: string,\n releaseSlug: ReleaseSlug,\n producerVersion: string,\n config: ReleasesConfigResponse\n): ResolveReleaseRequestResult {\n if (!config.surface.sourceProvider) {\n return { ok: false, reason: \"missing_source_provider\" };\n }\n\n const destinationProvider = destinationProviderSchema.safeParse(\n config.surface.destinationProvider\n );\n if (!destinationProvider.success) {\n return { ok: false, reason: \"missing_destination_provider\" };\n }\n\n return {\n ok: true,\n request: {\n source: sources[config.surface.sourceProvider](),\n destination: destinations[destinationProvider.data](),\n buildId,\n slug: releaseSlug,\n producerVersion,\n },\n };\n}\n\nexport async function runReleasePipeline(\n input: ReleasePipelineInput\n): Promise<ReleasePipelineResult> {\n const { apiKey, apiUrl, bundler, discovery, releasesConfig } = input;\n const releaseRequest = resolveReleaseRequest(\n input.buildId,\n input.releaseSlug,\n input.producerVersion,\n releasesConfig\n );\n\n if (!releaseRequest.ok) {\n return {\n ok: false,\n failure: { kind: releaseRequest.reason, config: releasesConfig },\n };\n }\n\n const start = performance.now();\n const { httpClient, sdk } = createSdkClient({ apiKey, apiUrl });\n const [release, createReleaseMs] = await timed(() =>\n sdk.releases.create(releaseRequest.request)\n );\n const releaseSlug = releaseSlugSchema.parse(release.destination.slug);\n const buildId = release.build.hash ?? input.buildId;\n const [{ totalBytes }, uploadMs] = await timed(() =>\n uploadSourceMaps({\n apiUrl,\n bundler,\n discovered: discovery,\n httpClient,\n releaseSlug,\n })\n );\n const [, preflightMs] = await timed(() =>\n sdk.releases.preflight({ releaseSlug })\n );\n const [{ failures }, cleanupMs] = await timed(() =>\n cleanupSourceMaps(discovery.files)\n );\n\n return {\n ok: true,\n data: {\n buildId,\n cleanupFailures: failures,\n config: releasesConfig,\n fileCount: discovery.files.length,\n release: {\n ...release,\n destination: { ...release.destination, slug: releaseSlug },\n },\n releaseSlug,\n timing: {\n createRelease: createReleaseMs,\n upload: uploadMs,\n preflight: preflightMs,\n cleanup: cleanupMs,\n total: Math.round(performance.now() - start),\n fileCount: discovery.files.length,\n totalBytes,\n },\n },\n };\n}\n\nfunction runGitCommand(command: string): string | null {\n try {\n const output = execSync(command, {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n return output.length > 0 ? output : null;\n } catch {\n return null;\n }\n}\n\nfunction resolveGithubSource(): ReleaseSourceMetadata {\n return {\n provider: \"github\",\n branch:\n parseEnvValue(process.env[\"VERCEL_GIT_COMMIT_REF\"]) ??\n parseEnvValue(process.env[\"GITHUB_REF_NAME\"]) ??\n parseEnvValue(process.env[\"GITHUB_HEAD_REF\"]) ??\n runGitCommand(\"git rev-parse --abbrev-ref HEAD\") ??\n \"unknown\",\n commitMessage:\n parseEnvValue(process.env[\"VERCEL_GIT_COMMIT_MESSAGE\"]) ??\n runGitCommand(\"git log -1 --pretty=%B\") ??\n \"\",\n commitSha:\n parseEnvValue(process.env[\"VERCEL_GIT_COMMIT_SHA\"]) ??\n parseEnvValue(process.env[\"GITHUB_SHA\"]) ??\n runGitCommand(\"git rev-parse HEAD\"),\n };\n}\n\nfunction resolveVercelDestination(): ReleaseDestinationMetadata {\n const environment = parseEnvValue(\n process.env[\"VERCEL_ENV\"] ?? process.env[\"VERCEL_TARGET_ENV\"]\n );\n const deploymentId = parseEnvValue(process.env[\"VERCEL_DEPLOYMENT_ID\"]);\n\n return {\n provider: \"vercel\",\n destinationReleaseId: deploymentId,\n environment,\n deploymentId,\n deploymentUrl: resolveDeploymentUrl(),\n environmentName: environment,\n environmentTarget: environment,\n };\n}\n\nfunction resolveDeploymentUrl(): string | null {\n const deploymentUrl = parseEnvValue(process.env[\"VERCEL_URL\"]);\n if (deploymentUrl === null) {\n return null;\n }\n if (\n deploymentUrl.startsWith(\"https://\") ||\n deploymentUrl.startsWith(\"http://\")\n ) {\n return deploymentUrl;\n }\n return `https://${deploymentUrl}`;\n}\n"],"mappings":"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"}
@@ -17,7 +17,7 @@ declare function extractSubPath(request: Request): string;
17
17
  /**
18
18
  * Preserve any query string verbatim when forwarding upstream.
19
19
  *
20
- * The browser SDK uses `?_pv=…` and `?pk=…` (and `?_pt=…` in direct-ingestion
20
+ * The browser SDK uses `?_pv=…` and `?pk=…` (and `?_pt=…` in direct
21
21
  * mode) as a `navigator.sendBeacon` fallback when `visibilitychange→
22
22
  * hidden` aborts the keepalive fetch path, so authentication works without
23
23
  * custom headers. The collector's `otlpProducerGuard` and `surfacePkAuth`
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.mjs","names":[],"sources":["../../../src/internal/route/proxy.ts"],"sourcesContent":["import { resolveRoutePrefix } from \"@interfere/constants/route-prefix\";\nimport { omitUndefined } from \"@interfere/helpers/omit-undefined\";\nimport { extractCountryHeader } from \"@interfere/types/sdk/geo\";\n\nimport {\n type InterfereServerEnv,\n readInterfereServerEnv,\n} from \"../server/env.js\";\n\n// Never forward customer API keys from browser-originated ingestion requests.\nconst STRIPPED_AUTH_HEADERS = [\n \"authorization\",\n \"x-api-key\",\n \"x-interfere-api-key\",\n] as const;\n\n// Headers tied to the inbound HTTP transaction. These must not leak\n// onto the outbound `fetch` because the runtime computes them fresh\n// for the new body / connection, and forwarding them stale either\n// silently corrupts the request or hangs it until a timeout fires.\n// Same set Next's proxy strips (see `@interfere/next/internal/route/\n// proxy.ts` for the full incident postmortem on `content-length`).\nconst STRIPPED_TRANSPORT_HEADERS = [\n \"host\",\n \"content-length\",\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n \"te\",\n \"trailer\",\n \"upgrade\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n] as const;\n\n/**\n * Clone the inbound request's headers, removing entries that should\n * never cross the proxy boundary. Returns a mutable `Headers` so\n * callers can `.set()`/`.delete()` upstream-specific values without\n * having to merge record literals (which lose case-insensitivity).\n */\nexport function forwardedHeaders(request: Request): Headers {\n const headers = new Headers(request.headers);\n for (const name of STRIPPED_AUTH_HEADERS) {\n headers.delete(name);\n }\n for (const name of STRIPPED_TRANSPORT_HEADERS) {\n headers.delete(name);\n }\n return headers;\n}\n\nexport interface AuthenticatedEnv {\n apiUrl: string;\n forceEnable: InterfereServerEnv[\"forceEnable\"];\n}\n\nexport function resolveAuthenticatedEnv(): AuthenticatedEnv {\n const env = readInterfereServerEnv();\n return {\n apiUrl: env.apiUrl,\n forceEnable: env.forceEnable,\n };\n}\n\nexport function extractSubPath(request: Request): string {\n const prefix = resolveRoutePrefix();\n const url = new URL(request.url);\n const idx = url.pathname.indexOf(prefix);\n if (idx === -1) {\n return \"/\";\n }\n const remainder = url.pathname.slice(idx + prefix.length);\n if (remainder === \"\") {\n return \"/\";\n }\n return remainder.startsWith(\"/\") ? remainder : `/${remainder}`;\n}\n\n/**\n * Preserve any query string verbatim when forwarding upstream.\n *\n * The browser SDK uses `?_pv=…` and `?pk=…` (and `?_pt=…` in direct-ingestion\n * mode) as a `navigator.sendBeacon` fallback when `visibilitychange→\n * hidden` aborts the keepalive fetch path, so authentication works without\n * custom headers. The collector's `otlpProducerGuard` and `surfacePkAuth`\n * accept those values verbatim — but only if the proxy actually forwards them.\n */\nexport function extractSearch(request: Request): string {\n return new URL(request.url).search;\n}\n\nexport function formatProxyError(error: unknown): {\n message: string;\n lines: string[];\n} {\n if (!(error instanceof Error)) {\n return { message: String(error), lines: [String(error)] };\n }\n\n const lines = [`${error.name}: ${error.message}`];\n\n if (\"cause\" in error && error.cause) {\n const cause =\n error.cause instanceof Error ? error.cause.message : String(error.cause);\n lines.push(`cause: ${cause}`);\n }\n\n if (\n \"code\" in error &&\n typeof (error as NodeJS.ErrnoException).code === \"string\"\n ) {\n lines.push(`code: ${(error as NodeJS.ErrnoException).code}`);\n }\n\n return { message: `${error.name}: ${error.message}`, lines };\n}\n\nexport async function forwardToCollector(\n request: Request,\n env: AuthenticatedEnv,\n subPath: string\n): Promise<Response> {\n const url = `${env.apiUrl}${subPath}${extractSearch(request)}`;\n const hasBody = request.method !== \"GET\" && request.method !== \"HEAD\";\n const body = hasBody ? await request.text() : undefined;\n\n const headers = forwardedHeaders(request);\n // Default to JSON only when the inbound request didn't pin a\n // content-type — non-`/v1/ingest` paths relay the body bytes\n // verbatim, so honour whatever the caller said it was sending.\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n if (env.forceEnable) {\n headers.set(\"x-interfere-force-enable\", \"1\");\n }\n const country = extractCountryHeader(request);\n if (country !== null) {\n headers.set(\"x-country-code\", country);\n }\n\n const upstream = await fetch(url, {\n ...omitUndefined({ body }),\n method: request.method,\n headers,\n signal: AbortSignal.timeout(10_000),\n });\n\n if (!upstream.ok) {\n const text = await upstream.text().catch(() => \"\");\n\n if (upstream.status >= 500) {\n console.error(\n `[interfere] Upstream ${upstream.status} for ${request.method} ${subPath}: ${text}`\n );\n return Response.json(\n { code: \"INTERFERE_UPSTREAM_ERROR\", message: text },\n { status: upstream.status }\n );\n }\n if (upstream.status >= 400) {\n console.warn(\n `[interfere] Upstream ${upstream.status} for ${request.method} ${subPath}: ${text}`\n );\n return Response.json(\n { code: \"INTERFERE_UPSTREAM_WARNING\", message: text },\n { status: upstream.status }\n );\n }\n\n console.error(\n `[interfere] Upstream ${upstream.status} for ${request.method} ${subPath}: ${text}`\n );\n return Response.json(\n { code: \"INTERFERE_UPSTREAM_ERROR\", message: text },\n { status: upstream.status }\n );\n }\n\n return new Response(upstream.body, {\n status: upstream.status,\n headers: {\n \"content-type\":\n upstream.headers.get(\"content-type\") ?? \"application/json\",\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"proxy.mjs","names":[],"sources":["../../../src/internal/route/proxy.ts"],"sourcesContent":["import { resolveRoutePrefix } from \"@interfere/constants/route-prefix\";\nimport { omitUndefined } from \"@interfere/helpers/omit-undefined\";\nimport { extractCountryHeader } from \"@interfere/types/sdk/geo\";\n\nimport {\n type InterfereServerEnv,\n readInterfereServerEnv,\n} from \"../server/env.js\";\n\n// Never forward customer API keys from browser-originated ingestion requests.\nconst STRIPPED_AUTH_HEADERS = [\n \"authorization\",\n \"x-api-key\",\n \"x-interfere-api-key\",\n] as const;\n\n// Headers tied to the inbound HTTP transaction. These must not leak\n// onto the outbound `fetch` because the runtime computes them fresh\n// for the new body / connection, and forwarding them stale either\n// silently corrupts the request or hangs it until a timeout fires.\n// Same set Next's proxy strips (see `@interfere/next/internal/route/\n// proxy.ts` for the full incident postmortem on `content-length`).\nconst STRIPPED_TRANSPORT_HEADERS = [\n \"host\",\n \"content-length\",\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n \"te\",\n \"trailer\",\n \"upgrade\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n] as const;\n\n/**\n * Clone the inbound request's headers, removing entries that should\n * never cross the proxy boundary. Returns a mutable `Headers` so\n * callers can `.set()`/`.delete()` upstream-specific values without\n * having to merge record literals (which lose case-insensitivity).\n */\nexport function forwardedHeaders(request: Request): Headers {\n const headers = new Headers(request.headers);\n for (const name of STRIPPED_AUTH_HEADERS) {\n headers.delete(name);\n }\n for (const name of STRIPPED_TRANSPORT_HEADERS) {\n headers.delete(name);\n }\n return headers;\n}\n\nexport interface AuthenticatedEnv {\n apiUrl: string;\n forceEnable: InterfereServerEnv[\"forceEnable\"];\n}\n\nexport function resolveAuthenticatedEnv(): AuthenticatedEnv {\n const env = readInterfereServerEnv();\n return {\n apiUrl: env.apiUrl,\n forceEnable: env.forceEnable,\n };\n}\n\nexport function extractSubPath(request: Request): string {\n const prefix = resolveRoutePrefix();\n const url = new URL(request.url);\n const idx = url.pathname.indexOf(prefix);\n if (idx === -1) {\n return \"/\";\n }\n const remainder = url.pathname.slice(idx + prefix.length);\n if (remainder === \"\") {\n return \"/\";\n }\n return remainder.startsWith(\"/\") ? remainder : `/${remainder}`;\n}\n\n/**\n * Preserve any query string verbatim when forwarding upstream.\n *\n * The browser SDK uses `?_pv=…` and `?pk=…` (and `?_pt=…` in direct\n * mode) as a `navigator.sendBeacon` fallback when `visibilitychange→\n * hidden` aborts the keepalive fetch path, so authentication works without\n * custom headers. The collector's `otlpProducerGuard` and `surfacePkAuth`\n * accept those values verbatim — but only if the proxy actually forwards them.\n */\nexport function extractSearch(request: Request): string {\n return new URL(request.url).search;\n}\n\nexport function formatProxyError(error: unknown): {\n message: string;\n lines: string[];\n} {\n if (!(error instanceof Error)) {\n return { message: String(error), lines: [String(error)] };\n }\n\n const lines = [`${error.name}: ${error.message}`];\n\n if (\"cause\" in error && error.cause) {\n const cause =\n error.cause instanceof Error ? error.cause.message : String(error.cause);\n lines.push(`cause: ${cause}`);\n }\n\n if (\n \"code\" in error &&\n typeof (error as NodeJS.ErrnoException).code === \"string\"\n ) {\n lines.push(`code: ${(error as NodeJS.ErrnoException).code}`);\n }\n\n return { message: `${error.name}: ${error.message}`, lines };\n}\n\nexport async function forwardToCollector(\n request: Request,\n env: AuthenticatedEnv,\n subPath: string\n): Promise<Response> {\n const url = `${env.apiUrl}${subPath}${extractSearch(request)}`;\n const hasBody = request.method !== \"GET\" && request.method !== \"HEAD\";\n const body = hasBody ? await request.text() : undefined;\n\n const headers = forwardedHeaders(request);\n // Default to JSON only when the inbound request didn't pin a\n // content-type — non-`/v1/ingest` paths relay the body bytes\n // verbatim, so honour whatever the caller said it was sending.\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n if (env.forceEnable) {\n headers.set(\"x-interfere-force-enable\", \"1\");\n }\n const country = extractCountryHeader(request);\n if (country !== null) {\n headers.set(\"x-country-code\", country);\n }\n\n const upstream = await fetch(url, {\n ...omitUndefined({ body }),\n method: request.method,\n headers,\n signal: AbortSignal.timeout(10_000),\n });\n\n if (!upstream.ok) {\n const text = await upstream.text().catch(() => \"\");\n\n if (upstream.status >= 500) {\n console.error(\n `[interfere] Upstream ${upstream.status} for ${request.method} ${subPath}: ${text}`\n );\n return Response.json(\n { code: \"INTERFERE_UPSTREAM_ERROR\", message: text },\n { status: upstream.status }\n );\n }\n if (upstream.status >= 400) {\n console.warn(\n `[interfere] Upstream ${upstream.status} for ${request.method} ${subPath}: ${text}`\n );\n return Response.json(\n { code: \"INTERFERE_UPSTREAM_WARNING\", message: text },\n { status: upstream.status }\n );\n }\n\n console.error(\n `[interfere] Upstream ${upstream.status} for ${request.method} ${subPath}: ${text}`\n );\n return Response.json(\n { code: \"INTERFERE_UPSTREAM_ERROR\", message: text },\n { status: upstream.status }\n );\n }\n\n return new Response(upstream.body, {\n status: upstream.status,\n headers: {\n \"content-type\":\n upstream.headers.get(\"content-type\") ?? \"application/json\",\n },\n });\n}\n"],"mappings":"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"}
package/dist/package.mjs CHANGED
@@ -1 +1 @@
1
- var name=`@interfere/vite`,version=`10.0.1-canary.0`;export{name,version};
1
+ var name=`@interfere/vite`,version=`10.0.1-canary.1`;export{name,version};
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;KAwBY,iBAAA;AAAA,UAEK,oBAAA;EAFL;;;;AAAiB;AAE7B;EAOE,SAAA;;;;;;;;AA0BU;AA8WZ;;;;;;;;AAAqE;EAtXnE,IAAA,GAAO,iBAAiB;;;;;;;;EAQxB,UAAA;AAAA;AAAA,iBA8Wc,SAAA,CAAU,OAAA,GAAS,oBAAA,GAA4B,MAAM"}
1
+ {"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;KAwBY,iBAAA;AAAA,UAEK,oBAAA;EAFL;;;;AAAiB;AAE7B;EAOE,SAAA;;;;;;;;AA0BU;AAwWZ;;;;;;;;AAAqE;EAhXnE,IAAA,GAAO,iBAAiB;;;;;;;;EAQxB,UAAA;AAAA;AAAA,iBAwWc,SAAA,CAAU,OAAA,GAAS,oBAAA,GAA4B,MAAM"}
package/dist/plugin.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import{PRODUCER_VERSION}from"./version.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{existsSync,readdirSync,statSync}from"node:fs";import{join,resolve}from"node:path";import{loadEnv}from"vite";const SERVER_RUNTIME_PLUGIN_HINTS=[`tanstack-start`,`nitro`,`@tanstack/react-start`];function isTruthyEnvFlag(value){if(!value)return!1;let normalized=value.trim().toLowerCase();return normalized===`1`||normalized===`true`}function isTestEnv(){return!!(process.env.VITEST||process.env.VITEST_WORKER_ID)}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://support.interfere.com/docs/guides/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 detectServerRuntime(config){return config.plugins.some(p=>SERVER_RUNTIME_PLUGIN_HINTS.some(hint=>p.name?.includes(hint)))}function resolveMode(optsMode,config,logger){let requested=optsMode??`auto`;if(requested!==`auto`)return logger.info(`[interfere] mode=${requested} (explicit). Plugin will ${requested===`direct`?`stamp the public key for direct ingestion`:`stamp the public key and run the SDK in proxy mode`}.`),requested;let detected=detectServerRuntime(config)?`proxy`:`direct`;return logger.info(`[interfere] mode=${detected} (auto-detected). Override with \`interfere({ mode: "..." })\`.`),detected}function hasFreshServerSourceMaps(root,sinceMs){if(!existsSync(root))return!1;let entries=readdirSync(root,{withFileTypes:!0});for(let entry of entries){let absolute=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.enableUpload&&state.apiKey&&state.releaseSlug&&state.commitSha&&!state.uploadStarted)}function shouldWaitForServerOutput(state){return state.mode===`proxy`&&!hasFreshServerSourceMaps(join(state.uploadOutDir,`server`),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;logger.info(`[interfere] uploading source maps for release ${releaseSlug}…`);try{let{sdk}=createSdkClient({apiKey,apiUrl}),[discovered,releasesConfig]=await Promise.all([discoverByPairing({projectDir,distDir:uploadOutDir,subtrees:[`.`],publicPathRewrites:[{from:`public`,to:``}]}),sdk.releases.getConfig()]);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,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(err){let message=err instanceof Error?err.message:String(err);logger.warn(`[interfere] release pipeline failed: ${message}. Build continues; events from this release will ingest but be ignored as evidence.`)}}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,mode,publicKeyPresent,apiKey,releaseSlug,sourceMapsExplicit,isProductionBuild}=state;mode===`proxy`&&publicKeyPresent&&logger.info(`[interfere] mode=proxy: using VITE_INTERFERE_PUBLIC_KEY with the first-party proxy route.`),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 API 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 buildPluginState(options,config){let logger=config.logger,mode=resolveMode(options.mode,config,logger),publicKey=config.env.VITE_INTERFERE_PUBLIC_KEY;config.command===`build`&&!publicKey&&failMissingPublicKey();let commitSha=options.commitSha??config.env.VITE_INTERFERE_COMMIT_SHA??resolveCommitSha(),releaseSlug=commitSha?deriveReleaseSlug(commitSha):null,fileEnv=loadEnv(config.mode,config.envDir,``),pickEnv=key=>{let shell=process.env[key];if(shell&&shell.length>0)return shell;let file=fileEnv[key];return file&&file.length>0?file:void 0},apiKey=pickEnv(`INTERFERE_API_KEY`),apiUrl=pickEnv(`INTERFERE_API_URL`)??API_URL,enableUpload=options.sourceMaps??(apiKey!==void 0&&apiKey.length>0&&releaseSlug!==null),rawBrowserApiUrl=config.env.VITE_INTERFERE_API_URL,browserApiUrl=rawBrowserApiUrl&&rawBrowserApiUrl.length>0?rawBrowserApiUrl:void 0;return emitSetupWarnings({apiKey,isProductionBuild:config.command===`build`,logger,mode,publicKeyPresent:!!publicKey,releaseSlug,sourceMapsExplicit:options.sourceMaps}),{apiKey,apiUrl,browserApiUrl:mode===`direct`?browserApiUrl:void 0,commitSha,enableUpload,forceEnable:isTruthyEnvFlag(config.env.VITE_INTERFERE_FORCE_ENABLE),logger,mode,projectDir:config.root,publicKey,releaseSlug,uploadOutDir:resolve(config.root,config.build.outDir),uploadStarted:!1,buildStartedAt:Date.now()}}function interfere(options={}){let state=null;return{name:`interfere`,enforce:`pre`,configResolved(config){let next=buildPluginState(options,config);state?.mode===`proxy`&&next.mode===`proxy`&&(next.uploadOutDir=state.uploadOutDir,next.uploadStarted=state.uploadStarted,next.buildStartedAt=state.buildStartedAt),state=next},transformIndexHtml(){let assignments=[];return state?.releaseSlug&&assignments.push(`globalThis["__INTERFERE_RELEASE_SLUG__"]=${JSON.stringify(state.releaseSlug)};`),state?.publicKey&&assignments.push(`globalThis["__INTERFERE_PUBLIC_KEY__"]=${JSON.stringify(state.publicKey)};`),state?.forceEnable&&assignments.push(`globalThis["__INTERFERE_FORCE_ENABLE__"]=true;`),state?.mode===`proxy`&&state.publicKey&&assignments.push(`globalThis["__INTERFERE_PROXY_MODE__"]=true;`),state?.mode===`direct`&&state.browserApiUrl&&assignments.push(`globalThis["__INTERFERE_API_URL__"]=${JSON.stringify(state.browserApiUrl)};`),assignments.length===0?[]:[{tag:`script`,children:assignments.join(``),injectTo:`head-prepend`}]},async closeBundle(){state&&await uploadSourceMapsWhenReady(state)}}}export{interfere as default,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 detectServerRuntime(config){return config.plugins.some(p=>SERVER_RUNTIME_PLUGIN_HINTS.some(hint=>p.name?.includes(hint)))}function resolveMode(optsMode,config,logger){let requested=optsMode??`auto`;if(requested!==`auto`)return logger.info(`[interfere] mode=${requested} (explicit)`),requested;let detected=detectServerRuntime(config)?`proxy`:`direct`;return logger.info(`[interfere] mode=${detected} (auto-detected). Override with \`interfere({ mode: "..." })\`.`),detected}function hasFreshServerSourceMaps(root,sinceMs){if(!existsSync(root))return!1;let entries=readdirSync(root,{withFileTypes:!0});for(let entry of entries){let absolute=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.enableUpload&&state.apiKey&&state.releaseSlug&&state.commitSha&&!state.uploadStarted)}function shouldWaitForServerOutput(state){return state.mode===`proxy`&&!hasFreshServerSourceMaps(join(state.uploadOutDir,`server`),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;logger.info(`[interfere] uploading source maps for release ${releaseSlug}…`);try{let{sdk}=createSdkClient({apiKey,apiUrl}),[discovered,releasesConfig]=await Promise.all([discoverByPairing({projectDir,distDir:uploadOutDir,subtrees:[`.`],publicPathRewrites:[{from:`public`,to:``}]}),sdk.releases.getConfig()]);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,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(err){let message=err instanceof Error?err.message:String(err);logger.warn(`[interfere] release pipeline failed: ${message}. Build continues; events from this release will ingest but be ignored as evidence.`)}}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,mode,publicKeyPresent,apiKey,releaseSlug,sourceMapsExplicit,isProductionBuild}=state;mode===`proxy`&&publicKeyPresent&&logger.info(`[interfere] mode=proxy: using VITE_INTERFERE_PUBLIC_KEY with the first-party proxy route.`),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 API 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 buildPluginState(options,config){let logger=config.logger,mode=resolveMode(options.mode,config,logger),publicKey=config.env.VITE_INTERFERE_PUBLIC_KEY;config.command===`build`&&!publicKey&&failMissingPublicKey();let commitSha=options.commitSha??config.env.VITE_INTERFERE_COMMIT_SHA??resolveCommitSha(),releaseSlug=commitSha?deriveReleaseSlug(commitSha):null,fileEnv=loadEnv(config.mode,config.envDir,``),pickEnv=key=>{let shell=process.env[key];if(shell&&shell.length>0)return shell;let file=fileEnv[key];return file&&file.length>0?file:void 0},apiKey=pickEnv(`INTERFERE_API_KEY`),apiUrl=pickEnv(`INTERFERE_API_URL`)??API_URL,enableUpload=options.sourceMaps??(apiKey!==void 0&&apiKey.length>0&&releaseSlug!==null),rawBrowserApiUrl=config.env.VITE_INTERFERE_API_URL,browserApiUrl=rawBrowserApiUrl&&rawBrowserApiUrl.length>0?rawBrowserApiUrl:void 0;return emitSetupWarnings({apiKey,isProductionBuild:config.command===`build`,logger,mode,publicKeyPresent:!!publicKey,releaseSlug,sourceMapsExplicit:options.sourceMaps}),{apiKey,apiUrl,browserApiUrl:mode===`direct`?browserApiUrl:void 0,commitSha,enableUpload,forceEnable:isTruthyEnvFlag(config.env.VITE_INTERFERE_FORCE_ENABLE),logger,mode,projectDir:config.root,publicKey,releaseSlug,uploadOutDir:resolve(config.root,config.build.outDir),uploadStarted:!1,buildStartedAt:Date.now()}}function interfere(options={}){let state=null;return{name:`interfere`,enforce:`pre`,configResolved(config){let next=buildPluginState(options,config);state?.mode===`proxy`&&next.mode===`proxy`&&(next.uploadOutDir=state.uploadOutDir,next.uploadStarted=state.uploadStarted,next.buildStartedAt=state.buildStartedAt),state=next},transformIndexHtml(){let assignments=[];return state?.releaseSlug&&assignments.push(`globalThis["__INTERFERE_RELEASE_SLUG__"]=${JSON.stringify(state.releaseSlug)};`),state?.publicKey&&assignments.push(`globalThis["__INTERFERE_PUBLIC_KEY__"]=${JSON.stringify(state.publicKey)};`),state?.forceEnable&&assignments.push(`globalThis["__INTERFERE_FORCE_ENABLE__"]=true;`),state?.mode===`proxy`&&state.publicKey&&assignments.push(`globalThis["__INTERFERE_PROXY_MODE__"]=true;`),state?.mode===`direct`&&state.browserApiUrl&&assignments.push(`globalThis["__INTERFERE_API_URL__"]=${JSON.stringify(state.browserApiUrl)};`),assignments.length===0?[]:[{tag:`script`,children:assignments.join(``),injectTo:`head-prepend`}]},async closeBundle(){state&&await uploadSourceMapsWhenReady(state)}}}export{interfere as default,interfere};
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.mjs","names":["resolvePath"],"sources":["../src/plugin.ts"],"sourcesContent":["import { API_URL } from \"@interfere/constants/api\";\nimport {\n deriveReleaseSlug,\n type ReleaseSlug,\n} from \"@interfere/types/releases/slug\";\n\nimport { existsSync, readdirSync, statSync } from \"node:fs\";\nimport { join, resolve as resolvePath } from \"node:path\";\nimport {\n type HtmlTagDescriptor,\n type Logger,\n loadEnv,\n type Plugin,\n type ResolvedConfig,\n} from \"vite\";\n\nimport {\n createSdkClient,\n runReleasePipeline,\n} from \"./internal/build/pipeline.js\";\nimport { discoverByPairing } from \"./internal/build/source-maps.js\";\nimport { resolveCommitSha } from \"./internal/release-slug.js\";\nimport { PRODUCER_VERSION } from \"./version.js\";\n\nexport type InterfereViteMode = \"auto\" | \"direct\" | \"proxy\";\n\nexport interface InterfereViteOptions {\n /**\n * Override the commit SHA used to derive the release slug. Defaults\n * to `VITE_INTERFERE_COMMIT_SHA` env then `git rev-parse HEAD`.\n * Set this when the build runs outside a git checkout (e.g. a\n * Docker layer-cached image build).\n */\n commitSha?: string;\n /**\n * How the runtime SDK should reach the collector.\n *\n * - `\"auto\"` (default) — detect from the surrounding plugin set:\n * if `tanstack-start` / `nitro` is present, choose\n * `\"proxy\"`; otherwise `\"direct\"`.\n * - `\"direct\"` — stamp `__INTERFERE_PUBLIC_KEY__` into the bundle\n * so the kernel posts ingest at the configured collector URL\n * directly (`in.interfere.com/v2/sink`). The only option for\n * vanilla Vite SPAs.\n * - `\"proxy\"` — stamp the public key and force the kernel to post\n * ingest at `/api/interfere/*`. The server-side handler forwards\n * requests through as a first-party collector proxy. Strongly\n * preferred for any deployment with a server origin because adblock filter\n * lists target the collector hostname more aggressively than\n * first-party paths.\n */\n mode?: InterfereViteMode;\n /**\n * `false` skips the post-build release metadata pipeline. Defaults\n * to enabled when an `INTERFERE_API_KEY` is set; defaults to\n * disabled when one isn't (so dev runs without creds don't fail).\n * Useful when source maps are uploaded out-of-band via the\n * `@interfere/cli`.\n */\n sourceMaps?: boolean;\n}\n\nconst SERVER_RUNTIME_PLUGIN_HINTS = [\n \"tanstack-start\",\n \"nitro\",\n \"@tanstack/react-start\",\n] as const;\n\nfunction isTruthyEnvFlag(value: string | undefined): boolean {\n if (!value) {\n return false;\n }\n const normalized = value.trim().toLowerCase();\n return normalized === \"1\" || normalized === \"true\";\n}\n\nfunction isTestEnv(): boolean {\n return Boolean(process.env[\"VITEST\"] || process.env[\"VITEST_WORKER_ID\"]);\n}\n\n/**\n * Fail the build with a Next-style error block when the public key\n * env var is absent during a production build. Skipped\n * in dev (`vite serve`) and in\n * test runs (so plugin-level tests can exercise the configuration\n * surface without crashing the test process).\n */\nfunction failMissingPublicKey(): never {\n const lines = [\n \"interfere() requires VITE_INTERFERE_PUBLIC_KEY to identify your project at runtime.\",\n \"Set the VITE_INTERFERE_PUBLIC_KEY environment variable, or remove interfere() from your Vite config.\",\n \"See: https://support.interfere.com/docs/guides/vite\",\n ];\n if (isTestEnv()) {\n throw new Error(`Interfere: Public key not set\\n${lines.join(\"\\n\")}`);\n }\n console.error(\"\\n⨯ Interfere → Public key not set\");\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n if (!line) {\n continue;\n }\n const connector = i === lines.length - 1 ? \"└\" : \"├\";\n console.error(` ${connector} ${line}`);\n }\n process.exit(1);\n}\n\nfunction detectServerRuntime(config: ResolvedConfig): boolean {\n return config.plugins.some((p) =>\n SERVER_RUNTIME_PLUGIN_HINTS.some((hint) => p.name?.includes(hint))\n );\n}\n\nfunction resolveMode(\n optsMode: InterfereViteMode | undefined,\n config: ResolvedConfig,\n logger: Logger\n): \"direct\" | \"proxy\" {\n const requested = optsMode ?? \"auto\";\n if (requested !== \"auto\") {\n logger.info(\n `[interfere] mode=${requested} (explicit). Plugin will ${\n requested === \"direct\"\n ? \"stamp the public key for direct ingestion\"\n : \"stamp the public key and run the SDK in proxy mode\"\n }.`\n );\n return requested;\n }\n const detected = detectServerRuntime(config) ? \"proxy\" : \"direct\";\n logger.info(\n `[interfere] mode=${detected} (auto-detected). Override with \\`interfere({ mode: \"...\" })\\`.`\n );\n return detected;\n}\n\ninterface PluginState {\n apiKey: string | undefined;\n apiUrl: string;\n /**\n * The runtime collector URL stamped onto `__INTERFERE_API_URL__`\n * for the browser kernel. Distinct from `apiUrl` (server-side\n * env, used by the build pipeline) — direct-mode customers\n * pointing at a regional collector / self-hosted gateway /\n * localhost need to set `VITE_INTERFERE_API_URL` separately so\n * the browser bundle's destination matches the server-side one.\n * `undefined` in proxy mode and when the env is unset (kernel\n * falls through to the compiled-in `API_URL`).\n */\n browserApiUrl: string | undefined;\n buildStartedAt: number;\n commitSha: string | null;\n enableUpload: boolean;\n forceEnable: boolean;\n logger: Logger;\n mode: \"direct\" | \"proxy\";\n projectDir: string;\n publicKey: string | undefined;\n releaseSlug: ReleaseSlug | null;\n uploadOutDir: string;\n uploadStarted: boolean;\n}\n\ntype UploadReadyPluginState = PluginState & {\n apiKey: string;\n commitSha: string;\n releaseSlug: ReleaseSlug;\n};\n\nfunction hasFreshServerSourceMaps(root: string, sinceMs: number): boolean {\n if (!existsSync(root)) {\n return false;\n }\n const entries = readdirSync(root, { withFileTypes: true });\n for (const entry of entries) {\n const absolute = join(root, entry.name);\n if (entry.isDirectory()) {\n if (hasFreshServerSourceMaps(absolute, sinceMs)) {\n return true;\n }\n continue;\n }\n if (!entry.name.endsWith(\".map\")) {\n continue;\n }\n if (statSync(absolute).mtimeMs >= sinceMs) {\n return true;\n }\n }\n return false;\n}\n\nfunction isUploadReady(state: PluginState): state is UploadReadyPluginState {\n return Boolean(\n state.enableUpload &&\n state.apiKey &&\n state.releaseSlug &&\n state.commitSha &&\n !state.uploadStarted\n );\n}\n\nfunction shouldWaitForServerOutput(state: UploadReadyPluginState): boolean {\n return (\n state.mode === \"proxy\" &&\n !hasFreshServerSourceMaps(\n join(state.uploadOutDir, \"server\"),\n state.buildStartedAt\n )\n );\n}\n\ntype ReleasePipelineFailure = Extract<\n Awaited<ReturnType<typeof runReleasePipeline>>,\n { ok: false }\n>[\"failure\"];\ntype ReleasePipelineSuccess = Extract<\n Awaited<ReturnType<typeof runReleasePipeline>>,\n { ok: true }\n>;\n\nfunction logSkippedRelease(\n logger: Logger,\n failure: ReleasePipelineFailure\n): void {\n if (failure.kind === \"missing_source_provider\") {\n logger.warn(\n `[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}.`\n );\n return;\n }\n logger.warn(\n `[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}.`\n );\n}\n\nfunction logCleanupFailures(\n logger: Logger,\n failures: ReleasePipelineSuccess[\"data\"][\"cleanupFailures\"]\n): void {\n for (const failure of failures) {\n logger.warn(\n `[interfere] cleanup left ${failure.absolute} on disk: ${failure.code ?? failure.message}`\n );\n }\n}\n\nasync function uploadSourceMaps(state: UploadReadyPluginState): Promise<void> {\n const {\n apiKey,\n apiUrl,\n releaseSlug,\n commitSha,\n logger,\n projectDir,\n uploadOutDir,\n } = state;\n\n logger.info(`[interfere] uploading source maps for release ${releaseSlug}…`);\n\n try {\n const { sdk } = createSdkClient({ apiKey, apiUrl });\n\n const [discovered, releasesConfig] = await Promise.all([\n discoverByPairing({\n projectDir,\n distDir: uploadOutDir,\n // Walk the entire dist tree as a flat root. Covers vanilla Vite\n // and TanStack Start + Nitro under one preflighted release.\n subtrees: [\".\"],\n // Nitro serves `dist/public` at the deployment root, while server\n // chunks keep their `server/...` filesystem suffix.\n publicPathRewrites: [{ from: \"public\", to: \"\" }],\n }),\n sdk.releases.getConfig(),\n ]);\n\n if (discovered.files.length === 0) {\n logger.info(\"[interfere] no source maps found, skipping upload.\");\n return;\n }\n\n const result = await runReleasePipeline({\n apiKey,\n apiUrl,\n bundler: \"rollup\",\n buildId: commitSha,\n discovery: discovered,\n producerVersion: PRODUCER_VERSION,\n releasesConfig,\n releaseSlug,\n });\n\n if (!result.ok) {\n logSkippedRelease(logger, result.failure);\n return;\n }\n\n logCleanupFailures(logger, result.data.cleanupFailures);\n logger.info(\n `[interfere] release ${result.data.releaseSlug} confirmed — ${result.data.fileCount} maps, ${result.data.timing.total}ms.`\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger.warn(\n `[interfere] release pipeline failed: ${message}. Build continues; events from this release will ingest but be ignored as evidence.`\n );\n }\n}\n\nasync function uploadSourceMapsWhenReady(state: PluginState): Promise<void> {\n if (!isUploadReady(state)) {\n return;\n }\n if (shouldWaitForServerOutput(state)) {\n state.logger.info(\n \"[interfere] waiting for Nitro server output before uploading source maps.\"\n );\n return;\n }\n state.uploadStarted = true;\n await uploadSourceMaps(state);\n}\n\nfunction emitSetupWarnings(\n state: Pick<PluginState, \"apiKey\" | \"logger\" | \"mode\" | \"releaseSlug\"> & {\n isProductionBuild: boolean;\n publicKeyPresent: boolean;\n sourceMapsExplicit: boolean | undefined;\n }\n): void {\n const {\n logger,\n mode,\n publicKeyPresent,\n apiKey,\n releaseSlug,\n sourceMapsExplicit,\n isProductionBuild,\n } = state;\n\n if (mode === \"proxy\" && publicKeyPresent) {\n logger.info(\n \"[interfere] mode=proxy: using VITE_INTERFERE_PUBLIC_KEY with the first-party proxy route.\"\n );\n }\n if (sourceMapsExplicit !== false && apiKey && !releaseSlug) {\n logger.warn(\n \"[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.\"\n );\n }\n if (isProductionBuild && sourceMapsExplicit !== false && !apiKey) {\n logger.warn(\n \"[interfere] INTERFERE_API_KEY is not set. Release metadata will not be published. Create an Interfere API 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.\"\n );\n }\n}\n\nfunction buildPluginState(\n options: InterfereViteOptions,\n config: ResolvedConfig\n): PluginState {\n const logger = config.logger;\n const mode = resolveMode(options.mode, config, logger);\n const publicKey = config.env[\"VITE_INTERFERE_PUBLIC_KEY\"];\n\n if (config.command === \"build\" && !publicKey) {\n failMissingPublicKey();\n }\n\n const explicitSha =\n options.commitSha ?? config.env[\"VITE_INTERFERE_COMMIT_SHA\"];\n const commitSha = explicitSha ?? resolveCommitSha();\n const releaseSlug = commitSha ? deriveReleaseSlug(commitSha) : null;\n\n const fileEnv = loadEnv(config.mode, config.envDir, \"\");\n const pickEnv = (key: string): string | undefined => {\n const shell = process.env[key];\n if (shell && shell.length > 0) {\n return shell;\n }\n const file = fileEnv[key];\n return file && file.length > 0 ? file : undefined;\n };\n const apiKey = pickEnv(\"INTERFERE_API_KEY\");\n const apiUrl = pickEnv(\"INTERFERE_API_URL\") ?? API_URL;\n const enableUpload =\n options.sourceMaps ??\n (apiKey !== undefined && apiKey.length > 0 && releaseSlug !== null);\n\n const rawBrowserApiUrl = config.env[\"VITE_INTERFERE_API_URL\"];\n const browserApiUrl =\n rawBrowserApiUrl && rawBrowserApiUrl.length > 0\n ? rawBrowserApiUrl\n : undefined;\n\n emitSetupWarnings({\n apiKey,\n isProductionBuild: config.command === \"build\",\n logger,\n mode,\n publicKeyPresent: Boolean(publicKey),\n releaseSlug,\n sourceMapsExplicit: options.sourceMaps,\n });\n\n return {\n apiKey,\n apiUrl,\n browserApiUrl: mode === \"direct\" ? browserApiUrl : undefined,\n commitSha,\n enableUpload,\n forceEnable: isTruthyEnvFlag(config.env[\"VITE_INTERFERE_FORCE_ENABLE\"]),\n logger,\n mode,\n projectDir: config.root,\n publicKey,\n releaseSlug,\n uploadOutDir: resolvePath(config.root, config.build.outDir),\n uploadStarted: false,\n buildStartedAt: Date.now(),\n };\n}\n\nexport function interfere(options: InterfereViteOptions = {}): Plugin {\n let state: PluginState | null = null;\n\n return {\n name: \"interfere\",\n enforce: \"pre\",\n\n configResolved(config) {\n const next = buildPluginState(options, config);\n if (state?.mode === \"proxy\" && next.mode === \"proxy\") {\n next.uploadOutDir = state.uploadOutDir;\n next.uploadStarted = state.uploadStarted;\n next.buildStartedAt = state.buildStartedAt;\n }\n state = next;\n },\n\n transformIndexHtml() {\n const assignments: string[] = [];\n if (state?.releaseSlug) {\n assignments.push(\n `globalThis[\"__INTERFERE_RELEASE_SLUG__\"]=${JSON.stringify(state.releaseSlug)};`\n );\n }\n if (state?.publicKey) {\n assignments.push(\n `globalThis[\"__INTERFERE_PUBLIC_KEY__\"]=${JSON.stringify(state.publicKey)};`\n );\n }\n if (state?.forceEnable) {\n assignments.push('globalThis[\"__INTERFERE_FORCE_ENABLE__\"]=true;');\n }\n if (state?.mode === \"proxy\" && state.publicKey) {\n assignments.push('globalThis[\"__INTERFERE_PROXY_MODE__\"]=true;');\n }\n if (state?.mode === \"direct\" && state.browserApiUrl) {\n assignments.push(\n `globalThis[\"__INTERFERE_API_URL__\"]=${JSON.stringify(state.browserApiUrl)};`\n );\n }\n\n if (assignments.length === 0) {\n return [];\n }\n\n return [\n {\n tag: \"script\",\n children: assignments.join(\"\"),\n injectTo: \"head-prepend\",\n },\n ] satisfies HtmlTagDescriptor[];\n },\n\n async closeBundle() {\n if (!state) {\n return;\n }\n await uploadSourceMapsWhenReady(state);\n },\n };\n}\n\nexport default interfere;\n"],"mappings":"mdA8DA,MAAM,4BAA8B,CAClC,iBACA,QACA,uBACF,EAEA,SAAS,gBAAgB,MAAoC,CAC3D,GAAI,CAAC,MACH,MAAO,GAET,IAAM,WAAa,MAAM,KAAK,EAAE,YAAY,EAC5C,OAAO,aAAe,KAAO,aAAe,MAC9C,CAEA,SAAS,WAAqB,CAC5B,MAAO,GAAQ,QAAQ,IAAI,QAAa,QAAQ,IAAI,iBACtD,CASA,SAAS,sBAA8B,CACrC,IAAM,MAAQ,CACZ,sFACA,uGACA,qDACF,EACA,GAAI,UAAU,EACZ,MAAU,MAAM,kCAAkC,MAAM,KAAK;CAAI,GAAG,EAEtE,QAAQ,MAAM;iCAAoC,EAClD,IAAK,IAAI,EAAI,EAAG,EAAI,MAAM,OAAQ,GAAK,EAAG,CACxC,IAAM,KAAO,MAAM,GACnB,GAAI,CAAC,KACH,SAEF,IAAM,UAAY,IAAM,MAAM,OAAS,EAAI,IAAM,IACjD,QAAQ,MAAM,IAAI,UAAU,GAAG,MAAM,CACvC,CACA,QAAQ,KAAK,CAAC,CAChB,CAEA,SAAS,oBAAoB,OAAiC,CAC5D,OAAO,OAAO,QAAQ,KAAM,GAC1B,4BAA4B,KAAM,MAAS,EAAE,MAAM,SAAS,IAAI,CAAC,CACnE,CACF,CAEA,SAAS,YACP,SACA,OACA,OACoB,CACpB,IAAM,UAAY,UAAY,OAC9B,GAAI,YAAc,OAQhB,OAPA,OAAO,KACL,oBAAoB,UAAU,2BAC5B,YAAc,SACV,4CACA,qDACL,EACH,EACO,UAET,IAAM,SAAW,oBAAoB,MAAM,EAAI,QAAU,SAIzD,OAHA,OAAO,KACL,oBAAoB,SAAS,gEAC/B,EACO,QACT,CAmCA,SAAS,yBAAyB,KAAc,QAA0B,CACxE,GAAI,CAAC,WAAW,IAAI,EAClB,MAAO,GAET,IAAM,QAAU,YAAY,KAAM,CAAE,cAAe,EAAK,CAAC,EACzD,IAAK,IAAM,SAAS,QAAS,CAC3B,IAAM,SAAW,KAAK,KAAM,MAAM,IAAI,EACtC,GAAI,MAAM,YAAY,EAAG,CACvB,GAAI,yBAAyB,SAAU,OAAO,EAC5C,MAAO,GAET,QACF,CACK,SAAM,KAAK,SAAS,MAAM,GAG3B,SAAS,QAAQ,EAAE,SAAW,QAChC,MAAO,EAEX,CACA,MAAO,EACT,CAEA,SAAS,cAAc,MAAqD,CAC1E,MAAO,GACL,MAAM,cACJ,MAAM,QACN,MAAM,aACN,MAAM,WACN,CAAC,MAAM,cAEb,CAEA,SAAS,0BAA0B,MAAwC,CACzE,OACE,MAAM,OAAS,SACf,CAAC,yBACC,KAAK,MAAM,aAAc,QAAQ,EACjC,MAAM,cACR,CAEJ,CAWA,SAAS,kBACP,OACA,QACM,CACN,GAAI,QAAQ,OAAS,0BAA2B,CAC9C,OAAO,KACL,kIAAkI,QAAQ,OAAO,IAAI,KAAK,YAAY,QAAQ,OAAO,QAAQ,KAAK,EACpM,EACA,MACF,CACA,OAAO,KACL,2HAA2H,QAAQ,OAAO,IAAI,KAAK,YAAY,QAAQ,OAAO,QAAQ,KAAK,EAC7L,CACF,CAEA,SAAS,mBACP,OACA,SACM,CACN,IAAK,IAAM,WAAW,SACpB,OAAO,KACL,4BAA4B,QAAQ,SAAS,YAAY,QAAQ,MAAQ,QAAQ,SACnF,CAEJ,CAEA,eAAe,iBAAiB,MAA8C,CAC5E,GAAM,CACJ,OACA,OACA,YACA,UACA,OACA,WACA,cACE,MAEJ,OAAO,KAAK,iDAAiD,YAAY,EAAE,EAE3E,GAAI,CACF,GAAM,CAAE,KAAQ,gBAAgB,CAAE,OAAQ,MAAO,CAAC,EAE5C,CAAC,WAAY,gBAAkB,MAAM,QAAQ,IAAI,CACrD,kBAAkB,CAChB,WACA,QAAS,aAGT,SAAU,CAAC,GAAG,EAGd,mBAAoB,CAAC,CAAE,KAAM,SAAU,GAAI,EAAG,CAAC,CACjD,CAAC,EACD,IAAI,SAAS,UAAU,CACzB,CAAC,EAED,GAAI,WAAW,MAAM,SAAW,EAAG,CACjC,OAAO,KAAK,oDAAoD,EAChE,MACF,CAEA,IAAM,OAAS,MAAM,mBAAmB,CACtC,OACA,OACA,QAAS,SACT,QAAS,UACT,UAAW,WACX,gBAAiB,iBACjB,eACA,WACF,CAAC,EAED,GAAI,CAAC,OAAO,GAAI,CACd,kBAAkB,OAAQ,OAAO,OAAO,EACxC,MACF,CAEA,mBAAmB,OAAQ,OAAO,KAAK,eAAe,EACtD,OAAO,KACL,uBAAuB,OAAO,KAAK,YAAY,eAAe,OAAO,KAAK,UAAU,SAAS,OAAO,KAAK,OAAO,MAAM,IACxH,CACF,OAAS,IAAK,CACZ,IAAM,QAAU,eAAe,MAAQ,IAAI,QAAU,OAAO,GAAG,EAC/D,OAAO,KACL,wCAAwC,QAAQ,oFAClD,CACF,CACF,CAEA,eAAe,0BAA0B,MAAmC,CACrE,iBAAc,KAAK,EAGxB,IAAI,0BAA0B,KAAK,EAAG,CACpC,MAAM,OAAO,KACX,2EACF,EACA,MACF,CACA,MAAM,cAAgB,GACtB,MAAM,iBAAiB,KAAK,CAF5B,CAGF,CAEA,SAAS,kBACP,MAKM,CACN,GAAM,CACJ,OACA,KACA,iBACA,OACA,YACA,mBACA,mBACE,MAEA,OAAS,SAAW,kBACtB,OAAO,KACL,2FACF,EAEE,qBAAuB,IAAS,QAAU,CAAC,aAC7C,OAAO,KACL,iLACF,EAEE,mBAAqB,qBAAuB,IAAS,CAAC,QACxD,OAAO,KACL,oSACF,CAEJ,CAEA,SAAS,iBACP,QACA,OACa,CACb,IAAM,OAAS,OAAO,OAChB,KAAO,YAAY,QAAQ,KAAM,OAAQ,MAAM,EAC/C,UAAY,OAAO,IAAI,0BAEzB,OAAO,UAAY,SAAW,CAAC,WACjC,qBAAqB,EAKvB,IAAM,UADJ,QAAQ,WAAa,OAAO,IAAI,2BACD,iBAAiB,EAC5C,YAAc,UAAY,kBAAkB,SAAS,EAAI,KAEzD,QAAU,QAAQ,OAAO,KAAM,OAAO,OAAQ,EAAE,EAChD,QAAW,KAAoC,CACnD,IAAM,MAAQ,QAAQ,IAAI,KAC1B,GAAI,OAAS,MAAM,OAAS,EAC1B,OAAO,MAET,IAAM,KAAO,QAAQ,KACrB,OAAO,MAAQ,KAAK,OAAS,EAAI,KAAO,IAAA,EAC1C,EACM,OAAS,QAAQ,mBAAmB,EACpC,OAAS,QAAQ,mBAAmB,GAAK,QACzC,aACJ,QAAQ,aACP,SAAW,IAAA,IAAa,OAAO,OAAS,GAAK,cAAgB,MAE1D,iBAAmB,OAAO,IAAI,uBAC9B,cACJ,kBAAoB,iBAAiB,OAAS,EAC1C,iBACA,IAAA,GAYN,OAVA,kBAAkB,CAChB,OACA,kBAAmB,OAAO,UAAY,QACtC,OACA,KACA,iBAAkB,EAAQ,UAC1B,YACA,mBAAoB,QAAQ,UAC9B,CAAC,EAEM,CACL,OACA,OACA,cAAe,OAAS,SAAW,cAAgB,IAAA,GACnD,UACA,aACA,YAAa,gBAAgB,OAAO,IAAI,2BAA8B,EACtE,OACA,KACA,WAAY,OAAO,KACnB,UACA,YACA,aAAcA,QAAY,OAAO,KAAM,OAAO,MAAM,MAAM,EAC1D,cAAe,GACf,eAAgB,KAAK,IAAI,CAC3B,CACF,CAEA,SAAgB,UAAU,QAAgC,CAAC,EAAW,CACpE,IAAI,MAA4B,KAEhC,MAAO,CACL,KAAM,YACN,QAAS,MAET,eAAe,OAAQ,CACrB,IAAM,KAAO,iBAAiB,QAAS,MAAM,EACzC,OAAO,OAAS,SAAW,KAAK,OAAS,UAC3C,KAAK,aAAe,MAAM,aAC1B,KAAK,cAAgB,MAAM,cAC3B,KAAK,eAAiB,MAAM,gBAE9B,MAAQ,IACV,EAEA,oBAAqB,CACnB,IAAM,YAAwB,CAAC,EA2B/B,OA1BI,OAAO,aACT,YAAY,KACV,4CAA4C,KAAK,UAAU,MAAM,WAAW,EAAE,EAChF,EAEE,OAAO,WACT,YAAY,KACV,0CAA0C,KAAK,UAAU,MAAM,SAAS,EAAE,EAC5E,EAEE,OAAO,aACT,YAAY,KAAK,gDAAgD,EAE/D,OAAO,OAAS,SAAW,MAAM,WACnC,YAAY,KAAK,8CAA8C,EAE7D,OAAO,OAAS,UAAY,MAAM,eACpC,YAAY,KACV,uCAAuC,KAAK,UAAU,MAAM,aAAa,EAAE,EAC7E,EAGE,YAAY,SAAW,EAClB,CAAC,EAGH,CACL,CACE,IAAK,SACL,SAAU,YAAY,KAAK,EAAE,EAC7B,SAAU,cACZ,CACF,CACF,EAEA,MAAM,aAAc,CACb,OAGL,MAAM,0BAA0B,KAAK,CACvC,CACF,CACF"}
1
+ {"version":3,"file":"plugin.mjs","names":["resolvePath"],"sources":["../src/plugin.ts"],"sourcesContent":["import { API_URL } from \"@interfere/constants/api\";\nimport {\n deriveReleaseSlug,\n type ReleaseSlug,\n} from \"@interfere/types/releases/slug\";\n\nimport { existsSync, readdirSync, statSync } from \"node:fs\";\nimport { join, resolve as resolvePath } from \"node:path\";\nimport {\n type HtmlTagDescriptor,\n type Logger,\n loadEnv,\n type Plugin,\n type ResolvedConfig,\n} from \"vite\";\n\nimport {\n createSdkClient,\n runReleasePipeline,\n} from \"./internal/build/pipeline.js\";\nimport { discoverByPairing } from \"./internal/build/source-maps.js\";\nimport { resolveCommitSha } from \"./internal/release-slug.js\";\nimport { PRODUCER_VERSION } from \"./version.js\";\n\nexport type InterfereViteMode = \"auto\" | \"direct\" | \"proxy\";\n\nexport interface InterfereViteOptions {\n /**\n * Override the commit SHA used to derive the release slug. Defaults\n * to `VITE_INTERFERE_COMMIT_SHA` env then `git rev-parse HEAD`.\n * Set this when the build runs outside a git checkout (e.g. a\n * Docker layer-cached image build).\n */\n commitSha?: string;\n /**\n * How the runtime SDK should reach the collector.\n *\n * - `\"auto\"` (default) — detect from the surrounding plugin set:\n * if `tanstack-start` / `nitro` is present, choose\n * `\"proxy\"`; otherwise `\"direct\"`.\n * - `\"direct\"` — stamp `__INTERFERE_PUBLIC_KEY__` into the bundle\n * so the kernel posts ingest at the configured collector URL\n * directly (`in.interfere.com/v2/sink`). The only option for\n * vanilla Vite SPAs.\n * - `\"proxy\"` — stamp the public key and force the kernel to post\n * ingest at `/api/interfere/*`. The server-side handler forwards\n * requests through as a first-party collector proxy. Strongly\n * preferred for any deployment with a server origin because adblock filter\n * lists target the collector hostname more aggressively than\n * first-party paths.\n */\n mode?: InterfereViteMode;\n /**\n * `false` skips the post-build release metadata pipeline. Defaults\n * to enabled when an `INTERFERE_API_KEY` is set; defaults to\n * disabled when one isn't (so dev runs without creds don't fail).\n * Useful when source maps are uploaded out-of-band via the\n * `@interfere/cli`.\n */\n sourceMaps?: boolean;\n}\n\nconst SERVER_RUNTIME_PLUGIN_HINTS = [\n \"tanstack-start\",\n \"nitro\",\n \"@tanstack/react-start\",\n] as const;\n\nfunction isTruthyEnvFlag(value: string | undefined): boolean {\n if (!value) {\n return false;\n }\n const normalized = value.trim().toLowerCase();\n return normalized === \"1\" || normalized === \"true\";\n}\n\nfunction isTestEnv(): boolean {\n return Boolean(process.env[\"VITEST\"] || process.env[\"VITEST_WORKER_ID\"]);\n}\n\n/**\n * Fail the build with a Next-style error block when the public key\n * env var is absent during a production build. Skipped\n * in dev (`vite serve`) and in\n * test runs (so plugin-level tests can exercise the configuration\n * surface without crashing the test process).\n */\nfunction failMissingPublicKey(): never {\n const lines = [\n \"interfere() requires VITE_INTERFERE_PUBLIC_KEY to identify your project at runtime.\",\n \"Set the VITE_INTERFERE_PUBLIC_KEY environment variable, or remove interfere() from your Vite config.\",\n \"See: https://support.interfere.com/docs/guides/vite\",\n ];\n if (isTestEnv()) {\n throw new Error(`Interfere: Public key not set\\n${lines.join(\"\\n\")}`);\n }\n console.error(\"\\n⨯ Interfere → Public key not set\");\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n if (!line) {\n continue;\n }\n const connector = i === lines.length - 1 ? \"└\" : \"├\";\n console.error(` ${connector} ${line}`);\n }\n process.exit(1);\n}\n\nfunction detectServerRuntime(config: ResolvedConfig): boolean {\n return config.plugins.some((p) =>\n SERVER_RUNTIME_PLUGIN_HINTS.some((hint) => p.name?.includes(hint))\n );\n}\n\nfunction resolveMode(\n optsMode: InterfereViteMode | undefined,\n config: ResolvedConfig,\n logger: Logger\n): \"direct\" | \"proxy\" {\n const requested = optsMode ?? \"auto\";\n if (requested !== \"auto\") {\n logger.info(`[interfere] mode=${requested} (explicit)`);\n return requested;\n }\n const detected = detectServerRuntime(config) ? \"proxy\" : \"direct\";\n logger.info(\n `[interfere] mode=${detected} (auto-detected). Override with \\`interfere({ mode: \"...\" })\\`.`\n );\n return detected;\n}\n\ninterface PluginState {\n apiKey: string | undefined;\n apiUrl: string;\n /**\n * The runtime collector URL stamped onto `__INTERFERE_API_URL__`\n * for the browser kernel. Distinct from `apiUrl` (server-side\n * env, used by the build pipeline) — direct-mode customers\n * pointing at a regional collector / self-hosted gateway /\n * localhost need to set `VITE_INTERFERE_API_URL` separately so\n * the browser bundle's destination matches the server-side one.\n * `undefined` in proxy mode and when the env is unset (kernel\n * falls through to the compiled-in `API_URL`).\n */\n browserApiUrl: string | undefined;\n buildStartedAt: number;\n commitSha: string | null;\n enableUpload: boolean;\n forceEnable: boolean;\n logger: Logger;\n mode: \"direct\" | \"proxy\";\n projectDir: string;\n publicKey: string | undefined;\n releaseSlug: ReleaseSlug | null;\n uploadOutDir: string;\n uploadStarted: boolean;\n}\n\ntype UploadReadyPluginState = PluginState & {\n apiKey: string;\n commitSha: string;\n releaseSlug: ReleaseSlug;\n};\n\nfunction hasFreshServerSourceMaps(root: string, sinceMs: number): boolean {\n if (!existsSync(root)) {\n return false;\n }\n const entries = readdirSync(root, { withFileTypes: true });\n for (const entry of entries) {\n const absolute = join(root, entry.name);\n if (entry.isDirectory()) {\n if (hasFreshServerSourceMaps(absolute, sinceMs)) {\n return true;\n }\n continue;\n }\n if (!entry.name.endsWith(\".map\")) {\n continue;\n }\n if (statSync(absolute).mtimeMs >= sinceMs) {\n return true;\n }\n }\n return false;\n}\n\nfunction isUploadReady(state: PluginState): state is UploadReadyPluginState {\n return Boolean(\n state.enableUpload &&\n state.apiKey &&\n state.releaseSlug &&\n state.commitSha &&\n !state.uploadStarted\n );\n}\n\nfunction shouldWaitForServerOutput(state: UploadReadyPluginState): boolean {\n return (\n state.mode === \"proxy\" &&\n !hasFreshServerSourceMaps(\n join(state.uploadOutDir, \"server\"),\n state.buildStartedAt\n )\n );\n}\n\ntype ReleasePipelineFailure = Extract<\n Awaited<ReturnType<typeof runReleasePipeline>>,\n { ok: false }\n>[\"failure\"];\ntype ReleasePipelineSuccess = Extract<\n Awaited<ReturnType<typeof runReleasePipeline>>,\n { ok: true }\n>;\n\nfunction logSkippedRelease(\n logger: Logger,\n failure: ReleasePipelineFailure\n): void {\n if (failure.kind === \"missing_source_provider\") {\n logger.warn(\n `[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}.`\n );\n return;\n }\n logger.warn(\n `[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}.`\n );\n}\n\nfunction logCleanupFailures(\n logger: Logger,\n failures: ReleasePipelineSuccess[\"data\"][\"cleanupFailures\"]\n): void {\n for (const failure of failures) {\n logger.warn(\n `[interfere] cleanup left ${failure.absolute} on disk: ${failure.code ?? failure.message}`\n );\n }\n}\n\nasync function uploadSourceMaps(state: UploadReadyPluginState): Promise<void> {\n const {\n apiKey,\n apiUrl,\n releaseSlug,\n commitSha,\n logger,\n projectDir,\n uploadOutDir,\n } = state;\n\n logger.info(`[interfere] uploading source maps for release ${releaseSlug}…`);\n\n try {\n const { sdk } = createSdkClient({ apiKey, apiUrl });\n\n const [discovered, releasesConfig] = await Promise.all([\n discoverByPairing({\n projectDir,\n distDir: uploadOutDir,\n // Walk the entire dist tree as a flat root. Covers vanilla Vite\n // and TanStack Start + Nitro under one preflighted release.\n subtrees: [\".\"],\n // Nitro serves `dist/public` at the deployment root, while server\n // chunks keep their `server/...` filesystem suffix.\n publicPathRewrites: [{ from: \"public\", to: \"\" }],\n }),\n sdk.releases.getConfig(),\n ]);\n\n if (discovered.files.length === 0) {\n logger.info(\"[interfere] no source maps found, skipping upload.\");\n return;\n }\n\n const result = await runReleasePipeline({\n apiKey,\n apiUrl,\n bundler: \"rollup\",\n buildId: commitSha,\n discovery: discovered,\n producerVersion: PRODUCER_VERSION,\n releasesConfig,\n releaseSlug,\n });\n\n if (!result.ok) {\n logSkippedRelease(logger, result.failure);\n return;\n }\n\n logCleanupFailures(logger, result.data.cleanupFailures);\n logger.info(\n `[interfere] release ${result.data.releaseSlug} confirmed — ${result.data.fileCount} maps, ${result.data.timing.total}ms.`\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger.warn(\n `[interfere] release pipeline failed: ${message}. Build continues; events from this release will ingest but be ignored as evidence.`\n );\n }\n}\n\nasync function uploadSourceMapsWhenReady(state: PluginState): Promise<void> {\n if (!isUploadReady(state)) {\n return;\n }\n if (shouldWaitForServerOutput(state)) {\n state.logger.info(\n \"[interfere] waiting for Nitro server output before uploading source maps.\"\n );\n return;\n }\n state.uploadStarted = true;\n await uploadSourceMaps(state);\n}\n\nfunction emitSetupWarnings(\n state: Pick<PluginState, \"apiKey\" | \"logger\" | \"mode\" | \"releaseSlug\"> & {\n isProductionBuild: boolean;\n publicKeyPresent: boolean;\n sourceMapsExplicit: boolean | undefined;\n }\n): void {\n const {\n logger,\n mode,\n publicKeyPresent,\n apiKey,\n releaseSlug,\n sourceMapsExplicit,\n isProductionBuild,\n } = state;\n\n if (mode === \"proxy\" && publicKeyPresent) {\n logger.info(\n \"[interfere] mode=proxy: using VITE_INTERFERE_PUBLIC_KEY with the first-party proxy route.\"\n );\n }\n if (sourceMapsExplicit !== false && apiKey && !releaseSlug) {\n logger.warn(\n \"[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.\"\n );\n }\n if (isProductionBuild && sourceMapsExplicit !== false && !apiKey) {\n logger.warn(\n \"[interfere] INTERFERE_API_KEY is not set. Release metadata will not be published. Create an Interfere API 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.\"\n );\n }\n}\n\nfunction buildPluginState(\n options: InterfereViteOptions,\n config: ResolvedConfig\n): PluginState {\n const logger = config.logger;\n const mode = resolveMode(options.mode, config, logger);\n const publicKey = config.env[\"VITE_INTERFERE_PUBLIC_KEY\"];\n\n if (config.command === \"build\" && !publicKey) {\n failMissingPublicKey();\n }\n\n const explicitSha =\n options.commitSha ?? config.env[\"VITE_INTERFERE_COMMIT_SHA\"];\n const commitSha = explicitSha ?? resolveCommitSha();\n const releaseSlug = commitSha ? deriveReleaseSlug(commitSha) : null;\n\n const fileEnv = loadEnv(config.mode, config.envDir, \"\");\n const pickEnv = (key: string): string | undefined => {\n const shell = process.env[key];\n if (shell && shell.length > 0) {\n return shell;\n }\n const file = fileEnv[key];\n return file && file.length > 0 ? file : undefined;\n };\n const apiKey = pickEnv(\"INTERFERE_API_KEY\");\n const apiUrl = pickEnv(\"INTERFERE_API_URL\") ?? API_URL;\n const enableUpload =\n options.sourceMaps ??\n (apiKey !== undefined && apiKey.length > 0 && releaseSlug !== null);\n\n const rawBrowserApiUrl = config.env[\"VITE_INTERFERE_API_URL\"];\n const browserApiUrl =\n rawBrowserApiUrl && rawBrowserApiUrl.length > 0\n ? rawBrowserApiUrl\n : undefined;\n\n emitSetupWarnings({\n apiKey,\n isProductionBuild: config.command === \"build\",\n logger,\n mode,\n publicKeyPresent: Boolean(publicKey),\n releaseSlug,\n sourceMapsExplicit: options.sourceMaps,\n });\n\n return {\n apiKey,\n apiUrl,\n browserApiUrl: mode === \"direct\" ? browserApiUrl : undefined,\n commitSha,\n enableUpload,\n forceEnable: isTruthyEnvFlag(config.env[\"VITE_INTERFERE_FORCE_ENABLE\"]),\n logger,\n mode,\n projectDir: config.root,\n publicKey,\n releaseSlug,\n uploadOutDir: resolvePath(config.root, config.build.outDir),\n uploadStarted: false,\n buildStartedAt: Date.now(),\n };\n}\n\nexport function interfere(options: InterfereViteOptions = {}): Plugin {\n let state: PluginState | null = null;\n\n return {\n name: \"interfere\",\n enforce: \"pre\",\n\n configResolved(config) {\n const next = buildPluginState(options, config);\n if (state?.mode === \"proxy\" && next.mode === \"proxy\") {\n next.uploadOutDir = state.uploadOutDir;\n next.uploadStarted = state.uploadStarted;\n next.buildStartedAt = state.buildStartedAt;\n }\n state = next;\n },\n\n transformIndexHtml() {\n const assignments: string[] = [];\n if (state?.releaseSlug) {\n assignments.push(\n `globalThis[\"__INTERFERE_RELEASE_SLUG__\"]=${JSON.stringify(state.releaseSlug)};`\n );\n }\n if (state?.publicKey) {\n assignments.push(\n `globalThis[\"__INTERFERE_PUBLIC_KEY__\"]=${JSON.stringify(state.publicKey)};`\n );\n }\n if (state?.forceEnable) {\n assignments.push('globalThis[\"__INTERFERE_FORCE_ENABLE__\"]=true;');\n }\n if (state?.mode === \"proxy\" && state.publicKey) {\n assignments.push('globalThis[\"__INTERFERE_PROXY_MODE__\"]=true;');\n }\n if (state?.mode === \"direct\" && state.browserApiUrl) {\n assignments.push(\n `globalThis[\"__INTERFERE_API_URL__\"]=${JSON.stringify(state.browserApiUrl)};`\n );\n }\n\n if (assignments.length === 0) {\n return [];\n }\n\n return [\n {\n tag: \"script\",\n children: assignments.join(\"\"),\n injectTo: \"head-prepend\",\n },\n ] satisfies HtmlTagDescriptor[];\n },\n\n async closeBundle() {\n if (!state) {\n return;\n }\n await uploadSourceMapsWhenReady(state);\n },\n };\n}\n\nexport default interfere;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interfere/vite",
3
- "version": "10.0.1-canary.0",
3
+ "version": "10.0.1-canary.1",
4
4
  "license": "MIT",
5
5
  "description": "Vite build plugin and client + server initialization for Interfere.",
6
6
  "keywords": [
@@ -64,8 +64,8 @@
64
64
  "dependencies": {
65
65
  "@interfere/constants": "^9.0.2-canary.0",
66
66
  "@interfere/helpers": "^9.0.0",
67
- "@interfere/react": "^10.0.1-canary.0",
68
- "@interfere/sdk": "^10.0.1-canary.0",
67
+ "@interfere/react": "^10.0.1-canary.1",
68
+ "@interfere/sdk": "^10.0.1-canary.1",
69
69
  "@interfere/types": "^9.0.3-canary.0",
70
70
  "@opentelemetry/api": "^1.9.1",
71
71
  "@opentelemetry/api-logs": "^0.218.0",