@interfere/vite 11.2.1 → 12.0.0

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.
Files changed (56) hide show
  1. package/README.md +37 -33
  2. package/dist/init.d.mts +5 -5
  3. package/dist/init.mjs +1 -1
  4. package/dist/internal/browser-globals.d.mts +2 -3
  5. package/dist/internal/build/build-receipt.d.mts +62 -0
  6. package/dist/internal/build/build-receipt.mjs +1 -0
  7. package/dist/internal/build/canonical-build-path.d.mts +3 -0
  8. package/dist/internal/build/canonical-build-path.mjs +1 -0
  9. package/dist/internal/build/git.d.mts +2 -3
  10. package/dist/internal/build/git.mjs +1 -1
  11. package/dist/internal/build/pipeline.d.mts +81 -55
  12. package/dist/internal/build/pipeline.mjs +1 -1
  13. package/dist/internal/build/read-publication-status.d.mts +39 -0
  14. package/dist/internal/build/read-publication-status.mjs +1 -0
  15. package/dist/internal/build/render-debug-id.d.mts +6 -0
  16. package/dist/internal/build/render-debug-id.mjs +1 -0
  17. package/dist/internal/build/source-maps.d.mts +8 -26
  18. package/dist/internal/build/source-maps.mjs +1 -1
  19. package/dist/internal/build/transfer-source-maps.d.mts +59 -0
  20. package/dist/internal/build/transfer-source-maps.mjs +1 -0
  21. package/dist/internal/build/wait-for-source-maps.d.mts +48 -0
  22. package/dist/internal/build/wait-for-source-maps.mjs +1 -0
  23. package/dist/internal/release-slug.d.mts +4 -6
  24. package/dist/internal/server/capture.d.mts +2 -3
  25. package/dist/internal/server/capture.mjs +1 -1
  26. package/dist/internal/server/console-bridge.d.mts +2 -3
  27. package/dist/internal/server/console-bridge.mjs +1 -1
  28. package/dist/internal/server/env.d.mts +8 -10
  29. package/dist/internal/server/env.mjs +1 -1
  30. package/dist/internal/server/freshness-propagator.d.mts +18 -0
  31. package/dist/internal/server/freshness-propagator.mjs +1 -0
  32. package/dist/internal/server/instrumentation-options.d.mts +2 -3
  33. package/dist/internal/server/register.d.mts +2 -3
  34. package/dist/internal/server/register.mjs +1 -1
  35. package/dist/internal/server/remote-config.d.mts +3 -4
  36. package/dist/internal/server/remote-config.mjs +1 -1
  37. package/dist/internal/server/request-trace.d.mts +4 -5
  38. package/dist/internal/server/request-trace.mjs +1 -1
  39. package/dist/internal/server/trace-meta.d.mts +9 -8
  40. package/dist/internal/server/trace-meta.mjs +1 -1
  41. package/dist/internal/server/types.d.mts +2 -3
  42. package/dist/package.mjs +1 -1
  43. package/dist/plugin.d.mts +12 -7
  44. package/dist/plugin.mjs +2 -2
  45. package/dist/provider.d.mts +3 -3
  46. package/dist/provider.mjs +1 -1
  47. package/dist/tanstack-start.d.mts +2 -2
  48. package/dist/tanstack-start.mjs +1 -1
  49. package/dist/version.d.mts +2 -3
  50. package/package.json +45 -43
  51. package/dist/internal/build/baked-config.d.mts +0 -13
  52. package/dist/internal/build/baked-config.mjs +0 -1
  53. package/dist/internal/server/traceparent.d.mts +0 -17
  54. package/dist/internal/server/traceparent.mjs +0 -1
  55. package/dist/internal/server/url.d.mts +0 -4
  56. package/dist/internal/server/url.mjs +0 -1
@@ -0,0 +1 @@
1
+ import{readPublicationStatus}from"./read-publication-status.mjs";import{uploadSourceMapChunks}from"@interfere/build/upload-source-map-chunks";import{completeSourceMapsResponse}from"@interfere/types/data/source-maps";import{sourceMapPublicationStatus}from"@interfere/types/data/source-map-status";import{extractRichness}from"@interfere/types/sdk/source-map-richness";async function transferSourceMaps(input){let{client,bundler,releaseSlug}=input,{files,sourceFileCount}=input.discovery,start=performance.now(),signFiles=files.map(file=>({path:file.path,sizeBytes:new TextEncoder().encode(file.content).byteLength,...extractRichness(file.content)})),environment=input.environment??void 0,signed=await client.releases.signSourceMapUploads({releaseSlug,environment,files:signFiles},{abortSignal:AbortSignal.timeout(6e4)}),signedAt=performance.now(),session=null;if(input.readinessTimeoutMs===void 0?input.capturePublication&&(session=await readPublicationStatus(input)):session=sourceMapPublicationStatus.parse(await client.releases.getSourceMapPublicationStatus({releaseSlug,environment},{abortSignal:AbortSignal.timeout(6e4)})),input.readinessTimeoutMs!==void 0&&session&&(!session.sessionId||session.releaseSlug!==releaseSlug||session.environment!==input.environment))throw Error(`Source-map publication requires an identified upload session for this environment`);let sessionReadAt=performance.now(),totalBytes=await uploadSourceMapChunks({client,environment,releaseSlug,files,signed}),uploadedAt=performance.now(),completion={bundler,sourceFileCount,files:files.map(({path,hash,debugId,chunkUrl})=>({path,hash,debugId,chunkUrl}))},accepted=completeSourceMapsResponse.safeParse(await client.releases.finalizeSourceMaps({releaseSlug,environment,...completion},{abortSignal:AbortSignal.timeout(Math.max(0,signed.expiresAt*1e3-Date.now()))}));if(!accepted.success||!accepted.data.ok||accepted.data.fileCount!==files.length)throw Error(`Source-map server did not accept the complete batch`);let acceptedAt=performance.now();return{completion,session,acceptedAt,timing:{sign:Math.round(signedAt-start),session:Math.round(sessionReadAt-signedAt),upload:Math.round(uploadedAt-sessionReadAt),finalize:Math.round(acceptedAt-uploadedAt),totalBytes}}}export{transferSourceMaps};
@@ -0,0 +1,48 @@
1
+ import { InterfereClient } from "@interfere/javascript";
2
+ //#region src/internal/build/wait-for-source-maps.d.ts
3
+ export declare function waitForSourceMaps(input: {
4
+ client: InterfereClient;
5
+ releaseSlug: string;
6
+ environment: string | null;
7
+ sessionId: string;
8
+ batchDigest: string;
9
+ fileCount: number;
10
+ timeoutMs: number;
11
+ signal?: AbortSignal;
12
+ }): Promise<{
13
+ releaseSlug: `rel_${string}` & import("zod").$brand<"ReleaseSlug">;
14
+ environment: string | null;
15
+ sessionId: string | null;
16
+ state: "absent" | "accepted" | "expired" | "expiring" | "ready" | "uploading";
17
+ fileCount: number;
18
+ publishedFileCount: number;
19
+ batchDigest: string | null;
20
+ manifestReady: boolean;
21
+ diagnostics?: {
22
+ attempt: number;
23
+ scheduledAtMs: number | null;
24
+ startedAtMs: number;
25
+ completedAtMs: number | null;
26
+ phase: "complete" | "completion" | "continuation" | "files" | "manifest" | "release";
27
+ publishedAtStart: number;
28
+ progressObservedAtMs: number | null;
29
+ nativeRetryCount: number;
30
+ interruptedAttempts: number;
31
+ longestAttempt: {
32
+ attempt: number;
33
+ durationMs: number;
34
+ } | null;
35
+ longestAlarmDelay: {
36
+ attempt: number;
37
+ durationMs: number;
38
+ } | null;
39
+ nextAlarmAtMs: number | null;
40
+ lastFailure: {
41
+ attempt: number;
42
+ atMs: number;
43
+ phase: "complete" | "completion" | "continuation" | "files" | "manifest" | "release";
44
+ kind: "aborted" | "error" | "non-error" | "timeout";
45
+ } | null;
46
+ } | undefined;
47
+ }>;
48
+ //#endregion
@@ -0,0 +1 @@
1
+ import{sourceMapPublicationStatus}from"@interfere/types/data/source-map-status";import{setTimeout}from"node:timers/promises";async function waitForSourceMaps(input){let startedAt=performance.now(),deadline=AbortSignal.timeout(input.timeoutMs),signal=input.signal?AbortSignal.any([deadline,input.signal]):deadline,delayMs=250,phase=`request`,requestOutcome=`interrupted`,requestStartedAt=startedAt,requestFinishedAt,lastValidatedAt,lastStatus;try{for(;;){signal.throwIfAborted(),phase=`request`,requestStartedAt=performance.now(),requestFinishedAt=void 0,requestOutcome=`interrupted`;let response=await input.client.releases.getSourceMapPublicationStatus({releaseSlug:input.releaseSlug,environment:input.environment??void 0},{abortSignal:signal});requestFinishedAt=performance.now(),requestOutcome=`returned`;let status=sourceMapPublicationStatus.parse(response);if(signal.throwIfAborted(),status.releaseSlug!==input.releaseSlug||status.environment!==input.environment||status.sessionId!==input.sessionId)throw Error(`Source-map publication session changed`);if(status.state===`expired`||status.state===`expiring`||status.state===`absent`)throw Error(`Source-map publication session is ${status.state}`);if(status.batchDigest!==input.batchDigest||status.fileCount!==input.fileCount)throw Error(`Source-map publication batch does not match the accepted build`);if(lastStatus=status,lastValidatedAt=performance.now(),status.state===`ready`&&status.manifestReady&&status.publishedFileCount===input.fileCount)return status;phase=`backoff`,await setTimeout(delayMs,void 0,{signal}),delayMs=Math.min(delayMs*2,5e3)}}catch(error){if(!deadline.aborted||signal.reason!==deadline.reason)throw error;let now=performance.now(),elapsedMs=Math.round(now-startedAt),polling={phase,lastValidatedAfterMs:lastValidatedAt===void 0?null:Math.round(lastValidatedAt-startedAt),lastValidatedAgeMs:lastValidatedAt===void 0?null:Math.round(now-lastValidatedAt),requestDurationMs:Math.round((requestFinishedAt??now)-requestStartedAt),requestOutcome},progress=lastStatus?`last validated status: state=${lastStatus.state}, published=${lastStatus.publishedFileCount}/${input.fileCount}, manifestReady=${lastStatus.manifestReady}`:`no validated status received`,diagnostics=lastStatus?.diagnostics?`; diagnostics=${JSON.stringify(lastStatus.diagnostics)}`:``;throw Error(`Source-map publication timed out after ${elapsedMs}ms (deadline=${input.timeoutMs}ms, release=${input.releaseSlug}, environment=${input.environment??`unspecified`}, session=${input.sessionId}); ${progress}${diagnostics}; polling=${JSON.stringify(polling)}`,{cause:error})}}export{waitForSourceMaps};
@@ -1,10 +1,8 @@
1
- import { ReleaseSlug } from "@interfere/types/releases/slug";
2
1
  import { RunGitCommand } from "@interfere/types/sdk/github-source";
3
2
  //#region src/internal/release-slug.d.ts
4
- declare function resolveCommitSha(runGit?: RunGitCommand): string | null;
5
- declare function resolveReleaseSlug(runGit?: RunGitCommand): {
3
+ export declare function resolveCommitSha(runGit?: RunGitCommand): string | null;
4
+ export declare function resolveReleaseSlug(runGit?: RunGitCommand): {
6
5
  commitSha: string | null;
7
- slug: ReleaseSlug | null;
6
+ slug: (`rel_${string}` & import("zod").$brand<"ReleaseSlug">) | null;
8
7
  };
9
- //#endregion
10
- export { resolveCommitSha, resolveReleaseSlug };
8
+ //#endregion
@@ -1,5 +1,4 @@
1
1
  import { CaptureErrorContext } from "./types.mjs";
2
2
  //#region src/internal/server/capture.d.ts
3
- declare function captureError(error: unknown, ctx?: CaptureErrorContext): void;
4
- //#endregion
5
- export { captureError };
3
+ export declare function captureError(error: unknown, ctx?: CaptureErrorContext): void;
4
+ //#endregion
@@ -1 +1 @@
1
- import{isEnabledOnServer}from"./env.mjs";import{isPluginEnabled}from"./remote-config.mjs";import{MECHANISM_TYPE,isNonErrorException,toException}from"@interfere/types/sdk/errors";import{SpanKind,SpanStatusCode,context,trace}from"@opentelemetry/api";const DEFAULT_CAPTURE_ERROR_MECHANISM={type:MECHANISM_TYPE.node.captureError,handled:!0},seen=new WeakSet;function buildAttrs(value,mechanism,ctx){let attrs={"interfere.exception.mechanism":mechanism.type,"interfere.exception.handled":String(mechanism.handled)};return ctx?.request?.method&&(attrs[`http.request.method`]=ctx.request.method),ctx?.request?.path&&(attrs[`url.path`]=ctx.request.path),isNonErrorException(value)?(attrs[`exception.type`]=value.type,attrs[`exception.message`]=value.value,attrs[`interfere.exception.kind`]=`non-error`,attrs):(attrs[`exception.type`]=value.name,attrs[`exception.message`]=value.message,value.stack&&(attrs[`exception.stacktrace`]=value.stack),attrs)}function recordException(error,mechanism,ctx){let value=toException(error),attrs=buildAttrs(value,mechanism,ctx),tracer=trace.getTracer(`@interfere/vite/server`),active=trace.getActiveSpan();if(active){active.addEvent(`exception`,attrs),mechanism.handled||active.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message});return}let span=tracer.startSpan(`interfere.captureError`,{kind:SpanKind.INTERNAL},context.active());span.addEvent(`exception`,attrs),mechanism.handled||span.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message}),span.end()}function captureError(error,ctx){if(isEnabledOnServer()&&isPluginEnabled(`errors`)){if(error instanceof Error){if(seen.has(error))return;seen.add(error)}recordException(error,ctx?.mechanism??DEFAULT_CAPTURE_ERROR_MECHANISM,ctx)}}export{captureError};
1
+ import{isEnabledOnServer}from"./env.mjs";import{isPluginEnabled}from"./remote-config.mjs";import{MECHANISM_TYPE,isNonErrorException,toException}from"@interfere/types/sdk/errors";import{SpanKind,SpanStatusCode,context,trace}from"@opentelemetry/api";const DEFAULT_CAPTURE_ERROR_MECHANISM={handled:!0,type:MECHANISM_TYPE.node.captureError},seen=new WeakSet;function buildAttrs(value,mechanism,ctx){let attrs={"interfere.exception.handled":String(mechanism.handled),"interfere.exception.mechanism":mechanism.type};return ctx?.request?.method&&(attrs[`http.request.method`]=ctx.request.method),ctx?.request?.path&&(attrs[`url.path`]=ctx.request.path),isNonErrorException(value)?(attrs[`exception.type`]=value.type,attrs[`exception.message`]=value.value,attrs[`interfere.exception.kind`]=`non-error`,attrs):(attrs[`exception.type`]=value.name,attrs[`exception.message`]=value.message,value.stack&&(attrs[`exception.stacktrace`]=value.stack),attrs)}function recordException(error,mechanism,ctx){let value=toException(error),attrs=buildAttrs(value,mechanism,ctx),tracer=trace.getTracer(`@interfere/vite/server`),active=trace.getActiveSpan();if(active){active.addEvent(`exception`,attrs),mechanism.handled||active.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message});return}let span=tracer.startSpan(`interfere.captureError`,{kind:SpanKind.INTERNAL},context.active());span.addEvent(`exception`,attrs),mechanism.handled||span.setStatus({code:SpanStatusCode.ERROR,message:isNonErrorException(value)?value.value:value.message}),span.end()}function captureError(error,ctx){if(isEnabledOnServer()&&isPluginEnabled(`errors`)){if(error instanceof Error){if(seen.has(error))return;seen.add(error)}recordException(error,ctx?.mechanism??DEFAULT_CAPTURE_ERROR_MECHANISM,ctx)}}export{captureError};
@@ -23,6 +23,5 @@
23
23
  * bridge, same trade-offs, intentionally kept in the Vite adapter's
24
24
  * source tree so SDKs do not share build/runtime internals.
25
25
  */
26
- declare function bridgeConsoleToOtel(): () => void;
27
- //#endregion
28
- export { bridgeConsoleToOtel };
26
+ export declare function bridgeConsoleToOtel(): () => void;
27
+ //#endregion
@@ -1 +1 @@
1
- import{context,trace}from"@opentelemetry/api";import{logs}from"@opentelemetry/api-logs";const CONSOLE_METHODS=[`debug`,`log`,`info`,`warn`,`error`],SEVERITY={debug:5,log:9,info:9,warn:13,error:17},MAX_BODY_CHARS=8192;function buildBody(args){let body=args.map(stringify).join(` `);return body.length>MAX_BODY_CHARS?`${body.slice(0,MAX_BODY_CHARS)}…`:body}function stringify(value){if(typeof value==`string`)return value;if(value instanceof Error)return value.stack??`${value.name}: ${value.message}`;try{return JSON.stringify(value)}catch{return String(value)}}function extractException(args){for(let arg of args)if(arg instanceof Error)return arg}function buildExceptionAttributes(args){let exception=extractException(args);if(!exception)return;let attrs={"exception.type":exception.name,"exception.message":exception.message};return exception.stack&&(attrs[`exception.stacktrace`]=exception.stack),attrs}function bridgeConsoleToOtel(){let logger=logs.getLogger(`@interfere/vite/console`),emitting=!1,bridgeFailures=0,original={debug:console.debug,log:console.log,info:console.info,warn:console.warn,error:console.error};function emitLog(orig,severity,method,args,activeContext,activeSpan){if(!emitting){emitting=!0;try{let attributes=buildExceptionAttributes(args);logger.emit({severityNumber:severity,severityText:method.toUpperCase(),body:buildBody(args),...activeSpan?{context:activeContext}:{},...attributes?{attributes}:{}})}catch(error){bridgeFailures+=1,bridgeFailures%100==1&&orig.call(console,`[interfere] console→OTel bridge emission failed (${bridgeFailures} total):`,error)}finally{emitting=!1}}}for(let method of CONSOLE_METHODS){let orig=original[method],severity=SEVERITY[method];console[method]=(...args)=>{orig.apply(console,args);let activeContext=context.active(),activeSpan=trace.getSpan(activeContext);queueMicrotask(()=>{emitLog(orig,severity,method,args,activeContext,activeSpan)})}}return()=>{for(let method of CONSOLE_METHODS)console[method]=original[method]}}export{bridgeConsoleToOtel};
1
+ import{omitUndefined}from"@interfere/helpers/omit-undefined";import{context,trace}from"@opentelemetry/api";import{logs}from"@opentelemetry/api-logs";const CONSOLE_METHODS=[`debug`,`log`,`info`,`warn`,`error`],SEVERITY={debug:5,error:17,info:9,log:9,warn:13},MAX_BODY_CHARS=8192;function buildBody(args){let body=args.map(element=>stringify(element)).join(` `);return body.length>MAX_BODY_CHARS?`${body.slice(0,MAX_BODY_CHARS)}…`:body}function stringify(value){if(typeof value==`string`)return value;if(value instanceof Error)return value.stack??`${value.name}: ${value.message}`;try{return JSON.stringify(value)}catch{return String(value)}}function extractException(args){for(let arg of args)if(arg instanceof Error)return arg}function buildExceptionAttributes(args){let exception=extractException(args);if(exception)return omitUndefined({"exception.type":exception.name,"exception.message":exception.message,"exception.stacktrace":exception.stack||void 0})}function bridgeConsoleToOtel(){let logger=logs.getLogger(`@interfere/vite/console`),emitting=!1,bridgeFailures=0,original={debug:console.debug,error:console.error,info:console.info,log:console.log,warn:console.warn};function emitLog(orig,severity,method,args,activeContext,activeSpan){if(!emitting){emitting=!0;try{let attributes=buildExceptionAttributes(args);logger.emit(omitUndefined({body:buildBody(args),severityNumber:severity,severityText:method.toUpperCase(),context:activeSpan?activeContext:void 0,attributes}))}catch(error){bridgeFailures+=1,bridgeFailures%100==1&&orig.call(console,`[interfere] console→OTel bridge emission failed (${bridgeFailures} total):`,error)}finally{emitting=!1}}}for(let method of CONSOLE_METHODS){let orig=original[method],severity=SEVERITY[method];console[method]=(...args)=>{orig.apply(console,args);let activeContext=context.active(),activeSpan=trace.getSpan(activeContext);queueMicrotask(()=>{emitLog(orig,severity,method,args,activeContext,activeSpan)})}}return(()=>{for(let method of CONSOLE_METHODS)console[method]=original[method]})}export{bridgeConsoleToOtel};
@@ -1,10 +1,4 @@
1
1
  //#region src/internal/server/env.d.ts
2
- interface InterfereServerEnv {
3
- readonly apiUrl: string;
4
- readonly forceEnable: boolean;
5
- readonly nodeEnvironment: string;
6
- readonly publicKey: string | null;
7
- }
8
2
  /**
9
3
  * Server-side gate for the proxy handler, the `captureError` /
10
4
  * `onRequestError` helpers, and the OTel register path. Reads the same
@@ -15,7 +9,11 @@ interface InterfereServerEnv {
15
9
  * ingress remains the authority on whether local / unknown / production
16
10
  * events are allowed.
17
11
  */
18
- declare function isEnabledOnServer(): boolean;
19
- declare function readInterfereServerEnv(): InterfereServerEnv;
20
- //#endregion
21
- export { InterfereServerEnv, isEnabledOnServer, readInterfereServerEnv };
12
+ export declare function isEnabledOnServer(): boolean;
13
+ export declare function readInterfereServerEnv(): {
14
+ apiUrl: string;
15
+ forceEnable: boolean;
16
+ nodeEnvironment: string;
17
+ publicKey: string | null;
18
+ };
19
+ //#endregion
@@ -1 +1 @@
1
- import{API_URL}from"@interfere/constants/api";import{parseEnvValue}from"@interfere/types/sdk/env";import{resolveEnvironment}from"@interfere/types/sdk/runtime";function isEnabledOnServer(){return process.env.NODE_ENV===`production`||!!process.env.VITE_INTERFERE_PUBLIC_KEY}function readInterfereServerEnv(){let nodeEnvironment=resolveEnvironment({env:process.env});return{apiUrl:parseEnvValue(process.env.INTERFERE_API_URL)??API_URL,forceEnable:!!process.env.INTERFERE_FORCE_ENABLE,nodeEnvironment,publicKey:parseEnvValue(process.env.VITE_INTERFERE_PUBLIC_KEY)}}export{isEnabledOnServer,readInterfereServerEnv};
1
+ import{API_URL}from"@interfere/constants/api";import{parseEnvValue}from"@interfere/types/sdk/env";import{resolveEnvironment}from"@interfere/types/sdk/runtime";function isEnabledOnServer(){return process.env.NODE_ENV===`production`||!!process.env.VITE_INTERFERE_PUBLIC_KEY}function readInterfereServerEnv(){let nodeEnvironment=resolveEnvironment({env:process.env,explicit:import.meta.env.VITE_INTERFERE_ENVIRONMENT});return{apiUrl:parseEnvValue(process.env.INTERFERE_API_URL)??API_URL,forceEnable:!!process.env.INTERFERE_FORCE_ENABLE,nodeEnvironment,publicKey:parseEnvValue(process.env.VITE_INTERFERE_PUBLIC_KEY)}}export{isEnabledOnServer,readInterfereServerEnv};
@@ -0,0 +1,18 @@
1
+ import { Context, TextMapPropagator, TextMapSetter } from "@opentelemetry/api";
2
+ //#region src/internal/server/freshness-propagator.d.ts
3
+ /**
4
+ * Stamp-only propagator that writes a `tracestate` freshness entry
5
+ * (`interfere=<unix seconds>`) at inject time, never touching
6
+ * `traceparent`. The SSR trace-meta tags feed the browser SDK's
7
+ * propagation reader, which drops traceparents older than its freshness
8
+ * window — so HTML cached by a CDN can't stitch every visitor in the
9
+ * cache window onto one stale trace. Mirrors `@interfere/next`'s
10
+ * stamper; Vite SSR has no framework-level ISR, so no static-generation
11
+ * gating is needed here.
12
+ */
13
+ export declare class FreshnessStampingPropagator implements TextMapPropagator {
14
+ inject<Carrier>(context: Context, carrier: Carrier, setter: TextMapSetter<Carrier>): void;
15
+ extract(context: Context): Context;
16
+ fields(): string[];
17
+ }
18
+ //#endregion
@@ -0,0 +1 @@
1
+ import{isSpanContextValid,trace}from"@opentelemetry/api";import{TraceState,isTracingSuppressed}from"@opentelemetry/core";import{TRACESTATE_FRESHNESS_KEY,freshnessStamp}from"@interfere/types/sdk/trace-freshness";const TRACE_STATE_HEADER=`tracestate`;var FreshnessStampingPropagator=class{inject(context,carrier,setter){let spanContext=trace.getSpanContext(context);if(!spanContext||!isSpanContextValid(spanContext)||isTracingSuppressed(context))return;let traceState=(spanContext.traceState??new TraceState).set(TRACESTATE_FRESHNESS_KEY,freshnessStamp(Date.now()));setter.set(carrier,TRACE_STATE_HEADER,traceState.serialize())}extract(context){return context}fields(){return[TRACE_STATE_HEADER]}};export{FreshnessStampingPropagator};
@@ -11,7 +11,7 @@
11
11
  * in the Next adapter, which is internal-only per the parity
12
12
  * decisions).
13
13
  */
14
- interface ServerInstrumentationOptions {
14
+ export interface ServerInstrumentationOptions {
15
15
  /**
16
16
  * `false` disables the `console.*` → OTel `LogRecord` bridge.
17
17
  * Defaults to enabled. Customers running their own structured
@@ -40,5 +40,4 @@ interface ServerInstrumentationOptions {
40
40
  */
41
41
  serviceName?: string;
42
42
  }
43
- //#endregion
44
- export { ServerInstrumentationOptions };
43
+ //#endregion
@@ -24,6 +24,5 @@ import { ServerInstrumentationOptions } from "./instrumentation-options.mjs";
24
24
  * runtime. Cloudflare Workers / Deno presets need a separate edge
25
25
  * stub (Phase 5).
26
26
  */
27
- declare function register(opts?: ServerInstrumentationOptions): Promise<void>;
28
- //#endregion
29
- export { register };
27
+ export declare function register(opts?: ServerInstrumentationOptions): Promise<void>;
28
+ //#endregion
@@ -1 +1 @@
1
- import{PRODUCER_VERSION}from"../../version.mjs";import{resolveReleaseSlug}from"../release-slug.mjs";import{readInterfereServerEnv}from"./env.mjs";import{withPublicKey}from"./url.mjs";import{fetchAndCacheRemoteConfig}from"./remote-config.mjs";import{bridgeConsoleToOtel}from"./console-bridge.mjs";import{metrics,propagation}from"@opentelemetry/api";import{readLogSourcingMode}from"@interfere/types/sdk/log-sourcing";import{readServerTracingMode}from"@interfere/types/sdk/server-tracing";import{BaggageSpanProcessor}from"@opentelemetry/baggage-span-processor";import{AsyncLocalStorageContextManager}from"@opentelemetry/context-async-hooks";import{CompositePropagator,W3CBaggagePropagator,W3CTraceContextPropagator}from"@opentelemetry/core";import{OTLPLogExporter}from"@opentelemetry/exporter-logs-otlp-http";import{AggregationTemporalityPreference,OTLPMetricExporter}from"@opentelemetry/exporter-metrics-otlp-http";import{OTLPTraceExporter}from"@opentelemetry/exporter-trace-otlp-http";import{registerInstrumentations}from"@opentelemetry/instrumentation";import{UndiciInstrumentation}from"@opentelemetry/instrumentation-undici";import{resourceFromAttributes}from"@opentelemetry/resources";import{BatchLogRecordProcessor,LoggerProvider}from"@opentelemetry/sdk-logs";import{MeterProvider,PeriodicExportingMetricReader}from"@opentelemetry/sdk-metrics";import{BatchSpanProcessor}from"@opentelemetry/sdk-trace-base";import{NodeTracerProvider}from"@opentelemetry/sdk-trace-node";let registered=!1;function matchesAny(url,patterns){for(let pattern of patterns)if(pattern instanceof RegExp?pattern.test(url):url.includes(pattern))return!0;return!1}async function register(opts={}){if(registered)return;let env=readInterfereServerEnv();if(!env.publicKey)return;let{slug:releaseSlug}=resolveReleaseSlug();releaseSlug||console.warn("[interfere] No commit SHA available; server spans will ship without `release.slug`. Set `INTERFERE_SOURCE_ID` (or any of `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA`) on the runtime env.");let sinkUrl=withPublicKey(`${env.apiUrl}/v2/sink`,env.publicKey),ignoreUrls=[sinkUrl,...opts.ignoreUrls??[]],propagateContextUrls=opts.propagateContextUrls??[],serverTracingMode=readServerTracingMode(),isLogDrainMode=readLogSourcingMode()===`drain`,exporterHeaders={"x-interfere-producer-version":PRODUCER_VERSION},resourceAttributes={"service.name":opts.serviceName??`interfere-sdk-vite-server`,"service.namespace":`interfere`,"deployment.environment.name":env.nodeEnvironment,"telemetry.sdk.language":`nodejs`,"interfere.sdk.name":`@interfere/vite`,"interfere.sdk.version":PRODUCER_VERSION,...releaseSlug?{"release.slug":releaseSlug}:{}},resource=resourceFromAttributes(resourceAttributes),isDrainMode=serverTracingMode===`drain`,tracerProvider=new NodeTracerProvider({resource,spanProcessors:[new BaggageSpanProcessor(()=>!0),...isDrainMode?[]:[new BatchSpanProcessor(new OTLPTraceExporter({url:sinkUrl,headers:exporterHeaders}))]]});if(propagation.fields().length===0&&propagation.setGlobalPropagator(new CompositePropagator({propagators:[new W3CTraceContextPropagator,new W3CBaggagePropagator]})),tracerProvider.register({contextManager:new AsyncLocalStorageContextManager().enable()}),!isLogDrainMode){let loggerProvider=new LoggerProvider({resource,processors:[new BatchLogRecordProcessor({exporter:new OTLPLogExporter({url:sinkUrl,headers:exporterHeaders})})]}),{logs:logsApi}=await import(`@opentelemetry/api-logs`);logsApi.setGlobalLoggerProvider(loggerProvider)}let meterProvider=new MeterProvider({resource,readers:[new PeriodicExportingMetricReader({exporter:new OTLPMetricExporter({url:sinkUrl,headers:exporterHeaders,temporalityPreference:AggregationTemporalityPreference.DELTA}),exportIntervalMillis:3e4})]});metrics.setGlobalMeterProvider(meterProvider),isDrainMode?console.info(`[interfere] server tracing in drain mode; SDK trace exporter + outbound HTTP instrumentation disabled — server traces come from the platform trace drain.`):registerInstrumentations({tracerProvider,meterProvider,instrumentations:[new UndiciInstrumentation({ignoreRequestHook:req=>matchesAny(`${req.origin}${req.path}`,ignoreUrls),requestHook:(span,req)=>{matchesAny(`${req.origin}${req.path}`,propagateContextUrls)&&span.setAttribute(`interfere.propagated`,!0)}})]}),!isLogDrainMode&&opts.consoleBridge!==!1&&bridgeConsoleToOtel(),registered=!0,await fetchAndCacheRemoteConfig()}export{register};
1
+ import{PRODUCER_VERSION}from"../../version.mjs";import{resolveReleaseSlug}from"../release-slug.mjs";import{readInterfereServerEnv}from"./env.mjs";import{fetchAndCacheRemoteConfig}from"./remote-config.mjs";import{bridgeConsoleToOtel}from"./console-bridge.mjs";import{FreshnessStampingPropagator}from"./freshness-propagator.mjs";import{drainUrl}from"@interfere/constants/api";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{metrics,propagation}from"@opentelemetry/api";import{logSourcingModeSchema,readLogSourcingMode}from"@interfere/types/sdk/log-sourcing";import{readServerTracingMode,serverTracingModeSchema}from"@interfere/types/sdk/server-tracing";import{BaggageSpanProcessor}from"@opentelemetry/baggage-span-processor";import{AsyncLocalStorageContextManager}from"@opentelemetry/context-async-hooks";import{CompositePropagator,W3CBaggagePropagator,W3CTraceContextPropagator}from"@opentelemetry/core";import{OTLPLogExporter}from"@opentelemetry/exporter-logs-otlp-http";import{AggregationTemporalityPreference,OTLPMetricExporter}from"@opentelemetry/exporter-metrics-otlp-http";import{OTLPTraceExporter}from"@opentelemetry/exporter-trace-otlp-http";import{registerInstrumentations}from"@opentelemetry/instrumentation";import{UndiciInstrumentation}from"@opentelemetry/instrumentation-undici";import{resourceFromAttributes}from"@opentelemetry/resources";import{BatchLogRecordProcessor,LoggerProvider}from"@opentelemetry/sdk-logs";import{MeterProvider,PeriodicExportingMetricReader}from"@opentelemetry/sdk-metrics";import{BatchSpanProcessor}from"@opentelemetry/sdk-trace-base";import{NodeTracerProvider}from"@opentelemetry/sdk-trace-node";let registered=!1;function matchesAny(url,patterns){for(let pattern of patterns)if(pattern instanceof RegExp?pattern.test(url):url.includes(pattern))return!0;return!1}async function register(opts={}){if(registered)return;let env=readInterfereServerEnv();if(!env.publicKey)return;let{slug:releaseSlug}=resolveReleaseSlug();releaseSlug||console.warn("[interfere] No commit SHA available; server spans will ship without `release.slug`. Set `INTERFERE_SOURCE_ID` (or any of `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA`) on the runtime env.");let{publicKey}=env,tracesUrl=drainUrl(env.apiUrl,`traces`,publicKey),logsUrl=drainUrl(env.apiUrl,`logs`,publicKey),metricsUrl=drainUrl(env.apiUrl,`metrics`,publicKey),ignoreUrls=[tracesUrl,logsUrl,metricsUrl,...opts.ignoreUrls??[]],propagateContextUrls=opts.propagateContextUrls??[],bakedTracing=serverTracingModeSchema.safeParse(import.meta.env?.VITE_INTERFERE_SERVER_TRACING),bakedLogs=logSourcingModeSchema.safeParse(import.meta.env?.VITE_INTERFERE_LOG_SOURCING),serverTracingMode=bakedTracing.success?bakedTracing.data:readServerTracingMode(),isLogDrainMode=(bakedLogs.success?bakedLogs.data:readLogSourcingMode())===`drain`,exporterHeaders={"x-interfere-producer-version":PRODUCER_VERSION},resourceAttributes=omitUndefined({"deployment.environment.name":env.nodeEnvironment,"interfere.sdk.name":`@interfere/vite`,"interfere.sdk.version":PRODUCER_VERSION,"service.name":opts.serviceName??`interfere-sdk-vite-server`,"service.namespace":`interfere`,"telemetry.sdk.language":`nodejs`,"release.slug":releaseSlug||void 0}),resource=resourceFromAttributes(resourceAttributes),isDrainMode=serverTracingMode===`drain`,tracerProvider=new NodeTracerProvider({resource,spanProcessors:[new BaggageSpanProcessor(()=>!0),...isDrainMode?[]:[new BatchSpanProcessor(new OTLPTraceExporter({headers:exporterHeaders,url:tracesUrl}))]]});if(propagation.fields().length===0&&propagation.setGlobalPropagator(new CompositePropagator({propagators:[new W3CTraceContextPropagator,new FreshnessStampingPropagator,new W3CBaggagePropagator]})),tracerProvider.register({contextManager:new AsyncLocalStorageContextManager().enable()}),!isLogDrainMode){let loggerProvider=new LoggerProvider({processors:[new BatchLogRecordProcessor({exporter:new OTLPLogExporter({url:logsUrl,headers:exporterHeaders})})],resource}),{logs:logsApi}=await import(`@opentelemetry/api-logs`);logsApi.setGlobalLoggerProvider(loggerProvider)}let meterProvider=new MeterProvider({readers:[new PeriodicExportingMetricReader({exporter:new OTLPMetricExporter({url:metricsUrl,headers:exporterHeaders,temporalityPreference:AggregationTemporalityPreference.DELTA}),exportIntervalMillis:3e4})],resource});metrics.setGlobalMeterProvider(meterProvider),isDrainMode?console.info(`[interfere] server tracing in drain mode; SDK trace exporter + outbound HTTP instrumentation disabled — server traces come from the platform trace drain.`):registerInstrumentations({instrumentations:[new UndiciInstrumentation({ignoreRequestHook:req=>matchesAny(`${req.origin}${req.path}`,ignoreUrls),requestHook:(span,req)=>{matchesAny(`${req.origin}${req.path}`,propagateContextUrls)&&span.setAttribute(`interfere.propagated`,!0)}})],meterProvider,tracerProvider}),!isLogDrainMode&&opts.consoleBridge!==!1&&bridgeConsoleToOtel(),registered=!0,await fetchAndCacheRemoteConfig()}export{register};
@@ -1,5 +1,4 @@
1
1
  //#region src/internal/server/remote-config.d.ts
2
- declare function fetchAndCacheRemoteConfig(): Promise<void>;
3
- declare function isPluginEnabled(plugin: string): boolean;
4
- //#endregion
5
- export { fetchAndCacheRemoteConfig, isPluginEnabled };
2
+ export declare function fetchAndCacheRemoteConfig(): Promise<void>;
3
+ export declare function isPluginEnabled(plugin: string): boolean;
4
+ //#endregion
@@ -1 +1 @@
1
- import{isEnabledOnServer,readInterfereServerEnv}from"./env.mjs";import{withPublicKey}from"./url.mjs";import{readBakedPlugins}from"@interfere/types/sdk/remote-config";import{API_PATHS}from"@interfere/constants/api";let cachedConfig=null;async function fetchAndCacheRemoteConfig(){if(readBakedPlugins()||!isEnabledOnServer())return;let env=readInterfereServerEnv();if(env.publicKey===null)return;let config;try{let url=withPublicKey(`${env.apiUrl}${API_PATHS.CONFIG}`,env.publicKey),response=await fetch(url,{method:`GET`,headers:{"content-type":`application/json`},signal:AbortSignal.timeout(1e4)});if(!response.ok)return;config=await response.json()}catch{return}config?.plugins&&(cachedConfig=config.plugins)}function isPluginEnabled(plugin){let config=readBakedPlugins()??cachedConfig;return!config||config[plugin]!==!1}export{fetchAndCacheRemoteConfig,isPluginEnabled};
1
+ import{isEnabledOnServer,readInterfereServerEnv}from"./env.mjs";import{readBakedPlugins,remoteConfigInputSchema}from"@interfere/types/sdk/remote-config";import{InterfereClient}from"@interfere/javascript";let cachedConfig=null;async function fetchAndCacheRemoteConfig(){if(readBakedPlugins()||!isEnabledOnServer())return;let env=readInterfereServerEnv();if(env.publicKey!==null)try{let client=new InterfereClient({baseUrl:env.apiUrl,publicKeyHeader:{apiKey:env.publicKey},timeoutInSeconds:10,maxRetries:0}),config=remoteConfigInputSchema.parse(await client.config.getSdkConfig());config.plugins&&(cachedConfig=config.plugins)}catch{return}}function isPluginEnabled(plugin){let config=readBakedPlugins()??cachedConfig;return!config||config[plugin]!==!1}export{fetchAndCacheRemoteConfig,isPluginEnabled};
@@ -1,9 +1,8 @@
1
1
  //#region src/internal/server/request-trace.d.ts
2
- interface TraceMetaDescriptor {
2
+ export interface TraceMetaDescriptor {
3
3
  content: string;
4
4
  name: string;
5
5
  }
6
- declare function traceMeta(): TraceMetaDescriptor[];
7
- declare function withRequestSpan<T>(request: Request, next: () => Promise<T>): Promise<T>;
8
- //#endregion
9
- export { TraceMetaDescriptor, traceMeta, withRequestSpan };
6
+ export declare function traceMeta(): TraceMetaDescriptor[];
7
+ export declare function withRequestSpan<T>(request: Request, next: () => Promise<T>): Promise<T>;
8
+ //#endregion
@@ -1 +1 @@
1
- import{isEnabledOnServer}from"./env.mjs";import{SpanKind,SpanStatusCode,TraceFlags,context,propagation,trace}from"@opentelemetry/api";const SAMPLED_BIT=TraceFlags.SAMPLED,PROPAGATED_FIELDS=[`traceparent`,`tracestate`,`baggage`];function traceMeta(){let span=trace.getActiveSpan();if(!span||(span.spanContext().traceFlags&SAMPLED_BIT)!==SAMPLED_BIT)return[];let carrier={};propagation.inject(context.active(),carrier);let meta=[];for(let name of PROPAGATED_FIELDS){let content=carrier[name];content&&meta.push({content,name})}return meta}function withRequestSpan(request,next){if(!isEnabledOnServer())return next();let parent=propagation.extract(context.active(),headersToCarrier(request.headers));return context.with(parent,()=>trace.getTracer(`@interfere/vite/tanstack-start`).startActiveSpan(spanName(request),{attributes:requestAttributes(request),kind:SpanKind.SERVER},async span=>{try{return await next()}catch(error){throw span.recordException(error),span.setStatus({code:SpanStatusCode.ERROR}),error}finally{span.end()}}))}function headersToCarrier(headers){let carrier={};for(let[key,value]of headers)carrier[key]=value;return carrier}function spanName(request){return`${request.method} ${new URL(request.url).pathname}`}function requestAttributes(request){let url=new URL(request.url);return{"http.request.method":request.method,"server.address":url.host,"url.full":url.href,"url.path":url.pathname}}export{traceMeta,withRequestSpan};
1
+ import{isEnabledOnServer}from"./env.mjs";import{toError}from"@interfere/types/sdk/errors";import{SpanKind,SpanStatusCode,TraceFlags,context,propagation,trace}from"@opentelemetry/api";const SAMPLED_BIT=TraceFlags.SAMPLED,PROPAGATED_FIELDS=[`traceparent`,`tracestate`,`baggage`];function traceMeta(){let span=trace.getActiveSpan();if(!span||(span.spanContext().traceFlags&SAMPLED_BIT)===0)return[];let carrier={};propagation.inject(context.active(),carrier);let meta=[];for(let name of PROPAGATED_FIELDS){let content=carrier[name];content&&meta.push({content,name})}return meta}async function withRequestSpan(request,next){if(!isEnabledOnServer())return next();let parent=propagation.extract(context.active(),headersToCarrier(request.headers));return context.with(parent,async()=>trace.getTracer(`@interfere/vite/tanstack-start`).startActiveSpan(spanName(request),{attributes:requestAttributes(request),kind:SpanKind.SERVER},async span=>{try{return await next()}catch(error){throw span.recordException(toError(error)),span.setStatus({code:SpanStatusCode.ERROR}),error}finally{span.end()}}))}function headersToCarrier(headers){let carrier={};for(let[key,value]of headers)carrier[key]=value;return carrier}function spanName(request){return`${request.method} ${new URL(request.url).pathname}`}function requestAttributes(request){let url=new URL(request.url);return{"http.request.method":request.method,"server.address":url.host,"url.full":url.href,"url.path":url.pathname}}export{traceMeta,withRequestSpan};
@@ -1,11 +1,13 @@
1
1
  //#region src/internal/server/trace-meta.d.ts
2
2
  /**
3
- * Server Component that emits a `<meta name="traceparent">` tag so
4
- * the client SDK can stitch every browser span onto the server-side
5
- * trace.
3
+ * Server Component that emits the W3C trace-context meta tags
4
+ * (`traceparent`, plus `tracestate` and `baggage` when present) so the
5
+ * client SDK can stitch every browser span onto the server-side trace.
6
+ * The `tracestate` tag carries the freshness stamp that lets the browser
7
+ * reject trace context served from cached HTML.
6
8
  *
7
9
  * Customer usage — drop into the root layout's `<head>` (TanStack
8
- * Start uses the `head()` route option, but the meta tag also works
10
+ * Start uses the `head()` route option, but the meta tags also work
9
11
  * if rendered into the React tree):
10
12
  *
11
13
  * ```tsx
@@ -25,10 +27,9 @@
25
27
  *
26
28
  * Renders nothing when no OTel span is active (dev without OTel
27
29
  * registered, or unsampled traces). The client SDK's propagation
28
- * reader handles a missing meta tag the same as no parent — every
30
+ * reader handles missing meta tags the same as no parent — every
29
31
  * browser span starts a fresh root, which is at least internally
30
32
  * consistent.
31
33
  */
32
- declare function TraceMeta(): import("react").JSX.Element | null;
33
- //#endregion
34
- export { TraceMeta };
34
+ export declare function TraceMeta(): import("react").JSX.Element | null;
35
+ //#endregion
@@ -1 +1 @@
1
- import{activeTraceparent}from"./traceparent.mjs";import{jsx}from"react/jsx-runtime";function TraceMeta(){let traceparent=activeTraceparent();return traceparent?jsx(`meta`,{content:traceparent,name:`traceparent`}):null}export{TraceMeta};
1
+ import{traceMeta}from"./request-trace.mjs";import{Fragment,jsx}from"react/jsx-runtime";function TraceMeta(){let meta=traceMeta();return meta.length===0?null:jsx(Fragment,{children:meta.map(({name,content})=>jsx(`meta`,{content,name},name))})}export{TraceMeta};
@@ -1,6 +1,6 @@
1
1
  import { ErrorMechanism } from "@interfere/types/sdk/plugins/payload/errors";
2
2
  //#region src/internal/server/types.d.ts
3
- interface CaptureErrorContext {
3
+ export interface CaptureErrorContext {
4
4
  readonly mechanism?: ErrorMechanism;
5
5
  /**
6
6
  * Optional request method / path for the captured error. Stamped
@@ -12,5 +12,4 @@ interface CaptureErrorContext {
12
12
  path?: string;
13
13
  };
14
14
  }
15
- //#endregion
16
- export { CaptureErrorContext };
15
+ //#endregion
package/dist/package.mjs CHANGED
@@ -1 +1 @@
1
- var name=`@interfere/vite`,version=`11.2.1`;export{name,version};
1
+ var name=`@interfere/vite`,version=`12.0.0`;export{name,version};
package/dist/plugin.d.mts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { Plugin } from "vite";
2
2
  //#region src/plugin.d.ts
3
- interface InterfereViteOptions {
3
+ export interface InterfereViteOptions {
4
+ /** Writes private build evidence outside public and build output directories. */
5
+ buildReceipt?: string;
4
6
  /**
5
7
  * The endpoint the browser SDK posts telemetry to. Defaults to
6
8
  * Interfere's endpoint. Set it to a subdomain of your own site that
@@ -16,6 +18,8 @@ interface InterfereViteOptions {
16
18
  * Docker layer-cached image build).
17
19
  */
18
20
  commitSha?: string;
21
+ /** Separates source maps for distinct bundles built from the same commit. */
22
+ buildVariant?: string;
19
23
  /**
20
24
  * Directory (relative to the project root) where the SSR/server build
21
25
  * writes its `.js.map` files, when that isn't `<build.outDir>/server`.
@@ -27,11 +31,12 @@ interface InterfereViteOptions {
27
31
  * `false` skips the post-build release metadata pipeline. Defaults
28
32
  * to enabled when an `INTERFERE_API_KEY` is set; defaults to
29
33
  * disabled when one isn't (so dev runs without creds don't fail).
30
- * Useful when source maps are uploaded out-of-band via the
31
- * `@interfere/cli`.
34
+ * Useful when source maps are uploaded separately through the release API.
35
+ * `"required"` fails the build unless source-map publication succeeds.
32
36
  */
33
- sourceMaps?: boolean;
37
+ sourceMaps?: boolean | "required";
38
+ /** Wait for the exact source-map batch to be published, with a separate deadline in milliseconds. Requires sourceMaps: "required". */
39
+ sourceMapReadinessTimeoutMs?: number;
34
40
  }
35
- declare function interfere(options?: InterfereViteOptions): Plugin;
36
- //#endregion
37
- export { InterfereViteOptions, interfere };
41
+ export declare function interfere(options?: InterfereViteOptions): Plugin<any>;
42
+ //#endregion
package/dist/plugin.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import{PRODUCER_VERSION}from"./version.mjs";import{resolveBakedConfig}from"./internal/build/baked-config.mjs";import{runGitCommand}from"./internal/build/git.mjs";import{discoverByPairing}from"./internal/build/source-maps.mjs";import{createSdkClient,runReleasePipeline}from"./internal/build/pipeline.mjs";import{resolveCommitSha}from"./internal/release-slug.mjs";import{API_URL}from"@interfere/constants/api";import{deriveReleaseSlug}from"@interfere/types/releases/slug";import{classifyReleasePipelineError,diagnoseRelease,isInterfereApiKey}from"@interfere/types/sdk/build-diagnostics";import{resolveDeployEnvironment}from"@interfere/types/sdk/deploy-environment";import{existsSync,readdirSync,statSync}from"node:fs";import path from"node:path";import{loadEnv}from"vite";const SERVER_RUNTIME_PLUGIN_HINTS=[`tanstack-start`,`nitro`,`@tanstack/react-start`],DIAGNOSTIC_CONTEXT={publicKeyEnvVar:`VITE_INTERFERE_PUBLIC_KEY`};function reportReleaseDiagnostic(logger,code,input){let diagnostic=diagnoseRelease(code,DIAGNOSTIC_CONTEXT,input);logger.warn(`[interfere] ${diagnostic.title}`);for(let line of diagnostic.details)logger.warn(`[interfere] ${line}`)}function isTestEnv(){return!!(process.env.VITEST||process.env.VITEST_WORKER_ID)}function pickEnvValue(env,key){let value=env[key];return value&&value.length>0?value:void 0}function failMissingPublicKey(){let lines=[`interfere() requires VITE_INTERFERE_PUBLIC_KEY to identify your project at runtime.`,`Set the VITE_INTERFERE_PUBLIC_KEY environment variable, or remove interfere() from your Vite config.`,`See: https://interfere.com/docs/sdk/vite`];if(isTestEnv())throw Error(`Interfere: Public key not set\n${lines.join(`
1
+ import{PRODUCER_VERSION}from"./version.mjs";import{runGitCommand}from"./internal/build/git.mjs";import{createBuildReceipt}from"./internal/build/build-receipt.mjs";import{runReleasePipeline}from"./internal/build/pipeline.mjs";import{renderDebugId}from"./internal/build/render-debug-id.mjs";import{discoverByPairing}from"./internal/build/source-maps.mjs";import{resolveCommitSha}from"./internal/release-slug.mjs";import{InterfereClient}from"@interfere/javascript";import{createBuildConfigResolver}from"@interfere/build/config";import{API_URL}from"@interfere/constants/api";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{deriveReleaseSlug}from"@interfere/types/releases/slug";import{classifyReleasePipelineError,diagnoseRelease,isInterfereApiKey}from"@interfere/types/sdk/build-diagnostics";import{resolveDeployEnvironment}from"@interfere/types/sdk/deploy-environment";import{createHash}from"node:crypto";import{existsSync,readdirSync,statSync}from"node:fs";import path from"node:path";import{loadEnv}from"vite";const SERVER_RUNTIME_PLUGIN_HINTS=[`tanstack-start`,`nitro`,`@tanstack/react-start`],DIAGNOSTIC_CONTEXT={publicKeyEnvVar:`VITE_INTERFERE_PUBLIC_KEY`};function reportReleaseDiagnostic(logger,code,input){let diagnostic=diagnoseRelease(code,DIAGNOSTIC_CONTEXT,input);logger.warn(`[interfere] ${diagnostic.title}`);for(let line of diagnostic.details)logger.warn(`[interfere] ${line}`)}function isTestEnv(){return!!(process.env.VITEST||process.env.VITEST_WORKER_ID)}function pickEnvValue(env,key){let value=env[key];return value&&value.length>0?value:void 0}function failMissingPublicKey(){let lines=[`interfere() requires VITE_INTERFERE_PUBLIC_KEY to identify your project at runtime.`,`Set the VITE_INTERFERE_PUBLIC_KEY environment variable, or remove interfere() from your Vite config.`,`See: https://interfere.com/docs/sdk/vite`];if(isTestEnv())throw Error(`Interfere: Public key not set\n${lines.join(`
2
2
  `)}`);console.error(`
3
- ⨯ Interfere → Public key not set`);for(let i=0;i<lines.length;i+=1){let line=lines[i];if(!line)continue;let connector=i===lines.length-1?`└`:`├`;console.error(` ${connector} ${line}`)}process.exit(1)}function hasServerRuntimePlugin(pluginNames){return pluginNames.some(name=>SERVER_RUNTIME_PLUGIN_HINTS.some(hint=>name.includes(hint)))}function normalizeOrigin(host){try{return new URL(host).origin}catch{return null}}function resolveApiHost(options,env){let host=options.apiHost??env.VITE_INTERFERE_API_URL;if(!host)return;let origin=normalizeOrigin(host);if(!origin)throw Error(`interfere({ apiHost }) requires a full URL, e.g. apiHost: "https://in.yourapp.com"`);return origin}function resolveBrowserConfig(options,env,command){let commitSha=options.commitSha??env.VITE_INTERFERE_COMMIT_SHA??resolveCommitSha(runGitCommand);return{apiHost:resolveApiHost(options,env),commitSha,environment:command===`build`?resolveDeployEnvironment({...env,...process.env}):null,publicKey:env.VITE_INTERFERE_PUBLIC_KEY,releaseSlug:commitSha?deriveReleaseSlug(commitSha):null}}function browserDefine(browser){return{"import.meta.env.VITE_INTERFERE_API_HOST":JSON.stringify(browser.apiHost??null),"import.meta.env.VITE_INTERFERE_ENVIRONMENT":JSON.stringify(browser.environment),"import.meta.env.VITE_INTERFERE_RELEASE_SLUG":JSON.stringify(browser.releaseSlug)}}function hasFreshServerSourceMaps(root,sinceMs){if(!existsSync(root))return!1;let entries=readdirSync(root,{withFileTypes:!0});for(let entry of entries){let absolute=path.join(root,entry.name);if(entry.isDirectory()){if(hasFreshServerSourceMaps(absolute,sinceMs))return!0;continue}if(entry.name.endsWith(`.map`)&&statSync(absolute).mtimeMs>=sinceMs)return!0}return!1}function isUploadReady(state){return!!(state.isProductionBuild&&state.enableUpload&&state.apiKey&&state.releaseSlug&&state.commitSha&&!state.uploadStarted)}function shouldWaitForServerOutput(state){return state.serverBuild&&!hasFreshServerSourceMaps(state.serverSourcemapsDir,state.buildStartedAt)}function logSkippedRelease(logger,failure){if(failure.kind===`missing_source_provider`){logger.warn(`[interfere] release skipped: this surface has no version control provider configured. Configure one at https://interfere.com/~/${failure.config.org.slug}/surfaces/${failure.config.surface.slug}.`);return}logger.warn(`[interfere] release skipped: this surface has no deployment target configured. Configure one at https://interfere.com/~/${failure.config.org.slug}/surfaces/${failure.config.surface.slug}.`)}function logCleanupFailures(logger,failures){for(let failure of failures)logger.warn(`[interfere] cleanup left ${failure.absolute} on disk: ${failure.code??failure.message}`)}async function uploadSourceMaps(state){let{apiKey,apiUrl,releaseSlug,commitSha,logger,projectDir,uploadOutDir}=state;if(!isInterfereApiKey(apiKey)){reportReleaseDiagnostic(logger,`API_KEY_WRONG_PREFIX`,{apiKey});return}logger.info(`[interfere] uploading source maps for release ${releaseSlug}…`);try{let{sdk,security}=createSdkClient({apiKey,apiUrl}),[discovered,releasesConfig]=await Promise.all([discoverByPairing({projectDir,distDir:uploadOutDir,subtrees:[`.`],publicPathRewrites:[{from:`public`,to:``}]}),sdk.releases.getConfig(security)]);if(discovered.files.length===0){logger.info(`[interfere] no source maps found, skipping upload.`);return}let result=await runReleasePipeline({apiKey,apiUrl,bundler:`rollup`,buildId:commitSha,discovery:discovered,environment:state.environment,producerVersion:PRODUCER_VERSION,releasesConfig,releaseSlug});if(!result.ok){logSkippedRelease(logger,result.failure);return}logCleanupFailures(logger,result.data.cleanupFailures),logger.info(`[interfere] release ${result.data.releaseSlug} confirmed — ${result.data.fileCount} maps, ${result.data.timing.total}ms.`)}catch(error){let message=error instanceof Error?error.message:String(error);reportReleaseDiagnostic(logger,classifyReleasePipelineError(error),{errorMessage:message})}}async function uploadSourceMapsWhenReady(state){if(isUploadReady(state)){if(shouldWaitForServerOutput(state)){state.logger.info(`[interfere] waiting for Nitro server output before uploading source maps.`);return}state.uploadStarted=!0,await uploadSourceMaps(state)}}function emitSetupWarnings(state){let{logger,apiKey,releaseSlug,sourceMapsExplicit,isProductionBuild}=state;sourceMapsExplicit!==!1&&apiKey&&!releaseSlug&&logger.warn(`[interfere] No commit SHA available. Release metadata publishing is disabled. Set INTERFERE_SOURCE_ID, expose VERCEL_GIT_COMMIT_SHA / GITHUB_SHA, or run inside a git checkout.`),isProductionBuild&&sourceMapsExplicit!==!1&&!apiKey&&logger.warn("[interfere] INTERFERE_API_KEY is not set. Release metadata will not be published. Create an Interfere secret key in the dashboard and set it in your build environment, or pass `interfere({ sourceMaps: false })` to suppress this warning if release metadata is intentionally handled out-of-band.")}function resolveReleaseEnv(config){return{...loadEnv(config.mode,config.envDir,``),...process.env}}function shouldEnableUpload({apiKey,isProductionBuild,releaseSlug,sourceMaps}){return isProductionBuild?sourceMaps??!!(apiKey&&releaseSlug):!1}function buildPluginState(options,config,browser,announce){let{logger}=config,isProductionBuild=config.command===`build`;isProductionBuild&&!browser.publicKey&&failMissingPublicKey();let releaseEnv=resolveReleaseEnv(config),apiKey=pickEnvValue(releaseEnv,`INTERFERE_API_KEY`),apiUrl=pickEnvValue(releaseEnv,`INTERFERE_API_URL`)??API_URL,uploadOutDir=path.resolve(config.root,config.build.outDir),serverSourcemapsDir=options.serverSourcemapsDir?path.resolve(config.root,options.serverSourcemapsDir):path.join(uploadOutDir,`server`),serverBuild=hasServerRuntimePlugin(config.plugins.map(p=>p.name))||!!config.build?.ssr;return announce&&(logger.info(`[interfere] ingest → ${browser.apiHost??`Interfere (default)`}`),!browser.apiHost&&serverBuild&&logger.warn(`[interfere] Telemetry posts directly to Interfere. On a server-rendered app, set interfere({ apiHost }) to a subdomain of your own site to survive ad-blockers.`),emitSetupWarnings({apiKey,isProductionBuild,logger,releaseSlug:browser.releaseSlug,sourceMapsExplicit:options.sourceMaps})),{apiKey,apiUrl,commitSha:browser.commitSha,enableUpload:shouldEnableUpload({apiKey,isProductionBuild,releaseSlug:browser.releaseSlug,sourceMaps:options.sourceMaps}),environment:browser.environment,isProductionBuild,logger,serverBuild,projectDir:config.root,publicKey:browser.publicKey,releaseSlug:browser.releaseSlug,serverSourcemapsDir,uploadOutDir,uploadStarted:!1,buildStartedAt:Math.floor(Date.now()/1e3)*1e3}}function mirrorPublicKeyForDevServer(command,env){if(command!==`serve`)return;let publicKey=env.VITE_INTERFERE_PUBLIC_KEY;publicKey&&!process.env.VITE_INTERFERE_PUBLIC_KEY&&(process.env.VITE_INTERFERE_PUBLIC_KEY=publicKey)}function resolveEnvDir(userConfig){let root=userConfig.root?path.resolve(process.cwd(),userConfig.root):process.cwd();return userConfig.envDir?path.resolve(root,userConfig.envDir):root}function forceSourcemapForUpload(userConfig,options,command,env){if(!(command===`build`&&options.sourceMaps!==!1&&pickEnvValue(env,`INTERFERE_API_KEY`)))return;let previous=userConfig.build?.sourcemap;return userConfig.build={...userConfig.build,sourcemap:!0},previous===void 0||previous===!0?void 0:previous}function carryOverUploadProgress(previous,next){previous?.serverBuild&&next.serverBuild&&(next.uploadOutDir=previous.uploadOutDir,next.uploadStarted=previous.uploadStarted,next.buildStartedAt=previous.buildStartedAt)}function warnForcedSourcemap(logger,overridden){overridden!==void 0&&logger.warn(`[interfere] Forced build.sourcemap to true (was ${JSON.stringify(overridden)}). Source maps are required to symbolicate errors; they are uploaded then deleted from the build output, so they are never served. Pass interfere({ sourceMaps: false }) to opt out.`)}function interfere(options={}){let state=null,overriddenSourcemap,announced=!1;return{name:`interfere`,enforce:`pre`,async config(userConfig,{command,mode}){let env=loadEnv(mode,resolveEnvDir(userConfig),``);mirrorPublicKeyForDevServer(command,env),overriddenSourcemap=forceSourcemapForUpload(userConfig,options,command,env);let browser=resolveBrowserConfig(options,env,command),define=browserDefine(browser);if(command===`build`&&browser.publicKey&&!isTestEnv()){let baked=await resolveBakedConfig(browser.apiHost??API_URL,browser.publicKey);baked?.ingestUrl&&(define[`import.meta.env.VITE_INTERFERE_INGEST_URL`]=JSON.stringify(baked.ingestUrl)),baked?.plugins&&(define[`import.meta.env.VITE_INTERFERE_PLUGINS`]=JSON.stringify(JSON.stringify(baked.plugins)))}return{define}},configResolved(config){let next=buildPluginState(options,config,resolveBrowserConfig(options,config.env,config.command),!announced);announced=!0,carryOverUploadProgress(state,next),warnForcedSourcemap(next.logger,overriddenSourcemap),overriddenSourcemap=void 0,state=next},async closeBundle(){state&&await uploadSourceMapsWhenReady(state)}}}export{interfere};
3
+ ⨯ Interfere → Public key not set`);for(let i=0;i<lines.length;i+=1){let line=lines[i];if(!line)continue;let connector=i===lines.length-1?`└`:`├`;console.error(` ${connector} ${line}`)}process.exit(1)}function hasServerRuntimePlugin(pluginNames){return pluginNames.some(name=>SERVER_RUNTIME_PLUGIN_HINTS.some(hint=>name.includes(hint)))}function normalizeOrigin(host){try{return new URL(host).origin}catch{return null}}function resolveApiHost(options,env){let host=options.apiHost??env.VITE_INTERFERE_API_URL;if(!host)return;let origin=normalizeOrigin(host);if(!origin)throw Error(`interfere({ apiHost }) requires a full URL, e.g. apiHost: "https://in.yourapp.com"`);return origin}function resolveBrowserConfig(options,env,command){let commitSha=options.commitSha??env.VITE_INTERFERE_COMMIT_SHA??resolveCommitSha(runGitCommand);return{apiHost:resolveApiHost(options,env),commitSha,environment:command===`build`?resolveDeployEnvironment({...env,...process.env}):null,publicKey:env.VITE_INTERFERE_PUBLIC_KEY,releaseSlug:commitSha?deriveReleaseSlug(options.buildVariant?createHash(`sha256`).update(JSON.stringify([commitSha,options.buildVariant])).digest(`hex`):commitSha):null}}function browserDefine(browser){return{"import.meta.env.VITE_INTERFERE_API_HOST":JSON.stringify(browser.apiHost??null),"import.meta.env.VITE_INTERFERE_ENVIRONMENT":JSON.stringify(browser.environment),"import.meta.env.VITE_INTERFERE_RELEASE_SLUG":JSON.stringify(browser.releaseSlug)}}function hasFreshServerSourceMaps(root,sinceMs){if(!existsSync(root))return!1;let entries=readdirSync(root,{withFileTypes:!0});for(let entry of entries){let absolute=path.join(root,entry.name);if(entry.isDirectory()){if(hasFreshServerSourceMaps(absolute,sinceMs))return!0;continue}if(entry.name.endsWith(`.map`)&&statSync(absolute).mtimeMs>=sinceMs)return!0}return!1}function isUploadReady(state){return!!(state.isProductionBuild&&state.enableUpload&&state.apiKey&&state.releaseSlug&&state.commitSha&&!state.uploadStarted)}function shouldWaitForServerOutput(state){return state.serverBuild&&!hasFreshServerSourceMaps(state.serverSourcemapsDir,state.buildStartedAt)}function logSkippedRelease(logger,failure){if(failure.kind===`missing_source_provider`){logger.warn(`[interfere] release skipped: this surface has no version control provider configured. Configure one at https://interfere.com/~/${failure.config.org.slug}/surfaces/${failure.config.surface.slug}.`);return}logger.warn(`[interfere] release skipped: this surface has no deployment target configured. Configure one at https://interfere.com/~/${failure.config.org.slug}/surfaces/${failure.config.surface.slug}.`)}function logCleanupFailures(logger,failures){for(let failure of failures)logger.warn(`[interfere] cleanup left ${failure.absolute} on disk: ${failure.code??failure.message}`)}async function uploadSourceMaps(state){let{apiKey,apiUrl,releaseSlug,commitSha,logger,projectDir,uploadOutDir}=state;if(!isInterfereApiKey(apiKey)){if(reportReleaseDiagnostic(logger,`API_KEY_WRONG_PREFIX`,{apiKey}),state.requireUpload)throw Error(`Required source-map publication needs a valid INTERFERE_API_KEY`);return}logger.info(`[interfere] uploading source maps for release ${releaseSlug}…`);try{let client=new InterfereClient({baseUrl:apiUrl,apiKey:{token:apiKey},headers:{"user-agent":PRODUCER_VERSION},maxRetries:4,timeoutInSeconds:10}),[discovered,releasesConfig]=await Promise.all([discoverByPairing({distDir:uploadOutDir,projectDir,publicPathRewrites:[{from:`public`,to:``}],subtrees:[`.`]}),client.releases.getReleasesConfig({abortSignal:AbortSignal.timeout(6e4)})]);if(discovered.files.length===0){if(state.requireUpload)throw Error(`No source maps found for required publication`);logger.warn(`[interfere] No source maps found; the release remains unready. Enable source-map generation and upload them during the build.`);return}if(state.requireUpload&&discovered.files.length!==discovered.sourceFileCount)throw Error(`Required source-map publication is incomplete: loaded ${discovered.files.length} of ${discovered.sourceFileCount} referenced maps`);let result=await runReleasePipeline({client,buildId:commitSha,bundler:`rollup`,discovery:discovered,environment:state.environment,producerVersion:PRODUCER_VERSION,releaseSlug,releasesConfig,readinessTimeoutMs:state.readinessTimeoutMs,capturePublication:state.receipt!==void 0});if(!result.ok){if(logSkippedRelease(logger,result.failure),state.requireUpload)throw Error(`Required source-map publication failed: ${result.failure.kind}`);return}logCleanupFailures(logger,result.data.cleanupFailures),result.data.publication&&state.receipt?.recordPublication(result.data.publication),logger.info(`[interfere] source-map stages (ms): create=${result.data.timing.createRelease}, sign=${result.data.timing.sign}, session=${result.data.timing.session}, upload=${result.data.timing.upload}, finalize=${result.data.timing.finalize}, readiness=${result.data.timing.readiness}, receipt=${result.data.timing.receipt}, cleanup=${result.data.timing.cleanup}; bytes=${result.data.timing.totalBytes}.`),state.uploadComplete=!0,logger.info(`[interfere] release ${result.data.releaseSlug} confirmed — ${result.data.fileCount} maps, ${result.data.timing.total}ms.`)}catch(error){let message=error instanceof Error?error.message:String(error);if(reportReleaseDiagnostic(logger,classifyReleasePipelineError(error),{errorMessage:message}),state.requireUpload)throw error}}async function uploadSourceMapsWhenReady(state){return isUploadReady(state)?shouldWaitForServerOutput(state)?(state.logger.info(`[interfere] waiting for server build output in ${path.relative(state.projectDir,state.serverSourcemapsDir)} before uploading source maps.`),!1):(state.uploadStarted=!0,await uploadSourceMaps(state),state.uploadComplete):state.uploadComplete}function emitSetupWarnings(state){let{logger,apiKey,releaseSlug,sourceMapsExplicit,isProductionBuild}=state;sourceMapsExplicit!==!1&&apiKey&&!releaseSlug&&logger.warn(`[interfere] No commit SHA available. Release metadata publishing is disabled. Set INTERFERE_SOURCE_ID, expose VERCEL_GIT_COMMIT_SHA / GITHUB_SHA, or run inside a git checkout.`),isProductionBuild&&sourceMapsExplicit!==!1&&!apiKey&&logger.warn("[interfere] INTERFERE_API_KEY is not set. Release metadata will not be published. Create an Interfere secret key in the dashboard and set it in your build environment, or pass `interfere({ sourceMaps: false })` to suppress this warning if release metadata is intentionally handled out-of-band.")}function resolveReleaseEnv(config){return{...loadEnv(config.mode,config.envDir,``),...process.env}}function shouldEnableUpload({apiKey,isProductionBuild,releaseSlug,sourceMaps}){return isProductionBuild?sourceMaps===`required`||(sourceMaps??!!(apiKey&&releaseSlug)):!1}function buildPluginState(options,config,browser){let{logger}=config,isProductionBuild=config.command===`build`;isProductionBuild&&!browser.publicKey&&failMissingPublicKey();let releaseEnv=resolveReleaseEnv(config),apiKey=pickEnvValue(releaseEnv,`INTERFERE_API_KEY`),apiUrl=pickEnvValue(releaseEnv,`INTERFERE_API_URL`)??API_URL,requireUpload=isProductionBuild&&options.sourceMaps===`required`,readinessTimeoutMs=options.sourceMapReadinessTimeoutMs;if(readinessTimeoutMs!==void 0&&(options.sourceMaps!==`required`||!Number.isSafeInteger(readinessTimeoutMs)||readinessTimeoutMs<1||readinessTimeoutMs>6e5))throw Error(`sourceMapReadinessTimeoutMs requires sourceMaps: required and an integer between 1 and 600000 milliseconds`);if(requireUpload&&!apiKey)throw Error(`Required source-map publication needs INTERFERE_API_KEY`);if(requireUpload&&!browser.commitSha)throw Error(`Required source-map publication needs a commit SHA`);let uploadOutDir=path.resolve(config.root,config.build.outDir),serverSourcemapsDir=options.serverSourcemapsDir?path.resolve(config.root,options.serverSourcemapsDir):path.join(uploadOutDir,`server`),serverBuild=hasServerRuntimePlugin(config.plugins.map(p=>p.name))||!!config.build.ssr;return{apiHost:browser.apiHost,readinessTimeoutMs,apiKey,apiUrl,buildStartedAt:Math.floor(Date.now()/1e3)*1e3,commitSha:browser.commitSha,enableUpload:shouldEnableUpload({apiKey,isProductionBuild,releaseSlug:browser.releaseSlug,sourceMaps:options.sourceMaps}),environment:browser.environment,isProductionBuild,logger,projectDir:config.root,publicKey:browser.publicKey,requireUpload,releaseSlug:browser.releaseSlug,serverBuild,serverSourcemapsDir,uploadOutDir,uploadStarted:!1,uploadComplete:!1}}function announceSetup(state,options){state.logger.info(`[interfere] ingest → ${state.apiHost??`Interfere (default)`}`),!state.apiHost&&state.serverBuild&&state.logger.warn(`[interfere] Telemetry posts directly to Interfere. On a server-rendered app, set interfere({ apiHost }) to a subdomain of your own site to survive ad-blockers.`),emitSetupWarnings({apiKey:state.apiKey,isProductionBuild:state.isProductionBuild,logger:state.logger,releaseSlug:state.releaseSlug,sourceMapsExplicit:options.sourceMaps})}function mirrorPublicKeyForDevServer(command,env){if(command!==`serve`)return;let publicKey=env.VITE_INTERFERE_PUBLIC_KEY;publicKey&&!process.env.VITE_INTERFERE_PUBLIC_KEY&&(process.env.VITE_INTERFERE_PUBLIC_KEY=publicKey)}function resolveEnvDir(userConfig){let root=userConfig.root?path.resolve(process.cwd(),userConfig.root):process.cwd();return userConfig.envDir===!1||userConfig.envDir===void 0||userConfig.envDir===``?root:path.resolve(root,userConfig.envDir)}function forceSourcemapForUpload(userConfig,options,command,env){if(!(command===`build`&&options.sourceMaps!==!1&&pickEnvValue(env,`INTERFERE_API_KEY`)))return;let previous=userConfig.build?.sourcemap;return userConfig.build={...userConfig.build,sourcemap:!0},previous===void 0||previous===!0?void 0:previous}function carryOverUploadProgress(previous,next){previous?.serverBuild&&next.serverBuild&&(next.uploadOutDir=previous.uploadOutDir,next.serverSourcemapsDir=previous.serverSourcemapsDir,next.uploadStarted=previous.uploadStarted,next.uploadComplete=previous.uploadComplete,next.buildStartedAt=previous.buildStartedAt)}function warnForcedSourcemap(logger,overridden){overridden!==void 0&&logger.warn(`[interfere] Forced build.sourcemap to true (was ${JSON.stringify(overridden)}). Source maps are required to symbolicate errors; they are uploaded then deleted from the build output, so they are never served. Pass interfere({ sourceMaps: false }) to opt out.`)}function interfere(options={}){let receipt=options.buildReceipt?createBuildReceipt(options.buildReceipt):void 0,resolveBuildConfig=createBuildConfigResolver(receipt?.observeConfig),state=null,overriddenSourcemap,announced=!1,writeReceipt=async()=>{try{await receipt?.write()}catch{state?.logger.warn(`[interfere] Private build receipt could not be written.`)}};return{buildApp:{order:`post`,async handler(builder){if(!state?.requireUpload)return;let environments=Object.values(builder.environments);if(environments.every(environment=>!environment.isBuilt))for(let environment of environments)await builder.build(environment);if(!await uploadSourceMapsWhenReady(state))throw Error(`Required publication is waiting for fresh server source maps`);await writeReceipt()}},buildStart(){state&&!announced&&(announceSetup(state,options),warnForcedSourcemap(state.logger,overriddenSourcemap),announced=!0)},async closeBundle(){state&&await uploadSourceMapsWhenReady(state),await writeReceipt()},async writeBundle(output,bundle){output.dir&&await receipt?.recordOutput(output.dir,Object.keys(bundle))},outputOptions(output){return state?.enableUpload?{...output,sourcemapDebugIds:!1}:null},renderChunk:{order:`post`,handler(code){return state?.enableUpload?renderDebugId(code):null}},async config(userConfig,{command,mode}){let env=loadEnv(mode,resolveEnvDir(userConfig),``);mirrorPublicKeyForDevServer(command,env),overriddenSourcemap=forceSourcemapForUpload(userConfig,options,command,env);let browser=resolveBrowserConfig(options,env,command),baked=command===`build`&&browser.publicKey&&!isTestEnv()?await resolveBuildConfig(browser.apiHost??API_URL,browser.publicKey):null;return{define:omitUndefined({...browserDefine(browser),"import.meta.env.VITE_INTERFERE_INGEST_URL":baked?.ingestUrl?JSON.stringify(baked.ingestUrl):void 0,"import.meta.env.VITE_INTERFERE_SERVER_TRACING":baked?.serverTracing?JSON.stringify(baked.serverTracing):void 0,"import.meta.env.VITE_INTERFERE_LOG_SOURCING":baked?.logSourcing?JSON.stringify(baked.logSourcing):void 0,"import.meta.env.VITE_INTERFERE_PLUGINS":baked?.plugins?JSON.stringify(JSON.stringify(baked.plugins)):void 0}),ssr:{noExternal:[`@interfere/vite`]}}},async configResolved(config){let browser=resolveBrowserConfig(options,config.env,config.command),next=buildPluginState(options,config,browser);carryOverUploadProgress(state,next),state={...next,receipt},config.command===`build`&&await receipt?.configure(config,{commitSha:browser.commitSha,releaseSlug:browser.releaseSlug,environment:browser.environment,publicKey:browser.publicKey,apiOrigin:browser.apiHost??API_URL,producerVersion:PRODUCER_VERSION})},enforce:`pre`,name:`interfere`,sharedDuringBuild:!0}}export{interfere};
@@ -1,5 +1,5 @@
1
1
  import { PluginOverrides, useInterfere, useSession } from "@interfere/react/provider";
2
- import { PropsWithChildren, ReactNode } from "react";
2
+ import { PropsWithChildren } from "react";
3
3
  import { ConsentCategory, ConsentState, ConsentState as ConsentState$1, GateableCategory } from "@interfere/types/sdk/plugins/manifest";
4
4
  //#region src/provider.d.ts
5
5
  interface InterfereProviderProps extends PropsWithChildren {
@@ -11,6 +11,6 @@ interface InterfereProviderProps extends PropsWithChildren {
11
11
  * Vite wrapper around `@interfere/react`'s provider that reads the
12
12
  * kernel created by `@interfere/vite/init`.
13
13
  */
14
- declare function InterfereProvider({ children, consent, plugins, errorBoundary }: InterfereProviderProps): ReactNode;
14
+ export declare function InterfereProvider({ children, consent, plugins, errorBoundary }: InterfereProviderProps): import("react").JSX.Element;
15
15
  //#endregion
16
- export { type ConsentCategory, type ConsentState, type GateableCategory, InterfereProvider, useInterfere, useSession };
16
+ export { type ConsentCategory, type ConsentState, type GateableCategory, useInterfere, useSession };
package/dist/provider.mjs CHANGED
@@ -1 +1 @@
1
- import{getKernelOrNull,subscribeToKernel}from"./init.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{InterfereProvider as InterfereProvider$1,useInterfere,useSession}from"@interfere/react/provider";import{useSyncExternalStore}from"react";import{jsx}from"react/jsx-runtime";const nullSnapshot=()=>null;function InterfereProvider({children,consent,plugins,errorBoundary}){let kernel=useSyncExternalStore(subscribeToKernel,getKernelOrNull,nullSnapshot);return jsx(InterfereProvider$1,{kernel,...omitUndefined({consent,plugins,errorBoundary}),children})}export{InterfereProvider,useInterfere,useSession};
1
+ import{getKernelOrNull,subscribeToKernel}from"./init.mjs";import{omitUndefined}from"@interfere/helpers/omit-undefined";import{InterfereProvider as InterfereProvider$1,useInterfere,useSession}from"@interfere/react/provider";import{useSyncExternalStore}from"react";import{jsx}from"react/jsx-runtime";const nullSnapshot=()=>null;function InterfereProvider({children,consent,plugins,errorBoundary}){let kernel=useSyncExternalStore(subscribeToKernel,getKernelOrNull,nullSnapshot);return jsx(InterfereProvider$1,{kernel,...omitUndefined({consent,errorBoundary,plugins}),children})}export{InterfereProvider,useInterfere,useSession};
@@ -1,5 +1,5 @@
1
1
  import { TraceMetaDescriptor, traceMeta } from "./internal/server/request-trace.mjs";
2
2
  //#region src/tanstack-start.d.ts
3
- declare const interfereRequestMiddleware: import("@tanstack/react-start").RequestMiddlewareAfterServer<{}, undefined, undefined>;
3
+ export declare const interfereRequestMiddleware: import("@tanstack/react-start").RequestMiddlewareAfterServer<{}, undefined, undefined>;
4
4
  //#endregion
5
- export { type TraceMetaDescriptor, interfereRequestMiddleware, traceMeta };
5
+ export { type TraceMetaDescriptor, traceMeta };
@@ -1 +1 @@
1
- import{traceMeta,withRequestSpan}from"./internal/server/request-trace.mjs";import{createMiddleware}from"@tanstack/react-start";const interfereRequestMiddleware=createMiddleware().server(({next,request})=>withRequestSpan(request,()=>Promise.resolve(next())));export{interfereRequestMiddleware,traceMeta};
1
+ import{traceMeta,withRequestSpan}from"./internal/server/request-trace.mjs";import{createMiddleware}from"@tanstack/react-start";const interfereRequestMiddleware=createMiddleware().server(async({next,request})=>withRequestSpan(request,async()=>next()));export{interfereRequestMiddleware,traceMeta};
@@ -1,4 +1,3 @@
1
1
  //#region src/version.d.ts
2
- declare const PRODUCER_VERSION: string;
3
- //#endregion
4
- export { PRODUCER_VERSION };
2
+ export declare const PRODUCER_VERSION: string;
3
+ //#endregion
package/package.json CHANGED
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "name": "@interfere/vite",
3
- "version": "11.2.1",
4
- "license": "MIT",
3
+ "version": "12.0.0",
5
4
  "description": "Vite build plugin and client + server initialization for Interfere.",
6
5
  "keywords": [
6
+ "error-tracking",
7
+ "nitro",
7
8
  "observability",
8
- "typescript",
9
- "vite",
9
+ "session-replay",
10
10
  "tanstack-start",
11
- "nitro",
12
- "error-tracking",
13
- "session-replay"
11
+ "typescript",
12
+ "vite"
14
13
  ],
15
14
  "homepage": "https://interfere.com",
16
15
  "bugs": {
17
16
  "url": "mailto:support@interfere.com"
18
17
  },
18
+ "license": "MIT",
19
19
  "author": "Interfere <support@interfere.com> (https://interfere.com)",
20
20
  "repository": {
21
21
  "type": "git",
@@ -62,32 +62,49 @@
62
62
  "typecheck": "tsc --noEmit"
63
63
  },
64
64
  "dependencies": {
65
- "@interfere/constants": "^9.0.6",
66
- "@interfere/helpers": "^9.1.2",
67
- "@interfere/react": "^11.2.1",
68
- "@interfere/sdk": "^11.2.1",
69
- "@interfere/types": "^10.1.2",
65
+ "@interfere/build": "^0.2.0",
66
+ "@interfere/constants": "^9.1.0",
67
+ "@interfere/helpers": "^9.2.0",
68
+ "@interfere/javascript": "^0.2.0",
69
+ "@interfere/react": "^12.0.0",
70
+ "@interfere/types": "^10.2.0",
70
71
  "@opentelemetry/api": "^1.9.1",
71
- "@opentelemetry/api-logs": "^0.221.0",
72
+ "@opentelemetry/api-logs": "^0.222.0",
72
73
  "@opentelemetry/baggage-span-processor": "^0.5.0",
73
- "@opentelemetry/context-async-hooks": "^2.7.1",
74
- "@opentelemetry/core": "^2.7.0",
75
- "@opentelemetry/exporter-logs-otlp-http": "^0.221.0",
76
- "@opentelemetry/exporter-metrics-otlp-http": "^0.221.0",
77
- "@opentelemetry/exporter-trace-otlp-http": "^0.221.0",
78
- "@opentelemetry/instrumentation": "^0.221.0",
79
- "@opentelemetry/instrumentation-undici": "^0.31.0",
80
- "@opentelemetry/resources": "^2.7.0",
81
- "@opentelemetry/sdk-logs": "^0.221.0",
82
- "@opentelemetry/sdk-metrics": "^2.7.0",
83
- "@opentelemetry/sdk-trace-base": "^2.7.0",
84
- "@opentelemetry/sdk-trace-node": "^2.7.1"
74
+ "@opentelemetry/context-async-hooks": "^2.11.0",
75
+ "@opentelemetry/core": "^2.11.0",
76
+ "@opentelemetry/exporter-logs-otlp-http": "^0.222.0",
77
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.222.0",
78
+ "@opentelemetry/exporter-trace-otlp-http": "^0.222.0",
79
+ "@opentelemetry/instrumentation": "^0.222.0",
80
+ "@opentelemetry/instrumentation-undici": "^0.32.0",
81
+ "@opentelemetry/resources": "^2.11.0",
82
+ "@opentelemetry/sdk-logs": "^0.222.0",
83
+ "@opentelemetry/sdk-metrics": "^2.11.0",
84
+ "@opentelemetry/sdk-trace-base": "^2.11.0",
85
+ "@opentelemetry/sdk-trace-node": "^2.11.0"
86
+ },
87
+ "devDependencies": {
88
+ "@interfere/typescript-config": "^9.0.2",
89
+ "@interfere/vitest-config": "0.0.0",
90
+ "@tanstack/react-start": "1.168.49",
91
+ "@types/node": "^26.4.1",
92
+ "@types/react": "19.2.18",
93
+ "@types/react-dom": "19.2.7",
94
+ "@vitest/coverage-v8": "5.0.1",
95
+ "msw": "^2.14.6",
96
+ "react": "19.3.0",
97
+ "react-dom": "19.3.0",
98
+ "tsdown": "^0.23.0",
99
+ "typescript": "^7.0.2",
100
+ "vite": "^8.2.2",
101
+ "vitest": "5.0.1"
85
102
  },
86
103
  "peerDependencies": {
87
- "@tanstack/react-start": "1.168.42",
88
- "react": "^19.2.6",
104
+ "@tanstack/react-start": "1.168.49",
105
+ "react": "^19.0.0",
89
106
  "react-dom": "^19.0.0",
90
- "vite": "^8.2.1"
107
+ "vite": "^8.2.2"
91
108
  },
92
109
  "peerDependenciesMeta": {
93
110
  "@tanstack/react-start": {
@@ -102,20 +119,5 @@
102
119
  "vite": {
103
120
  "optional": true
104
121
  }
105
- },
106
- "devDependencies": {
107
- "@interfere/test-utils": "^9.1.2",
108
- "@interfere/typescript-config": "^9.0.2",
109
- "@tanstack/react-start": "1.168.42",
110
- "@types/node": "^26.2.0",
111
- "@types/react": "19.2.18",
112
- "@types/react-dom": "19.2.4",
113
- "@vitest/coverage-v8": "4.1.10",
114
- "react": "^19.2.6",
115
- "react-dom": "19.2.8",
116
- "tsdown": "^0.22.2",
117
- "typescript": "^7.0.2",
118
- "vite": "^8.2.1",
119
- "vitest": "4.1.10"
120
122
  }
121
123
  }