@interfere/react 11.0.0-canary.6 → 11.0.0-canary.7
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/dist/internal/config.d.mts +1 -1
- package/dist/internal/config.mjs +1 -1
- package/dist/internal/kernel.d.mts +20 -14
- package/dist/internal/kernel.mjs +1 -1
- package/dist/internal/otel/exporter.d.mts +15 -55
- package/dist/internal/otel/exporter.mjs +1 -1
- package/dist/internal/otel/index.d.mts +1 -2
- package/dist/internal/otel/index.mjs +1 -1
- package/dist/internal/otel/instrumentations.d.mts +5 -0
- package/dist/internal/otel/instrumentations.mjs +1 -1
- package/dist/internal/otel/provider.mjs +1 -1
- package/dist/package.mjs +1 -1
- package/dist/plugins/replay/delivery.mjs +1 -1
- package/dist/tracking/api.mjs +1 -1
- package/package.json +9 -15
- package/dist/internal/sw.d.mts +0 -24
- package/dist/internal/sw.mjs +0 -1
- package/dist/sw.d.mts +0 -2
- package/dist/sw.mjs +0 -2
|
@@ -8,7 +8,7 @@ interface AuthHeaders {
|
|
|
8
8
|
}
|
|
9
9
|
declare function withQueryParam(url: string, key: string, value: string): string;
|
|
10
10
|
declare function appendPathBeforeQuery(url: string, path: string): string;
|
|
11
|
-
declare function resolveTargets(): {
|
|
11
|
+
declare function resolveTargets(apiHost?: string): {
|
|
12
12
|
/**
|
|
13
13
|
* Base URL for OTLP exports — the trace/metric exporters append the
|
|
14
14
|
* sink path themselves. Avoids the kernel having to regex-strip
|
package/dist/internal/config.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getGlobal}from"../util/global.mjs";import{API_PATHS,API_URL}from"@interfere/constants/api";
|
|
1
|
+
import{getGlobal}from"../util/global.mjs";import{API_PATHS,API_URL}from"@interfere/constants/api";const TRAILING_SLASH_RE=/\/$/,RELATIVE_URL_BASE=`https://interfere.local`;function resolvePublicKey(){let injected=getGlobal(`__INTERFERE_PUBLIC_KEY__`);if(injected)return injected;if(typeof process<`u`)return process.env.INTERFERE_PUBLIC_KEY??void 0}function isForceEnableActive(){return!!getGlobal(`__INTERFERE_FORCE_ENABLE__`)}function resolveApiHost(explicit){return explicit??getGlobal(`__INTERFERE_API_URL__`)}function normalizeOrigin(host){try{return new URL(host).origin}catch{return API_URL}}function serializeUrl(input,url){return input.startsWith(`/`)?`${url.pathname}${url.search}`:url.toString()}function withQueryParam(url,key,value){let next=new URL(url,RELATIVE_URL_BASE);return next.searchParams.set(key,value),serializeUrl(url,next)}function appendPublicKey(url,publicKey){return publicKey?withQueryParam(url,`pk`,publicKey):url}function appendPathBeforeQuery(url,path){let next=new URL(url,RELATIVE_URL_BASE),suffix=path.startsWith(`/`)?path:`/${path}`;return next.pathname=`${next.pathname.replace(TRAILING_SLASH_RE,``)}${suffix}`,serializeUrl(url,next)}function resolveTargets(apiHost){let publicKey=resolvePublicKey(),headers=new Headers({"content-type":`application/json`});isForceEnableActive()&&headers.set(`x-interfere-force-enable`,`1`);let host=resolveApiHost(apiHost),baseUrl=host?normalizeOrigin(host):API_URL;return{collectorBaseUrl:appendPublicKey(baseUrl,publicKey),config:{url:appendPublicKey(`${baseUrl}${API_PATHS.CONFIG}`,publicKey),headers},ingest:{headers},session:{url:appendPublicKey(`${baseUrl}${API_PATHS.SESSION}`,publicKey),headers}}}export{appendPathBeforeQuery,resolveTargets,withQueryParam};
|
|
@@ -19,6 +19,13 @@ import { ErrorMechanism } from "@interfere/types/sdk/plugins/payload/errors";
|
|
|
19
19
|
//#region src/internal/kernel.d.ts
|
|
20
20
|
declare function buildSdkStack(wrapperVersions?: string[]): string[];
|
|
21
21
|
interface KernelOptions {
|
|
22
|
+
/**
|
|
23
|
+
* The endpoint the SDK posts telemetry to. Defaults to Interfere's
|
|
24
|
+
* endpoint. Set it to a subdomain of your own site that resolves to
|
|
25
|
+
* Interfere via a DNS record (e.g. `"https://in.example.com"`) to
|
|
26
|
+
* survive ad-blockers, or to a regional endpoint. Must be a full URL.
|
|
27
|
+
*/
|
|
28
|
+
apiHost?: string;
|
|
22
29
|
consent?: ConsentState;
|
|
23
30
|
/**
|
|
24
31
|
* Override the automatic dev-mode guard. When `undefined`, the SDK
|
|
@@ -39,16 +46,6 @@ interface KernelOptions {
|
|
|
39
46
|
* cleanly by surface.
|
|
40
47
|
*/
|
|
41
48
|
serviceName?: string;
|
|
42
|
-
/**
|
|
43
|
-
* `false` skips registering the SDK's standalone ingest service worker.
|
|
44
|
-
* Set this when the host already runs a service worker at scope `/`
|
|
45
|
-
* (e.g. a Workbox precache worker) and wires {@link installIngestQueue}
|
|
46
|
-
* into it instead — two workers can't both control `/`, and a worker
|
|
47
|
-
* scoped to `/api/interfere/` never sees page requests.
|
|
48
|
-
*
|
|
49
|
-
* @default true
|
|
50
|
-
*/
|
|
51
|
-
serviceWorker?: boolean;
|
|
52
49
|
/**
|
|
53
50
|
* `false` skips the OTel module entirely — the bundler code-splits
|
|
54
51
|
* `internal/otel/*` out of this path so the error-only bundle never
|
|
@@ -65,6 +62,14 @@ interface KernelOptions {
|
|
|
65
62
|
* need to touch any of these — sensible defaults ship out of the box.
|
|
66
63
|
*/
|
|
67
64
|
interface TracingOptions {
|
|
65
|
+
/**
|
|
66
|
+
* Hostnames where your backend runs, e.g. `["api.example.com"]`. Every
|
|
67
|
+
* request to a listed host carries the W3C `traceparent` header so the
|
|
68
|
+
* browser trace continues on the server. Same-origin requests always
|
|
69
|
+
* propagate — you only need this when your API is on a different host
|
|
70
|
+
* than your app. Matched by hostname (any path, port, or scheme); no regex.
|
|
71
|
+
*/
|
|
72
|
+
apiHosts?: string[];
|
|
68
73
|
/**
|
|
69
74
|
* Promote failed `fetch` / `XHR` responses to exception events on the
|
|
70
75
|
* active span. **On by default** — captures same-origin (and
|
|
@@ -82,10 +87,11 @@ interface TracingOptions {
|
|
|
82
87
|
*/
|
|
83
88
|
ignoreUrls?: (string | RegExp)[];
|
|
84
89
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
90
|
+
* @deprecated Prefer {@link apiHosts} — it takes plain hostnames and
|
|
91
|
+
* needs no regex. Still fully supported: raw URL patterns injected with
|
|
92
|
+
* `traceparent` + `baggage` on outgoing fetch / XHR. Note string entries
|
|
93
|
+
* match the full request URL by exact equality (an OpenTelemetry quirk),
|
|
94
|
+
* so cross-origin values usually have to be a `RegExp`.
|
|
89
95
|
*/
|
|
90
96
|
propagateContextUrls?: (string | RegExp)[];
|
|
91
97
|
/**
|
package/dist/internal/kernel.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"../util/log.mjs";import{onPageHidden}from"./page-lifecycle.mjs";import{getGlobal}from"../util/global.mjs";import{resolveTargets}from"./config.mjs";import{SessionTracker}from"../tracking/api.mjs";import{safeStringify}from"../util/stringify.mjs";import{recordInitialNavigation}from"./otel/initial-navigation.mjs";import{PluginRuntime}from"./plugin-runtime.mjs";import{
|
|
1
|
+
import{createLogger}from"../util/log.mjs";import{onPageHidden}from"./page-lifecycle.mjs";import{getGlobal}from"../util/global.mjs";import{resolveTargets}from"./config.mjs";import{SessionTracker}from"../tracking/api.mjs";import{safeStringify}from"../util/stringify.mjs";import{recordInitialNavigation}from"./otel/initial-navigation.mjs";import{PluginRuntime}from"./plugin-runtime.mjs";import{PRODUCER_VERSION}from"./version.mjs";import{isNonErrorException,shouldDropBrowserExtensionNoise,shouldDropUnresolvableStack,toExceptions}from"@interfere/types/sdk/errors";import{trace}from"@opentelemetry/api";import{releaseSlugSchema}from"@interfere/types/releases/slug";const log=createLogger(`kernel`),TRAILING_SLASH_RE=/\/$/,REPLAY_UPLOAD_RE=/\/v2\/replay\/upload\//;function buildSdkStack(wrapperVersions){return[...wrapperVersions??[],PRODUCER_VERSION]}function buildExceptionEventAttrs(value,opts){let attrs={"interfere.exception.mechanism":opts.mechanism.type,"interfere.exception.handled":opts.mechanism.handled};if(opts.errorDigest&&(attrs[`interfere.error.digest`]=opts.errorDigest),opts.subtype&&(attrs[`interfere.exception.subtype`]=opts.subtype),isNonErrorException(value))return attrs[`exception.type`]=value.type,attrs[`exception.message`]=value.value,attrs[`interfere.exception.kind`]=`non-error`,attrs[`interfere.exception.serialized`]=safeStringify(value.serialized),attrs;attrs[`exception.type`]=value.name,attrs[`exception.message`]=value.message,attrs[`interfere.exception.kind`]=`error`,value.stack&&(attrs[`exception.stacktrace`]=value.stack),attrs[`interfere.exception.chain`]=safeStringify(toExceptions(value,opts.mechanism));let debugIds=globalThis._debugIds;return debugIds&&typeof debugIds==`object`&&Object.keys(debugIds).length>0&&(attrs[`interfere.exception.debug_ids`]=safeStringify(debugIds)),opts.appendFrames&&opts.appendFrames.length>0&&(attrs[`interfere.exception.appended_frames`]=safeStringify(opts.appendFrames)),opts.fallbackFrames&&opts.fallbackFrames.length>0&&(attrs[`interfere.exception.fallback_frames`]=safeStringify(opts.fallbackFrames)),attrs}var Kernel=class{consent;identity;device;session;tracker;seen=new WeakSet;initialNavigationSeen=new Set;runtime=null;otel=null;otelDispose=null;unsubscribePageHidden=null;constructor(tracker){this.tracker=tracker,this.consent={get:()=>this.requireRuntime().getConsent(),set:value=>{let runtime=this.requireRuntime();value?runtime.setConsent(value):runtime.resetConsent()}},this.identity={clear:()=>this.tracker.reset(),get:()=>this.tracker.getIdentity(),set:params=>this.tracker.identify(params)},this.device={getDeviceId:()=>this.tracker.getDeviceId(),getFpHash:()=>this.tracker.getFpHash()},this.session={getId:()=>this.tracker.sessionId(),getWindowId:()=>this.tracker.windowId()},this.unsubscribePageHidden=onPageHidden(()=>{this.flush().catch(()=>void 0)})}attachRuntime(runtime){this.runtime=runtime}requireRuntime(){if(!this.runtime)throw Error(`Kernel runtime not attached. createKernel must call attachRuntime before any consent/dispose path runs.`);return this.runtime}getSessionId(){return this.tracker.sessionId()??``}onSessionPause(handler){return this.tracker.onSessionPause(handler)}onSessionResume(handler){return this.tracker.onSessionResume(handler)}getPluginConfigState(key){return this.requireRuntime().getPluginConfigState(key)}subscribePluginConfigState(key,listener){return this.requireRuntime().subscribePluginConfigState(key,listener)}recordException(value,opts){if(!isNonErrorException(value)){if(this.seen.has(value))return;this.seen.add(value)}if(!isNonErrorException(value)){let stacks=[],current=value;for(let depth=0;current&&depth<5;depth+=1)current.stack&&stacks.push(current.stack),current=current.cause instanceof Error?current.cause:void 0;if(shouldDropBrowserExtensionNoise(stacks)||shouldDropUnresolvableStack(stacks))return}let attrs=buildExceptionEventAttrs(value,opts),active=trace.getActiveSpan();if(active?.isRecording()){active.addEvent(`exception`,attrs);return}let span=this.otel?.tracerProvider.getTracer(`@interfere/react/exception`).startSpan(`interfere.exception.capture`);span&&(span.addEvent(`exception`,attrs),span.end())}recordLog(input){this.otel&&this.otel.loggerProvider.getLogger(`@interfere/react`).emit({severityText:input.severityText,severityNumber:input.severityNumber,body:input.body,...input.attributes?{attributes:input.attributes}:{}})}recordInitialNavigation(loggerProvider){recordInitialNavigation({getSessionId:()=>this.session.getId(),getWindowId:()=>this.session.getWindowId(),loggerProvider,seen:this.initialNavigationSeen})}async flush(){this.otel&&await this.otel.flush()}attachOtel(handle,dispose){this.otel=handle,this.otelDispose=dispose}syncConsent(value){let runtime=this.requireRuntime();if(value){runtime.setConsent(value);return}runtime.resetConsent()}async dispose(){this.unsubscribePageHidden?.(),this.unsubscribePageHidden=null,this.runtime&&await this.runtime.dispose(),this.tracker.dispose(),this.otelDispose?.(),this.otelDispose=null,this.otel&&=(await this.otel.shutdown(),null)}};async function createKernel(input={}){let{opts={}}=input,fetcher=opts.fetch??globalThis.fetch.bind(globalThis),targets=resolveTargets(opts.apiHost);log.info(`collector: %s`,targets.collectorBaseUrl),getGlobal(`__INTERFERE_FORCE_ENABLE__`)&&log.warn(`FORCE_ENABLE is active. Events will be dropped by production collectors. This should only be set during local development.`);let tracker=new SessionTracker({target:targets.session,fetcher});opts._wrapperVersions?.length&&(globalThis.__INTERFERE_SDK_STACK__=opts._wrapperVersions);let kernel=new Kernel(tracker),runtime=new PluginRuntime(kernel,opts.plugins,opts.consent);if(kernel.attachRuntime(runtime),tracker.start(),runtime.start(),opts.tracing!==!1){let tracingOpts=typeof opts.tracing==`object`?opts.tracing:void 0;await wireOtel(kernel,targets,opts._wrapperVersions,{...tracingOpts??{},...opts.serviceName?{serviceName:opts.serviceName}:{},...opts._internalAdditionalSpanProcessors?{additionalSpanProcessors:opts._internalAdditionalSpanProcessors}:{},...opts._internalAdditionalLogRecordProcessors?{additionalLogRecordProcessors:opts._internalAdditionalLogRecordProcessors}:{},...opts._internalAdditionalMetricReaders?{additionalMetricReaders:opts._internalAdditionalMetricReaders}:{}})}return await fetchRemoteConfig(targets.config,fetcher,runtime),kernel}async function wireOtel(kernel,targets,wrapperVersions,tracing){let{buildOtelProvider,captureWebVitals,readPropagationFromDocument,registerBundledInstrumentations}=await import(`./otel/index.mjs`),collectorUrl=targets.collectorBaseUrl.replace(TRAILING_SLASH_RE,``),releaseSlug=readReleaseSlugFromGlobals(),handle=buildOtelProvider({collectorUrl,authHeaders:targets.ingest.headers,sdkStack:buildSdkStack(wrapperVersions),getSessionId:()=>kernel.session.getId(),deviceId:kernel.device.getDeviceId(),releaseSlug,...tracing.serviceName?{serviceName:tracing.serviceName}:{},...tracing.additionalSpanProcessors?{additionalSpanProcessors:tracing.additionalSpanProcessors}:{},...tracing.additionalLogRecordProcessors?{additionalLogRecordProcessors:tracing.additionalLogRecordProcessors}:{},...tracing.additionalMetricReaders?{additionalMetricReaders:tracing.additionalMetricReaders}:{}}),extracted=readPropagationFromDocument();extracted&&handle.contextManager.setPageScope(extracted);let dispose=registerBundledInstrumentations({tracerProvider:handle.tracerProvider,loggerProvider:handle.loggerProvider,ignoreUrls:[`${collectorUrl}/v2/sink`,targets.config.url,targets.session.url,REPLAY_UPLOAD_RE],...tracing.apiHosts?{apiHosts:tracing.apiHosts}:{},...tracing.propagateContextUrls?{propagateContextUrls:tracing.propagateContextUrls}:{},...tracing.resolveRoute?{resolveRoute:tracing.resolveRoute}:{},...tracing.captureFailedRequests===void 0?{}:{captureFailedRequests:tracing.captureFailedRequests}});tracing.webVitals!==!1&&captureWebVitals({meter:handle.meterProvider.getMeter(`@interfere/react/web-vitals`),flush:()=>handle.flush(),...tracing.resolveRoute?{resolveRoute:tracing.resolveRoute}:{}}),kernel.attachOtel(handle,dispose),kernel.recordInitialNavigation(handle.loggerProvider)}async function fetchRemoteConfig(configTarget,fetcher,runtime){try{let headers=Object.fromEntries(configTarget.headers.entries()),res=await fetcher(configTarget.url,{method:`GET`,headers,credentials:`omit`,signal:AbortSignal.timeout(1e4)});if(!res.ok){runtime.applyRemoteConfigDefaults();return}let config=await res.json();if(config?.plugins){runtime.applyRemoteConfig(config.plugins),log.debug(`applied remote config`);return}runtime.applyRemoteConfigDefaults()}catch{runtime.applyRemoteConfigDefaults(),log.warn(`remote config fetch failed, using local defaults`)}}function isEnabledByEnvironment(){if(typeof process>`u`||!process.env)return!0;let nodeEnv=process.env.NODE_ENV;return nodeEnv===`production`||nodeEnv===void 0?!0:!!(getGlobal(`__INTERFERE_FORCE_ENABLE__`)||process.env.INTERFERE_FORCE_ENABLE)}function readReleaseSlugFromGlobals(){let value=getGlobal(`__INTERFERE_RELEASE_SLUG__`);return typeof value==`string`?releaseSlugSchema.parse(value):null}export{Kernel,buildSdkStack,createKernel,isEnabledByEnvironment,readReleaseSlugFromGlobals};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { ExportResult } from "@opentelemetry/core";
|
|
2
2
|
import { AggregationTemporality, PushMetricExporter, ResourceMetrics } from "@opentelemetry/sdk-metrics";
|
|
3
3
|
import { ReadableLogRecord } from "@opentelemetry/sdk-logs";
|
|
4
|
-
import {
|
|
4
|
+
import { SpanExporter } from "@opentelemetry/sdk-trace-base";
|
|
5
5
|
|
|
6
6
|
//#region src/internal/otel/exporter.d.ts
|
|
7
|
+
type SignalSender = (payload: Uint8Array | undefined, resultCallback: (result: ExportResult) => void) => void;
|
|
7
8
|
interface ExporterCommon {
|
|
8
9
|
/**
|
|
9
10
|
* Kept for wrapper compatibility. Public-key identity is baked into
|
|
@@ -16,70 +17,29 @@ interface ExporterCommon {
|
|
|
16
17
|
/** Exported for direct unit testing. */
|
|
17
18
|
declare function buildSinkUrl(collectorUrl: string): string;
|
|
18
19
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
20
|
+
* Ingest URL carrying the producer-version query while preserving the
|
|
21
|
+
* `pk` query already baked into `collectorUrl`.
|
|
21
22
|
*
|
|
22
23
|
* Exported for direct unit testing.
|
|
23
24
|
*/
|
|
24
|
-
declare function
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* Dispatches every export via `navigator.sendBeacon` — which is the
|
|
29
|
-
* only browser transport that reliably commits a request the page is
|
|
30
|
-
* also tearing down (`visibilitychange→hidden`, hard navigation). The
|
|
31
|
-
* `keepalive: true` fetch path the OTLP HTTP exporter ships with
|
|
32
|
-
* works for small payloads but falls back to ordinary fetch (which
|
|
33
|
-
* the renderer aborts on unload) once the cumulative 64KiB / 9-
|
|
34
|
-
* concurrent budget is exhausted. Production data on 2026-05-11
|
|
35
|
-
* attributed ~15% browser-fetch span loss to that fallback; the
|
|
36
|
-
* beacon path closes that hole by design.
|
|
37
|
-
*
|
|
38
|
-
* Identity rides the URL because beacons can't carry custom headers.
|
|
39
|
-
*
|
|
40
|
-
* No retry on failure. The service worker backstop captures 5xx /
|
|
41
|
-
* network failures separately by intercepting the same beacon POST
|
|
42
|
-
* and queueing into IndexedDB for replay (`internal/sw.ts`).
|
|
43
|
-
*/
|
|
44
|
-
declare class BeaconTraceExporter implements SpanExporter {
|
|
45
|
-
private readonly url;
|
|
46
|
-
constructor(url: string);
|
|
47
|
-
export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void;
|
|
48
|
-
shutdown(): Promise<void>;
|
|
49
|
-
forceFlush(): Promise<void>;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Browser-side OTLP log exporter — same beacon transport, log
|
|
53
|
-
* payload. See `BeaconTraceExporter` for the design rationale.
|
|
54
|
-
*/
|
|
55
|
-
declare class BeaconLogExporter {
|
|
56
|
-
private readonly url;
|
|
57
|
-
constructor(url: string);
|
|
25
|
+
declare function buildIngestUrl(collectorUrl: string): string;
|
|
26
|
+
declare class IngestLogExporter {
|
|
27
|
+
private readonly send;
|
|
28
|
+
constructor(send: SignalSender);
|
|
58
29
|
export(logs: ReadableLogRecord[], resultCallback: (result: ExportResult) => void): void;
|
|
59
30
|
shutdown(): Promise<void>;
|
|
60
31
|
forceFlush(): Promise<void>;
|
|
61
32
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
*
|
|
66
|
-
* `DELTA` matches what the OTLP HTTP exporter the kit's previous
|
|
67
|
-
* `OTLPMetricExporter` was configured with (see the
|
|
68
|
-
* `temporalityPreference: AggregationTemporalityPreference.DELTA`
|
|
69
|
-
* arg `provider.ts` used to pass). Returning the same temporality
|
|
70
|
-
* for every instrument type keeps the wire shape downstream
|
|
71
|
-
* consumers receive unchanged across the migration.
|
|
72
|
-
*/
|
|
73
|
-
declare class BeaconMetricExporter implements PushMetricExporter {
|
|
74
|
-
private readonly url;
|
|
75
|
-
constructor(url: string);
|
|
33
|
+
declare class IngestMetricExporter implements PushMetricExporter {
|
|
34
|
+
private readonly send;
|
|
35
|
+
constructor(send: SignalSender);
|
|
76
36
|
export(metrics: ResourceMetrics, resultCallback: (result: ExportResult) => void): void;
|
|
77
37
|
selectAggregationTemporality(): AggregationTemporality;
|
|
78
38
|
forceFlush(): Promise<void>;
|
|
79
39
|
shutdown(): Promise<void>;
|
|
80
40
|
}
|
|
81
|
-
declare function
|
|
82
|
-
declare function
|
|
83
|
-
declare function
|
|
41
|
+
declare function createTraceExporter(input: ExporterCommon): SpanExporter;
|
|
42
|
+
declare function createMetricExporter(input: ExporterCommon): IngestMetricExporter;
|
|
43
|
+
declare function createLogExporter(input: ExporterCommon): IngestLogExporter;
|
|
84
44
|
//#endregion
|
|
85
|
-
export {
|
|
45
|
+
export { buildIngestUrl, buildSinkUrl, createLogExporter, createMetricExporter, createTraceExporter };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{appendPathBeforeQuery,withQueryParam}from"../config.mjs";import{PRODUCER_VERSION}from"../version.mjs";import{
|
|
1
|
+
import{appendPathBeforeQuery,withQueryParam}from"../config.mjs";import{PRODUCER_VERSION}from"../version.mjs";import{ExportResultCode}from"@opentelemetry/core";import{JsonLogsSerializer}from"@opentelemetry/otlp-transformer/build/esm/logs/json/logs.js";import{JsonMetricsSerializer}from"@opentelemetry/otlp-transformer/build/esm/metrics/json/metrics.js";import{JsonTraceSerializer}from"@opentelemetry/otlp-transformer/build/esm/trace/json/trace.js";import{AggregationTemporality}from"@opentelemetry/sdk-metrics";function buildSinkUrl(collectorUrl){return appendPathBeforeQuery(collectorUrl,`/v2/sink`)}function buildIngestUrl(collectorUrl){return withQueryParam(buildSinkUrl(collectorUrl),`_pv`,PRODUCER_VERSION)}function makeSender(input){let url=buildIngestUrl(input.collectorUrl);return(payload,resultCallback)=>{payload&&payload.byteLength>0&&fetch(url,{method:`POST`,keepalive:!0,credentials:`omit`,headers:{"content-type":`application/json`},body:payload}).catch(()=>void 0),resultCallback({code:ExportResultCode.SUCCESS})}}var IngestTraceExporter=class{send;constructor(send){this.send=send}export(spans,resultCallback){if(spans.length===0){resultCallback({code:ExportResultCode.SUCCESS});return}this.send(JsonTraceSerializer.serializeRequest(spans),resultCallback)}shutdown(){return Promise.resolve()}forceFlush(){return Promise.resolve()}},IngestLogExporter=class{send;constructor(send){this.send=send}export(logs,resultCallback){if(logs.length===0){resultCallback({code:ExportResultCode.SUCCESS});return}this.send(JsonLogsSerializer.serializeRequest(logs),resultCallback)}shutdown(){return Promise.resolve()}forceFlush(){return Promise.resolve()}},IngestMetricExporter=class{send;constructor(send){this.send=send}export(metrics,resultCallback){this.send(JsonMetricsSerializer.serializeRequest(metrics),resultCallback)}selectAggregationTemporality(){return AggregationTemporality.DELTA}forceFlush(){return Promise.resolve()}shutdown(){return Promise.resolve()}};function createTraceExporter(input){return new IngestTraceExporter(makeSender(input))}function createMetricExporter(input){return new IngestMetricExporter(makeSender(input))}function createLogExporter(input){return new IngestLogExporter(makeSender(input))}export{buildIngestUrl,buildSinkUrl,createLogExporter,createMetricExporter,createTraceExporter};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { createBeaconMetricExporter, createBeaconTraceExporter } from "./exporter.mjs";
|
|
2
1
|
import { CaptureFailedRequestsOption, CaptureFailedRequestsOptions, HttpStatusCodeRange, InstrumentationsInput, registerBundledInstrumentations } from "./instrumentations.mjs";
|
|
3
2
|
import { readPropagationFromDocument } from "./propagation.mjs";
|
|
4
3
|
import { OtelProviderHandle, OtelProviderInput, buildOtelProvider } from "./provider.mjs";
|
|
5
4
|
import { captureWebVitals } from "./web-vitals.mjs";
|
|
6
|
-
export { type CaptureFailedRequestsOption, type CaptureFailedRequestsOptions, type HttpStatusCodeRange, type InstrumentationsInput, type OtelProviderHandle, type OtelProviderInput, buildOtelProvider, captureWebVitals,
|
|
5
|
+
export { type CaptureFailedRequestsOption, type CaptureFailedRequestsOptions, type HttpStatusCodeRange, type InstrumentationsInput, type OtelProviderHandle, type OtelProviderInput, buildOtelProvider, captureWebVitals, readPropagationFromDocument, registerBundledInstrumentations };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{registerBundledInstrumentations}from"./instrumentations.mjs";import{readPropagationFromDocument}from"./propagation.mjs";import{buildOtelProvider}from"./provider.mjs";import{captureWebVitals}from"./web-vitals.mjs";export{buildOtelProvider,captureWebVitals,readPropagationFromDocument,registerBundledInstrumentations};
|
|
@@ -42,6 +42,11 @@ declare const _testOnlyResolveFailureRanges: typeof resolveFailureRanges;
|
|
|
42
42
|
declare const _testOnlyCreateFailureGate: typeof createFailureGate;
|
|
43
43
|
declare const _testOnlyClassifyNetworkError: typeof classifyNetworkError;
|
|
44
44
|
interface InstrumentationsInput {
|
|
45
|
+
/**
|
|
46
|
+
* Hostnames the fetch + XHR instrumentations inject `traceparent` onto,
|
|
47
|
+
* matched by host (any path/port/scheme). Same-origin always propagates.
|
|
48
|
+
*/
|
|
49
|
+
apiHosts?: string[];
|
|
45
50
|
/** See {@link CaptureFailedRequestsOption}. On by default. */
|
|
46
51
|
captureFailedRequests?: CaptureFailedRequestsOption;
|
|
47
52
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{THIRD_PARTY_IGNORE}from"../third-party-hosts.mjs";import{describeActionable,isActionable}from"../dom/actionable.mjs";import{MECHANISM_TYPE}from"@interfere/types/sdk/errors";import{registerInstrumentations}from"@opentelemetry/instrumentation";import{BrowserNavigationInstrumentation,defaultSanitizeUrl}from"@opentelemetry/instrumentation-browser-navigation";import{DocumentLoadInstrumentation}from"@opentelemetry/instrumentation-document-load";import{FetchInstrumentation}from"@opentelemetry/instrumentation-fetch";import{LongTaskInstrumentation}from"@opentelemetry/instrumentation-long-task";import{UserInteractionInstrumentation}from"@opentelemetry/instrumentation-user-interaction";import{XMLHttpRequestInstrumentation}from"@opentelemetry/instrumentation-xml-http-request";const ATTR_HTTP_ROUTE=`http.route`,ATTR_URL_FULL=`url.full`,ATTR_URL_PATH=`url.path`,DEFAULT_FAILURE_RANGES=[[400,599]];function resolveFailureRanges(opt){if(opt===!1)return null;if(opt===void 0||opt===!0)return DEFAULT_FAILURE_RANGES;let codes=opt.statusCodes;return codes&&codes.length>0?codes:DEFAULT_FAILURE_RANGES}function createFailureGate(){let seen=new Set,windowStart=0,windowCount=0;return key=>{if(seen.has(key))return!1;let now=Date.now();return now-windowStart>6e4&&(windowStart=now,windowCount=0),windowCount>=20?!1:(seen.size>=100&&seen.clear(),seen.add(key),windowCount+=1,!0)}}function failureKey(method,discriminator,url){let path;try{path=new URL(url).pathname}catch{path=url}return`${method} ${discriminator} ${path}`}function statusMatches(status,ranges){for(let range of ranges)if(typeof range==`number`){if(status===range)return!0}else if(status>=range[0]&&status<=range[1])return!0;return!1}function recordHttpFailure(span,status,method,url,mechanism){span.addEvent(`exception`,{"exception.type":`HttpResponseError`,"exception.message":`HTTP ${status} ${method} ${url}`,"interfere.exception.mechanism":mechanism,"interfere.exception.handled":!1,"interfere.exception.kind":`error`})}function maybeCaptureFetchFailure(span,result,failureRanges,method,url,gate){if(!failureRanges)return;let status=result?.status;typeof status!=`number`||status<=0||!statusMatches(status,failureRanges)||gate&&!gate(failureKey(method,status,url))||recordHttpFailure(span,status,method,url,MECHANISM_TYPE.browser.fetchFailedResponse)}const NETWORK_TIMEOUT_RE=/timeout|timed out/i,NETWORK_ABORT_RE=/abort/i;function classifyNetworkError(message){return NETWORK_TIMEOUT_RE.test(message)?`timeout`:NETWORK_ABORT_RE.test(message)?`abort`:`network`}function recordNetworkFailure(span,kind,method,url,mechanism){span.addEvent(`exception`,{"exception.type":`NetworkError`,"exception.message":`Network ${kind} ${method} ${url}`,"interfere.exception.mechanism":mechanism,"interfere.exception.handled":!1,"interfere.exception.kind":`error`,"interfere.exception.subtype":kind})}function maybeCaptureNetworkFailure(span,result,failureRanges,method,url,gate){if(!failureRanges)return;let message=result.message;if(typeof message!=`string`||message.length===0)return;let kind=classifyNetworkError(message);gate&&!gate(failureKey(method,kind,url))||recordNetworkFailure(span,kind,method,url,MECHANISM_TYPE.browser.fetchNetworkError)}function applyFetchSpanAttrs(span,request,result,failureRanges,isTrackedUrl,resolveRoute,gate){let url=resolveFetchUrl(span,request,result);if(!url)return;let parsed;try{parsed=new URL(url)}catch{return}span.setAttribute(ATTR_URL_PATH,parsed.pathname);let tracked=isTrackedUrl(url),method=resolveFetchMethod(request);if(tracked){let route=resolveRoute?.(parsed.pathname);route&&span.setAttribute(ATTR_HTTP_ROUTE,route),span.updateName(`${method} ${route??parsed.pathname}`),result instanceof Response?maybeCaptureFetchFailure(span,result,failureRanges,method,url,gate):maybeCaptureNetworkFailure(span,result,failureRanges,method,url,gate)}}function applyXhrSpanAttrs(span,xhr,failureRanges,isTrackedUrl,gate){if(!failureRanges)return;let url=xhr.responseURL;if(!(url&&isTrackedUrl(url)))return;let status=xhr.status;if(typeof status==`number`){if(status===0){(!gate||gate(failureKey(`XHR`,`network`,url)))&&recordNetworkFailure(span,`network`,`XHR`,url,MECHANISM_TYPE.browser.xhrNetworkError);return}statusMatches(status,failureRanges)&&(gate&&!gate(failureKey(`XHR`,status,url))||recordHttpFailure(span,status,`XHR`,url,MECHANISM_TYPE.browser.xhrFailedResponse))}}function resolveFetchUrl(span,request,result){return responseUrl(result)??requestUrl(request)??spanUrlFull(span)}function isReadableSpan(span){return`attributes`in span}function spanUrlFull(span){if(!isReadableSpan(span))return null;let url=span.attributes[ATTR_URL_FULL]??span.attributes[`http.url`];return typeof url==`string`?url:null}function responseUrl(result){return result instanceof Response&&result.url?result.url:null}function requestUrl(request){return typeof request==`string`?request:readStringProp(request,`url`)??readStringProp(request,`href`)}function resolveFetchMethod(request){return readStringProp(request,`method`)??`GET`}function readStringProp(value,key){if(!(value&&typeof value==`object`&&key in value))return null;let prop=value[key];return typeof prop==`string`?prop:null}const _testOnlyApplyFetchSpanAttrs=applyFetchSpanAttrs,_testOnlyApplyXhrSpanAttrs=applyXhrSpanAttrs,_testOnlyResolveFailureRanges=resolveFailureRanges,_testOnlyCreateFailureGate=createFailureGate,_testOnlyClassifyNetworkError=classifyNetworkError,SEMCONV_STABILITY_OPT_IN=`http,database,messaging,gen_ai_latest_experimental`,NEVER_MATCH=/^$/,ESCAPE_RE=/[.*+?^${}()|[\]\\]/g;function sameOriginPattern(){if(typeof window>`u`)return NEVER_MATCH;let{protocol,host}=window.location;return RegExp(`^${protocol}//${host.replace(ESCAPE_RE,`\\$&`)}`)}function stampInteractionAttrs(eventType,element,span){let desc=describeActionable(element);span.setAttribute(`ui.event_type`,eventType),span.setAttribute(`ui.target.tag`,desc.tag),desc.id&&span.setAttribute(`ui.target.id`,desc.id),desc.name&&span.setAttribute(`ui.target.name`,desc.name),desc.role&&span.setAttribute(`ui.target.role`,desc.role),desc.ariaLabel&&span.setAttribute(`ui.target.aria_label`,desc.ariaLabel),desc.text&&span.setAttribute(`ui.target.text`,desc.text)}function stampUrlAttrs(span,resolveRoute){if(typeof window>`u`)return;let pathname=window.location.pathname,route=resolveRoute?.(pathname);span.setAttribute(ATTR_URL_PATH,route??pathname),span.setAttribute(ATTR_URL_FULL,window.location.href),route&&span.setAttribute(ATTR_HTTP_ROUTE,route)}function registerBundledInstrumentations(input){let{tracerProvider,loggerProvider,ignoreUrls,propagateContextUrls=[]}=input,resolveRoute=input.resolveRoute,origin=sameOriginPattern(),fetchIgnore=[...ignoreUrls,...THIRD_PARTY_IGNORE],failureRanges=resolveFailureRanges(input.captureFailedRequests),failureGate=createFailureGate();function isTrackedUrl(url){return origin.test(url)||
|
|
1
|
+
import{THIRD_PARTY_IGNORE}from"../third-party-hosts.mjs";import{describeActionable,isActionable}from"../dom/actionable.mjs";import{MECHANISM_TYPE}from"@interfere/types/sdk/errors";import{registerInstrumentations}from"@opentelemetry/instrumentation";import{BrowserNavigationInstrumentation,defaultSanitizeUrl}from"@opentelemetry/instrumentation-browser-navigation";import{DocumentLoadInstrumentation}from"@opentelemetry/instrumentation-document-load";import{FetchInstrumentation}from"@opentelemetry/instrumentation-fetch";import{LongTaskInstrumentation}from"@opentelemetry/instrumentation-long-task";import{UserInteractionInstrumentation}from"@opentelemetry/instrumentation-user-interaction";import{XMLHttpRequestInstrumentation}from"@opentelemetry/instrumentation-xml-http-request";const ATTR_HTTP_ROUTE=`http.route`,ATTR_URL_FULL=`url.full`,ATTR_URL_PATH=`url.path`,DEFAULT_FAILURE_RANGES=[[400,599]];function resolveFailureRanges(opt){if(opt===!1)return null;if(opt===void 0||opt===!0)return DEFAULT_FAILURE_RANGES;let codes=opt.statusCodes;return codes&&codes.length>0?codes:DEFAULT_FAILURE_RANGES}function createFailureGate(){let seen=new Set,windowStart=0,windowCount=0;return key=>{if(seen.has(key))return!1;let now=Date.now();return now-windowStart>6e4&&(windowStart=now,windowCount=0),windowCount>=20?!1:(seen.size>=100&&seen.clear(),seen.add(key),windowCount+=1,!0)}}function failureKey(method,discriminator,url){let path;try{path=new URL(url).pathname}catch{path=url}return`${method} ${discriminator} ${path}`}function statusMatches(status,ranges){for(let range of ranges)if(typeof range==`number`){if(status===range)return!0}else if(status>=range[0]&&status<=range[1])return!0;return!1}function recordHttpFailure(span,status,method,url,mechanism){span.addEvent(`exception`,{"exception.type":`HttpResponseError`,"exception.message":`HTTP ${status} ${method} ${url}`,"interfere.exception.mechanism":mechanism,"interfere.exception.handled":!1,"interfere.exception.kind":`error`})}function maybeCaptureFetchFailure(span,result,failureRanges,method,url,gate){if(!failureRanges)return;let status=result?.status;typeof status!=`number`||status<=0||!statusMatches(status,failureRanges)||gate&&!gate(failureKey(method,status,url))||recordHttpFailure(span,status,method,url,MECHANISM_TYPE.browser.fetchFailedResponse)}const NETWORK_TIMEOUT_RE=/timeout|timed out/i,NETWORK_ABORT_RE=/abort/i;function classifyNetworkError(message){return NETWORK_TIMEOUT_RE.test(message)?`timeout`:NETWORK_ABORT_RE.test(message)?`abort`:`network`}function recordNetworkFailure(span,kind,method,url,mechanism){span.addEvent(`exception`,{"exception.type":`NetworkError`,"exception.message":`Network ${kind} ${method} ${url}`,"interfere.exception.mechanism":mechanism,"interfere.exception.handled":!1,"interfere.exception.kind":`error`,"interfere.exception.subtype":kind})}function maybeCaptureNetworkFailure(span,result,failureRanges,method,url,gate){if(!failureRanges)return;let message=result.message;if(typeof message!=`string`||message.length===0)return;let kind=classifyNetworkError(message);gate&&!gate(failureKey(method,kind,url))||recordNetworkFailure(span,kind,method,url,MECHANISM_TYPE.browser.fetchNetworkError)}function applyFetchSpanAttrs(span,request,result,failureRanges,isTrackedUrl,resolveRoute,gate){let url=resolveFetchUrl(span,request,result);if(!url)return;let parsed;try{parsed=new URL(url)}catch{return}span.setAttribute(ATTR_URL_PATH,parsed.pathname);let tracked=isTrackedUrl(url),method=resolveFetchMethod(request);if(tracked){let route=resolveRoute?.(parsed.pathname);route&&span.setAttribute(ATTR_HTTP_ROUTE,route),span.updateName(`${method} ${route??parsed.pathname}`),result instanceof Response?maybeCaptureFetchFailure(span,result,failureRanges,method,url,gate):maybeCaptureNetworkFailure(span,result,failureRanges,method,url,gate)}}function applyXhrSpanAttrs(span,xhr,failureRanges,isTrackedUrl,gate){if(!failureRanges)return;let url=xhr.responseURL;if(!(url&&isTrackedUrl(url)))return;let status=xhr.status;if(typeof status==`number`){if(status===0){(!gate||gate(failureKey(`XHR`,`network`,url)))&&recordNetworkFailure(span,`network`,`XHR`,url,MECHANISM_TYPE.browser.xhrNetworkError);return}statusMatches(status,failureRanges)&&(gate&&!gate(failureKey(`XHR`,status,url))||recordHttpFailure(span,status,`XHR`,url,MECHANISM_TYPE.browser.xhrFailedResponse))}}function resolveFetchUrl(span,request,result){return responseUrl(result)??requestUrl(request)??spanUrlFull(span)}function isReadableSpan(span){return`attributes`in span}function spanUrlFull(span){if(!isReadableSpan(span))return null;let url=span.attributes[ATTR_URL_FULL]??span.attributes[`http.url`];return typeof url==`string`?url:null}function responseUrl(result){return result instanceof Response&&result.url?result.url:null}function requestUrl(request){return typeof request==`string`?request:readStringProp(request,`url`)??readStringProp(request,`href`)}function resolveFetchMethod(request){return readStringProp(request,`method`)??`GET`}function readStringProp(value,key){if(!(value&&typeof value==`object`&&key in value))return null;let prop=value[key];return typeof prop==`string`?prop:null}const _testOnlyApplyFetchSpanAttrs=applyFetchSpanAttrs,_testOnlyApplyXhrSpanAttrs=applyXhrSpanAttrs,_testOnlyResolveFailureRanges=resolveFailureRanges,_testOnlyCreateFailureGate=createFailureGate,_testOnlyClassifyNetworkError=classifyNetworkError,SEMCONV_STABILITY_OPT_IN=`http,database,messaging,gen_ai_latest_experimental`,NEVER_MATCH=/^$/,ESCAPE_RE=/[.*+?^${}()|[\]\\]/g;function sameOriginPattern(){if(typeof window>`u`)return NEVER_MATCH;let{protocol,host}=window.location;return RegExp(`^${protocol}//${host.replace(ESCAPE_RE,`\\$&`)}`)}function stampInteractionAttrs(eventType,element,span){let desc=describeActionable(element);span.setAttribute(`ui.event_type`,eventType),span.setAttribute(`ui.target.tag`,desc.tag),desc.id&&span.setAttribute(`ui.target.id`,desc.id),desc.name&&span.setAttribute(`ui.target.name`,desc.name),desc.role&&span.setAttribute(`ui.target.role`,desc.role),desc.ariaLabel&&span.setAttribute(`ui.target.aria_label`,desc.ariaLabel),desc.text&&span.setAttribute(`ui.target.text`,desc.text)}function stampUrlAttrs(span,resolveRoute){if(typeof window>`u`)return;let pathname=window.location.pathname,route=resolveRoute?.(pathname);span.setAttribute(ATTR_URL_PATH,route??pathname),span.setAttribute(ATTR_URL_FULL,window.location.href),route&&span.setAttribute(ATTR_HTTP_ROUTE,route)}function escapeRegExp(value){return value.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function apiHostToMatcher(host){return RegExp(`^https?://${escapeRegExp(host)}(:\\d+)?([/?#]|$)`)}function registerBundledInstrumentations(input){let{tracerProvider,loggerProvider,ignoreUrls,apiHosts=[],propagateContextUrls=[]}=input,resolveRoute=input.resolveRoute,origin=sameOriginPattern(),propagateMatchers=[...apiHosts.map(apiHostToMatcher),...propagateContextUrls],fetchIgnore=[...ignoreUrls,...THIRD_PARTY_IGNORE],failureRanges=resolveFailureRanges(input.captureFailedRequests),failureGate=createFailureGate();function isTrackedUrl(url){return origin.test(url)||propagateMatchers.some(re=>re instanceof RegExp?re.test(url):url.includes(re))}return registerInstrumentations({tracerProvider,...loggerProvider?{loggerProvider}:{},instrumentations:[new FetchInstrumentation({propagateTraceHeaderCorsUrls:[origin,...propagateMatchers],ignoreUrls:fetchIgnore,semconvStabilityOptIn:SEMCONV_STABILITY_OPT_IN,measureRequestSize:!0,applyCustomAttributesOnSpan:(span,request,result)=>{applyFetchSpanAttrs(span,request,result,failureRanges,isTrackedUrl,resolveRoute,failureGate)}}),new XMLHttpRequestInstrumentation({propagateTraceHeaderCorsUrls:[origin,...propagateMatchers],ignoreUrls:fetchIgnore,semconvStabilityOptIn:SEMCONV_STABILITY_OPT_IN,applyCustomAttributesOnSpan:(span,xhr)=>{applyXhrSpanAttrs(span,xhr,failureRanges,isTrackedUrl,failureGate)}}),new DocumentLoadInstrumentation({semconvStabilityOptIn:SEMCONV_STABILITY_OPT_IN,applyCustomAttributesOnSpan:{resourceFetch:(span,entry)=>{let name=entry?.name;if(typeof name==`string`)try{let url=new URL(name),isCrossOrigin=typeof window<`u`&&url.origin!==window.location.origin;span.updateName(isCrossOrigin?`resource: ${url.host}${url.pathname}`:`resource: ${url.pathname}`),span.setAttribute(ATTR_URL_FULL,name),span.setAttribute(ATTR_URL_PATH,url.pathname),span.setAttribute(`server.address`,url.host)}catch{return}}}}),new BrowserNavigationInstrumentation({sanitizeUrl:defaultSanitizeUrl}),new UserInteractionInstrumentation({eventNames:[`click`,`submit`],shouldPreventSpanCreation:(eventType,element,span)=>eventType===`click`&&!isActionable(element)?!0:(stampInteractionAttrs(eventType,element,span),stampUrlAttrs(span,resolveRoute),!1)}),new LongTaskInstrumentation({observerCallback:(span,info)=>{stampUrlAttrs(span,resolveRoute);let containerType=info.longtaskEntry.attribution[0]?.containerType;containerType&&span.setAttribute(`longtask.container_type`,containerType)}})]})}export{_testOnlyApplyFetchSpanAttrs,_testOnlyApplyXhrSpanAttrs,_testOnlyClassifyNetworkError,_testOnlyCreateFailureGate,_testOnlyResolveFailureRanges,registerBundledInstrumentations};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getGlobal}from"../../util/global.mjs";import{SDK_NAME,SDK_VERSION}from"../version.mjs";import{
|
|
1
|
+
import{getGlobal}from"../../util/global.mjs";import{SDK_NAME,SDK_VERSION}from"../version.mjs";import{createLogExporter,createMetricExporter,createTraceExporter}from"./exporter.mjs";import{PageScopeContextManager}from"./page-scope-context-manager.mjs";import{propagation}from"@opentelemetry/api";import{CompositePropagator,W3CBaggagePropagator,W3CTraceContextPropagator}from"@opentelemetry/core";import{MeterProvider,PeriodicExportingMetricReader}from"@opentelemetry/sdk-metrics";import{resolveEnvironment}from"@interfere/types/sdk/runtime";import{browserDetector}from"@opentelemetry/opentelemetry-browser-detector";import{detectResources,resourceFromAttributes}from"@opentelemetry/resources";import{BatchLogRecordProcessor,LoggerProvider}from"@opentelemetry/sdk-logs";import{BatchSpanProcessor}from"@opentelemetry/sdk-trace-base";import{WebTracerProvider}from"@opentelemetry/sdk-trace-web";import{createSessionLogRecordProcessor,createSessionSpanProcessor}from"@opentelemetry/web-common";const BROWSER_BATCH_OPTIONS={maxQueueSize:50,maxExportBatchSize:10,scheduledDelayMillis:2e3,exportTimeoutMillis:1e4};function buildOtelProvider(input){let environment=resolveEnvironment({explicit:getGlobal(`__INTERFERE_ENVIRONMENT__`),env:typeof process>`u`?{}:{INTERFERE_ENVIRONMENT:process.env.INTERFERE_ENVIRONMENT,VERCEL_ENV:process.env.VERCEL_ENV,NODE_ENV:process.env.NODE_ENV}}),baseResource=resourceFromAttributes({"service.name":input.serviceName??`interfere-sdk`,"service.namespace":`interfere`,"deployment.environment.name":environment,"telemetry.sdk.language":`webjs`,"interfere.sdk.name":SDK_NAME,"interfere.sdk.version":SDK_VERSION,"interfere.sdk.stack":input.sdkStack.join(`, `),...input.releaseSlug?{"release.slug":input.releaseSlug}:{},...input.deviceId?{"device.id":input.deviceId}:{}}),detected=detectResources({detectors:[browserDetector]}),resource=baseResource.merge(detected),traceExporter=createTraceExporter({collectorUrl:input.collectorUrl,authHeaders:input.authHeaders}),getSessionId=input.getSessionId,tracerProvider=new WebTracerProvider({resource,spanProcessors:[createSessionSpanProcessor({getSessionId}),new BatchSpanProcessor(traceExporter,BROWSER_BATCH_OPTIONS),...input.additionalSpanProcessors??[]]}),contextManager=new PageScopeContextManager;tracerProvider.register({contextManager}),propagation.fields().length===0&&propagation.setGlobalPropagator(new CompositePropagator({propagators:[new W3CTraceContextPropagator,new W3CBaggagePropagator]}));let metricReader=new PeriodicExportingMetricReader({exporter:createMetricExporter({collectorUrl:input.collectorUrl,authHeaders:input.authHeaders}),exportIntervalMillis:3e4}),meterProvider=new MeterProvider({resource,readers:[metricReader,...input.additionalMetricReaders??[]]}),logExporter=createLogExporter({collectorUrl:input.collectorUrl,authHeaders:input.authHeaders}),loggerProvider=new LoggerProvider({resource,processors:[createSessionLogRecordProcessor({getSessionId}),new BatchLogRecordProcessor(logExporter,BROWSER_BATCH_OPTIONS),...input.additionalLogRecordProcessors??[]]});return{contextManager,tracerProvider,meterProvider,metricReader,loggerProvider,flush:async()=>{await Promise.all([tracerProvider.forceFlush(),metricReader.forceFlush(),loggerProvider.forceFlush()])},shutdown:async()=>{await Promise.all([tracerProvider.shutdown(),meterProvider.shutdown(),loggerProvider.shutdown()])}}}export{buildOtelProvider};
|
package/dist/package.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var name=`@interfere/react`,version=`11.0.0-canary.
|
|
1
|
+
var name=`@interfere/react`,version=`11.0.0-canary.7`;export{name,version};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"../../util/log.mjs";import{appendPathBeforeQuery}from"../../internal/config.mjs";import{trace}from"@opentelemetry/api";const log=createLogger(`replay`),textEncoder=new TextEncoder;async function prepareBody(compressor,rawBytes){if(!compressor.gzip)return{body:rawBytes,encoding:null};try{return{body:await compressor.gzip(rawBytes),encoding:`gzip`}}catch(error){return log.warn(`gzip failed for chunk; sending uncompressed: %o`,error),{body:rawBytes,encoding:null}}}async function deliverChunk({ctx,uploadBaseUrl,authHeaders,compressor,payload,skipCompression}){let sessionId=ctx.getSessionId();if(!sessionId)return;let rawBytes=textEncoder.encode(payload.bodyJson),uncompressedBytes=rawBytes.byteLength,prepared=skipCompression?{body:rawBytes,encoding:null}:await prepareBody(compressor,rawBytes),headers={...authHeaders,"content-type":`application/json`,...prepared.encoding===`gzip`?{"content-encoding":`gzip`}:{}},res=await fetch(appendPathBeforeQuery(uploadBaseUrl,`/v2/replay/upload/${encodeURIComponent(sessionId)}`),{method:`POST`,headers,body:prepared.body});if(!res.ok){log.warn(`replay chunk upload rejected (%d)`,res.status);return}let{objectKey}=await res.json();trace.getTracer(`@interfere/react/replay`).startSpan(`replay.chunk`,{attributes:{"replay.chunk.uri":objectKey,"replay.chunk.size_bytes":uncompressedBytes,...prepared.encoding===`gzip`?{"replay.chunk.compressed_bytes":prepared.body.byteLength}:{},"replay.chunk.event_count":payload.eventCount,...payload.firstTs===null?{}:{"replay.chunk.first_event_ts":payload.firstTs},...payload.lastTs===null?{}:{"replay.chunk.last_event_ts":payload.lastTs}}}).end()}export{deliverChunk};
|
|
1
|
+
import{createLogger}from"../../util/log.mjs";import{appendPathBeforeQuery}from"../../internal/config.mjs";import{trace}from"@opentelemetry/api";const log=createLogger(`replay`),textEncoder=new TextEncoder;async function prepareBody(compressor,rawBytes){if(!compressor.gzip)return{body:rawBytes,encoding:null};try{return{body:await compressor.gzip(rawBytes),encoding:`gzip`}}catch(error){return log.warn(`gzip failed for chunk; sending uncompressed: %o`,error),{body:rawBytes,encoding:null}}}async function deliverChunk({ctx,uploadBaseUrl,authHeaders,compressor,payload,skipCompression}){let sessionId=ctx.getSessionId();if(!sessionId)return;let rawBytes=textEncoder.encode(payload.bodyJson),uncompressedBytes=rawBytes.byteLength,prepared=skipCompression?{body:rawBytes,encoding:null}:await prepareBody(compressor,rawBytes),headers={...authHeaders,"content-type":`application/json`,...prepared.encoding===`gzip`?{"content-encoding":`gzip`}:{}},res=await fetch(appendPathBeforeQuery(uploadBaseUrl,`/v2/replay/upload/${encodeURIComponent(sessionId)}`),{method:`POST`,headers,credentials:`omit`,body:prepared.body});if(!res.ok){log.warn(`replay chunk upload rejected (%d)`,res.status);return}let{objectKey}=await res.json();trace.getTracer(`@interfere/react/replay`).startSpan(`replay.chunk`,{attributes:{"replay.chunk.uri":objectKey,"replay.chunk.size_bytes":uncompressedBytes,...prepared.encoding===`gzip`?{"replay.chunk.compressed_bytes":prepared.body.byteLength}:{},"replay.chunk.event_count":payload.eventCount,...payload.firstTs===null?{}:{"replay.chunk.first_event_ts":payload.firstTs},...payload.lastTs===null?{}:{"replay.chunk.last_event_ts":payload.lastTs}}}).end()}export{deliverChunk};
|
package/dist/tracking/api.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"../util/log.mjs";import{ActivityMonitor}from"../internal/activity.mjs";import{appendPathBeforeQuery}from"../internal/config.mjs";import{DeviceManager}from"./device.mjs";import{GeoDetector}from"./geo.mjs";import{SessionManager}from"./session.mjs";const log=createLogger(`tracking`);var SessionTracker=class{target;fetcher;device;geo;mgr=null;activity=new ActivityMonitor;currentIdentity=null;identifiedSessionId=null;syncedSessionId=null;syncAttemptMs=0;generation=0;constructor(opts){this.target=opts.target,this.fetcher=opts.fetcher??globalThis.fetch.bind(globalThis),this.device=opts.device??new DeviceManager,this.geo=opts.geo??new GeoDetector}start(){this.device.init(),this.geo.detect(),this.mgr=new SessionManager(id=>{this.onRotate(id).catch(()=>void 0)}),this.activity.start({markActivity:()=>this.markActivity(),reset:()=>this.rotateSession(),startedAt:()=>this.getSessionStartedAt()})}onSessionPause(handler){return this.activity.onPause(handler)}onSessionResume(handler){return this.activity.onResume(handler)}sessionId(){let id=this.mgr?.getSessionId()??null;return id&&this.ensureSynced(id),id}windowId(){return this.mgr?.getWindowId()??null}markActivity(){this.mgr?.markActivity()}getSessionStartedAt(){return this.mgr?.getSessionStartedAt()??0}getDeviceId(){return this.device.getDeviceId()}getFpHash(){return this.device.getFpHash()}headers(){let h={},sid=this.mgr?.getSessionId()??null;sid&&(h[`x-interfere-session`]=sid);let wid=this.mgr?.getWindowId()??null;wid&&(h[`x-interfere-window`]=wid);let did=this.device.getDeviceId();return did&&(h[`x-interfere-device`]=did),h}getIdentity(){return this.currentIdentity}async identify(params){if(!this.mgr)return;let sessionId=this.mgr.getSessionId();if(this.identifiedSessionId===sessionId){log.debug(`skipped, already identified for session %s`,sessionId);return}this.currentIdentity=params,this.identifiedSessionId=sessionId;let gen=this.generation,[fpHash,country]=await Promise.all([this.device.whenFingerprintReady(),this.geo.detect()]),deviceId=this.device.getDeviceId();if(gen!==this.generation){this.identifiedSessionId=null;return}log.info(`POST session %s → identify`,sessionId),this.fetcher(appendPathBeforeQuery(this.target.url,`/identify`),{method:`POST`,headers:this.requestHeaders(),body:JSON.stringify({sessionId,deviceId,fpHash,...params,...country&&{country}}),keepalive:!0,signal:AbortSignal.timeout(1e4)}).catch(()=>{this.identifiedSessionId=null,log.warn(`identify failed for session %s`,sessionId)})}clearIdentity(){this.currentIdentity=null,this.identifiedSessionId=null}reset(){this.clearIdentity(),this.mgr?.reset()}rotateSession(){this.mgr?.reset()}dispose(){this.activity.stop(),this.generation+=1,this.clearIdentity(),this.syncedSessionId=null,this.syncAttemptMs=0,this.mgr=null}requestHeaders(){return{...Object.fromEntries(this.target.headers.entries()),...this.headers()}}ensureSynced(sessionId){this.syncedSessionId!==sessionId&&(Date.now()-this.syncAttemptMs<5e3||this.syncSession(sessionId,this.device.getDeviceId(),this.device.getFpHash()))}syncSession(sessionId,deviceId,fpHash){this.syncAttemptMs=Date.now(),this.syncedSessionId=sessionId,this.fetcher(this.target.url,{method:`POST`,headers:this.requestHeaders(),body:JSON.stringify({sessionId,deviceId,fpHash}),keepalive:!0,signal:AbortSignal.timeout(1e4)}).then(res=>{res.ok||(this.syncedSessionId=null)}).catch(()=>{this.syncedSessionId=null,log.warn(`session sync failed, will retry`)})}async onRotate(sessionId){this.syncedSessionId=null,this.syncAttemptMs=Date.now();let gen=this.generation,fpHash=await this.device.whenFingerprintReady(),deviceId=this.device.getDeviceId();gen===this.generation&&(log.debug(`POST session %s (device=%s fp=%s)`,sessionId,deviceId??`pending`,fpHash??`none`),this.syncSession(sessionId,deviceId,fpHash))}};export{SessionTracker};
|
|
1
|
+
import{createLogger}from"../util/log.mjs";import{ActivityMonitor}from"../internal/activity.mjs";import{appendPathBeforeQuery}from"../internal/config.mjs";import{DeviceManager}from"./device.mjs";import{GeoDetector}from"./geo.mjs";import{SessionManager}from"./session.mjs";const log=createLogger(`tracking`);var SessionTracker=class{target;fetcher;device;geo;mgr=null;activity=new ActivityMonitor;currentIdentity=null;identifiedSessionId=null;syncedSessionId=null;syncAttemptMs=0;generation=0;constructor(opts){this.target=opts.target,this.fetcher=opts.fetcher??globalThis.fetch.bind(globalThis),this.device=opts.device??new DeviceManager,this.geo=opts.geo??new GeoDetector}start(){this.device.init(),this.geo.detect(),this.mgr=new SessionManager(id=>{this.onRotate(id).catch(()=>void 0)}),this.activity.start({markActivity:()=>this.markActivity(),reset:()=>this.rotateSession(),startedAt:()=>this.getSessionStartedAt()})}onSessionPause(handler){return this.activity.onPause(handler)}onSessionResume(handler){return this.activity.onResume(handler)}sessionId(){let id=this.mgr?.getSessionId()??null;return id&&this.ensureSynced(id),id}windowId(){return this.mgr?.getWindowId()??null}markActivity(){this.mgr?.markActivity()}getSessionStartedAt(){return this.mgr?.getSessionStartedAt()??0}getDeviceId(){return this.device.getDeviceId()}getFpHash(){return this.device.getFpHash()}headers(){let h={},sid=this.mgr?.getSessionId()??null;sid&&(h[`x-interfere-session`]=sid);let wid=this.mgr?.getWindowId()??null;wid&&(h[`x-interfere-window`]=wid);let did=this.device.getDeviceId();return did&&(h[`x-interfere-device`]=did),h}getIdentity(){return this.currentIdentity}async identify(params){if(!this.mgr)return;let sessionId=this.mgr.getSessionId();if(this.identifiedSessionId===sessionId){log.debug(`skipped, already identified for session %s`,sessionId);return}this.currentIdentity=params,this.identifiedSessionId=sessionId;let gen=this.generation,[fpHash,country]=await Promise.all([this.device.whenFingerprintReady(),this.geo.detect()]),deviceId=this.device.getDeviceId();if(gen!==this.generation){this.identifiedSessionId=null;return}log.info(`POST session %s → identify`,sessionId),this.fetcher(appendPathBeforeQuery(this.target.url,`/identify`),{method:`POST`,headers:this.requestHeaders(),body:JSON.stringify({sessionId,deviceId,fpHash,...params,...country&&{country}}),keepalive:!0,credentials:`omit`,signal:AbortSignal.timeout(1e4)}).catch(()=>{this.identifiedSessionId=null,log.warn(`identify failed for session %s`,sessionId)})}clearIdentity(){this.currentIdentity=null,this.identifiedSessionId=null}reset(){this.clearIdentity(),this.mgr?.reset()}rotateSession(){this.mgr?.reset()}dispose(){this.activity.stop(),this.generation+=1,this.clearIdentity(),this.syncedSessionId=null,this.syncAttemptMs=0,this.mgr=null}requestHeaders(){return{...Object.fromEntries(this.target.headers.entries()),...this.headers()}}ensureSynced(sessionId){this.syncedSessionId!==sessionId&&(Date.now()-this.syncAttemptMs<5e3||this.syncSession(sessionId,this.device.getDeviceId(),this.device.getFpHash()))}syncSession(sessionId,deviceId,fpHash){this.syncAttemptMs=Date.now(),this.syncedSessionId=sessionId,this.fetcher(this.target.url,{method:`POST`,headers:this.requestHeaders(),body:JSON.stringify({sessionId,deviceId,fpHash}),keepalive:!0,credentials:`omit`,signal:AbortSignal.timeout(1e4)}).then(res=>{res.ok||(this.syncedSessionId=null)}).catch(()=>{this.syncedSessionId=null,log.warn(`session sync failed, will retry`)})}async onRotate(sessionId){this.syncedSessionId=null,this.syncAttemptMs=Date.now();let gen=this.generation,fpHash=await this.device.whenFingerprintReady(),deviceId=this.device.getDeviceId();gen===this.generation&&(log.debug(`POST session %s (device=%s fp=%s)`,sessionId,deviceId??`pending`,fpHash??`none`),this.syncSession(sessionId,deviceId,fpHash))}};export{SessionTracker};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interfere/react",
|
|
3
|
-
"version": "11.0.0-canary.
|
|
3
|
+
"version": "11.0.0-canary.7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Client-side React SDK for Interfere. Error tracking, session replay, and analytics.",
|
|
6
6
|
"keywords": [
|
|
@@ -65,10 +65,6 @@
|
|
|
65
65
|
"types": "./dist/internal/wrapper-singleton.d.mts",
|
|
66
66
|
"default": "./dist/internal/wrapper-singleton.mjs"
|
|
67
67
|
},
|
|
68
|
-
"./sw": {
|
|
69
|
-
"types": "./dist/sw.d.mts",
|
|
70
|
-
"default": "./dist/sw.mjs"
|
|
71
|
-
},
|
|
72
68
|
"./sw-runtime": {
|
|
73
69
|
"monorepo": "./src/internal/sw-queue.ts",
|
|
74
70
|
"types": "./dist/internal/sw-queue.d.mts",
|
|
@@ -87,13 +83,12 @@
|
|
|
87
83
|
"scripts": {
|
|
88
84
|
"build": "tsdown",
|
|
89
85
|
"test": "vitest run",
|
|
90
|
-
"typecheck": "tsgo --noEmit"
|
|
91
|
-
"typegen": "bun run scripts/build-sw.ts"
|
|
86
|
+
"typecheck": "tsgo --noEmit"
|
|
92
87
|
},
|
|
93
88
|
"dependencies": {
|
|
94
89
|
"@fingerprintjs/fingerprintjs": "^5.2.0",
|
|
95
|
-
"@interfere/constants": "^9.0.2-canary.
|
|
96
|
-
"@interfere/types": "^10.0.0-canary.
|
|
90
|
+
"@interfere/constants": "^9.0.2-canary.3",
|
|
91
|
+
"@interfere/types": "^10.0.0-canary.6",
|
|
97
92
|
"@opentelemetry/api": "^1.9.1",
|
|
98
93
|
"@opentelemetry/api-logs": "^0.219.0",
|
|
99
94
|
"@opentelemetry/context-zone": "^2.7.1",
|
|
@@ -114,7 +109,7 @@
|
|
|
114
109
|
"@opentelemetry/sdk-trace-web": "^2.7.1",
|
|
115
110
|
"@opentelemetry/web-common": "^0.219.0",
|
|
116
111
|
"@ua-parser-js/pro-enterprise": "^2.0.6",
|
|
117
|
-
"rrweb": "2.0
|
|
112
|
+
"rrweb": "2.1.0",
|
|
118
113
|
"uuid": "^14.0.1",
|
|
119
114
|
"web-vitals": "^5.3.0",
|
|
120
115
|
"workbox-background-sync": "^7.4.0"
|
|
@@ -132,12 +127,12 @@
|
|
|
132
127
|
}
|
|
133
128
|
},
|
|
134
129
|
"devDependencies": {
|
|
135
|
-
"@interfere/test-utils": "^9.0.1-canary.
|
|
136
|
-
"@interfere/typescript-config": "^9.0.1-canary.
|
|
137
|
-
"@types/node": "^26.
|
|
130
|
+
"@interfere/test-utils": "^9.0.1-canary.1",
|
|
131
|
+
"@interfere/typescript-config": "^9.0.1-canary.1",
|
|
132
|
+
"@types/node": "^26.1.0",
|
|
138
133
|
"@types/react": "19.2.17",
|
|
139
134
|
"@types/react-dom": "19.2.3",
|
|
140
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
135
|
+
"@typescript/native-preview": "7.0.0-dev.20260701.1",
|
|
141
136
|
"@vitest/browser": "4.1.9",
|
|
142
137
|
"@vitest/browser-playwright": "4.1.9",
|
|
143
138
|
"@vitest/coverage-v8": "4.1.9",
|
|
@@ -145,7 +140,6 @@
|
|
|
145
140
|
"playwright": "^1.61.1",
|
|
146
141
|
"react": "^19.2.6",
|
|
147
142
|
"react-dom": "^19.2.6",
|
|
148
|
-
"rolldown": "^1.1.1",
|
|
149
143
|
"tsdown": "^0.22.2",
|
|
150
144
|
"vitest": "4.1.9",
|
|
151
145
|
"vitest-browser-react": "2.2.0"
|
package/dist/internal/sw.d.mts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
//#region src/internal/sw.d.ts
|
|
2
|
-
interface SwRegistrationOptions {
|
|
3
|
-
/**
|
|
4
|
-
* Optional hook to bridge SW → kernel telemetry. The SW posts
|
|
5
|
-
* `{ type: "interfere.sw.queued" | ... }` messages back to the
|
|
6
|
-
* page when it queues, replays, or drops a request; if provided,
|
|
7
|
-
* this fires for each one so the kernel can record an OTel span
|
|
8
|
-
* event.
|
|
9
|
-
*/
|
|
10
|
-
onMessage?: (message: SwMessage) => void;
|
|
11
|
-
}
|
|
12
|
-
interface SwMessage {
|
|
13
|
-
reason?: string;
|
|
14
|
-
type: "interfere.sw.queued" | "interfere.sw.replayed" | "interfere.sw.dropped";
|
|
15
|
-
url: string;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Best-effort: never throws, never blocks. SW registration is a
|
|
19
|
-
* page-load-time durability bonus; if it fails, the SDK falls back to
|
|
20
|
-
* direct fetch and behaves exactly like 9.x did.
|
|
21
|
-
*/
|
|
22
|
-
declare function registerServiceWorker(opts?: SwRegistrationOptions): Promise<void>;
|
|
23
|
-
//#endregion
|
|
24
|
-
export { SwMessage, SwRegistrationOptions, registerServiceWorker };
|
package/dist/internal/sw.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createLogger}from"../util/log.mjs";import{resolveRoutePrefix}from"@interfere/constants/route-prefix";const log=createLogger(`sw`);async function registerServiceWorker(opts={}){if(typeof navigator>`u`||!(`serviceWorker`in navigator))return;let prefix=resolveRoutePrefix(),swPath=`${prefix}/sw`,swScope=`${prefix}/`;try{await navigator.serviceWorker.register(swPath,{scope:swScope}),log.debug(`registered at %s (scope %s)`,swPath,swScope)}catch(err){log.warn(`registration failed, falling back to direct fetch`,err);return}opts.onMessage&&navigator.serviceWorker.addEventListener(`message`,event=>{let data=event.data;isSwMessage(data)&&opts.onMessage?.(data)})}function isSwMessage(value){if(!value||typeof value!=`object`)return!1;let v=value;return(v.type===`interfere.sw.queued`||v.type===`interfere.sw.replayed`||v.type===`interfere.sw.dropped`)&&typeof v.url==`string`}export{registerServiceWorker};
|
package/dist/sw.d.mts
DELETED
package/dist/sw.mjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
// AUTOGENERATED by scripts/build-sw.ts — do not edit.
|
|
2
|
-
export const SW_SCRIPT = "(function(){try{self[`workbox:core:7.4.0`]&&_()}catch{}let e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n};var t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}};let n=(e,t)=>t.some(t=>e instanceof t),r,i;function a(){return r||=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction]}function o(){return i||=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey]}let s=new WeakMap,c=new WeakMap,l=new WeakMap,u=new WeakMap,d=new WeakMap;function f(e){let t=new Promise((t,n)=>{let r=()=>{e.removeEventListener(`success`,i),e.removeEventListener(`error`,a)},i=()=>{t(y(e.result)),r()},a=()=>{n(e.error),r()};e.addEventListener(`success`,i),e.addEventListener(`error`,a)});return t.then(t=>{t instanceof IDBCursor&&s.set(t,e)}).catch(()=>{}),d.set(t,e),t}function p(e){if(c.has(e))return;let t=new Promise((t,n)=>{let r=()=>{e.removeEventListener(`complete`,i),e.removeEventListener(`error`,a),e.removeEventListener(`abort`,a)},i=()=>{t(),r()},a=()=>{n(e.error||new DOMException(`AbortError`,`AbortError`)),r()};e.addEventListener(`complete`,i),e.addEventListener(`error`,a),e.addEventListener(`abort`,a)});c.set(e,t)}let m={get(e,t,n){if(e instanceof IDBTransaction){if(t===`done`)return c.get(e);if(t===`objectStoreNames`)return e.objectStoreNames||l.get(e);if(t===`store`)return n.objectStoreNames[1]?void 0:n.objectStore(n.objectStoreNames[0])}return y(e[t])},set(e,t,n){return e[t]=n,!0},has(e,t){return e instanceof IDBTransaction&&(t===`done`||t===`store`)?!0:t in e}};function h(e){m=e(m)}function g(e){return e===IDBDatabase.prototype.transaction&&!(`objectStoreNames`in IDBTransaction.prototype)?function(t,...n){let r=e.call(b(this),t,...n);return l.set(r,t.sort?t.sort():[t]),y(r)}:o().includes(e)?function(...t){return e.apply(b(this),t),y(s.get(this))}:function(...t){return y(e.apply(b(this),t))}}function v(e){return typeof e==`function`?g(e):(e instanceof IDBTransaction&&p(e),n(e,a())?new Proxy(e,m):e)}function y(e){if(e instanceof IDBRequest)return f(e);if(u.has(e))return u.get(e);let t=v(e);return t!==e&&(u.set(e,t),d.set(t,e)),t}let b=e=>d.get(e);function x(e,t,{blocked:n,upgrade:r,blocking:i,terminated:a}={}){let o=indexedDB.open(e,t),s=y(o);return r&&o.addEventListener(`upgradeneeded`,e=>{r(y(o.result),e.oldVersion,e.newVersion,y(o.transaction),e)}),n&&o.addEventListener(`blocked`,e=>n(e.oldVersion,e.newVersion,e)),s.then(e=>{a&&e.addEventListener(`close`,()=>a()),i&&e.addEventListener(`versionchange`,e=>i(e.oldVersion,e.newVersion,e))}).catch(()=>{}),s}let S=[`get`,`getKey`,`getAll`,`getAllKeys`,`count`],C=[`put`,`add`,`delete`,`clear`],w=new Map;function T(e,t){if(!(e instanceof IDBDatabase&&!(t in e)&&typeof t==`string`))return;if(w.get(t))return w.get(t);let n=t.replace(/FromIndex$/,``),r=t!==n,i=C.includes(n);if(!(n in(r?IDBIndex:IDBObjectStore).prototype)||!(i||S.includes(n)))return;let a=async function(e,...t){let a=this.transaction(e,i?`readwrite`:`readonly`),o=a.store;return r&&(o=o.index(t.shift())),(await Promise.all([o[n](...t),i&&a.done]))[0]};return w.set(t,a),a}h(e=>({...e,get:(t,n,r)=>T(t,n)||e.get(t,n,r),has:(t,n)=>!!T(t,n)||e.has(t,n)}));try{self[`workbox:background-sync:7.4.0`]&&_()}catch{}let E=`requests`,D=`queueName`;var O=class{constructor(){this._db=null}async addEntry(e){let t=(await this.getDb()).transaction(E,`readwrite`,{durability:`relaxed`});await t.store.add(e),await t.done}async getFirstEntryId(){return(await(await this.getDb()).transaction(E).store.openCursor())?.value.id}async getAllEntriesByQueueName(e){return await(await this.getDb()).getAllFromIndex(E,D,IDBKeyRange.only(e))||[]}async getEntryCountByQueueName(e){return(await this.getDb()).countFromIndex(E,D,IDBKeyRange.only(e))}async deleteEntry(e){await(await this.getDb()).delete(E,e)}async getFirstEntryByQueueName(e){return await this.getEndEntryFromIndex(IDBKeyRange.only(e),`next`)}async getLastEntryByQueueName(e){return await this.getEndEntryFromIndex(IDBKeyRange.only(e),`prev`)}async getEndEntryFromIndex(e,t){return(await(await this.getDb()).transaction(E).store.index(D).openCursor(e,t))?.value}async getDb(){return this._db||=await x(`workbox-background-sync`,3,{upgrade:this._upgradeDb}),this._db}_upgradeDb(e,t){t>0&&t<3&&e.objectStoreNames.contains(E)&&e.deleteObjectStore(E),e.createObjectStore(E,{autoIncrement:!0,keyPath:`id`}).createIndex(D,D,{unique:!1})}},k=class{constructor(e){this._queueName=e,this._queueDb=new O}async pushEntry(e){delete e.id,e.queueName=this._queueName,await this._queueDb.addEntry(e)}async unshiftEntry(e){let t=await this._queueDb.getFirstEntryId();t?e.id=t-1:delete e.id,e.queueName=this._queueName,await this._queueDb.addEntry(e)}async popEntry(){return this._removeEntry(await this._queueDb.getLastEntryByQueueName(this._queueName))}async shiftEntry(){return this._removeEntry(await this._queueDb.getFirstEntryByQueueName(this._queueName))}async getAll(){return await this._queueDb.getAllEntriesByQueueName(this._queueName)}async size(){return await this._queueDb.getEntryCountByQueueName(this._queueName)}async deleteEntry(e){await this._queueDb.deleteEntry(e)}async _removeEntry(e){return e&&await this.deleteEntry(e.id),e}};let A=[`method`,`referrer`,`referrerPolicy`,`mode`,`credentials`,`cache`,`redirect`,`integrity`,`keepalive`];var j=class e{static async fromRequest(t){let n={url:t.url,headers:{}};t.method!==`GET`&&(n.body=await t.clone().arrayBuffer());for(let[e,r]of t.headers.entries())n.headers[e]=r;for(let e of A)t[e]!==void 0&&(n[e]=t[e]);return new e(n)}constructor(e){e.mode===`navigate`&&(e.mode=`same-origin`),this._requestData=e}toObject(){let e=Object.assign({},this._requestData);return e.headers=Object.assign({},this._requestData.headers),e.body&&=e.body.slice(0),e}toRequest(){return new Request(this._requestData.url,this._requestData)}clone(){return new e(this.toObject())}};let M=`workbox-background-sync`,N=new Set,P=e=>{let t={request:new j(e.requestData).toRequest(),timestamp:e.timestamp};return e.metadata&&(t.metadata=e.metadata),t};var F=class{constructor(e,{forceSyncFallback:n,onSync:r,maxRetentionTime:i}={}){if(this._syncInProgress=!1,this._requestsAddedDuringSync=!1,N.has(e))throw new t(`duplicate-queue-name`,{name:e});N.add(e),this._name=e,this._onSync=r||this.replayRequests,this._maxRetentionTime=i||10080,this._forceSyncFallback=!!n,this._queueStore=new k(this._name),this._addSyncListener()}get name(){return this._name}async pushRequest(e){await this._addRequest(e,`push`)}async unshiftRequest(e){await this._addRequest(e,`unshift`)}async popRequest(){return this._removeRequest(`pop`)}async shiftRequest(){return this._removeRequest(`shift`)}async getAll(){let e=await this._queueStore.getAll(),t=Date.now(),n=[];for(let r of e){let e=this._maxRetentionTime*60*1e3;t-r.timestamp>e?await this._queueStore.deleteEntry(r.id):n.push(P(r))}return n}async size(){return await this._queueStore.size()}async _addRequest({request:e,metadata:t,timestamp:n=Date.now()},r){let i={requestData:(await j.fromRequest(e.clone())).toObject(),timestamp:n};switch(t&&(i.metadata=t),r){case`push`:await this._queueStore.pushEntry(i);break;case`unshift`:await this._queueStore.unshiftEntry(i);break}this._syncInProgress?this._requestsAddedDuringSync=!0:await this.registerSync()}async _removeRequest(e){let t=Date.now(),n;switch(e){case`pop`:n=await this._queueStore.popEntry();break;case`shift`:n=await this._queueStore.shiftEntry();break}if(n){let r=this._maxRetentionTime*60*1e3;return t-n.timestamp>r?this._removeRequest(e):P(n)}else return}async replayRequests(){let e;for(;e=await this.shiftRequest();)try{await fetch(e.request.clone())}catch{throw await this.unshiftRequest(e),new t(`queue-replay-failed`,{name:this._name})}}async registerSync(){if(`sync`in self.registration&&!this._forceSyncFallback)try{await self.registration.sync.register(`${M}:${this._name}`)}catch{}}_addSyncListener(){`sync`in self.registration&&!this._forceSyncFallback?self.addEventListener(`sync`,e=>{e.tag===`${M}:${this._name}`&&e.waitUntil((async()=>{this._syncInProgress=!0;let t;try{await this._onSync({queue:this})}catch(e){if(e instanceof Error)throw t=e,t}finally{this._requestsAddedDuringSync&&!(t&&!e.lastChance)&&await this.registerSync(),this._syncInProgress=!1,this._requestsAddedDuringSync=!1}})())}):this._onSync({queue:this})}static get _queueNames(){return N}};let I={status:202,statusText:`Queued`};async function L(e){let t=await self.clients.matchAll({includeUncontrolled:!1});for(let n of t)n.postMessage(e)}async function R(e){let t=await e.getAll(),n=0;for(let e of t){let t=e.metadata;typeof t?.bytes==`number`&&(n+=t.bytes)}return n}async function z(e,t){let n=await R(e);for(;n+t>10485760;){let t=await e.shiftRequest();if(!t)break;let r=t.metadata;n-=r?.bytes??0,await L({type:`interfere.sw.dropped`,url:t.request.url,reason:`queue_size_cap`})}}async function B(e,t){let n=t.clone(),r=(await n.clone().arrayBuffer()).byteLength;await z(e,r),await e.pushRequest({request:n,metadata:{bytes:r}}),await L({type:`interfere.sw.queued`,url:t.url})}function V(e={}){let t=e.match??(e=>e.pathname.startsWith(`/api/interfere/`)),n=new F(e.queueName??`interfere-ingest`,{maxRetentionTime:1440,onSync:async({queue:e})=>{let t=await e.shiftRequest();for(;t;){try{let e=await self.fetch(t.request.clone());if(!e.ok&&(e.status>=500||e.status===429))throw Error(`retry status ${e.status}`);await L({type:`interfere.sw.replayed`,url:t.request.url})}catch(n){throw await e.unshiftRequest(t),n}t=await e.shiftRequest()}}});self.addEventListener(`activate`,e=>{e.waitUntil(n.replayRequests().catch(()=>void 0))}),self.addEventListener(`fetch`,e=>{let{request:r}=e;if(r.method!==`POST`)return;let i;try{i=new URL(r.url)}catch{return}t(i,r)&&e.respondWith((async()=>{try{let e=await self.fetch(r.clone());return e.ok?e:e.status>=500||e.status===429?(await B(n,r),new Response(null,I)):e}catch{return await B(n,r),new Response(null,I)}})())})}self.addEventListener(`install`,()=>{self.skipWaiting()}),self.addEventListener(`activate`,e=>{e.waitUntil(self.clients.claim())}),V({match:e=>e.pathname.startsWith(`/api/interfere/`)})})();";
|