@interfere/react 1.0.0-alpha.7 → 1.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/dist/error-boundary.d.mts +27 -0
- package/dist/error-boundary.d.mts.map +1 -0
- package/dist/error-boundary.mjs +44 -0
- package/dist/error-boundary.mjs.map +1 -0
- package/dist/internal/client.d.mts +32 -0
- package/dist/internal/client.d.mts.map +1 -0
- package/dist/internal/client.mjs +100 -0
- package/dist/internal/client.mjs.map +1 -0
- package/dist/internal/config.d.mts +9 -0
- package/dist/internal/config.d.mts.map +1 -0
- package/dist/internal/config.mjs +27 -0
- package/dist/internal/config.mjs.map +1 -0
- package/dist/internal/consent.d.mts +15 -0
- package/dist/internal/consent.d.mts.map +1 -0
- package/dist/internal/consent.mjs +25 -0
- package/dist/internal/consent.mjs.map +1 -0
- package/dist/internal/context.d.mts +6 -0
- package/dist/internal/context.d.mts.map +1 -0
- package/dist/internal/context.mjs +32 -0
- package/dist/internal/context.mjs.map +1 -0
- package/dist/internal/envelope.d.mts +14 -0
- package/dist/internal/envelope.d.mts.map +1 -0
- package/dist/internal/envelope.mjs +20 -0
- package/dist/internal/envelope.mjs.map +1 -0
- package/dist/internal/errors.d.mts +4 -0
- package/dist/internal/errors.d.mts.map +1 -0
- package/dist/internal/errors.mjs +4 -0
- package/dist/internal/errors.mjs.map +1 -0
- package/dist/internal/plugin-runtime.d.mts +26 -0
- package/dist/internal/plugin-runtime.d.mts.map +1 -0
- package/dist/internal/plugin-runtime.mjs +85 -0
- package/dist/internal/plugin-runtime.mjs.map +1 -0
- package/dist/internal/sw.d.mts +4 -0
- package/dist/internal/sw.d.mts.map +1 -0
- package/dist/internal/sw.mjs +10 -0
- package/dist/internal/sw.mjs.map +1 -0
- package/dist/plugins/errors.d.mts +6 -0
- package/dist/plugins/errors.d.mts.map +1 -0
- package/dist/plugins/errors.mjs +59 -0
- package/dist/plugins/errors.mjs.map +1 -0
- package/dist/plugins/fingerprint.d.mts +6 -0
- package/dist/plugins/fingerprint.d.mts.map +1 -0
- package/dist/plugins/fingerprint.mjs +13 -0
- package/dist/plugins/fingerprint.mjs.map +1 -0
- package/dist/plugins/lib/loader.d.mts +10 -0
- package/dist/plugins/lib/loader.d.mts.map +1 -0
- package/dist/plugins/lib/loader.mjs +43 -0
- package/dist/plugins/lib/loader.mjs.map +1 -0
- package/dist/plugins/lib/types.d.mts +14 -0
- package/dist/plugins/lib/types.d.mts.map +1 -0
- package/dist/plugins/lib/types.mjs +1 -0
- package/dist/plugins/pages.d.mts +6 -0
- package/dist/plugins/pages.d.mts.map +1 -0
- package/dist/plugins/pages.mjs +102 -0
- package/dist/plugins/pages.mjs.map +1 -0
- package/dist/plugins/rage-clicks.d.mts +6 -0
- package/dist/plugins/rage-clicks.d.mts.map +1 -0
- package/dist/plugins/rage-clicks.mjs +53 -0
- package/dist/plugins/rage-clicks.mjs.map +1 -0
- package/dist/plugins/replay.d.mts +6 -0
- package/dist/plugins/replay.d.mts.map +1 -0
- package/dist/plugins/replay.mjs +62 -0
- package/dist/plugins/replay.mjs.map +1 -0
- package/dist/provider.d.mts +30 -0
- package/dist/provider.d.mts.map +1 -0
- package/dist/provider.mjs +30 -0
- package/dist/provider.mjs.map +1 -0
- package/dist/tracking/api.d.mts +17 -0
- package/dist/tracking/api.d.mts.map +1 -0
- package/dist/tracking/api.mjs +76 -0
- package/dist/tracking/api.mjs.map +1 -0
- package/dist/tracking/session.d.mts +19 -0
- package/dist/tracking/session.d.mts.map +1 -0
- package/dist/tracking/session.mjs +76 -0
- package/dist/tracking/session.mjs.map +1 -0
- package/dist/tracking/visitor.d.mts +7 -0
- package/dist/tracking/visitor.d.mts.map +1 -0
- package/dist/tracking/visitor.mjs +40 -0
- package/dist/tracking/visitor.mjs.map +1 -0
- package/dist/transport/http.d.mts +16 -0
- package/dist/transport/http.d.mts.map +1 -0
- package/dist/transport/http.mjs +56 -0
- package/dist/transport/http.mjs.map +1 -0
- package/dist/transport/queue.d.mts +25 -0
- package/dist/transport/queue.d.mts.map +1 -0
- package/dist/transport/queue.mjs +60 -0
- package/dist/transport/queue.mjs.map +1 -0
- package/dist/util/log.d.mts +13 -0
- package/dist/util/log.d.mts.map +1 -0
- package/dist/util/log.mjs +37 -0
- package/dist/util/log.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Component, ErrorInfo, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/error-boundary.d.ts
|
|
4
|
+
interface ErrorBoundaryProps {
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);
|
|
7
|
+
onError?: (error: Error, info: ErrorInfo) => void;
|
|
8
|
+
}
|
|
9
|
+
interface ErrorBoundaryState {
|
|
10
|
+
error: Error | null;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Catches render-phase React errors, reports them to the SDK, and renders a
|
|
14
|
+
* fallback. Requires the SDK to be bootstrapped via `init()` before React
|
|
15
|
+
* renders so `capture` has an active runtime.
|
|
16
|
+
*
|
|
17
|
+
* Class component required — React has no hook-based error boundary API.
|
|
18
|
+
*/
|
|
19
|
+
declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
20
|
+
state: ErrorBoundaryState;
|
|
21
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState;
|
|
22
|
+
componentDidCatch(error: Error, info: ErrorInfo): void;
|
|
23
|
+
private readonly reset;
|
|
24
|
+
render(): ReactNode;
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { ErrorBoundary, ErrorBoundaryProps };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-boundary.d.mts","names":[],"sources":["../src/error-boundary.tsx"],"mappings":";;;UASiB,kBAAA;EACf,QAAA,EAAU,SAAA;EACV,QAAA,GAAW,SAAA,KAAc,KAAA,EAAO,KAAA,EAAO,KAAA,iBAAsB,SAAA;EAC7D,OAAA,IAAW,KAAA,EAAO,KAAA,EAAO,IAAA,EAAM,SAAA;AAAA;AAAA,UAGvB,kBAAA;EACR,KAAA,EAAO,KAAA;AAAA;;;;;;;;cAUI,aAAA,SAAsB,SAAA,CACjC,kBAAA,EACA,kBAAA;EAES,KAAA,EAAO,kBAAA;EAAA,OAET,wBAAA,CAAyB,KAAA,EAAO,KAAA,GAAQ,kBAAA;EAItC,iBAAA,CAAkB,KAAA,EAAO,KAAA,EAAO,IAAA,EAAM,SAAA;EAAA,iBAiB9B,KAAA;EAIR,MAAA,CAAA,GAAM,SAAA;AAAA"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { seen } from "./internal/errors.mjs";
|
|
3
|
+
import { getClient } from "./internal/client.mjs";
|
|
4
|
+
import { toExceptions } from "@interfere/types/sdk/errors";
|
|
5
|
+
import { Component } from "react";
|
|
6
|
+
//#region src/error-boundary.tsx
|
|
7
|
+
/**
|
|
8
|
+
* Catches render-phase React errors, reports them to the SDK, and renders a
|
|
9
|
+
* fallback. Requires the SDK to be bootstrapped via `init()` before React
|
|
10
|
+
* renders so `capture` has an active runtime.
|
|
11
|
+
*
|
|
12
|
+
* Class component required — React has no hook-based error boundary API.
|
|
13
|
+
*/
|
|
14
|
+
var ErrorBoundary = class extends Component {
|
|
15
|
+
state = { error: null };
|
|
16
|
+
static getDerivedStateFromError(error) {
|
|
17
|
+
return { error };
|
|
18
|
+
}
|
|
19
|
+
componentDidCatch(error, info) {
|
|
20
|
+
if (seen.has(error)) return;
|
|
21
|
+
seen.add(error);
|
|
22
|
+
try {
|
|
23
|
+
getClient().capture("error", { exceptions: toExceptions(error, {
|
|
24
|
+
type: "react",
|
|
25
|
+
handled: true
|
|
26
|
+
}) });
|
|
27
|
+
} catch {}
|
|
28
|
+
this.props.onError?.(error, info);
|
|
29
|
+
}
|
|
30
|
+
reset = () => {
|
|
31
|
+
this.setState({ error: null });
|
|
32
|
+
};
|
|
33
|
+
render() {
|
|
34
|
+
const { error } = this.state;
|
|
35
|
+
if (error) {
|
|
36
|
+
const { fallback } = this.props;
|
|
37
|
+
if (typeof fallback === "function") return fallback(error, this.reset);
|
|
38
|
+
return fallback ?? null;
|
|
39
|
+
}
|
|
40
|
+
return this.props.children;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
export { ErrorBoundary };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-boundary.mjs","names":[],"sources":["../src/error-boundary.tsx"],"sourcesContent":["\"use client\";\n\nimport { toExceptions } from \"@interfere/types/sdk/errors\";\n\nimport { Component, type ErrorInfo, type ReactNode } from \"react\";\n\nimport { getClient } from \"./internal/client.js\";\nimport { seen } from \"./internal/errors.js\";\n\nexport interface ErrorBoundaryProps {\n children: ReactNode;\n fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);\n onError?: (error: Error, info: ErrorInfo) => void;\n}\n\ninterface ErrorBoundaryState {\n error: Error | null;\n}\n\n/**\n * Catches render-phase React errors, reports them to the SDK, and renders a\n * fallback. Requires the SDK to be bootstrapped via `init()` before React\n * renders so `capture` has an active runtime.\n *\n * Class component required — React has no hook-based error boundary API.\n */\nexport class ErrorBoundary extends Component<\n ErrorBoundaryProps,\n ErrorBoundaryState\n> {\n override state: ErrorBoundaryState = { error: null };\n\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n return { error };\n }\n\n override componentDidCatch(error: Error, info: ErrorInfo) {\n if (seen.has(error)) {\n return;\n }\n seen.add(error);\n\n try {\n getClient().capture(\"error\", {\n exceptions: toExceptions(error, { type: \"react\", handled: true }),\n });\n } catch {\n // SDK not initialized — error boundary still renders fallback\n }\n\n this.props.onError?.(error, info);\n }\n\n private readonly reset = () => {\n this.setState({ error: null });\n };\n\n override render() {\n const { error } = this.state;\n\n if (error) {\n const { fallback } = this.props;\n if (typeof fallback === \"function\") {\n return fallback(error, this.reset);\n }\n return fallback ?? null;\n }\n\n return this.props.children;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA0BA,IAAa,gBAAb,cAAmC,UAGjC;CACA,QAAqC,EAAE,OAAO,MAAM;CAEpD,OAAO,yBAAyB,OAAkC;AAChE,SAAO,EAAE,OAAO;;CAGlB,kBAA2B,OAAc,MAAiB;AACxD,MAAI,KAAK,IAAI,MAAM,CACjB;AAEF,OAAK,IAAI,MAAM;AAEf,MAAI;AACF,cAAW,CAAC,QAAQ,SAAS,EAC3B,YAAY,aAAa,OAAO;IAAE,MAAM;IAAS,SAAS;IAAM,CAAC,EAClE,CAAC;UACI;AAIR,OAAK,MAAM,UAAU,OAAO,KAAK;;CAGnC,cAA+B;AAC7B,OAAK,SAAS,EAAE,OAAO,MAAM,CAAC;;CAGhC,SAAkB;EAChB,MAAM,EAAE,UAAU,KAAK;AAEvB,MAAI,OAAO;GACT,MAAM,EAAE,aAAa,KAAK;AAC1B,OAAI,OAAO,aAAa,WACtB,QAAO,SAAS,OAAO,KAAK,MAAM;AAEpC,UAAO,YAAY;;AAGrB,SAAO,KAAK,MAAM"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PluginOverrides } from "../plugins/lib/loader.mjs";
|
|
2
|
+
import { ConsentState } from "@interfere/types/sdk/plugins/manifest";
|
|
3
|
+
import { EnvelopePayload, EventType } from "@interfere/types/sdk/envelope";
|
|
4
|
+
|
|
5
|
+
//#region src/internal/client.d.ts
|
|
6
|
+
interface ClientOptions {
|
|
7
|
+
consent?: ConsentState;
|
|
8
|
+
plugins?: PluginOverrides;
|
|
9
|
+
}
|
|
10
|
+
declare class Client {
|
|
11
|
+
private readonly metadata;
|
|
12
|
+
private readonly queue;
|
|
13
|
+
private readonly runtime;
|
|
14
|
+
constructor(opts: ClientOptions, buildId: string, releaseId: string | null);
|
|
15
|
+
capture<T extends EventType>(type: T, payload: EnvelopePayload<T>): void;
|
|
16
|
+
flush(): void;
|
|
17
|
+
dispose(): void;
|
|
18
|
+
getConsent(): ConsentState | null;
|
|
19
|
+
setConsent(value?: ConsentState): void;
|
|
20
|
+
resetConsent(): void;
|
|
21
|
+
}
|
|
22
|
+
declare function getClient(): Client;
|
|
23
|
+
declare function init(opts?: ClientOptions): void;
|
|
24
|
+
declare function close(): void;
|
|
25
|
+
declare const consent: {
|
|
26
|
+
get(): ConsentState | null;
|
|
27
|
+
set(value?: ConsentState): void;
|
|
28
|
+
};
|
|
29
|
+
declare function syncConsent(consentState: ConsentState | undefined): void;
|
|
30
|
+
declare function flush(): void;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { ClientOptions, close, consent, flush, getClient, init, syncConsent };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.mts","names":[],"sources":["../../src/internal/client.ts"],"mappings":";;;;;UAiBiB,aAAA;EACf,OAAA,GAAU,YAAA;EACV,OAAA,GAAU,eAAA;AAAA;AAAA,cAGN,MAAA;EAAA,iBACa,QAAA;EAAA,iBACA,KAAA;EAAA,iBACA,OAAA;cAEL,IAAA,EAAM,aAAA,EAAe,OAAA,UAAiB,SAAA;EAkClD,OAAA,WAAkB,SAAA,CAAA,CAAW,IAAA,EAAM,CAAA,EAAG,OAAA,EAAS,eAAA,CAAgB,CAAA;EAS/D,KAAA,CAAA;EAIA,OAAA,CAAA;EAMA,UAAA,CAAA,GAAc,YAAA;EAId,UAAA,CAAW,KAAA,GAAQ,YAAA;EAInB,YAAA,CAAA;AAAA;AAAA,iBAOc,SAAA,CAAA,GAAa,MAAA;AAAA,iBASb,IAAA,CAAK,IAAA,GAAM,aAAA;AAAA,iBAsBX,KAAA,CAAA;AAAA,cASH,OAAA;SACJ,YAAA;cAIK,YAAA;AAAA;AAAA,iBAKE,WAAA,CAAY,YAAA,EAAc,YAAA;AAAA,iBAa1B,KAAA,CAAA"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { createLogger } from "../util/log.mjs";
|
|
2
|
+
import { HttpTransport } from "../transport/http.mjs";
|
|
3
|
+
import { bootstrap, session, teardown } from "../tracking/api.mjs";
|
|
4
|
+
import { BatchQueue } from "../transport/queue.mjs";
|
|
5
|
+
import { resolveTargets } from "./config.mjs";
|
|
6
|
+
import { collectContext } from "./context.mjs";
|
|
7
|
+
import { buildEnvelope } from "./envelope.mjs";
|
|
8
|
+
import { PluginRuntime } from "./plugin-runtime.mjs";
|
|
9
|
+
import { registerServiceWorker } from "./sw.mjs";
|
|
10
|
+
import { inferRuntime, normalizeEnv } from "@interfere/types/sdk/runtime";
|
|
11
|
+
//#region src/internal/client.ts
|
|
12
|
+
const log = createLogger("client");
|
|
13
|
+
var Client = class {
|
|
14
|
+
metadata;
|
|
15
|
+
queue;
|
|
16
|
+
runtime;
|
|
17
|
+
constructor(opts, buildId, releaseId) {
|
|
18
|
+
const targets = resolveTargets();
|
|
19
|
+
bootstrap(targets.session);
|
|
20
|
+
log.info("target: %s", targets.ingest.url);
|
|
21
|
+
this.metadata = {
|
|
22
|
+
context: collectContext(),
|
|
23
|
+
environment: normalizeEnv(typeof process === "undefined" ? void 0 : process.env.NODE_ENV),
|
|
24
|
+
runtime: inferRuntime(),
|
|
25
|
+
buildId,
|
|
26
|
+
releaseId
|
|
27
|
+
};
|
|
28
|
+
registerServiceWorker();
|
|
29
|
+
this.queue = new BatchQueue({ transport: new HttpTransport(targets.ingest) });
|
|
30
|
+
this.queue.start();
|
|
31
|
+
this.runtime = new PluginRuntime({
|
|
32
|
+
capture: (type, payload) => this.capture(type, payload),
|
|
33
|
+
getSessionId: () => session.getId() ?? ""
|
|
34
|
+
}, opts.plugins, opts.consent);
|
|
35
|
+
this.runtime.start();
|
|
36
|
+
}
|
|
37
|
+
capture(type, payload) {
|
|
38
|
+
const sessionId = session.getId();
|
|
39
|
+
if (!(sessionId && this.runtime.canCapture(type))) return;
|
|
40
|
+
this.queue.enqueue(buildEnvelope(type, payload, sessionId, this.metadata));
|
|
41
|
+
}
|
|
42
|
+
flush() {
|
|
43
|
+
this.queue.flush();
|
|
44
|
+
}
|
|
45
|
+
dispose() {
|
|
46
|
+
this.runtime.dispose();
|
|
47
|
+
teardown();
|
|
48
|
+
this.queue.dispose();
|
|
49
|
+
}
|
|
50
|
+
getConsent() {
|
|
51
|
+
return this.runtime.getConsent();
|
|
52
|
+
}
|
|
53
|
+
setConsent(value) {
|
|
54
|
+
this.runtime.setConsent(value);
|
|
55
|
+
}
|
|
56
|
+
resetConsent() {
|
|
57
|
+
this.runtime.resetConsent();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
let instance = null;
|
|
61
|
+
function getClient() {
|
|
62
|
+
if (!instance) throw new Error("Interfere SDK not initialized. Call init() from your instrumentation-client entrypoint.");
|
|
63
|
+
return instance;
|
|
64
|
+
}
|
|
65
|
+
function init(opts = {}) {
|
|
66
|
+
if (instance) return;
|
|
67
|
+
const buildId = globalThis.__INTERFERE_BUILD_ID__;
|
|
68
|
+
const releaseId = globalThis.__INTERFERE_RELEASE_ID__;
|
|
69
|
+
if (!buildId) {
|
|
70
|
+
log.error("buildId not found — ensure withInterfere() is configured in next.config and instrumentation-client.ts exists in your project root.");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
instance = new Client(opts, buildId, releaseId ?? null);
|
|
74
|
+
}
|
|
75
|
+
function close() {
|
|
76
|
+
if (!instance) return;
|
|
77
|
+
instance.dispose();
|
|
78
|
+
instance = null;
|
|
79
|
+
}
|
|
80
|
+
const consent = {
|
|
81
|
+
get() {
|
|
82
|
+
return instance?.getConsent() ?? null;
|
|
83
|
+
},
|
|
84
|
+
set(value) {
|
|
85
|
+
instance?.setConsent(value);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
function syncConsent(consentState) {
|
|
89
|
+
if (!instance) return;
|
|
90
|
+
if (consentState) {
|
|
91
|
+
instance.setConsent(consentState);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
instance.resetConsent();
|
|
95
|
+
}
|
|
96
|
+
function flush() {
|
|
97
|
+
instance?.flush();
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { close, consent, flush, getClient, init, syncConsent };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../../src/internal/client.ts"],"sourcesContent":["import type { EnvelopePayload, EventType } from \"@interfere/types/sdk/envelope\";\nimport type { ConsentState } from \"@interfere/types/sdk/plugins/manifest\";\nimport { inferRuntime, normalizeEnv } from \"@interfere/types/sdk/runtime\";\n\nimport type { PluginOverrides } from \"../plugins/lib/loader.js\";\nimport { bootstrap, session, teardown } from \"../tracking/api.js\";\nimport { HttpTransport } from \"../transport/http.js\";\nimport { BatchQueue } from \"../transport/queue.js\";\nimport { createLogger } from \"../util/log.js\";\nimport { resolveTargets } from \"./config.js\";\nimport { collectContext } from \"./context.js\";\nimport { buildEnvelope, type EnvelopeMetadata } from \"./envelope.js\";\nimport { PluginRuntime } from \"./plugin-runtime.js\";\nimport { registerServiceWorker } from \"./sw.js\";\n\nconst log = createLogger(\"client\");\n\nexport interface ClientOptions {\n consent?: ConsentState;\n plugins?: PluginOverrides;\n}\n\nclass Client {\n private readonly metadata: EnvelopeMetadata;\n private readonly queue: BatchQueue;\n private readonly runtime: PluginRuntime;\n\n constructor(opts: ClientOptions, buildId: string, releaseId: string | null) {\n const targets = resolveTargets();\n bootstrap(targets.session);\n\n log.info(\"target: %s\", targets.ingest.url);\n\n this.metadata = {\n context: collectContext(),\n environment: normalizeEnv(\n typeof process === \"undefined\" ? undefined : process.env.NODE_ENV\n ),\n runtime: inferRuntime(),\n buildId,\n releaseId,\n };\n\n registerServiceWorker();\n\n const transport = new HttpTransport(targets.ingest);\n this.queue = new BatchQueue({ transport });\n this.queue.start();\n\n this.runtime = new PluginRuntime(\n {\n capture: (type, payload) => this.capture(type, payload),\n getSessionId: () => session.getId() ?? \"\",\n },\n opts.plugins,\n opts.consent\n );\n\n this.runtime.start();\n }\n\n capture<T extends EventType>(type: T, payload: EnvelopePayload<T>): void {\n const sessionId = session.getId();\n if (!(sessionId && this.runtime.canCapture(type))) {\n return;\n }\n\n this.queue.enqueue(buildEnvelope(type, payload, sessionId, this.metadata));\n }\n\n flush(): void {\n this.queue.flush();\n }\n\n dispose(): void {\n this.runtime.dispose();\n teardown();\n this.queue.dispose();\n }\n\n getConsent(): ConsentState | null {\n return this.runtime.getConsent();\n }\n\n setConsent(value?: ConsentState): void {\n this.runtime.setConsent(value);\n }\n\n resetConsent(): void {\n this.runtime.resetConsent();\n }\n}\n\nlet instance: Client | null = null;\n\nexport function getClient(): Client {\n if (!instance) {\n throw new Error(\n \"Interfere SDK not initialized. Call init() from your instrumentation-client entrypoint.\"\n );\n }\n return instance;\n}\n\nexport function init(opts: ClientOptions = {}): void {\n if (instance) {\n return;\n }\n\n const buildId = (globalThis as Record<string, unknown>)\n .__INTERFERE_BUILD_ID__ as string | undefined;\n\n const releaseId = (globalThis as Record<string, unknown>)\n .__INTERFERE_RELEASE_ID__ as string | null | undefined;\n\n if (!buildId) {\n log.error(\n \"buildId not found — ensure withInterfere() is configured in \" +\n \"next.config and instrumentation-client.ts exists in your project root.\"\n );\n return;\n }\n\n instance = new Client(opts, buildId, releaseId ?? null);\n}\n\nexport function close(): void {\n if (!instance) {\n return;\n }\n\n instance.dispose();\n instance = null;\n}\n\nexport const consent = {\n get(): ConsentState | null {\n return instance?.getConsent() ?? null;\n },\n\n set(value?: ConsentState): void {\n instance?.setConsent(value);\n },\n};\n\nexport function syncConsent(consentState: ConsentState | undefined): void {\n if (!instance) {\n return;\n }\n\n if (consentState) {\n instance.setConsent(consentState);\n return;\n }\n\n instance.resetConsent();\n}\n\nexport function flush(): void {\n instance?.flush();\n}\n"],"mappings":";;;;;;;;;;;AAeA,MAAM,MAAM,aAAa,SAAS;AAOlC,IAAM,SAAN,MAAa;CACX;CACA;CACA;CAEA,YAAY,MAAqB,SAAiB,WAA0B;EAC1E,MAAM,UAAU,gBAAgB;AAChC,YAAU,QAAQ,QAAQ;AAE1B,MAAI,KAAK,cAAc,QAAQ,OAAO,IAAI;AAE1C,OAAK,WAAW;GACd,SAAS,gBAAgB;GACzB,aAAa,aACX,OAAO,YAAY,cAAc,KAAA,IAAY,QAAQ,IAAI,SAC1D;GACD,SAAS,cAAc;GACvB;GACA;GACD;AAED,yBAAuB;AAGvB,OAAK,QAAQ,IAAI,WAAW,EAAE,WADZ,IAAI,cAAc,QAAQ,OAAO,EACV,CAAC;AAC1C,OAAK,MAAM,OAAO;AAElB,OAAK,UAAU,IAAI,cACjB;GACE,UAAU,MAAM,YAAY,KAAK,QAAQ,MAAM,QAAQ;GACvD,oBAAoB,QAAQ,OAAO,IAAI;GACxC,EACD,KAAK,SACL,KAAK,QACN;AAED,OAAK,QAAQ,OAAO;;CAGtB,QAA6B,MAAS,SAAmC;EACvE,MAAM,YAAY,QAAQ,OAAO;AACjC,MAAI,EAAE,aAAa,KAAK,QAAQ,WAAW,KAAK,EAC9C;AAGF,OAAK,MAAM,QAAQ,cAAc,MAAM,SAAS,WAAW,KAAK,SAAS,CAAC;;CAG5E,QAAc;AACZ,OAAK,MAAM,OAAO;;CAGpB,UAAgB;AACd,OAAK,QAAQ,SAAS;AACtB,YAAU;AACV,OAAK,MAAM,SAAS;;CAGtB,aAAkC;AAChC,SAAO,KAAK,QAAQ,YAAY;;CAGlC,WAAW,OAA4B;AACrC,OAAK,QAAQ,WAAW,MAAM;;CAGhC,eAAqB;AACnB,OAAK,QAAQ,cAAc;;;AAI/B,IAAI,WAA0B;AAE9B,SAAgB,YAAoB;AAClC,KAAI,CAAC,SACH,OAAM,IAAI,MACR,0FACD;AAEH,QAAO;;AAGT,SAAgB,KAAK,OAAsB,EAAE,EAAQ;AACnD,KAAI,SACF;CAGF,MAAM,UAAW,WACd;CAEH,MAAM,YAAa,WAChB;AAEH,KAAI,CAAC,SAAS;AACZ,MAAI,MACF,qIAED;AACD;;AAGF,YAAW,IAAI,OAAO,MAAM,SAAS,aAAa,KAAK;;AAGzD,SAAgB,QAAc;AAC5B,KAAI,CAAC,SACH;AAGF,UAAS,SAAS;AAClB,YAAW;;AAGb,MAAa,UAAU;CACrB,MAA2B;AACzB,SAAO,UAAU,YAAY,IAAI;;CAGnC,IAAI,OAA4B;AAC9B,YAAU,WAAW,MAAM;;CAE9B;AAED,SAAgB,YAAY,cAA8C;AACxE,KAAI,CAAC,SACH;AAGF,KAAI,cAAc;AAChB,WAAS,WAAW,aAAa;AACjC;;AAGF,UAAS,cAAc;;AAGzB,SAAgB,QAAc;AAC5B,WAAU,OAAO"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../../src/internal/config.ts"],"mappings":";;;iBAcgB,cAAA,CAAA;EACd,MAAA,EAAQ,YAAA;EACR,OAAA,EAAS,YAAA;AAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { API_PATHS, API_URL } from "@interfere/constants/api";
|
|
2
|
+
//#region src/internal/config.ts
|
|
3
|
+
const DEFAULT_PROXY_URL = "/api/interfere";
|
|
4
|
+
const DEFAULT_SESSION_PATH = "/v1/session";
|
|
5
|
+
function resolvePublicKey() {
|
|
6
|
+
if (typeof process === "undefined") return;
|
|
7
|
+
return process.env.INTERFERE_PUBLIC_KEY ?? void 0;
|
|
8
|
+
}
|
|
9
|
+
function resolveTargets() {
|
|
10
|
+
const publicKey = resolvePublicKey();
|
|
11
|
+
const headers = new Headers({ "content-type": "application/json" });
|
|
12
|
+
if (publicKey) headers.set("x-interfere-pub-token", publicKey);
|
|
13
|
+
const baseUrl = publicKey ? API_URL : DEFAULT_PROXY_URL;
|
|
14
|
+
const sessionPath = API_PATHS.SESSION ?? DEFAULT_SESSION_PATH;
|
|
15
|
+
return {
|
|
16
|
+
ingest: {
|
|
17
|
+
url: `${baseUrl}${API_PATHS.INGEST}`,
|
|
18
|
+
headers
|
|
19
|
+
},
|
|
20
|
+
session: {
|
|
21
|
+
url: `${baseUrl}${sessionPath}`,
|
|
22
|
+
headers
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { resolveTargets };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../../src/internal/config.ts"],"sourcesContent":["import { API_PATHS, API_URL } from \"@interfere/constants/api\";\n\nimport type { IngestTarget } from \"../transport/http.js\";\n\nconst DEFAULT_PROXY_URL = \"/api/interfere\";\nconst DEFAULT_SESSION_PATH = \"/v1/session\";\n\nfunction resolvePublicKey(): string | undefined {\n if (typeof process === \"undefined\") {\n return undefined;\n }\n return process.env.INTERFERE_PUBLIC_KEY ?? undefined;\n}\n\nexport function resolveTargets(): {\n ingest: IngestTarget;\n session: IngestTarget;\n} {\n const publicKey = resolvePublicKey();\n const headers = new Headers({ \"content-type\": \"application/json\" });\n if (publicKey) {\n headers.set(\"x-interfere-pub-token\", publicKey);\n }\n\n const baseUrl = publicKey ? API_URL : DEFAULT_PROXY_URL;\n const sessionPath =\n (API_PATHS as { SESSION?: string }).SESSION ?? DEFAULT_SESSION_PATH;\n\n return {\n ingest: {\n url: `${baseUrl}${API_PATHS.INGEST}`,\n headers,\n },\n session: {\n url: `${baseUrl}${sessionPath}`,\n headers,\n },\n };\n}\n"],"mappings":";;AAIA,MAAM,oBAAoB;AAC1B,MAAM,uBAAuB;AAE7B,SAAS,mBAAuC;AAC9C,KAAI,OAAO,YAAY,YACrB;AAEF,QAAO,QAAQ,IAAI,wBAAwB,KAAA;;AAG7C,SAAgB,iBAGd;CACA,MAAM,YAAY,kBAAkB;CACpC,MAAM,UAAU,IAAI,QAAQ,EAAE,gBAAgB,oBAAoB,CAAC;AACnE,KAAI,UACF,SAAQ,IAAI,yBAAyB,UAAU;CAGjD,MAAM,UAAU,YAAY,UAAU;CACtC,MAAM,cACH,UAAmC,WAAW;AAEjD,QAAO;EACL,QAAQ;GACN,KAAK,GAAG,UAAU,UAAU;GAC5B;GACD;EACD,SAAS;GACP,KAAK,GAAG,UAAU;GAClB;GACD;EACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ConsentCategory, ConsentState, PluginKey } from "@interfere/types/sdk/plugins/manifest";
|
|
2
|
+
import { EventType } from "@interfere/types/sdk/envelope";
|
|
3
|
+
|
|
4
|
+
//#region src/internal/consent.d.ts
|
|
5
|
+
declare const DEFAULT_CONSENT: {
|
|
6
|
+
readonly analytics: true;
|
|
7
|
+
readonly replay: true;
|
|
8
|
+
};
|
|
9
|
+
declare function getPluginConsentCategory(key: PluginKey): ConsentCategory;
|
|
10
|
+
declare function isConsentAllowed(category: ConsentCategory, consentState: ConsentState | null): boolean;
|
|
11
|
+
declare function shouldCaptureEvent(type: EventType, consentState: ConsentState | null): boolean;
|
|
12
|
+
declare function resolveGrantedConsent(consent?: ConsentState): ConsentState;
|
|
13
|
+
declare function hasConsentChanged(current: ConsentState | null, next: ConsentState | null): boolean;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { DEFAULT_CONSENT, getPluginConsentCategory, hasConsentChanged, isConsentAllowed, resolveGrantedConsent, shouldCaptureEvent };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.d.mts","names":[],"sources":["../../src/internal/consent.ts"],"mappings":";;;;cAQa,eAAA;EAAA,SAGoB,SAAA;EAAA,SAAA,MAAA;AAAA;AAAA,iBAYjB,wBAAA,CAAyB,GAAA,EAAK,SAAA,GAAY,eAAA;AAAA,iBAI1C,gBAAA,CACd,QAAA,EAAU,eAAA,EACV,YAAA,EAAc,YAAA;AAAA,iBASA,kBAAA,CACd,IAAA,EAAM,SAAA,EACN,YAAA,EAAc,YAAA;AAAA,iBAKA,qBAAA,CAAsB,OAAA,GAAU,YAAA,GAAe,YAAA;AAAA,iBAI/C,iBAAA,CACd,OAAA,EAAS,YAAA,SACT,IAAA,EAAM,YAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { PLUGIN_MANIFEST } from "@interfere/types/sdk/plugins/manifest";
|
|
2
|
+
//#region src/internal/consent.ts
|
|
3
|
+
const DEFAULT_CONSENT = {
|
|
4
|
+
analytics: true,
|
|
5
|
+
replay: true
|
|
6
|
+
};
|
|
7
|
+
const PLUGIN_CONSENT_BY_KEY = Object.fromEntries(PLUGIN_MANIFEST.map((plugin) => [plugin.name, plugin.consentCategory]));
|
|
8
|
+
const EVENT_CONSENT_BY_TYPE = Object.fromEntries(PLUGIN_MANIFEST.flatMap((plugin) => plugin.events.map((event) => [event.name, plugin.consentCategory])));
|
|
9
|
+
function getPluginConsentCategory(key) {
|
|
10
|
+
return PLUGIN_CONSENT_BY_KEY[key];
|
|
11
|
+
}
|
|
12
|
+
function isConsentAllowed(category, consentState) {
|
|
13
|
+
return category === "necessary" || consentState === null || consentState[category] === true;
|
|
14
|
+
}
|
|
15
|
+
function shouldCaptureEvent(type, consentState) {
|
|
16
|
+
return isConsentAllowed(EVENT_CONSENT_BY_TYPE[type], consentState);
|
|
17
|
+
}
|
|
18
|
+
function resolveGrantedConsent(consent) {
|
|
19
|
+
return consent ?? { ...DEFAULT_CONSENT };
|
|
20
|
+
}
|
|
21
|
+
function hasConsentChanged(current, next) {
|
|
22
|
+
return current?.analytics !== next?.analytics || current?.replay !== next?.replay;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { DEFAULT_CONSENT, getPluginConsentCategory, hasConsentChanged, isConsentAllowed, resolveGrantedConsent, shouldCaptureEvent };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.mjs","names":[],"sources":["../../src/internal/consent.ts"],"sourcesContent":["import type { EventType } from \"@interfere/types/sdk/envelope\";\nimport {\n type ConsentCategory,\n type ConsentState,\n PLUGIN_MANIFEST,\n type PluginKey,\n} from \"@interfere/types/sdk/plugins/manifest\";\n\nexport const DEFAULT_CONSENT = {\n analytics: true,\n replay: true,\n} as const satisfies ConsentState;\n\nconst PLUGIN_CONSENT_BY_KEY = Object.fromEntries(\n PLUGIN_MANIFEST.map((plugin) => [plugin.name, plugin.consentCategory])\n) as Record<PluginKey, ConsentCategory>;\n\nconst EVENT_CONSENT_BY_TYPE = Object.fromEntries(\n PLUGIN_MANIFEST.flatMap((plugin) =>\n plugin.events.map((event) => [event.name, plugin.consentCategory] as const)\n )\n) as Record<EventType, ConsentCategory>;\n\nexport function getPluginConsentCategory(key: PluginKey): ConsentCategory {\n return PLUGIN_CONSENT_BY_KEY[key];\n}\n\nexport function isConsentAllowed(\n category: ConsentCategory,\n consentState: ConsentState | null\n): boolean {\n return (\n category === \"necessary\" ||\n consentState === null ||\n consentState[category] === true\n );\n}\n\nexport function shouldCaptureEvent(\n type: EventType,\n consentState: ConsentState | null\n): boolean {\n return isConsentAllowed(EVENT_CONSENT_BY_TYPE[type], consentState);\n}\n\nexport function resolveGrantedConsent(consent?: ConsentState): ConsentState {\n return consent ?? { ...DEFAULT_CONSENT };\n}\n\nexport function hasConsentChanged(\n current: ConsentState | null,\n next: ConsentState | null\n): boolean {\n return (\n current?.analytics !== next?.analytics || current?.replay !== next?.replay\n );\n}\n"],"mappings":";;AAQA,MAAa,kBAAkB;CAC7B,WAAW;CACX,QAAQ;CACT;AAED,MAAM,wBAAwB,OAAO,YACnC,gBAAgB,KAAK,WAAW,CAAC,OAAO,MAAM,OAAO,gBAAgB,CAAC,CACvE;AAED,MAAM,wBAAwB,OAAO,YACnC,gBAAgB,SAAS,WACvB,OAAO,OAAO,KAAK,UAAU,CAAC,MAAM,MAAM,OAAO,gBAAgB,CAAU,CAC5E,CACF;AAED,SAAgB,yBAAyB,KAAiC;AACxE,QAAO,sBAAsB;;AAG/B,SAAgB,iBACd,UACA,cACS;AACT,QACE,aAAa,eACb,iBAAiB,QACjB,aAAa,cAAc;;AAI/B,SAAgB,mBACd,MACA,cACS;AACT,QAAO,iBAAiB,sBAAsB,OAAO,aAAa;;AAGpE,SAAgB,sBAAsB,SAAsC;AAC1E,QAAO,WAAW,EAAE,GAAG,iBAAiB;;AAG1C,SAAgB,kBACd,SACA,MACS;AACT,QACE,SAAS,cAAc,MAAM,aAAa,SAAS,WAAW,MAAM"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.mts","names":[],"sources":["../../src/internal/context.ts"],"mappings":";;;iBAwCgB,cAAA,CAAA,GAAkB,cAAA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { UAParser } from "@ua-parser-js/pro-enterprise";
|
|
2
|
+
//#region src/internal/context.ts
|
|
3
|
+
function getDeviceMetadata() {
|
|
4
|
+
if (typeof navigator === "undefined") return null;
|
|
5
|
+
return UAParser(navigator.userAgent) ?? null;
|
|
6
|
+
}
|
|
7
|
+
function getBrowserMetadata() {
|
|
8
|
+
if ([
|
|
9
|
+
typeof navigator,
|
|
10
|
+
typeof screen,
|
|
11
|
+
typeof globalThis
|
|
12
|
+
].some((x) => x === "undefined")) return null;
|
|
13
|
+
const { language } = navigator;
|
|
14
|
+
return {
|
|
15
|
+
language,
|
|
16
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
17
|
+
display: { screen: {
|
|
18
|
+
height: screen.availHeight,
|
|
19
|
+
width: screen.availWidth,
|
|
20
|
+
orientation: screen.orientation?.type
|
|
21
|
+
} }
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function collectContext() {
|
|
25
|
+
return {
|
|
26
|
+
runtime: "browser",
|
|
27
|
+
browser: getBrowserMetadata(),
|
|
28
|
+
device: getDeviceMetadata()
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { collectContext };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.mjs","names":[],"sources":["../../src/internal/context.ts"],"sourcesContent":["import type {\n BrowserContext,\n BrowserMetadata,\n DeviceMetadata,\n} from \"@interfere/types/sdk/plugins/context/browser\";\n\nimport { UAParser } from \"@ua-parser-js/pro-enterprise\";\n\nfunction getDeviceMetadata(): DeviceMetadata | null {\n if (typeof navigator === \"undefined\") {\n return null;\n }\n\n return UAParser(navigator.userAgent) ?? null;\n}\n\nfunction getBrowserMetadata(): BrowserMetadata | null {\n if (\n [typeof navigator, typeof screen, typeof globalThis].some(\n (x) => x === \"undefined\"\n )\n ) {\n return null;\n }\n\n const { language } = navigator;\n\n return {\n language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n display: {\n screen: {\n height: screen.availHeight,\n width: screen.availWidth,\n orientation: screen.orientation?.type,\n },\n },\n };\n}\n\nexport function collectContext(): BrowserContext {\n return {\n runtime: \"browser\",\n browser: getBrowserMetadata(),\n device: getDeviceMetadata(),\n };\n}\n"],"mappings":";;AAQA,SAAS,oBAA2C;AAClD,KAAI,OAAO,cAAc,YACvB,QAAO;AAGT,QAAO,SAAS,UAAU,UAAU,IAAI;;AAG1C,SAAS,qBAA6C;AACpD,KACE;EAAC,OAAO;EAAW,OAAO;EAAQ,OAAO;EAAW,CAAC,MAClD,MAAM,MAAM,YACd,CAED,QAAO;CAGT,MAAM,EAAE,aAAa;AAErB,QAAO;EACL;EACA,UAAU,KAAK,gBAAgB,CAAC,iBAAiB,CAAC;EAClD,SAAS,EACP,QAAQ;GACN,QAAQ,OAAO;GACf,OAAO,OAAO;GACd,aAAa,OAAO,aAAa;GAClC,EACF;EACF;;AAGH,SAAgB,iBAAiC;AAC/C,QAAO;EACL,SAAS;EACT,SAAS,oBAAoB;EAC7B,QAAQ,mBAAmB;EAC5B"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Env, Runtime } from "@interfere/types/sdk/runtime";
|
|
2
|
+
import { Envelope, EnvelopeContext, EnvelopePayload, EventType } from "@interfere/types/sdk/envelope";
|
|
3
|
+
|
|
4
|
+
//#region src/internal/envelope.d.ts
|
|
5
|
+
interface EnvelopeMetadata {
|
|
6
|
+
buildId: string;
|
|
7
|
+
context: EnvelopeContext;
|
|
8
|
+
environment: Env;
|
|
9
|
+
releaseId: string | null;
|
|
10
|
+
runtime: Runtime;
|
|
11
|
+
}
|
|
12
|
+
declare function buildEnvelope<T extends EventType>(type: T, payload: EnvelopePayload<T>, sessionId: string, metadata: EnvelopeMetadata): Envelope<T>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { EnvelopeMetadata, buildEnvelope };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.d.mts","names":[],"sources":["../../src/internal/envelope.ts"],"mappings":";;;;UAUiB,gBAAA;EACf,OAAA;EACA,OAAA,EAAS,eAAA;EACT,WAAA,EAAa,GAAA;EACb,SAAA;EACA,OAAA,EAAS,OAAA;AAAA;AAAA,iBAGK,aAAA,WAAwB,SAAA,CAAA,CACtC,IAAA,EAAM,CAAA,EACN,OAAA,EAAS,eAAA,CAAgB,CAAA,GACzB,SAAA,UACA,QAAA,EAAU,gBAAA,GACT,QAAA,CAAS,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { v7 } from "uuid";
|
|
2
|
+
//#region src/internal/envelope.ts
|
|
3
|
+
function buildEnvelope(type, payload, sessionId, metadata) {
|
|
4
|
+
return {
|
|
5
|
+
uuid: v7(),
|
|
6
|
+
v: 0,
|
|
7
|
+
type,
|
|
8
|
+
payload,
|
|
9
|
+
context: metadata.context,
|
|
10
|
+
runtime: metadata.runtime,
|
|
11
|
+
environment: metadata.environment,
|
|
12
|
+
buildId: metadata.buildId,
|
|
13
|
+
releaseId: metadata.releaseId,
|
|
14
|
+
clientTs: Date.now(),
|
|
15
|
+
sessionId,
|
|
16
|
+
sessionSource: "client"
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { buildEnvelope };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.mjs","names":["uuidv7"],"sources":["../../src/internal/envelope.ts"],"sourcesContent":["import type {\n Envelope,\n EnvelopeContext,\n EnvelopePayload,\n EventType,\n} from \"@interfere/types/sdk/envelope\";\nimport type { Env, Runtime } from \"@interfere/types/sdk/runtime\";\n\nimport { v7 as uuidv7 } from \"uuid\";\n\nexport interface EnvelopeMetadata {\n buildId: string;\n context: EnvelopeContext;\n environment: Env;\n releaseId: string | null;\n runtime: Runtime;\n}\n\nexport function buildEnvelope<T extends EventType>(\n type: T,\n payload: EnvelopePayload<T>,\n sessionId: string,\n metadata: EnvelopeMetadata\n): Envelope<T> {\n return {\n uuid: uuidv7(),\n v: 0 as const,\n type,\n payload,\n context: metadata.context,\n runtime: metadata.runtime,\n environment: metadata.environment,\n buildId: metadata.buildId,\n releaseId: metadata.releaseId,\n clientTs: Date.now(),\n sessionId,\n sessionSource: \"client\" as const,\n } as Envelope<T>;\n}\n"],"mappings":";;AAkBA,SAAgB,cACd,MACA,SACA,WACA,UACa;AACb,QAAO;EACL,MAAMA,IAAQ;EACd,GAAG;EACH;EACA;EACA,SAAS,SAAS;EAClB,SAAS,SAAS;EAClB,aAAa,SAAS;EACtB,SAAS,SAAS;EAClB,WAAW,SAAS;EACpB,UAAU,KAAK,KAAK;EACpB;EACA,eAAe;EAChB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.mts","names":[],"sources":["../../src/internal/errors.ts"],"mappings":";cAAa,IAAA,EAAI,OAAA,CAAA,KAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../../src/internal/errors.ts"],"sourcesContent":["export const seen = new WeakSet<Error>();\n"],"mappings":";AAAA,MAAa,uBAAO,IAAI,SAAgB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PluginContext } from "../plugins/lib/types.mjs";
|
|
2
|
+
import { PluginOverrides } from "../plugins/lib/loader.mjs";
|
|
3
|
+
import { ConsentState } from "@interfere/types/sdk/plugins/manifest";
|
|
4
|
+
import { EventType } from "@interfere/types/sdk/envelope";
|
|
5
|
+
|
|
6
|
+
//#region src/internal/plugin-runtime.d.ts
|
|
7
|
+
declare class PluginRuntime {
|
|
8
|
+
private readonly activeCleanups;
|
|
9
|
+
private readonly context;
|
|
10
|
+
private readonly features;
|
|
11
|
+
private consentState;
|
|
12
|
+
private syncVersion;
|
|
13
|
+
constructor(context: PluginContext, overrides: PluginOverrides | undefined, initialConsent: ConsentState | undefined);
|
|
14
|
+
getConsent(): ConsentState | null;
|
|
15
|
+
setConsent(nextConsent?: ConsentState): void;
|
|
16
|
+
resetConsent(): void;
|
|
17
|
+
canCapture(type: EventType): boolean;
|
|
18
|
+
start(): void;
|
|
19
|
+
dispose(): void;
|
|
20
|
+
private shouldEnablePlugin;
|
|
21
|
+
private deactivate;
|
|
22
|
+
private activate;
|
|
23
|
+
private sync;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { PluginRuntime };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-runtime.d.mts","names":[],"sources":["../../src/internal/plugin-runtime.ts"],"mappings":";;;;;;cAyBa,aAAA;EAAA,iBACM,cAAA;EAAA,iBACA,OAAA;EAAA,iBACA,QAAA;EAAA,QACT,YAAA;EAAA,QACA,WAAA;cAGN,OAAA,EAAS,aAAA,EACT,SAAA,EAAW,eAAA,cACX,cAAA,EAAgB,YAAA;EAOlB,UAAA,CAAA,GAAc,YAAA;EAId,UAAA,CAAW,WAAA,GAAc,YAAA;EAUzB,YAAA,CAAA;EASA,UAAA,CAAW,IAAA,EAAM,SAAA;EAIjB,KAAA,CAAA;EAWA,OAAA,CAAA;EAAA,QAMQ,kBAAA;EAAA,QAOA,UAAA;EAAA,QAeM,QAAA;EAAA,QAoBN,IAAA;AAAA"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createLogger } from "../util/log.mjs";
|
|
2
|
+
import errorsPlugin from "../plugins/errors.mjs";
|
|
3
|
+
import { loadPlugin, resolveFeatures } from "../plugins/lib/loader.mjs";
|
|
4
|
+
import { getPluginConsentCategory, hasConsentChanged, isConsentAllowed, resolveGrantedConsent, shouldCaptureEvent } from "./consent.mjs";
|
|
5
|
+
import { PLUGIN_MANIFEST } from "@interfere/types/sdk/plugins/manifest";
|
|
6
|
+
//#region src/internal/plugin-runtime.ts
|
|
7
|
+
const log = createLogger("plugin-runtime");
|
|
8
|
+
var PluginRuntime = class {
|
|
9
|
+
activeCleanups = /* @__PURE__ */ new Map();
|
|
10
|
+
context;
|
|
11
|
+
features;
|
|
12
|
+
consentState;
|
|
13
|
+
syncVersion = 0;
|
|
14
|
+
constructor(context, overrides, initialConsent) {
|
|
15
|
+
this.context = context;
|
|
16
|
+
this.features = resolveFeatures(overrides);
|
|
17
|
+
this.consentState = initialConsent ?? null;
|
|
18
|
+
}
|
|
19
|
+
getConsent() {
|
|
20
|
+
return this.consentState;
|
|
21
|
+
}
|
|
22
|
+
setConsent(nextConsent) {
|
|
23
|
+
const nextState = resolveGrantedConsent(nextConsent);
|
|
24
|
+
if (!hasConsentChanged(this.consentState, nextState)) return;
|
|
25
|
+
this.consentState = nextState;
|
|
26
|
+
this.sync();
|
|
27
|
+
}
|
|
28
|
+
resetConsent() {
|
|
29
|
+
if (!hasConsentChanged(this.consentState, null)) return;
|
|
30
|
+
this.consentState = null;
|
|
31
|
+
this.sync();
|
|
32
|
+
}
|
|
33
|
+
canCapture(type) {
|
|
34
|
+
return shouldCaptureEvent(type, this.consentState);
|
|
35
|
+
}
|
|
36
|
+
start() {
|
|
37
|
+
if (this.features.errors) {
|
|
38
|
+
const cleanup = errorsPlugin.setup(this.context);
|
|
39
|
+
if (cleanup) this.activeCleanups.set("errors", cleanup);
|
|
40
|
+
}
|
|
41
|
+
this.sync();
|
|
42
|
+
}
|
|
43
|
+
dispose() {
|
|
44
|
+
for (const key of this.activeCleanups.keys()) this.deactivate(key);
|
|
45
|
+
}
|
|
46
|
+
shouldEnablePlugin(key) {
|
|
47
|
+
return this.features[key] && isConsentAllowed(getPluginConsentCategory(key), this.consentState);
|
|
48
|
+
}
|
|
49
|
+
deactivate(key) {
|
|
50
|
+
const cleanup = this.activeCleanups.get(key);
|
|
51
|
+
if (!cleanup) return;
|
|
52
|
+
try {
|
|
53
|
+
cleanup();
|
|
54
|
+
} catch {
|
|
55
|
+
log.warn("cleanup failed for %s", key);
|
|
56
|
+
}
|
|
57
|
+
this.activeCleanups.delete(key);
|
|
58
|
+
}
|
|
59
|
+
async activate(key) {
|
|
60
|
+
if (this.activeCleanups.has(key) || !this.shouldEnablePlugin(key)) return;
|
|
61
|
+
const version = this.syncVersion;
|
|
62
|
+
const cleanup = await loadPlugin(key, this.context);
|
|
63
|
+
if (!cleanup) return;
|
|
64
|
+
if (version !== this.syncVersion || !this.shouldEnablePlugin(key)) {
|
|
65
|
+
cleanup();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
this.activeCleanups.set(key, cleanup);
|
|
69
|
+
}
|
|
70
|
+
sync() {
|
|
71
|
+
this.syncVersion += 1;
|
|
72
|
+
for (const plugin of PLUGIN_MANIFEST) {
|
|
73
|
+
if (plugin.name === "errors") continue;
|
|
74
|
+
if (this.shouldEnablePlugin(plugin.name)) {
|
|
75
|
+
this.activate(plugin.name).catch(() => {
|
|
76
|
+
log.warn("non-critical plugin loading failed");
|
|
77
|
+
});
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
this.deactivate(plugin.name);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
export { PluginRuntime };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-runtime.mjs","names":[],"sources":["../../src/internal/plugin-runtime.ts"],"sourcesContent":["import type { EventType } from \"@interfere/types/sdk/envelope\";\nimport {\n type ConsentState,\n PLUGIN_MANIFEST,\n type PluginKey,\n} from \"@interfere/types/sdk/plugins/manifest\";\n\nimport errorsPlugin from \"../plugins/errors.js\";\nimport {\n loadPlugin,\n type PluginOverrides,\n resolveFeatures,\n} from \"../plugins/lib/loader.js\";\nimport type { PluginCleanup, PluginContext } from \"../plugins/lib/types.js\";\nimport { createLogger } from \"../util/log.js\";\nimport {\n getPluginConsentCategory,\n hasConsentChanged,\n isConsentAllowed,\n resolveGrantedConsent,\n shouldCaptureEvent,\n} from \"./consent.js\";\n\nconst log = createLogger(\"plugin-runtime\");\n\nexport class PluginRuntime {\n private readonly activeCleanups = new Map<PluginKey, PluginCleanup>();\n private readonly context: PluginContext;\n private readonly features: Record<PluginKey, boolean>;\n private consentState: ConsentState | null;\n private syncVersion = 0;\n\n constructor(\n context: PluginContext,\n overrides: PluginOverrides | undefined,\n initialConsent: ConsentState | undefined\n ) {\n this.context = context;\n this.features = resolveFeatures(overrides);\n this.consentState = initialConsent ?? null;\n }\n\n getConsent(): ConsentState | null {\n return this.consentState;\n }\n\n setConsent(nextConsent?: ConsentState): void {\n const nextState = resolveGrantedConsent(nextConsent);\n if (!hasConsentChanged(this.consentState, nextState)) {\n return;\n }\n\n this.consentState = nextState;\n this.sync();\n }\n\n resetConsent(): void {\n if (!hasConsentChanged(this.consentState, null)) {\n return;\n }\n\n this.consentState = null;\n this.sync();\n }\n\n canCapture(type: EventType): boolean {\n return shouldCaptureEvent(type, this.consentState);\n }\n\n start(): void {\n if (this.features.errors) {\n const cleanup = errorsPlugin.setup(this.context);\n if (cleanup) {\n this.activeCleanups.set(\"errors\", cleanup);\n }\n }\n\n this.sync();\n }\n\n dispose(): void {\n for (const key of this.activeCleanups.keys()) {\n this.deactivate(key);\n }\n }\n\n private shouldEnablePlugin(key: PluginKey): boolean {\n return (\n this.features[key] &&\n isConsentAllowed(getPluginConsentCategory(key), this.consentState)\n );\n }\n\n private deactivate(key: PluginKey): void {\n const cleanup = this.activeCleanups.get(key);\n if (!cleanup) {\n return;\n }\n\n try {\n cleanup();\n } catch {\n log.warn(\"cleanup failed for %s\", key);\n }\n\n this.activeCleanups.delete(key);\n }\n\n private async activate(key: PluginKey): Promise<void> {\n if (this.activeCleanups.has(key) || !this.shouldEnablePlugin(key)) {\n return;\n }\n\n const version = this.syncVersion;\n const cleanup = await loadPlugin(key, this.context);\n if (!cleanup) {\n return;\n }\n\n const staleSync = version !== this.syncVersion;\n if (staleSync || !this.shouldEnablePlugin(key)) {\n cleanup();\n return;\n }\n\n this.activeCleanups.set(key, cleanup);\n }\n\n private sync(): void {\n this.syncVersion += 1;\n\n for (const plugin of PLUGIN_MANIFEST) {\n if (plugin.name === \"errors\") {\n continue;\n }\n\n if (this.shouldEnablePlugin(plugin.name)) {\n this.activate(plugin.name).catch(() => {\n log.warn(\"non-critical plugin loading failed\");\n });\n continue;\n }\n\n this.deactivate(plugin.name);\n }\n }\n}\n"],"mappings":";;;;;;AAuBA,MAAM,MAAM,aAAa,iBAAiB;AAE1C,IAAa,gBAAb,MAA2B;CACzB,iCAAkC,IAAI,KAA+B;CACrE;CACA;CACA;CACA,cAAsB;CAEtB,YACE,SACA,WACA,gBACA;AACA,OAAK,UAAU;AACf,OAAK,WAAW,gBAAgB,UAAU;AAC1C,OAAK,eAAe,kBAAkB;;CAGxC,aAAkC;AAChC,SAAO,KAAK;;CAGd,WAAW,aAAkC;EAC3C,MAAM,YAAY,sBAAsB,YAAY;AACpD,MAAI,CAAC,kBAAkB,KAAK,cAAc,UAAU,CAClD;AAGF,OAAK,eAAe;AACpB,OAAK,MAAM;;CAGb,eAAqB;AACnB,MAAI,CAAC,kBAAkB,KAAK,cAAc,KAAK,CAC7C;AAGF,OAAK,eAAe;AACpB,OAAK,MAAM;;CAGb,WAAW,MAA0B;AACnC,SAAO,mBAAmB,MAAM,KAAK,aAAa;;CAGpD,QAAc;AACZ,MAAI,KAAK,SAAS,QAAQ;GACxB,MAAM,UAAU,aAAa,MAAM,KAAK,QAAQ;AAChD,OAAI,QACF,MAAK,eAAe,IAAI,UAAU,QAAQ;;AAI9C,OAAK,MAAM;;CAGb,UAAgB;AACd,OAAK,MAAM,OAAO,KAAK,eAAe,MAAM,CAC1C,MAAK,WAAW,IAAI;;CAIxB,mBAA2B,KAAyB;AAClD,SACE,KAAK,SAAS,QACd,iBAAiB,yBAAyB,IAAI,EAAE,KAAK,aAAa;;CAItE,WAAmB,KAAsB;EACvC,MAAM,UAAU,KAAK,eAAe,IAAI,IAAI;AAC5C,MAAI,CAAC,QACH;AAGF,MAAI;AACF,YAAS;UACH;AACN,OAAI,KAAK,yBAAyB,IAAI;;AAGxC,OAAK,eAAe,OAAO,IAAI;;CAGjC,MAAc,SAAS,KAA+B;AACpD,MAAI,KAAK,eAAe,IAAI,IAAI,IAAI,CAAC,KAAK,mBAAmB,IAAI,CAC/D;EAGF,MAAM,UAAU,KAAK;EACrB,MAAM,UAAU,MAAM,WAAW,KAAK,KAAK,QAAQ;AACnD,MAAI,CAAC,QACH;AAIF,MADkB,YAAY,KAAK,eAClB,CAAC,KAAK,mBAAmB,IAAI,EAAE;AAC9C,YAAS;AACT;;AAGF,OAAK,eAAe,IAAI,KAAK,QAAQ;;CAGvC,OAAqB;AACnB,OAAK,eAAe;AAEpB,OAAK,MAAM,UAAU,iBAAiB;AACpC,OAAI,OAAO,SAAS,SAClB;AAGF,OAAI,KAAK,mBAAmB,OAAO,KAAK,EAAE;AACxC,SAAK,SAAS,OAAO,KAAK,CAAC,YAAY;AACrC,SAAI,KAAK,qCAAqC;MAC9C;AACF;;AAGF,QAAK,WAAW,OAAO,KAAK"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sw.d.mts","names":[],"sources":["../../src/internal/sw.ts"],"mappings":";iBAMgB,qBAAA,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createLogger } from "../util/log.mjs";
|
|
2
|
+
//#region src/internal/sw.ts
|
|
3
|
+
const log = createLogger("sw");
|
|
4
|
+
const SW_PATH = "/api/interfere/sw";
|
|
5
|
+
function registerServiceWorker() {
|
|
6
|
+
if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) return;
|
|
7
|
+
navigator.serviceWorker.register(SW_PATH, { scope: "/" }).then(() => log.debug("registered")).catch(() => log.warn("registration failed, using direct fetch"));
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { registerServiceWorker };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sw.mjs","names":[],"sources":["../../src/internal/sw.ts"],"sourcesContent":["import { createLogger } from \"../util/log.js\";\n\nconst log = createLogger(\"sw\");\n\nconst SW_PATH = \"/api/interfere/sw\";\n\nexport function registerServiceWorker(): void {\n if (typeof navigator === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return;\n }\n\n navigator.serviceWorker\n .register(SW_PATH, { scope: \"/\" })\n .then(() => log.debug(\"registered\"))\n .catch(() => log.warn(\"registration failed, using direct fetch\"));\n}\n"],"mappings":";;AAEA,MAAM,MAAM,aAAa,KAAK;AAE9B,MAAM,UAAU;AAEhB,SAAgB,wBAA8B;AAC5C,KAAI,OAAO,cAAc,eAAe,EAAE,mBAAmB,WAC3D;AAGF,WAAU,cACP,SAAS,SAAS,EAAE,OAAO,KAAK,CAAC,CACjC,WAAW,IAAI,MAAM,aAAa,CAAC,CACnC,YAAY,IAAI,KAAK,0CAA0C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.mts","names":[],"sources":["../../src/plugins/errors.ts"],"mappings":";;;cA2Ba,YAAA,EAAc,MAAA"}
|