@interfere/vite 11.0.0-canary.4 → 11.0.0-canary.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/package.mjs +1 -1
- package/dist/plugin.mjs +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -156,8 +156,8 @@ export const Route = createFileRoute("/api/interfere/$")({
|
|
|
156
156
|
The SDK splits its credentials by concern: a **public key** travels with the browser bundle (it's not a secret), and an **API key** stays server-side (CI / GSM / `.env.local`).
|
|
157
157
|
|
|
158
158
|
1. **Install** `@interfere/vite` and add the plugin (above).
|
|
159
|
-
2. **Public key** — copy the dashboard's `
|
|
160
|
-
3. **API key** — create an Interfere API key and put the `
|
|
159
|
+
2. **Public key** — copy the dashboard's regional publishable key (`interfere_pub_us_*` or `interfere_pub_eu_*`) into `.env.local` as `VITE_INTERFERE_PUBLIC_KEY=...`. Public by design; safe to commit to a public repo.
|
|
160
|
+
3. **API key** — create an Interfere API key and put the regional secret key (`interfere_secret_us_*` or `interfere_secret_eu_*`) in `.env.local` as `INTERFERE_API_KEY=...`. This key is what the build pipeline uses to publish release metadata. **Server-side; never commit.**
|
|
161
161
|
4. **First build** — run `bun run build`. The plugin's `closeBundle` hook publishes release metadata and preflight-confirms the release. After this the commit's slug is accepted at ingest.
|
|
162
162
|
5. **Dev cycle** — `bun run dev` then exercises the bundle in your browser. Events flow with the commit's preflight-confirmed slug (the build did the work). New commit? Re-run `bun run build` so the new commit's slug is preflighted.
|
|
163
163
|
|
|
@@ -167,8 +167,8 @@ For **Vercel preview / production**, set `INTERFERE_API_KEY` in your `.env.local
|
|
|
167
167
|
|
|
168
168
|
| Variable | Required | Description |
|
|
169
169
|
| --- | --- | --- |
|
|
170
|
-
| `VITE_INTERFERE_PUBLIC_KEY` | Yes | Surface
|
|
171
|
-
| `INTERFERE_API_KEY` | Yes for release metadata | Interfere
|
|
170
|
+
| `VITE_INTERFERE_PUBLIC_KEY` | Yes | Surface publishable key in the `interfere_pub_us_*` or `interfere_pub_eu_*` format. Stamped into the page by the plugin at build time for both direct and proxy mode. |
|
|
171
|
+
| `INTERFERE_API_KEY` | Yes for release metadata | Interfere secret key in the `interfere_secret_us_*` or `interfere_secret_eu_*` format. Used by the plugin at build time. Without it, release metadata is not published and the collector rejects events as `COLLECTOR_RELEASE_PREFLIGHT_UNCONFIRMED`. |
|
|
172
172
|
| `INTERFERE_API_URL` | No | Override the collector URL the build pipeline targets. Defaults to `https://in.interfere.com`. |
|
|
173
173
|
| `VITE_INTERFERE_API_URL` | No | Override the collector URL the browser kernel targets in direct mode. Proxy mode uses `INTERFERE_API_URL` server-side. |
|
|
174
174
|
| `VITE_INTERFERE_COMMIT_SHA` | No | Override the auto-detected commit SHA used to derive the release slug. Defaults to `git rev-parse HEAD`. |
|
|
@@ -190,7 +190,7 @@ interfere({
|
|
|
190
190
|
|
|
191
191
|
## Migrating From 0.x
|
|
192
192
|
|
|
193
|
-
Upgrade all `@interfere/*` SDK packages together. Keep the env var names, but replace old `int_pub_*` values with the dashboard's
|
|
193
|
+
Upgrade all `@interfere/*` SDK packages together. Keep the env var names, but replace old `int_pub_*` / `interfere_pk_*` values with the dashboard's regional publishable key, and replace old `ak_*` / `interfere_ak_*` build keys with the regional Interfere secret key.
|
|
194
194
|
|
|
195
195
|
## Identity Management
|
|
196
196
|
|
package/dist/package.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var name=`@interfere/vite`,version=`11.0.0-canary.
|
|
1
|
+
var name=`@interfere/vite`,version=`11.0.0-canary.5`;export{name,version};
|
package/dist/plugin.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
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{
|
|
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{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{join,resolve}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 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 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://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,announce){let requested=optsMode??`auto`;if(requested!==`auto`)return announce&&logger.info(`[interfere] mode=${requested} (explicit)`),requested;let detected=detectServerRuntime(config)?`proxy`:`direct`;return announce&&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.isProductionBuild&&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;if(!apiKey
|
|
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,announce){let requested=optsMode??`auto`;if(requested!==`auto`)return announce&&logger.info(`[interfere] mode=${requested} (explicit)`),requested;let detected=detectServerRuntime(config)?`proxy`:`direct`;return announce&&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.isProductionBuild&&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;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(err){let message=err instanceof Error?err.message:String(err);reportReleaseDiagnostic(logger,classifyReleasePipelineError(err),{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,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 resolveReleaseEnv(config){return{...loadEnv(config.mode,config.envDir,``),...process.env}}function resolveReleaseIdentity(options,config){let commitSha=options.commitSha??config.env.VITE_INTERFERE_COMMIT_SHA??resolveCommitSha();return{commitSha,releaseSlug:commitSha?deriveReleaseSlug(commitSha):null}}function shouldEnableUpload({apiKey,isProductionBuild,releaseSlug,sourceMaps}){return isProductionBuild?sourceMaps??!!(apiKey&&releaseSlug):!1}function buildPluginState(options,config,announce){let logger=config.logger,mode=resolveMode(options.mode,config,logger,announce),publicKey=config.env.VITE_INTERFERE_PUBLIC_KEY,isProductionBuild=config.command===`build`;isProductionBuild&&!publicKey&&failMissingPublicKey();let releaseEnv=resolveReleaseEnv(config),pickEnv=key=>pickEnvValue(releaseEnv,key),apiKey=pickEnv(`INTERFERE_API_KEY`),apiUrl=pickEnv(`INTERFERE_API_URL`)??API_URL,{commitSha,releaseSlug}=resolveReleaseIdentity(options,config),environment=isProductionBuild?resolveDeployEnvironment(releaseEnv):null,rawBrowserApiUrl=config.env.VITE_INTERFERE_API_URL,browserApiUrl=rawBrowserApiUrl&&rawBrowserApiUrl.length>0?rawBrowserApiUrl:void 0;return announce&&emitSetupWarnings({apiKey,isProductionBuild,logger,mode,publicKeyPresent:!!publicKey,releaseSlug,sourceMapsExplicit:options.sourceMaps}),{apiKey,apiUrl,browserApiUrl:mode===`direct`?browserApiUrl:void 0,commitSha,enableUpload:shouldEnableUpload({apiKey,isProductionBuild,releaseSlug,sourceMaps:options.sourceMaps}),environment,forceEnable:isTruthyEnvFlag(config.env.VITE_INTERFERE_FORCE_ENABLE),isProductionBuild,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,forcedSourcemapOriginal,announced=!1;return{name:`interfere`,enforce:`pre`,config(userConfig,{command,mode}){if(command!==`build`||options.sourceMaps===!1)return;let root=userConfig.root?resolve(process.cwd(),userConfig.root):process.cwd();if(!pickEnvValue(loadEnv(mode,userConfig.envDir?resolve(root,userConfig.envDir):root,``),`INTERFERE_API_KEY`))return;let current=userConfig.build?.sourcemap;current!==void 0&¤t!==!0&&(forcedSourcemapOriginal=current),userConfig.build={...userConfig.build,sourcemap:!0}},configResolved(config){let next=buildPluginState(options,config,!announced);announced=!0,state?.mode===`proxy`&&next.mode===`proxy`&&(next.uploadOutDir=state.uploadOutDir,next.uploadStarted=state.uploadStarted,next.buildStartedAt=state.buildStartedAt),state=next,forcedSourcemapOriginal!==void 0&&(next.logger.warn(`[interfere] Forced build.sourcemap to true (was ${JSON.stringify(forcedSourcemapOriginal)}). 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.`),forcedSourcemapOriginal=void 0)},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?.environment&&assignments.push(`globalThis["__INTERFERE_ENVIRONMENT__"]=${JSON.stringify(state.environment)};`),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};
|