@interfere/react 11.2.1 → 12.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/activity-BfYxFVcK.d.mts +36 -0
- package/dist/api-wpS4xoMY.d.mts +70 -0
- package/dist/api.d.mts +8 -7
- package/dist/api.mjs +1 -1
- package/dist/config-Rm-ct4K1.d.mts +12 -0
- package/dist/device-vFtWy12y.d.mts +35 -0
- package/dist/error-boundary.d.mts +7 -7
- package/dist/error-boundary.mjs +1 -1
- package/dist/exporter-logs-CeGwMiMo.d.mts +51 -0
- package/dist/geo-PDfIZY4L.d.mts +16 -0
- package/dist/index-D-HGWlgi.d.mts +4 -0
- package/dist/instrumentations-Bgm2EazU.d.mts +91 -0
- package/dist/internal/activity.d.mts +1 -35
- package/dist/internal/activity.mjs +1 -1
- package/dist/internal/browser-context.d.mts +44 -4
- package/dist/internal/browser-context.mjs +1 -1
- package/dist/internal/capture-boundary.d.mts +6 -5
- package/dist/internal/capture-boundary.mjs +1 -1
- package/dist/internal/capture.d.mts +3 -4
- package/dist/internal/capture.mjs +1 -1
- package/dist/internal/config.d.mts +26 -27
- package/dist/internal/config.mjs +1 -1
- package/dist/internal/consent.d.mts +16 -8
- package/dist/internal/console-patch.d.mts +6 -7
- package/dist/internal/console-patch.mjs +1 -1
- package/dist/internal/dom/actionable.d.mts +5 -7
- package/dist/internal/dom/actionable.mjs +1 -1
- package/dist/internal/hydration.d.mts +2 -3
- package/dist/internal/kernel-registry.d.mts +5 -6
- package/dist/internal/kernel.d.mts +2 -292
- package/dist/internal/kernel.mjs +1 -1
- package/dist/internal/otel/exporter-logs.d.mts +1 -12
- package/dist/internal/otel/exporter-logs.mjs +1 -1
- package/dist/internal/otel/exporter-metrics.d.mts +2 -14
- package/dist/internal/otel/exporter-metrics.mjs +1 -1
- package/dist/internal/otel/exporter.d.mts +2 -29
- package/dist/internal/otel/exporter.mjs +1 -1
- package/dist/internal/otel/http-span-route.d.mts +4 -0
- package/dist/internal/otel/http-span-route.mjs +1 -0
- package/dist/internal/otel/index.d.mts +7 -5
- package/dist/internal/otel/index.mjs +1 -1
- package/dist/internal/otel/initial-navigation.d.mts +3 -4
- package/dist/internal/otel/initial-navigation.mjs +1 -1
- package/dist/internal/otel/instrumentations.d.mts +2 -89
- package/dist/internal/otel/instrumentations.mjs +1 -1
- package/dist/internal/otel/navigation.d.mts +2 -0
- package/dist/internal/otel/navigation.mjs +1 -0
- package/dist/internal/otel/normalize-path.d.mts +9 -0
- package/dist/internal/otel/normalize-path.mjs +1 -0
- package/dist/internal/otel/page-scope-context-manager.d.mts +2 -31
- package/dist/internal/otel/page-scope-context-manager.mjs +1 -1
- package/dist/internal/otel/propagation.d.mts +1 -19
- package/dist/internal/otel/propagation.mjs +1 -1
- package/dist/internal/otel/provider.d.mts +1 -86
- package/dist/internal/otel/provider.mjs +1 -1
- package/dist/internal/otel/url-attrs.d.mts +7 -0
- package/dist/internal/otel/url-attrs.mjs +1 -0
- package/dist/internal/otel/web-vitals.d.mts +1 -33
- package/dist/internal/otel/web-vitals.mjs +1 -1
- package/dist/internal/page-lifecycle.d.mts +4 -5
- package/dist/internal/page-lifecycle.mjs +1 -1
- package/dist/internal/plugin-runtime.d.mts +1 -38
- package/dist/internal/plugin-runtime.mjs +1 -1
- package/dist/internal/react-context.d.mts +1 -43
- package/dist/internal/react-context.mjs +1 -1
- package/dist/internal/server-digest.d.mts +4 -3
- package/dist/internal/server-digest.mjs +1 -1
- package/dist/internal/sw-queue.d.mts +4 -5
- package/dist/internal/sw-queue.mjs +1 -1
- package/dist/internal/third-party-hosts.d.mts +2 -3
- package/dist/internal/third-party-hosts.mjs +1 -1
- package/dist/internal/version.d.mts +4 -5
- package/dist/internal/version.mjs +1 -1
- package/dist/internal/wrapper-singleton.d.mts +5 -6
- package/dist/internal/wrapper-singleton.mjs +1 -1
- package/dist/navigation-CkiV6lJY.d.mts +54 -0
- package/dist/page-scope-context-manager-r92ZMJok.d.mts +17 -0
- package/dist/plugins/device.d.mts +5 -0
- package/dist/plugins/device.mjs +1 -0
- package/dist/plugins/errors.d.mts +1 -1
- package/dist/plugins/errors.mjs +1 -1
- package/dist/plugins/lib/loader.d.mts +2 -11
- package/dist/plugins/lib/loader.mjs +1 -1
- package/dist/plugins/lib/types.d.mts +2 -24
- package/dist/plugins/logs.d.mts +1 -1
- package/dist/plugins/logs.mjs +1 -1
- package/dist/plugins/page-events.d.mts +5 -0
- package/dist/plugins/page-events.mjs +1 -0
- package/dist/plugins/rage-clicks.d.mts +1 -1
- package/dist/plugins/rage-clicks.mjs +1 -1
- package/dist/plugins/replay/buffer.d.mts +9 -6
- package/dist/plugins/replay/buffer.mjs +1 -1
- package/dist/plugins/replay/chunking.d.mts +18 -9
- package/dist/plugins/replay/chunking.mjs +1 -1
- package/dist/plugins/replay/compressor-host.d.mts +3 -4
- package/dist/plugins/replay/compressor-host.mjs +1 -1
- package/dist/plugins/replay/config.d.mts +1 -11
- package/dist/plugins/replay/config.mjs +1 -1
- package/dist/plugins/replay/delivery.d.mts +8 -9
- package/dist/plugins/replay/delivery.mjs +1 -1
- package/dist/plugins/replay/index.d.mts +1 -1
- package/dist/plugins/replay/index.mjs +1 -1
- package/dist/plugins/replay/recorder.d.mts +12 -6
- package/dist/plugins/replay/recorder.mjs +1 -1
- package/dist/plugins/replay/sequence.d.mts +5 -4
- package/dist/plugins/replay/sequence.mjs +1 -1
- package/dist/plugins/replay/upload.d.mts +4 -0
- package/dist/plugins/replay/upload.mjs +1 -0
- package/dist/propagation-CssinKCy.d.mts +26 -0
- package/dist/provider-CF0h4Mah.d.mts +101 -0
- package/dist/provider-DRuvRafW.d.mts +466 -0
- package/dist/provider.d.mts +2 -58
- package/dist/provider.mjs +1 -1
- package/dist/react-error-handler.d.mts +9 -11
- package/dist/react-error-handler.mjs +1 -1
- package/dist/rrweb-CE6GKCEq.mjs +150 -0
- package/dist/tracking/api.d.mts +1 -54
- package/dist/tracking/api.mjs +1 -1
- package/dist/tracking/device.d.mts +1 -32
- package/dist/tracking/device.mjs +1 -1
- package/dist/tracking/geo.d.mts +1 -13
- package/dist/tracking/geo.mjs +1 -1
- package/dist/tracking/session.d.mts +6 -7
- package/dist/tracking/session.mjs +1 -1
- package/dist/util/bot.d.mts +2 -3
- package/dist/util/bot.mjs +1 -1
- package/dist/util/global.d.mts +4 -10
- package/dist/util/global.mjs +1 -1
- package/dist/util/log.d.mts +11 -7
- package/dist/util/log.mjs +1 -1
- package/dist/util/stringify.d.mts +2 -3
- package/dist/version-XRSM9Pwm.mjs +1 -0
- package/dist/web-vitals-x8y70B5x.d.mts +34 -0
- package/package.json +67 -48
- package/dist/package.mjs +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
const THIRD_PARTY_IGNORE=[/(?:^|\/\/)clerk\./u,/\.clerk\./u,/\.sentry\./u,/\.intercom\./u,/\.posthog\./u,/\.googletagmanager\.com/u,/\.google-analytics\.com/u,/\.googleapis\.com/u,/\.doubleclick\.net/u,/\.facebook\.com/u,/\.fbcdn\.net/u,/\.analytics\.google\.com/u,/px\.ads\.linkedin\.com/u
|
|
1
|
+
const THIRD_PARTY_IGNORE=[/(?:^|\/\/)clerk\./u,/\.clerk\./u,/\.sentry\./u,/\.intercom\./u,/\.posthog\./u,/\.googletagmanager\.com/u,/\.google-analytics\.com/u,/\.googleapis\.com/u,/\.doubleclick\.net/u,/\.facebook\.com/u,/\.fbcdn\.net/u,/\.analytics\.google\.com/u,/px\.ads\.linkedin\.com/u,/\/__nextjs_/u];export{THIRD_PARTY_IGNORE};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
//#region src/internal/version.d.ts
|
|
2
|
-
declare const SDK_NAME: string;
|
|
3
|
-
declare const SDK_VERSION: string;
|
|
4
|
-
declare const PRODUCER_VERSION: string;
|
|
5
|
-
//#endregion
|
|
6
|
-
export { PRODUCER_VERSION, SDK_NAME, SDK_VERSION };
|
|
2
|
+
export declare const SDK_NAME: string;
|
|
3
|
+
export declare const SDK_VERSION: string;
|
|
4
|
+
export declare const PRODUCER_VERSION: string;
|
|
5
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{n as SDK_NAME,r as SDK_VERSION,t as PRODUCER_VERSION}from"../version-XRSM9Pwm.mjs";export{PRODUCER_VERSION,SDK_NAME,SDK_VERSION};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as KernelOptions, s as Kernel } from "../provider-DRuvRafW.mjs";
|
|
2
2
|
import { ConsentState } from "@interfere/types/sdk/plugins/manifest";
|
|
3
3
|
import { IdentifyParams } from "@interfere/types/sdk/identify";
|
|
4
4
|
//#region src/internal/wrapper-singleton.d.ts
|
|
5
|
-
interface WrapperSingleton {
|
|
5
|
+
export interface WrapperSingleton {
|
|
6
6
|
close: () => Promise<void>;
|
|
7
7
|
consent: {
|
|
8
8
|
get: () => ConsentState | null;
|
|
@@ -18,7 +18,7 @@ interface WrapperSingleton {
|
|
|
18
18
|
init: (opts?: KernelOptions) => Promise<Kernel | null>;
|
|
19
19
|
subscribeToKernel: (listener: () => void) => () => void;
|
|
20
20
|
}
|
|
21
|
-
interface WrapperSingletonInput {
|
|
21
|
+
export interface WrapperSingletonInput {
|
|
22
22
|
/**
|
|
23
23
|
* Surfaced in the "kernel not initialized" error so the message points
|
|
24
24
|
* customers at the right entry file (e.g. "instrumentation-client" /
|
|
@@ -42,6 +42,5 @@ interface WrapperSingletonInput {
|
|
|
42
42
|
* kernel is per-wrapper, not per-process. (Microfrontends can run two
|
|
43
43
|
* different wrappers on the same page; each gets its own kernel.)
|
|
44
44
|
*/
|
|
45
|
-
declare function createWrapperSingleton(input: WrapperSingletonInput): WrapperSingleton;
|
|
46
|
-
//#endregion
|
|
47
|
-
export { WrapperSingleton, WrapperSingletonInput, createWrapperSingleton };
|
|
45
|
+
export declare function createWrapperSingleton(input: WrapperSingletonInput): WrapperSingleton;
|
|
46
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{registerKernel,unregisterKernel}from"./kernel-registry.mjs";import{
|
|
1
|
+
import{registerKernel,unregisterKernel}from"./kernel-registry.mjs";import{isBotUserAgent}from"../util/bot.mjs";import{isForceEnableActive}from"./config.mjs";import{createKernel,isEnabledByEnvironment}from"./kernel.mjs";function createWrapperSingleton(input){let kernel=null,pending=null,listeners=new Set;function emit(){for(let listener of listeners)listener()}async function initKernel(internalOpts){try{let k=await createKernel({opts:internalOpts});return kernel=k,registerKernel(k),emit(),k}finally{pending=null}}return{async close(){if(!kernel)return;let previous=kernel;await previous.dispose(),kernel=null,unregisterKernel(previous),emit()},consent:{get:()=>kernel?.consent.get()??null,set:value=>kernel?.consent.set(value)},getKernel(){if(!kernel)throw Error(`Interfere SDK not initialized. Call init() from your ${input.initEntryName} entrypoint.`);return kernel},getKernelOrNull(){return kernel},identity:{clear:()=>kernel?.identity.clear(),get:()=>kernel?.identity.get()??null,set:async params=>kernel?.identity.set(params)??Promise.resolve()},async init(opts={}){return kernel||pending||(!(opts.enabled??isEnabledByEnvironment())||!isForceEnableActive()&&isBotUserAgent()?null:(pending=initKernel({...opts,_wrapperVersions:[input.producerVersion]}),pending))},subscribeToKernel(listener){return listeners.add(listener),(()=>{listeners.delete(listener)})}}}export{createWrapperSingleton};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Context, Tracer } from "@opentelemetry/api";
|
|
2
|
+
//#region src/internal/otel/navigation.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* A parked tab accretes spans onto its page scope for as long as the
|
|
5
|
+
* scope lives; without a ceiling one trace was observed holding 4,251
|
|
6
|
+
* spans across 19.5 hours. Rotated traces link back to their
|
|
7
|
+
* predecessor, so the session story survives the cut.
|
|
8
|
+
*/
|
|
9
|
+
declare const PAGE_SCOPE_MAX_AGE_MS: number;
|
|
10
|
+
interface NavigationScopeInput {
|
|
11
|
+
resolveRoute?: ((pathname: string) => string | undefined) | undefined;
|
|
12
|
+
setPageScope: (ctx: Context) => void;
|
|
13
|
+
tracer: Tracer;
|
|
14
|
+
}
|
|
15
|
+
interface ScopeSpanInput {
|
|
16
|
+
resolveRoute?: ((pathname: string) => string | undefined) | undefined;
|
|
17
|
+
tracer: Tracer;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Client-side page scope for the initial page view, used when no SSR
|
|
21
|
+
* `<meta name="traceparent">` exists (SPA surfaces, static pages, cached
|
|
22
|
+
* HTML the freshness guard refused). Without it, every observation made
|
|
23
|
+
* before the first soft navigation — long tasks, clicks, fetches — falls
|
|
24
|
+
* back to `ROOT_CONTEXT` and becomes its own single-span trace.
|
|
25
|
+
*/
|
|
26
|
+
declare function seedPageScope(input: ScopeSpanInput): Context;
|
|
27
|
+
/**
|
|
28
|
+
* Rotator for {@link PageScopeContextManager.setRotation}: starts a fresh
|
|
29
|
+
* `navigation` root (trigger `max_age`) linked to the expiring scope's
|
|
30
|
+
* span, so a tab parked on one route all day produces a chain of bounded
|
|
31
|
+
* traces instead of one unbounded one.
|
|
32
|
+
*/
|
|
33
|
+
declare function createPageScopeRotator(input: ScopeSpanInput): (previous: Context) => Context;
|
|
34
|
+
/**
|
|
35
|
+
* Refreshes the page-scope context on SPA navigations so each page view
|
|
36
|
+
* gets its own bounded trace.
|
|
37
|
+
*
|
|
38
|
+
* The initial page scope comes from the SSR `<meta name="traceparent">`
|
|
39
|
+
* tag, read once at SDK init. Frameworks never update that tag on soft
|
|
40
|
+
* navigation, so without this every span from page 2..N of a session
|
|
41
|
+
* would keep stitching onto page 1's server trace. On each real URL
|
|
42
|
+
* change (history push/replace or popstate; hash-only changes excluded)
|
|
43
|
+
* this starts a zero-duration `navigation` root span and re-anchors the
|
|
44
|
+
* page scope to it — the server render of the *next* page still joins
|
|
45
|
+
* the *previous* page's trace via the propagated router fetch, which is
|
|
46
|
+
* causally where it belongs.
|
|
47
|
+
*
|
|
48
|
+
* Deliberately not consent-gated: it shapes trace topology the same way
|
|
49
|
+
* the SSR meta tag does, and records only the URL attrs every other
|
|
50
|
+
* span already carries.
|
|
51
|
+
*/
|
|
52
|
+
declare function installNavigationScope(input: NavigationScopeInput): () => void;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { seedPageScope as a, installNavigationScope as i, PAGE_SCOPE_MAX_AGE_MS as n, createPageScopeRotator as r, NavigationScopeInput as t };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Context } from "@opentelemetry/api";
|
|
2
|
+
import { ZoneContextManager } from "@opentelemetry/context-zone";
|
|
3
|
+
//#region src/internal/otel/page-scope-context-manager.d.ts
|
|
4
|
+
type PageScopeRotator = (previous: Context) => Context;
|
|
5
|
+
declare class PageScopeContextManager extends ZoneContextManager {
|
|
6
|
+
private pageScope;
|
|
7
|
+
private readonly pageSpans;
|
|
8
|
+
private pageScopeSetAtMs;
|
|
9
|
+
private rotator;
|
|
10
|
+
private maxAgeMs;
|
|
11
|
+
private rotating;
|
|
12
|
+
setPageScope(ctx: Context): void;
|
|
13
|
+
setRotation(rotator: PageScopeRotator, maxAgeMs: number): void;
|
|
14
|
+
active(): Context;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { PageScopeRotator as n, PageScopeContextManager as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const devicePlugin={name:`device`,setup(ctx){return ctx.startDeviceInfoCapture()??void 0}};export{devicePlugin as default};
|
package/dist/plugins/errors.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{hasServerDigest}from"../internal/server-digest.mjs";import{onConsoleCall}from"../internal/console-patch.mjs";import{THIRD_PARTY_IGNORE}from"../internal/third-party-hosts.mjs";import{MECHANISM_TYPE,isNonErrorException,toException}from"@interfere/types/sdk/errors";function capture(ctx,opts){ctx.recordException(opts.error,{mechanism:opts.mechanism
|
|
1
|
+
import{hasServerDigest}from"../internal/server-digest.mjs";import{onConsoleCall}from"../internal/console-patch.mjs";import{THIRD_PARTY_IGNORE}from"../internal/third-party-hosts.mjs";import{MECHANISM_TYPE,isNonErrorException,toException}from"@interfere/types/sdk/errors";import{omitUndefined}from"@interfere/helpers/omit-undefined";function capture(ctx,opts){ctx.recordException(opts.error,omitUndefined({mechanism:opts.mechanism,fallbackFrames:opts.fallbackFrame&&!isNonErrorException(opts.error)?[opts.fallbackFrame]:void 0}))}function findErrorArg(args){return args.find(arg=>arg instanceof Error)??null}function resourceError(target){return target instanceof HTMLImageElement||target instanceof HTMLScriptElement?target.src?{tag:target.localName,url:target.src}:null:target instanceof HTMLLinkElement&&target.href?{tag:target.localName,url:target.href}:null}function isIgnoredResourceUrl(url){return THIRD_PARTY_IGNORE.some(pattern=>pattern.test(url))}function createResourceGate(){let seen=new Set,windowStart=0,windowCount=0;return(url=>{if(seen.has(url))return!1;let now=Date.now();return now-windowStart>6e4&&(windowStart=now,windowCount=0),windowCount>=20?!1:(seen.size>=100&&seen.clear(),seen.add(url),windowCount+=1,!0)})}function createRepeatGate(){let counts=new Map,windowStart=0;return(key=>{let now=Date.now();now-windowStart>6e4&&(windowStart=now,counts.clear());let count=counts.get(key)??0;return count>=5?!1:(counts.size>=100&&counts.clear(),counts.set(key,count+1),!0)})}const errorsPlugin={name:`errors`,setup(ctx){let originalOnError=globalThis.onerror,originalStackTraceLimit=Error.stackTraceLimit;Error.stackTraceLimit<50&&(Error.stackTraceLimit=50);let repeatGate=createRepeatGate();globalThis.onerror=(msg,source,line,col,error)=>{if(error instanceof Error&&!hasServerDigest(error)&&repeatGate(`${typeof msg==`string`?msg:msg.type}|${String(source)}|${String(line)}|${String(col)}`)){let fallbackFrame=typeof source==`string`?omitUndefined({file:source,id:`fallback-0`,line:typeof line==`number`?line:void 0,column:typeof col==`number`?col:void 0}):null;capture(ctx,omitUndefined({error,mechanism:{handled:!1,type:MECHANISM_TYPE.browser.onerror},fallbackFrame:fallbackFrame??void 0}))}return typeof originalOnError==`function`&&originalOnError.call(globalThis,msg,source,line,col,error)};let onUnhandledRejection=event=>{hasServerDigest(event.reason)||capture(ctx,{error:toException(event.reason),mechanism:omitUndefined({handled:!1,type:MECHANISM_TYPE.browser.onunhandledrejection,synthetic:event.reason instanceof Error?void 0:!0})})},cspReportsByDisposition={enforce:new Set,report:new Set},onSecurityPolicyViolation=event=>{let seenCspReports=cspReportsByDisposition[event.disposition],key=`${event.violatedDirective}|${event.blockedURI}`;seenCspReports.has(key)||seenCspReports.size>=5||(seenCspReports.add(key),capture(ctx,{error:{kind:`non-error`,serialized:{blockedURI:event.blockedURI,columnNumber:event.columnNumber,disposition:event.disposition,lineNumber:event.lineNumber,sourceFile:event.sourceFile,violatedDirective:event.violatedDirective},type:`SecurityPolicyViolation`,value:event.disposition===`report`?`CSP ${event.violatedDirective} reported ${event.blockedURI||`inline`} (report-only)`:`CSP ${event.violatedDirective} blocked ${event.blockedURI||`inline`}`},mechanism:{handled:event.disposition===`report`,type:MECHANISM_TYPE.browser.securityPolicyViolation}}))},resourceGate=createResourceGate(),onResourceError=event=>{let resource=resourceError(event.target);resource&&!isIgnoredResourceUrl(resource.url)&&resourceGate(resource.url)&&capture(ctx,{error:{kind:`non-error`,serialized:{url:resource.url},type:`ResourceLoadError`,value:`Failed to load ${resource.tag}: ${resource.url}`},mechanism:{handled:!1,type:MECHANISM_TYPE.browser.resourceError}})},supportsEventTarget=typeof globalThis.addEventListener==`function`&&typeof globalThis.removeEventListener==`function`;supportsEventTarget&&(globalThis.addEventListener(`unhandledrejection`,onUnhandledRejection),globalThis.addEventListener(`securitypolicyviolation`,onSecurityPolicyViolation),globalThis.addEventListener(`error`,onResourceError,!0));let unsubscribeConsole=onConsoleCall((level,args)=>{if(level!==`error`)return;let error=findErrorArg(args);error&&!hasServerDigest(error)&&capture(ctx,{error,mechanism:{handled:!0,type:MECHANISM_TYPE.browser.consoleError}})});return()=>{Error.stackTraceLimit=originalStackTraceLimit,globalThis.onerror=originalOnError,supportsEventTarget&&(globalThis.removeEventListener(`unhandledrejection`,onUnhandledRejection),globalThis.removeEventListener(`securitypolicyviolation`,onSecurityPolicyViolation),globalThis.removeEventListener(`error`,onResourceError,!0)),unsubscribeConsole()}}};export{errorsPlugin as default};
|
|
@@ -1,11 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { PluginKey } from "@interfere/types/sdk/plugins/manifest";
|
|
4
|
-
//#region src/plugins/lib/loader.d.ts
|
|
5
|
-
type PluginOverrides = Partial<Record<Exclude<PluginKey, "replay">, boolean>> & {
|
|
6
|
-
replay?: ReplayOption;
|
|
7
|
-
};
|
|
8
|
-
declare function resolveFeatures(overrides?: PluginOverrides): Record<PluginKey, boolean>;
|
|
9
|
-
declare function loadPlugin(key: PluginKey, context: PluginContext): Promise<PluginCleanup | null>;
|
|
10
|
-
//#endregion
|
|
11
|
-
export { PluginOverrides, loadPlugin, resolveFeatures };
|
|
1
|
+
import { C as loadPlugin, S as PluginOverrides, w as resolveFeatures, x as LOADERS } from "../../provider-DRuvRafW.mjs";
|
|
2
|
+
export { LOADERS, PluginOverrides, loadPlugin, resolveFeatures };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"../../util/log.mjs";import{PLUGIN_MANIFEST}from"@interfere/types/sdk/plugins/manifest";const log=createLogger(`plugins`),LOADERS={
|
|
1
|
+
import{createLogger}from"../../util/log.mjs";import errorsPlugin from"../errors.mjs";import{PLUGIN_MANIFEST}from"@interfere/types/sdk/plugins/manifest";const log=createLogger(`plugins`),LOADERS={device:async()=>import(`../device.mjs`),errors:()=>errorsPlugin,logs:async()=>import(`../logs.mjs`),pageEvents:async()=>import(`../page-events.mjs`),rageClick:async()=>import(`../rage-clicks.mjs`),replay:async()=>import(`../replay/index.mjs`)},DEFAULTS=Object.fromEntries(PLUGIN_MANIFEST.map(p=>[p.name,p.defaultEnabled]));function resolveFeatures(overrides){if(!overrides)return{...DEFAULTS};let{replay,...rest}=overrides;return{...DEFAULTS,...rest,replay:replay===void 0?DEFAULTS.replay:replay!==!1}}function resolvePlugin(mod){return`default`in mod?mod.default:mod}async function loadPlugin(key,context){let loader=LOADERS[key];if(loader===void 0)return null;try{let cleanup=resolvePlugin(await loader()).setup(context);return log.debug(`loaded %s`,key),cleanup??null}catch{return log.error(`failed to load plugin %s`,key),null}}export{LOADERS,loadPlugin,resolveFeatures};
|
|
@@ -1,24 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { RecordExceptionOpts } from "../../internal/kernel.mjs";
|
|
4
|
-
import { NonErrorException } from "@interfere/types/sdk/errors";
|
|
5
|
-
import { PluginKey } from "@interfere/types/sdk/plugins/manifest";
|
|
6
|
-
//#region src/plugins/lib/types.d.ts
|
|
7
|
-
type PluginCleanup = () => void;
|
|
8
|
-
type PluginConfigState = "disabled" | "enabled" | "pending";
|
|
9
|
-
interface PluginContext {
|
|
10
|
-
getPluginConfigState: (key: PluginKey) => PluginConfigState;
|
|
11
|
-
getReplayConfig: () => ReplayConfig;
|
|
12
|
-
getSessionId: () => string;
|
|
13
|
-
getWindowId: () => string;
|
|
14
|
-
onSessionPause: (handler: () => void) => () => void;
|
|
15
|
-
onSessionResume: (handler: (reason: SessionResumeReason) => void) => () => void;
|
|
16
|
-
recordException: (value: Error | NonErrorException, opts: RecordExceptionOpts) => void;
|
|
17
|
-
subscribePluginConfigState: (key: PluginKey, listener: (state: PluginConfigState) => void) => () => void;
|
|
18
|
-
}
|
|
19
|
-
interface Plugin {
|
|
20
|
-
readonly name: string;
|
|
21
|
-
setup: (ctx: PluginContext) => PluginCleanup | undefined;
|
|
22
|
-
}
|
|
23
|
-
//#endregion
|
|
24
|
-
export { Plugin, PluginCleanup, PluginConfigState, PluginContext };
|
|
1
|
+
import { D as PluginConfigState, E as PluginCleanup, O as PluginContext, T as Plugin, k as PluginTeardownReason } from "../../provider-DRuvRafW.mjs";
|
|
2
|
+
export { Plugin, PluginCleanup, PluginConfigState, PluginContext, PluginTeardownReason };
|
package/dist/plugins/logs.d.mts
CHANGED
package/dist/plugins/logs.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{activeKernel}from"../internal/kernel-registry.mjs";import{
|
|
1
|
+
import{activeKernel}from"../internal/kernel-registry.mjs";import{safeStringify}from"../util/stringify.mjs";import{onConsoleCall}from"../internal/console-patch.mjs";import{SeverityNumber}from"@opentelemetry/api-logs";const LEVEL_TO_SEVERITY={debug:{number:SeverityNumber.DEBUG,text:`DEBUG`},error:{number:SeverityNumber.ERROR,text:`ERROR`},info:{number:SeverityNumber.INFO,text:`INFO`},log:{number:SeverityNumber.INFO,text:`INFO`},warn:{number:SeverityNumber.WARN,text:`WARN`}},logsPlugin={name:`logs`,setup(){return onConsoleCall((level,args)=>{if(args.some(a=>a instanceof Error))return;let kernel=activeKernel();if(!kernel)return;let sev=LEVEL_TO_SEVERITY[level];kernel.recordLog({attributes:{"console.level":level},body:args.length===1?safeStringify(args[0]):args.map(element=>safeStringify(element)).join(` `),severityNumber:sev.number,severityText:sev.text})})}};export{logsPlugin as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const pageEventsPlugin={name:`pageEvents`,setup(ctx){return ctx.startPageEventsCapture()??void 0}};export{pageEventsPlugin as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
function distance(a,b){return Math.hypot(a.x-b.x,a.y-b.y)}function selectorFor(el){if(!el)return`unknown`;if(el.id)return`#${el.id}`;let classes=[...el.classList].slice(0,3).join(`.`),tag=el.tagName.toLowerCase();return classes?`${tag}.${classes}`:tag}const rageClicksPlugin={name:`rage-clicks`,setup(ctx){let clicks=[],onClick=event=>{let now=Date.now();for(clicks.push({target:event.target instanceof Element?event.target:null,ts:now,x:event.clientX,y:event.clientY});clicks.length>0&&now-(clicks[0]?.ts??0)>800;)clicks.shift();if(clicks.length<3)return;let[anchor]=clicks;if(!anchor)return;let clustered=clicks.filter(c=>distance(anchor,c)<=30);if(clustered.length<3)return;let last=clustered.at(-1);last&&(ctx.getTracer(`@interfere/react/rage-clicks`).startSpan(`rage_click`,{attributes:{"ui.event_type":`rage_click`,"ui.rage_click.count":clustered.length,"ui.rage_click.selector":selectorFor(last.target),"ui.rage_click.text":last.target?.textContent.trim().slice(0,120)??``,"ui.rage_click.time_window_ms":last.ts-anchor.ts,"ui.rage_click.x":last.x,"ui.rage_click.y":last.y}}).end(),clicks.length=0)};return document.addEventListener(`click`,onClick,{capture:!0}),()=>{document.removeEventListener(`click`,onClick,{capture:!0})}}};export{rageClicksPlugin as default};
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { BufferedReplayEvent } from "./chunking.mjs";
|
|
2
|
+
import { eventWithTime } from "@rrweb/types";
|
|
2
3
|
//#region src/plugins/replay/buffer.d.ts
|
|
3
|
-
interface ReplayBuffer {
|
|
4
|
+
export interface ReplayBuffer {
|
|
4
5
|
drain: () => BufferedReplayEvent[];
|
|
5
|
-
push: (event:
|
|
6
|
+
push: (event: eventWithTime) => void;
|
|
6
7
|
}
|
|
7
|
-
declare function createReplayBuffer(opts: {
|
|
8
|
+
export declare function createReplayBuffer(opts: {
|
|
8
9
|
onSoftLimit: () => void;
|
|
9
10
|
softLimitBytes: number;
|
|
10
|
-
}):
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
}): {
|
|
12
|
+
drain(): BufferedReplayEvent[];
|
|
13
|
+
push(event: eventWithTime): void;
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{classifyReplayEvent,utf8ByteLength}from"./chunking.mjs";function createReplayBuffer(opts){let entries=[],totalBytes=0;return{push(event){let json=JSON.stringify(event),bytes=utf8ByteLength(json),{isActivity,isFullSnapshot}=classifyReplayEvent(event);entries.push({json,ts:Date.now(),bytes,isActivity,isFullSnapshot}),totalBytes+=bytes,totalBytes>=opts.softLimitBytes&&opts.onSoftLimit()}
|
|
1
|
+
import{classifyReplayEvent,utf8ByteLength}from"./chunking.mjs";function createReplayBuffer(opts){let entries=[],totalBytes=0;return{drain(){let drained=entries;return entries=[],totalBytes=0,drained},push(event){let json=JSON.stringify(event),bytes=utf8ByteLength(json),{isActivity,isFullSnapshot}=classifyReplayEvent(event);entries.push({json,ts:Date.now(),bytes,isActivity,isFullSnapshot}),totalBytes+=bytes,totalBytes>=opts.softLimitBytes&&opts.onSoftLimit()}}}export{createReplayBuffer};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
//#region src/plugins/replay/chunking.d.ts
|
|
2
|
-
interface BufferedReplayEvent {
|
|
2
|
+
export interface BufferedReplayEvent {
|
|
3
3
|
bytes: number;
|
|
4
4
|
isActivity: boolean;
|
|
5
5
|
isFullSnapshot: boolean;
|
|
6
6
|
json: string;
|
|
7
7
|
ts: number;
|
|
8
8
|
}
|
|
9
|
-
interface ReplayChunkPayload {
|
|
9
|
+
export interface ReplayChunkPayload {
|
|
10
10
|
activityCount: number;
|
|
11
11
|
bodyJson: string;
|
|
12
12
|
eventCount: number;
|
|
@@ -14,16 +14,25 @@ interface ReplayChunkPayload {
|
|
|
14
14
|
hasFullSnapshot: boolean;
|
|
15
15
|
lastTs: number | null;
|
|
16
16
|
}
|
|
17
|
-
interface SplitReplayEventsResult {
|
|
17
|
+
export interface SplitReplayEventsResult {
|
|
18
18
|
chunks: BufferedReplayEvent[][];
|
|
19
19
|
droppedEventBytes: number[];
|
|
20
20
|
}
|
|
21
|
-
declare function classifyReplayEvent(event: unknown): {
|
|
21
|
+
export declare function classifyReplayEvent(event: unknown): {
|
|
22
22
|
isActivity: boolean;
|
|
23
23
|
isFullSnapshot: boolean;
|
|
24
24
|
};
|
|
25
|
-
declare function toReplayChunkPayload(group: BufferedReplayEvent[]):
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
export declare function toReplayChunkPayload(group: BufferedReplayEvent[]): {
|
|
26
|
+
activityCount: number;
|
|
27
|
+
bodyJson: string;
|
|
28
|
+
eventCount: number;
|
|
29
|
+
firstTs: number | null;
|
|
30
|
+
hasFullSnapshot: boolean;
|
|
31
|
+
lastTs: number | null;
|
|
32
|
+
};
|
|
33
|
+
export declare function splitReplayEvents(buffered: BufferedReplayEvent[]): {
|
|
34
|
+
chunks: BufferedReplayEvent[][];
|
|
35
|
+
droppedEventBytes: number[];
|
|
36
|
+
};
|
|
37
|
+
export declare function utf8ByteLength(value: string): number;
|
|
38
|
+
//#endregion
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{EventType,IncrementalSource}from"@rrweb/types";const textEncoder=new TextEncoder,ACTIVITY_SOURCES=new Set([IncrementalSource.MouseMove,IncrementalSource.MouseInteraction,IncrementalSource.Scroll,IncrementalSource.Input,IncrementalSource.TouchMove,IncrementalSource.Drag,IncrementalSource.Selection]);function classifyReplayEvent(event){
|
|
1
|
+
import{EventType,IncrementalSource}from"@rrweb/types";const textEncoder=new TextEncoder,ACTIVITY_SOURCES=new Set([IncrementalSource.MouseMove,IncrementalSource.MouseInteraction,IncrementalSource.Scroll,IncrementalSource.Input,IncrementalSource.TouchMove,IncrementalSource.Drag,IncrementalSource.Selection]);function classifyReplayEvent(event){if(typeof event!=`object`||!event||!(`type`in event)||typeof event.type!=`number`)return{isActivity:!1,isFullSnapshot:!1};if(event.type===EventType.FullSnapshot)return{isActivity:!1,isFullSnapshot:!0};if(event.type===EventType.IncrementalSnapshot&&`data`in event&&event.data!==null&&typeof event.data==`object`&&`source`in event.data){let{source}=event.data;if(typeof source==`number`&&ACTIVITY_SOURCES.has(source))return{isActivity:!0,isFullSnapshot:!1}}return{isActivity:!1,isFullSnapshot:!1}}function countActivityEvents(group){let count=0;for(let entry of group)entry.isActivity&&(count+=1);return count}function toReplayChunkPayload(group){let parts=[],hasFullSnapshot=!1;for(let entry of group)parts.push(entry.json),entry.isFullSnapshot&&(hasFullSnapshot=!0);let firstTs=group[0]?.ts??null;return{activityCount:countActivityEvents(group),bodyJson:parts.length>0?`${parts.join(`
|
|
2
2
|
`)}\n`:``,eventCount:group.length,firstTs,hasFullSnapshot,lastTs:group.at(-1)?.ts??null}}function splitReplayEvents(buffered){let chunks=[],droppedEventBytes=[],current=[],currentBytes=0;for(let entry of buffered){let singleEventBodyBytes=entry.bytes+1;if(singleEventBodyBytes>8388608){droppedEventBytes.push(singleEventBodyBytes);continue}current.length>0&¤tBytes+singleEventBodyBytes>6291456&&(chunks.push(current),current=[],currentBytes=0),currentBytes+=singleEventBodyBytes,current.push(entry)}return current.length>0&&chunks.push(current),{chunks,droppedEventBytes}}function utf8ByteLength(value){return textEncoder.encode(value).length}export{classifyReplayEvent,splitReplayEvents,toReplayChunkPayload,utf8ByteLength};
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
//#region src/plugins/replay/compressor-host.d.ts
|
|
2
2
|
/** `gzip` is `null` when compression is unavailable — callers must POST the chunk uncompressed. */
|
|
3
|
-
interface Compressor {
|
|
3
|
+
export interface Compressor {
|
|
4
4
|
readonly dispose: () => void;
|
|
5
5
|
readonly gzip: ((bytes: Uint8Array) => Promise<Uint8Array<ArrayBuffer>>) | null;
|
|
6
6
|
}
|
|
7
|
-
declare function createCompressor(): Compressor;
|
|
8
|
-
//#endregion
|
|
9
|
-
export { Compressor, createCompressor };
|
|
7
|
+
export declare function createCompressor(): Compressor;
|
|
8
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"../../util/log.mjs";const log=createLogger(`replay/compressor`),FAIL_OPEN={
|
|
1
|
+
import{createLogger}from"../../util/log.mjs";const log=createLogger(`replay/compressor`),FAIL_OPEN={dispose:()=>void 0,gzip:null},gzip=async bytes=>{let cs=new CompressionStream(`gzip`),compressed=new Response(cs.readable).arrayBuffer(),writer=cs.writable.getWriter();return await writer.write(new Uint8Array(bytes)),await writer.close(),new Uint8Array(await compressed)};function createCompressor(){return typeof CompressionStream>`u`?(log.warn(`CompressionStream unavailable; replay chunks will POST uncompressed`),FAIL_OPEN):{dispose:()=>void 0,gzip}}export{createCompressor};
|
|
@@ -1,12 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { recordOptions } from "rrweb";
|
|
3
|
-
//#region src/plugins/replay/config.d.ts
|
|
4
|
-
declare const INTERFERE_MASK_TEXT_CLASS = "interfere-mask";
|
|
5
|
-
declare const INTERFERE_BLOCK_CLASS = "interfere-block";
|
|
6
|
-
declare const INTERFERE_IGNORE_CLASS = "interfere-ignore";
|
|
7
|
-
type ReplayConfig = Pick<recordOptions<unknown>, "blockClass" | "blockSelector" | "ignoreClass" | "ignoreSelector" | "maskAllInputs" | "maskInputFn" | "maskInputOptions" | "maskTextClass" | "maskTextFn" | "maskTextSelector" | "recordCanvas">;
|
|
8
|
-
type ReplayOption = boolean | ReplayConfig;
|
|
9
|
-
type ReplayRecordOptions = SetRequired<ReplayConfig, "maskTextClass" | "blockClass" | "ignoreClass">;
|
|
10
|
-
declare function toRecordOptions(config: ReplayConfig): ReplayRecordOptions;
|
|
11
|
-
//#endregion
|
|
1
|
+
import { a as ReplayOption, i as ReplayConfig, n as INTERFERE_IGNORE_CLASS, o as ReplayRecordOptions, r as INTERFERE_MASK_TEXT_CLASS, s as toRecordOptions, t as INTERFERE_BLOCK_CLASS } from "../../config-Rm-ct4K1.mjs";
|
|
12
2
|
export { INTERFERE_BLOCK_CLASS, INTERFERE_IGNORE_CLASS, INTERFERE_MASK_TEXT_CLASS, ReplayConfig, ReplayOption, ReplayRecordOptions, toRecordOptions };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const INTERFERE_MASK_TEXT_CLASS=`interfere-mask`,INTERFERE_BLOCK_CLASS=`interfere-block`,INTERFERE_IGNORE_CLASS=`interfere-ignore`;function toRecordOptions(config){return{...config,
|
|
1
|
+
const INTERFERE_MASK_TEXT_CLASS=`interfere-mask`,INTERFERE_BLOCK_CLASS=`interfere-block`,INTERFERE_IGNORE_CLASS=`interfere-ignore`;function toRecordOptions(config){return{...config,blockClass:INTERFERE_BLOCK_CLASS,ignoreClass:INTERFERE_IGNORE_CLASS,maskTextClass:INTERFERE_MASK_TEXT_CLASS}}export{INTERFERE_BLOCK_CLASS,INTERFERE_IGNORE_CLASS,INTERFERE_MASK_TEXT_CLASS,toRecordOptions};
|
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
import { PluginContext } from "
|
|
1
|
+
import { O as PluginContext } from "../../provider-DRuvRafW.mjs";
|
|
2
|
+
import { resolveTargets } from "../../internal/config.mjs";
|
|
2
3
|
import { ReplayChunkPayload } from "./chunking.mjs";
|
|
3
4
|
import { Compressor } from "./compressor-host.mjs";
|
|
4
5
|
//#region src/plugins/replay/delivery.d.ts
|
|
5
|
-
type DeliverResult = "ok" | "retryable" | "fatal";
|
|
6
|
-
interface ReplayChunkRef {
|
|
6
|
+
export type DeliverResult = "ok" | "retryable" | "fatal";
|
|
7
|
+
export interface ReplayChunkRef {
|
|
7
8
|
segmentId: number;
|
|
8
9
|
windowId: string;
|
|
9
10
|
}
|
|
10
|
-
interface DeliverInput {
|
|
11
|
-
|
|
11
|
+
export interface DeliverInput {
|
|
12
|
+
target: ReturnType<typeof resolveTargets>["ingest"];
|
|
12
13
|
chunk: ReplayChunkRef;
|
|
13
14
|
compressor: Compressor;
|
|
14
15
|
ctx: PluginContext;
|
|
15
16
|
payload: ReplayChunkPayload;
|
|
16
17
|
skipCompression: boolean;
|
|
17
|
-
uploadBaseUrl: string;
|
|
18
18
|
}
|
|
19
|
-
declare function deliverChunk({ ctx, chunk,
|
|
20
|
-
//#endregion
|
|
21
|
-
export { DeliverInput, DeliverResult, ReplayChunkRef, deliverChunk };
|
|
19
|
+
export declare function deliverChunk({ ctx, chunk, target, compressor, payload, skipCompression }: DeliverInput): Promise<"fatal" | "ok" | "retryable">;
|
|
20
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"../../util/log.mjs";import{
|
|
1
|
+
import{createLogger}from"../../util/log.mjs";import{uploadReplayChunk}from"./upload.mjs";import{trace}from"@opentelemetry/api";import{omitUndefined}from"@interfere/helpers/omit-undefined";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}}}function encodeReplayMeta(chunk,payload){return btoa(JSON.stringify({activityCount:payload.activityCount,eventCount:payload.eventCount,firstTs:payload.firstTs,hasFullSnapshot:payload.hasFullSnapshot,lastTs:payload.lastTs,segmentId:chunk.segmentId,windowId:chunk.windowId}))}async function deliverChunk({ctx,chunk,target,compressor,payload,skipCompression}){let sessionId=ctx.getSessionId();if(!sessionId)return`ok`;let rawBytes=textEncoder.encode(payload.bodyJson),uncompressedBytes=rawBytes.byteLength,prepared=skipCompression?{body:rawBytes,encoding:null}:await prepareBody(compressor,rawBytes),headers={"x-replay-meta":encodeReplayMeta(chunk,payload)};prepared.encoding===`gzip`&&(headers[`content-encoding`]=`gzip`);let response;try{let upload=await uploadReplayChunk(target,sessionId,prepared.body,headers);if(!upload.ok)return await upload.body?.cancel(),upload.status>=400&&upload.status<500?`fatal`:`retryable`;response=await upload.json()}catch(error){return log.warn(`replay chunk upload failed: %o`,error),`retryable`}let objectKey=typeof response==`object`&&response&&`objectKey`in response&&typeof response.objectKey==`string`?response.objectKey:null,attributes=omitUndefined({"replay.chunk.size_bytes":uncompressedBytes,"replay.chunk.event_count":payload.eventCount,"replay.chunk.uri":objectKey||void 0,"replay.chunk.compressed_bytes":prepared.encoding===`gzip`?prepared.body.byteLength:void 0,"replay.chunk.first_event_ts":payload.firstTs??void 0,"replay.chunk.last_event_ts":payload.lastTs??void 0});return trace.getTracer(`@interfere/react/replay`).startSpan(`replay.chunk`,{attributes}).end(),`ok`}export{deliverChunk};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"../../util/log.mjs";import{onPageHidden,onPageShown}from"../../internal/page-lifecycle.mjs";import{resolveTargets}from"../../internal/config.mjs";import{splitReplayEvents,toReplayChunkPayload}from"./chunking.mjs";import{createReplayBuffer}from"./buffer.mjs";import{createCompressor}from"./compressor-host.mjs";import{toRecordOptions}from"./config.mjs";import{deliverChunk}from"./delivery.mjs";import{startReplayRecorder}from"./recorder.mjs";import{createSegmentSequence}from"./sequence.mjs";const log=createLogger(`replay`),replayPlugin={name:`replay`,setup(ctx){let targets=resolveTargets(),
|
|
1
|
+
import{createLogger}from"../../util/log.mjs";import{onPageHidden,onPageShown}from"../../internal/page-lifecycle.mjs";import{resolveTargets}from"../../internal/config.mjs";import{splitReplayEvents,toReplayChunkPayload}from"./chunking.mjs";import{createReplayBuffer}from"./buffer.mjs";import{createCompressor}from"./compressor-host.mjs";import{toRecordOptions}from"./config.mjs";import{deliverChunk}from"./delivery.mjs";import{startReplayRecorder}from"./recorder.mjs";import{createSegmentSequence}from"./sequence.mjs";import{EventType}from"@rrweb/types";const log=createLogger(`replay`),replayPlugin={name:`replay`,setup(ctx){let targets=resolveTargets(),recorder=null,flushTimer=null,unsubscribeConfig=null,unsubscribeHidden=null,unsubscribeShown=null,unsubscribePause=null,unsubscribeResume=null,isPaused=!1,startup=new AbortController,compressor=createCompressor(),windowId=ctx.getWindowId(),sequence=createSegmentSequence(windowId),consecutiveFailures=0,recorderFaults=0,halted=!1;function halt(){halted||(halted=!0,startup.abort(),flushTimer&&=(clearInterval(flushTimer),null),recorder?.stop(),recorder=null)}function handleResult(result){if(result===`ok`){consecutiveFailures=0;return}if(result===`fatal`){log.warn(`replay halted: upload rejected`),halt();return}consecutiveFailures+=1,consecutiveFailures>=3&&(log.warn(`replay halted after %d consecutive upload failures`,consecutiveFailures),halt())}function handleRecorderFault(error){recorderFaults+=1,!(recorderFaults>10)&&(log.warn(`recorder fault: %s`,error instanceof Error?error.constructor.name:`UnknownError`),recorderFaults===10&&(log.warn(`replay halted after %d recorder faults`,10),flush(),halt()))}let buffer=createReplayBuffer({onSoftLimit:flush,softLimitBytes:4194304});function flush(opts){if(halted||ctx.getPluginConfigState(`replay`)!==`enabled`)return;let drained=buffer.drain();if(drained.length===0)return;let{chunks,droppedEventBytes}=splitReplayEvents(drained);for(let bytes of droppedEventBytes)log.warn(`dropping replay event of ~%d bytes: too large to upload`,bytes);for(let group of chunks)sendChunk(toReplayChunkPayload(group),opts?.skipCompression??!1)}async function sendChunk(payload,skipCompression){try{handleResult(await deliverChunk({target:targets.ingest,chunk:{segmentId:sequence.take(),windowId},compressor,ctx,payload,skipCompression}))}catch(error){log.warn(`replay chunk dropped: %s`,error instanceof Error?error.constructor.name:`UnknownError`),handleResult(`retryable`)}}let pauseRecording=unload=>{isPaused||(isPaused=!0,recorder?.stop(),flush(unload?{skipCompression:!0}:void 0))},resumeRecording=()=>{isPaused&&(isPaused=!1,recorder?.start())},recordOptions=toRecordOptions(ctx.getReplayConfig());return(async()=>{if(recorder=await startReplayRecorder({onEvent:(event,isCheckout)=>{startup.signal.aborted||(isCheckout&&event.type===EventType.Meta&&flush(),buffer.push(event))},onFault:handleRecorderFault,recordOptions,signal:startup.signal}),startup.signal.aborted){recorder?.stop(),recorder=null;return}recorder&&(unsubscribeConfig=ctx.subscribePluginConfigState(`replay`,state=>{state===`enabled`&&flush()}),flushTimer=setInterval(flush,1e4),unsubscribeHidden=onPageHidden(()=>{pauseRecording(!0)}),unsubscribeShown=onPageShown(resumeRecording),unsubscribePause=ctx.onSessionPause(()=>{pauseRecording(!1)}),unsubscribeResume=ctx.onSessionResume(resumeRecording),log.debug(`recording started`))})().catch(error=>{log.warn(`replay initialization failed: %s`,error instanceof Error?error.constructor.name:`UnknownError`),halt()}),reason=>{reason===`consent-revoked`?(buffer.drain(),log.debug(`replay buffer discarded: consent revoked`)):flush(),startup.abort(),recorder?.stop(),flushTimer&&clearInterval(flushTimer),unsubscribeConfig?.(),unsubscribeHidden?.(),unsubscribeShown?.(),unsubscribePause?.(),unsubscribeResume?.(),compressor.dispose()}}};export{replayPlugin as default};
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { ReplayRecordOptions } from "
|
|
1
|
+
import { o as ReplayRecordOptions } from "../../config-Rm-ct4K1.mjs";
|
|
2
|
+
import { eventWithTime } from "@rrweb/types";
|
|
2
3
|
//#region src/plugins/replay/recorder.d.ts
|
|
3
|
-
interface ReplayRecorder {
|
|
4
|
+
export interface ReplayRecorder {
|
|
5
|
+
start: () => boolean;
|
|
4
6
|
stop: () => void;
|
|
5
|
-
takeFullSnapshot: () => void;
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
export interface StartReplayRecorderOptions {
|
|
9
|
+
onEvent: (event: eventWithTime, isCheckout?: boolean) => void;
|
|
10
|
+
onFault: (error: unknown) => void;
|
|
11
|
+
recordOptions: ReplayRecordOptions;
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
}
|
|
14
|
+
export declare function startReplayRecorder({ onEvent, onFault, recordOptions, signal }: StartReplayRecorderOptions): Promise<ReplayRecorder | null>;
|
|
15
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger}from"../../util/log.mjs";const log=createLogger(`replay`);async function startReplayRecorder(onEvent,
|
|
1
|
+
import{createLogger}from"../../util/log.mjs";const log=createLogger(`replay`);async function startReplayRecorder({onEvent,onFault,recordOptions,signal}){let record;try{({record}=await import(`../../rrweb-CE6GKCEq.mjs`))}catch{return log.error(`rrweb failed to load, replay disabled`),null}if(signal?.aborted)return null;let stopRecording,recorder={start:()=>(stopRecording??=record({emit:onEvent,recordAfter:`load`,inlineStylesheet:!0,slimDOMOptions:`all`,checkoutEveryNms:6e4,sampling:{media:800,mousemove:50,scroll:150},...recordOptions,errorHandler:error=>(onFault(error),!0)}),stopRecording!==void 0),stop:()=>{stopRecording?.(),stopRecording=void 0}};return recorder.start()?recorder:(log.error(`rrweb failed to start, replay disabled`),null)}export{startReplayRecorder};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
//#region src/plugins/replay/sequence.d.ts
|
|
2
|
-
interface SegmentSequence {
|
|
2
|
+
export interface SegmentSequence {
|
|
3
3
|
take: () => number;
|
|
4
4
|
}
|
|
5
|
-
declare function createSegmentSequence(windowId: string):
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
export declare function createSegmentSequence(windowId: string): {
|
|
6
|
+
take(): number;
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function sessionStore(){try{let store=globalThis.sessionStorage,probe=`__interfere_replay_seq_probe__`;return store.setItem(probe,`1`),store.removeItem(probe),store}catch{return null}}function createSegmentSequence(windowId){let key=`interfere:replay_seq:${windowId}`,store=sessionStore(),stored=store?.getItem(key),next=stored?Number(stored):0;return(!Number.isInteger(next)||next<0)&&(next=0),{take(){let seq=next;return next+=1,store?.setItem(key,String(next)),seq}}}export{createSegmentSequence};
|
|
1
|
+
function sessionStore(){try{let store=globalThis.sessionStorage,probe=`__interfere_replay_seq_probe__`;return store.setItem(probe,`1`),store.removeItem(probe),store}catch{return null}}function createSegmentSequence(windowId){let key=`interfere:replay_seq:${windowId}`,store=sessionStore(),stored=store?.getItem(key),next=stored?Number(stored):0;return(!Number.isInteger(next)||next<0)&&(next=0),{take(){let persisted=Number(store?.getItem(key));Number.isSafeInteger(persisted)&&persisted>next&&(next=persisted);let seq=next;return next+=1,store?.setItem(key,String(next)),seq}}}export{createSegmentSequence};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { resolveTargets } from "../../internal/config.mjs";
|
|
2
|
+
//#region src/plugins/replay/upload.d.ts
|
|
3
|
+
export declare function uploadReplayChunk(target: ReturnType<typeof resolveTargets>["ingest"], sessionId: string, body: Uint8Array<ArrayBuffer>, headers: Record<string, string>): Promise<Response>;
|
|
4
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
async function uploadReplayChunk(target,sessionId,body,headers){let requestHeaders=new Headers(target.headers);target.publicKeyHeader&&requestHeaders.set(`x-interfere-public-key`,target.publicKeyHeader.apiKey);for(let[name,value]of Object.entries(headers))requestHeaders.set(name,value);return requestHeaders.set(`content-type`,`application/octet-stream`),fetch(new URL(`/v3/replay/upload/${encodeURIComponent(sessionId)}`,target.baseUrl),{method:`POST`,credentials:`omit`,headers:requestHeaders,body:new Blob([body]),signal:AbortSignal.timeout(target.timeoutInSeconds*1e3)})}export{uploadReplayChunk};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/internal/otel/propagation.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Reads the W3C `traceparent` (and optional `tracestate` / `baggage`) meta
|
|
4
|
+
* tags from the document head and extracts an OTel `Context`. SSR renderers (Next, Vite,
|
|
5
|
+
* …) inject the server-side request span's context so the client SDK can
|
|
6
|
+
* stitch its spans onto the same trace.
|
|
7
|
+
*
|
|
8
|
+
* Returns `null` when:
|
|
9
|
+
* - called server-side (no `document`)
|
|
10
|
+
* - no `traceparent` meta tag is present
|
|
11
|
+
* - the extracted context has an invalid trace id
|
|
12
|
+
* - the `sampled` flag is unset — the browser doesn't honor the bit when
|
|
13
|
+
* creating child spans, so an unsampled parent would orphan every
|
|
14
|
+
* browser span under a trace with no recorded server segments. Drop
|
|
15
|
+
* instead so spans fall back to a fresh root.
|
|
16
|
+
* - the server stamped a `tracestate` freshness timestamp
|
|
17
|
+
* (`interfere=<unix seconds>`) and it is older than two minutes —
|
|
18
|
+
* cached HTML (ISR revalidation, CDN full-page caches) serves one
|
|
19
|
+
* render's traceparent to every visitor in the cache window, which
|
|
20
|
+
* would pile all of their sessions onto a single stale trace. A
|
|
21
|
+
* missing or malformed stamp is accepted for compatibility with
|
|
22
|
+
* servers that don't emit one.
|
|
23
|
+
*/
|
|
24
|
+
declare function readPropagationFromDocument(): import("@opentelemetry/api").Context | null;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { readPropagationFromDocument as t };
|