@interfere/react 9.0.2 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +4 -4
  2. package/dist/api.d.mts +25 -0
  3. package/dist/api.d.mts.map +1 -0
  4. package/dist/api.mjs +68 -0
  5. package/dist/api.mjs.map +1 -0
  6. package/dist/error-boundary.d.mts +11 -4
  7. package/dist/error-boundary.d.mts.map +1 -1
  8. package/dist/error-boundary.mjs +6 -3
  9. package/dist/error-boundary.mjs.map +1 -1
  10. package/dist/internal/browser-context.d.mts +6 -0
  11. package/dist/internal/browser-context.d.mts.map +1 -0
  12. package/dist/internal/browser-context.mjs +59 -0
  13. package/dist/internal/browser-context.mjs.map +1 -0
  14. package/dist/internal/capture-boundary.d.mts +5 -1
  15. package/dist/internal/capture-boundary.d.mts.map +1 -1
  16. package/dist/internal/capture-boundary.mjs +9 -5
  17. package/dist/internal/capture-boundary.mjs.map +1 -1
  18. package/dist/internal/capture.d.mts +16 -5
  19. package/dist/internal/capture.d.mts.map +1 -1
  20. package/dist/internal/capture.mjs +20 -16
  21. package/dist/internal/capture.mjs.map +1 -1
  22. package/dist/internal/config.d.mts +20 -4
  23. package/dist/internal/config.d.mts.map +1 -1
  24. package/dist/internal/config.mjs +12 -12
  25. package/dist/internal/config.mjs.map +1 -1
  26. package/dist/internal/consent.d.mts.map +1 -1
  27. package/dist/internal/consent.mjs +3 -1
  28. package/dist/internal/consent.mjs.map +1 -1
  29. package/dist/internal/console-patch.d.mts +19 -0
  30. package/dist/internal/console-patch.d.mts.map +1 -0
  31. package/dist/internal/console-patch.mjs +62 -0
  32. package/dist/internal/console-patch.mjs.map +1 -0
  33. package/dist/internal/dom/actionable.d.mts +27 -0
  34. package/dist/internal/dom/actionable.d.mts.map +1 -0
  35. package/dist/internal/dom/actionable.mjs +62 -0
  36. package/dist/internal/dom/actionable.mjs.map +1 -0
  37. package/dist/internal/kernel-registry.d.mts +8 -0
  38. package/dist/internal/kernel-registry.d.mts.map +1 -0
  39. package/dist/internal/kernel-registry.mjs +31 -0
  40. package/dist/internal/kernel-registry.mjs.map +1 -0
  41. package/dist/internal/kernel.d.mts +267 -0
  42. package/dist/internal/kernel.d.mts.map +1 -0
  43. package/dist/internal/kernel.mjs +322 -0
  44. package/dist/internal/kernel.mjs.map +1 -0
  45. package/dist/internal/otel/exporter.d.mts +93 -0
  46. package/dist/internal/otel/exporter.d.mts.map +1 -0
  47. package/dist/internal/otel/exporter.mjs +212 -0
  48. package/dist/internal/otel/exporter.mjs.map +1 -0
  49. package/dist/internal/otel/index.d.mts +6 -0
  50. package/dist/internal/otel/index.mjs +6 -0
  51. package/dist/internal/otel/instrumentations.d.mts +42 -0
  52. package/dist/internal/otel/instrumentations.d.mts.map +1 -0
  53. package/dist/internal/otel/instrumentations.mjs +150 -0
  54. package/dist/internal/otel/instrumentations.mjs.map +1 -0
  55. package/dist/internal/otel/page-scope-context-manager.d.mts +32 -0
  56. package/dist/internal/otel/page-scope-context-manager.d.mts.map +1 -0
  57. package/dist/internal/otel/page-scope-context-manager.mjs +36 -0
  58. package/dist/internal/otel/page-scope-context-manager.mjs.map +1 -0
  59. package/dist/internal/otel/propagation.d.mts +21 -0
  60. package/dist/internal/otel/propagation.d.mts.map +1 -0
  61. package/dist/internal/otel/propagation.mjs +40 -0
  62. package/dist/internal/otel/propagation.mjs.map +1 -0
  63. package/dist/internal/otel/provider.d.mts +107 -0
  64. package/dist/internal/otel/provider.d.mts.map +1 -0
  65. package/dist/internal/otel/provider.mjs +151 -0
  66. package/dist/internal/otel/provider.mjs.map +1 -0
  67. package/dist/internal/otel/web-vitals.d.mts +35 -0
  68. package/dist/internal/otel/web-vitals.d.mts.map +1 -0
  69. package/dist/internal/otel/web-vitals.mjs +162 -0
  70. package/dist/internal/otel/web-vitals.mjs.map +1 -0
  71. package/dist/internal/page-lifecycle.d.mts +21 -0
  72. package/dist/internal/page-lifecycle.d.mts.map +1 -0
  73. package/dist/internal/page-lifecycle.mjs +33 -0
  74. package/dist/internal/page-lifecycle.mjs.map +1 -0
  75. package/dist/internal/plugin-runtime.d.mts +0 -2
  76. package/dist/internal/plugin-runtime.d.mts.map +1 -1
  77. package/dist/internal/plugin-runtime.mjs +1 -7
  78. package/dist/internal/plugin-runtime.mjs.map +1 -1
  79. package/dist/internal/react-context.d.mts +45 -0
  80. package/dist/internal/react-context.d.mts.map +1 -0
  81. package/dist/internal/react-context.mjs +34 -0
  82. package/dist/internal/react-context.mjs.map +1 -0
  83. package/dist/internal/sw.d.mts +22 -2
  84. package/dist/internal/sw.d.mts.map +1 -1
  85. package/dist/internal/sw.mjs +30 -3
  86. package/dist/internal/sw.mjs.map +1 -1
  87. package/dist/internal/version.d.mts +3 -1
  88. package/dist/internal/version.d.mts.map +1 -1
  89. package/dist/internal/version.mjs +4 -2
  90. package/dist/internal/version.mjs.map +1 -1
  91. package/dist/internal/wrapper-singleton.d.mts +47 -0
  92. package/dist/internal/wrapper-singleton.d.mts.map +1 -0
  93. package/dist/internal/wrapper-singleton.mjs +73 -0
  94. package/dist/internal/wrapper-singleton.mjs.map +1 -0
  95. package/dist/package.mjs +1 -1
  96. package/dist/plugins/errors.d.mts.map +1 -1
  97. package/dist/plugins/errors.mjs +18 -25
  98. package/dist/plugins/errors.mjs.map +1 -1
  99. package/dist/plugins/lib/loader.d.mts +1 -2
  100. package/dist/plugins/lib/loader.d.mts.map +1 -1
  101. package/dist/plugins/lib/loader.mjs +2 -11
  102. package/dist/plugins/lib/loader.mjs.map +1 -1
  103. package/dist/plugins/lib/types.d.mts +3 -2
  104. package/dist/plugins/lib/types.d.mts.map +1 -1
  105. package/dist/plugins/logs.d.mts +13 -0
  106. package/dist/plugins/logs.d.mts.map +1 -0
  107. package/dist/plugins/logs.mjs +53 -0
  108. package/dist/plugins/logs.mjs.map +1 -0
  109. package/dist/plugins/rage-clicks.d.mts.map +1 -1
  110. package/dist/plugins/rage-clicks.mjs +12 -10
  111. package/dist/plugins/rage-clicks.mjs.map +1 -1
  112. package/dist/plugins/replay.d.mts.map +1 -1
  113. package/dist/plugins/replay.mjs +58 -19
  114. package/dist/plugins/replay.mjs.map +1 -1
  115. package/dist/provider.d.mts +11 -20
  116. package/dist/provider.d.mts.map +1 -1
  117. package/dist/provider.mjs +13 -14
  118. package/dist/provider.mjs.map +1 -1
  119. package/dist/react-error-handler.d.mts +21 -5
  120. package/dist/react-error-handler.d.mts.map +1 -1
  121. package/dist/react-error-handler.mjs +15 -7
  122. package/dist/react-error-handler.mjs.map +1 -1
  123. package/dist/sw.d.mts +2 -0
  124. package/dist/sw.mjs +2 -0
  125. package/dist/tracking/api.d.mts +41 -15
  126. package/dist/tracking/api.d.mts.map +1 -1
  127. package/dist/tracking/api.mjs +122 -104
  128. package/dist/tracking/api.mjs.map +1 -1
  129. package/dist/tracking/device.d.mts +30 -7
  130. package/dist/tracking/device.d.mts.map +1 -1
  131. package/dist/tracking/device.mjs +70 -46
  132. package/dist/tracking/device.mjs.map +1 -1
  133. package/dist/tracking/geo.d.mts +11 -3
  134. package/dist/tracking/geo.d.mts.map +1 -1
  135. package/dist/tracking/geo.mjs +33 -29
  136. package/dist/tracking/geo.mjs.map +1 -1
  137. package/dist/tracking/session.d.mts +3 -1
  138. package/dist/tracking/session.d.mts.map +1 -1
  139. package/dist/tracking/session.mjs.map +1 -1
  140. package/dist/util/bot.d.mts +10 -0
  141. package/dist/util/bot.d.mts.map +1 -0
  142. package/dist/util/bot.mjs +14 -0
  143. package/dist/util/bot.mjs.map +1 -0
  144. package/dist/util/global.d.mts +10 -0
  145. package/dist/util/global.d.mts.map +1 -0
  146. package/dist/util/global.mjs +12 -0
  147. package/dist/util/global.mjs.map +1 -0
  148. package/dist/util/log.d.mts.map +1 -1
  149. package/dist/util/log.mjs +8 -1
  150. package/dist/util/log.mjs.map +1 -1
  151. package/dist/util/stringify.d.mts +9 -0
  152. package/dist/util/stringify.d.mts.map +1 -0
  153. package/dist/util/stringify.mjs +16 -0
  154. package/dist/util/stringify.mjs.map +1 -0
  155. package/package.json +73 -20
  156. package/dist/internal/client.d.mts +0 -48
  157. package/dist/internal/client.d.mts.map +0 -1
  158. package/dist/internal/client.mjs +0 -146
  159. package/dist/internal/client.mjs.map +0 -1
  160. package/dist/internal/context.d.mts +0 -6
  161. package/dist/internal/context.d.mts.map +0 -1
  162. package/dist/internal/context.mjs +0 -32
  163. package/dist/internal/context.mjs.map +0 -1
  164. package/dist/internal/envelope.d.mts +0 -15
  165. package/dist/internal/envelope.d.mts.map +0 -1
  166. package/dist/internal/envelope.mjs +0 -24
  167. package/dist/internal/envelope.mjs.map +0 -1
  168. package/dist/internal/errors.d.mts +0 -4
  169. package/dist/internal/errors.d.mts.map +0 -1
  170. package/dist/internal/errors.mjs +0 -4
  171. package/dist/internal/errors.mjs.map +0 -1
  172. package/dist/plugins/device.d.mts +0 -6
  173. package/dist/plugins/device.d.mts.map +0 -1
  174. package/dist/plugins/device.mjs +0 -13
  175. package/dist/plugins/device.mjs.map +0 -1
  176. package/dist/plugins/pages.d.mts +0 -6
  177. package/dist/plugins/pages.d.mts.map +0 -1
  178. package/dist/plugins/pages.mjs +0 -102
  179. package/dist/plugins/pages.mjs.map +0 -1
  180. package/dist/transport/http.d.mts +0 -25
  181. package/dist/transport/http.d.mts.map +0 -1
  182. package/dist/transport/http.mjs +0 -80
  183. package/dist/transport/http.mjs.map +0 -1
  184. package/dist/transport/queue.d.mts +0 -34
  185. package/dist/transport/queue.d.mts.map +0 -1
  186. package/dist/transport/queue.mjs +0 -100
  187. package/dist/transport/queue.mjs.map +0 -1
@@ -0,0 +1,322 @@
1
+ import { createLogger } from "../util/log.mjs";
2
+ import { getGlobal } from "../util/global.mjs";
3
+ import { resolveTargets } from "./config.mjs";
4
+ import { SessionTracker } from "../tracking/api.mjs";
5
+ import { safeStringify } from "../util/stringify.mjs";
6
+ import { onPageHidden } from "./page-lifecycle.mjs";
7
+ import { PluginRuntime } from "./plugin-runtime.mjs";
8
+ import { registerServiceWorker } from "./sw.mjs";
9
+ import { PRODUCER_VERSION } from "./version.mjs";
10
+ import { isNonErrorException, shouldDropBrowserExtensionNoise, shouldDropUnresolvableStack, toExceptions } from "@interfere/types/sdk/errors";
11
+ import { trace } from "@opentelemetry/api";
12
+ import { releaseSlugSchema } from "@interfere/types/releases/slug";
13
+ //#region src/internal/kernel.ts
14
+ const log = createLogger("kernel");
15
+ const TRAILING_SLASH_RE = /\/$/;
16
+ function buildSdkStack(wrapperVersions) {
17
+ return [...wrapperVersions ?? [], PRODUCER_VERSION];
18
+ }
19
+ function buildExceptionEventAttrs(value, opts) {
20
+ const attrs = {
21
+ "interfere.exception.mechanism": opts.mechanism.type,
22
+ "interfere.exception.handled": String(opts.mechanism.handled)
23
+ };
24
+ if (opts.errorDigest) attrs["interfere.error.digest"] = opts.errorDigest;
25
+ if (isNonErrorException(value)) {
26
+ attrs["exception.type"] = value.type;
27
+ attrs["exception.message"] = value.value;
28
+ attrs["interfere.exception.kind"] = "non-error";
29
+ attrs["interfere.exception.serialized"] = safeStringify(value.serialized);
30
+ return attrs;
31
+ }
32
+ attrs["exception.type"] = value.name;
33
+ attrs["exception.message"] = value.message;
34
+ attrs["interfere.exception.kind"] = "error";
35
+ if (value.stack) attrs["exception.stacktrace"] = value.stack;
36
+ attrs["interfere.exception.chain"] = safeStringify(toExceptions(value, opts.mechanism));
37
+ if (opts.appendFrames && opts.appendFrames.length > 0) attrs["interfere.exception.appended_frames"] = safeStringify(opts.appendFrames);
38
+ if (opts.fallbackFrames && opts.fallbackFrames.length > 0) attrs["interfere.exception.fallback_frames"] = safeStringify(opts.fallbackFrames);
39
+ return attrs;
40
+ }
41
+ /**
42
+ * The replacement for the old `Client` god-object. Pure construction graph
43
+ * via `createKernel`; no module-level mutable state, no global lookups.
44
+ * Framework wrappers (`@interfere/next`, `@interfere/vite`) own the
45
+ * singleton lifecycle.
46
+ *
47
+ * Implements `PluginContext` directly: plugins are wired with the kernel
48
+ * itself rather than a closure adapter. The kernel⇄runtime cycle is
49
+ * resolved by constructing the kernel first and binding the runtime via
50
+ * `attachRuntime` immediately after.
51
+ */
52
+ var Kernel = class {
53
+ consent;
54
+ identity;
55
+ device;
56
+ session;
57
+ tracker;
58
+ seen = /* @__PURE__ */ new WeakSet();
59
+ runtime = null;
60
+ otel = null;
61
+ otelDispose = null;
62
+ unsubscribePageHidden = null;
63
+ constructor(tracker) {
64
+ this.tracker = tracker;
65
+ this.consent = {
66
+ get: () => this.requireRuntime().getConsent(),
67
+ set: (value) => {
68
+ const runtime = this.requireRuntime();
69
+ if (value) runtime.setConsent(value);
70
+ else runtime.resetConsent();
71
+ }
72
+ };
73
+ this.identity = {
74
+ get: () => this.tracker.getIdentity(),
75
+ set: (params) => this.tracker.identify(params)
76
+ };
77
+ this.device = {
78
+ getDeviceId: () => this.tracker.getDeviceId(),
79
+ getFpHash: () => this.tracker.getFpHash()
80
+ };
81
+ this.session = {
82
+ getId: () => this.tracker.sessionId(),
83
+ getWindowId: () => this.tracker.windowId()
84
+ };
85
+ this.unsubscribePageHidden = onPageHidden(() => {
86
+ this.flush().catch(() => void 0);
87
+ });
88
+ }
89
+ /** @internal Bound by `createKernel` immediately after construction. */
90
+ attachRuntime(runtime) {
91
+ this.runtime = runtime;
92
+ }
93
+ requireRuntime() {
94
+ if (!this.runtime) throw new Error("Kernel runtime not attached. createKernel must call attachRuntime before any consent/dispose path runs.");
95
+ return this.runtime;
96
+ }
97
+ /** PluginContext shim: plugins expect a non-null string. */
98
+ getSessionId() {
99
+ return this.tracker.sessionId() ?? "";
100
+ }
101
+ /**
102
+ * Records an exception event on the active OTel span. No envelope
103
+ * path — the SDK is OTel-only. Single dedup boundary: drops repeats
104
+ * of the same `Error` instance, plus browser-extension noise and
105
+ * unresolvable stacks. Upstream callers (the errors plugin's
106
+ * `window.onerror` / `console.error` / `unhandledrejection`, the
107
+ * React error boundary) just call this — they don't pre-filter.
108
+ *
109
+ * Does NOT mark the active span as failed. A console.error inside a
110
+ * customer-owned `span()` block would otherwise taint the customer's
111
+ * span even when the customer caught the error explicitly. The
112
+ * customer's `span()` helper sets status itself when it observes a
113
+ * throw.
114
+ *
115
+ * `appendFrames` are always appended (component-stack supplements);
116
+ * `fallbackFrames` are only adopted when the parsed stack is empty
117
+ * (`window.onerror` source/line/col when the Error has no usable
118
+ * stack). Both ride on the serialised `interfere.exception.chain`
119
+ * attribute when the chain is non-trivial.
120
+ */
121
+ recordException(value, opts) {
122
+ if (!isNonErrorException(value)) {
123
+ if (this.seen.has(value)) return;
124
+ this.seen.add(value);
125
+ }
126
+ if (!isNonErrorException(value)) {
127
+ const stacks = [];
128
+ let current = value;
129
+ for (let depth = 0; current && depth < 5; depth += 1) {
130
+ if (current.stack) stacks.push(current.stack);
131
+ current = current.cause instanceof Error ? current.cause : void 0;
132
+ }
133
+ if (shouldDropBrowserExtensionNoise(stacks) || shouldDropUnresolvableStack(stacks)) return;
134
+ }
135
+ const span = trace.getActiveSpan();
136
+ if (span) span.addEvent("exception", buildExceptionEventAttrs(value, opts));
137
+ }
138
+ /**
139
+ * Emits an OTel `LogRecord` via the kernel's logger provider. Used by
140
+ * `plugins/logs.ts` to capture string-only `console.*` calls (the
141
+ * errors plugin still owns Error-bearing console calls — class
142
+ * boundary). No-ops when OTel is not wired (`tracing: false`) since
143
+ * there's no logger provider to emit through.
144
+ */
145
+ recordLog(input) {
146
+ if (!this.otel) return;
147
+ this.otel.loggerProvider.getLogger("@interfere/react").emit({
148
+ severityText: input.severityText,
149
+ severityNumber: input.severityNumber,
150
+ body: input.body,
151
+ ...input.attributes ? { attributes: input.attributes } : {}
152
+ });
153
+ }
154
+ /**
155
+ * Force-flushes the OTel exporters. Returns once they've settled
156
+ * (success or failure) so callers driving unload — page-lifecycle
157
+ * handlers, integration test teardown — can actually await
158
+ * completion. The OTel SDK swallows export errors internally; we
159
+ * surface the resolution either way.
160
+ */
161
+ async flush() {
162
+ if (this.otel) await this.otel.flush();
163
+ }
164
+ /**
165
+ * @internal Wired by `createKernel` after the OTel module has been
166
+ * lazy-loaded. Held on the kernel so `flush()` and `dispose()` can fan
167
+ * out to the providers. The tracer/meter providers stay private —
168
+ * customers don't get raw OTel access; the public surface is the
169
+ * `span()` / `capture()` helpers in `api.ts`.
170
+ */
171
+ attachOtel(handle, dispose) {
172
+ this.otel = handle;
173
+ this.otelDispose = dispose;
174
+ }
175
+ /**
176
+ * Provider-driven consent sync. Pass `undefined` to clear consent (treats
177
+ * the prop as "unmanaged"); pass a state to apply it.
178
+ */
179
+ syncConsent(value) {
180
+ const runtime = this.requireRuntime();
181
+ if (value) {
182
+ runtime.setConsent(value);
183
+ return;
184
+ }
185
+ runtime.resetConsent();
186
+ }
187
+ async dispose() {
188
+ this.unsubscribePageHidden?.();
189
+ this.unsubscribePageHidden = null;
190
+ if (this.runtime) await this.runtime.dispose();
191
+ this.tracker.dispose();
192
+ this.otelDispose?.();
193
+ this.otelDispose = null;
194
+ if (this.otel) {
195
+ await this.otel.shutdown();
196
+ this.otel = null;
197
+ }
198
+ }
199
+ };
200
+ /**
201
+ * Pure construction graph. Awaits the remote-config fetch before resolving,
202
+ * so the runtime sees a fully-applied config before any capture is attempted
203
+ * by the caller. Removes the capture-before-config race the old `Client`
204
+ * had.
205
+ */
206
+ async function createKernel(input = {}) {
207
+ const { opts = {} } = input;
208
+ const fetcher = opts.fetch ?? globalThis.fetch.bind(globalThis);
209
+ const targets = resolveTargets();
210
+ log.info("collector: %s", targets.collectorBaseUrl);
211
+ if (getGlobal("__INTERFERE_FORCE_ENABLE__")) log.warn("FORCE_ENABLE is active. Events will be dropped by production collectors. This should only be set during local development.");
212
+ const tracker = new SessionTracker({
213
+ target: targets.session,
214
+ fetcher
215
+ });
216
+ if (opts._wrapperVersions?.length) globalThis["__INTERFERE_SDK_STACK__"] = opts._wrapperVersions;
217
+ const kernel = new Kernel(tracker);
218
+ const runtime = new PluginRuntime(kernel, opts.plugins, opts.consent);
219
+ kernel.attachRuntime(runtime);
220
+ tracker.start();
221
+ runtime.start();
222
+ if (opts.tracing !== false) {
223
+ const tracingOpts = typeof opts.tracing === "object" ? opts.tracing : void 0;
224
+ await wireOtel(kernel, targets, opts._wrapperVersions, {
225
+ ...tracingOpts ?? {},
226
+ ...opts.serviceName ? { serviceName: opts.serviceName } : {},
227
+ ...opts._internalAdditionalSpanProcessors ? { additionalSpanProcessors: opts._internalAdditionalSpanProcessors } : {},
228
+ ...opts._internalAdditionalLogRecordProcessors ? { additionalLogRecordProcessors: opts._internalAdditionalLogRecordProcessors } : {},
229
+ ...opts._internalAdditionalMetricReaders ? { additionalMetricReaders: opts._internalAdditionalMetricReaders } : {}
230
+ });
231
+ }
232
+ await fetchRemoteConfig(targets.config, fetcher, runtime);
233
+ registerServiceWorker({ onMessage: (msg) => recordSwMessage(kernel, msg) }).catch(() => {});
234
+ return kernel;
235
+ }
236
+ function recordSwMessage(kernel, msg) {
237
+ kernel.recordLog({
238
+ severityText: "info",
239
+ severityNumber: 9,
240
+ body: msg.type,
241
+ attributes: {
242
+ "url.full": msg.url,
243
+ ...msg.reason ? { "interfere.sw.reason": msg.reason } : {}
244
+ }
245
+ });
246
+ }
247
+ async function wireOtel(kernel, targets, wrapperVersions, tracing) {
248
+ const { buildOtelProvider, captureWebVitals, readPropagationFromDocument, registerBundledInstrumentations } = await import("./otel/index.mjs");
249
+ const collectorUrl = targets.collectorBaseUrl.replace(TRAILING_SLASH_RE, "");
250
+ const releaseSlug = readReleaseSlugFromGlobals();
251
+ const handle = buildOtelProvider({
252
+ collectorUrl,
253
+ authHeaders: targets.ingest.headers,
254
+ sdkStack: buildSdkStack(wrapperVersions),
255
+ getSessionId: () => kernel.session.getId(),
256
+ deviceId: kernel.device.getDeviceId(),
257
+ releaseSlug,
258
+ ...tracing.serviceName ? { serviceName: tracing.serviceName } : {},
259
+ ...tracing.additionalSpanProcessors ? { additionalSpanProcessors: tracing.additionalSpanProcessors } : {},
260
+ ...tracing.additionalLogRecordProcessors ? { additionalLogRecordProcessors: tracing.additionalLogRecordProcessors } : {},
261
+ ...tracing.additionalMetricReaders ? { additionalMetricReaders: tracing.additionalMetricReaders } : {}
262
+ });
263
+ const extracted = readPropagationFromDocument();
264
+ if (extracted) handle.contextManager.setPageScope(extracted);
265
+ const dispose = registerBundledInstrumentations({
266
+ tracerProvider: handle.tracerProvider,
267
+ ignoreUrls: [
268
+ `${collectorUrl}/v2/sink`,
269
+ targets.config.url,
270
+ targets.session.url
271
+ ],
272
+ ...tracing.propagateContextUrls ? { propagateContextUrls: tracing.propagateContextUrls } : {},
273
+ ...tracing.resolveRoute ? { resolveRoute: tracing.resolveRoute } : {}
274
+ });
275
+ if (tracing.webVitals !== false) captureWebVitals({
276
+ meter: handle.meterProvider.getMeter("@interfere/react/web-vitals"),
277
+ flush: () => handle.flush(),
278
+ ...tracing.resolveRoute ? { resolveRoute: tracing.resolveRoute } : {}
279
+ });
280
+ kernel.attachOtel(handle, dispose);
281
+ }
282
+ async function fetchRemoteConfig(configTarget, fetcher, runtime) {
283
+ try {
284
+ const headers = Object.fromEntries(configTarget.headers.entries());
285
+ const res = await fetcher(configTarget.url, {
286
+ method: "GET",
287
+ headers,
288
+ signal: AbortSignal.timeout(1e4)
289
+ });
290
+ if (!res.ok) return;
291
+ const config = await res.json();
292
+ if (config?.plugins) {
293
+ runtime.applyRemoteConfig(config.plugins);
294
+ log.debug("applied remote config");
295
+ }
296
+ } catch {
297
+ log.warn("remote config fetch failed, using local defaults");
298
+ }
299
+ }
300
+ function isEnabledByEnvironment() {
301
+ if (typeof process === "undefined" || !process.env) return true;
302
+ const nodeEnv = process.env["NODE_ENV"];
303
+ if (nodeEnv === "production" || nodeEnv === void 0) return true;
304
+ return !!(getGlobal("__INTERFERE_FORCE_ENABLE__") || process.env["INTERFERE_FORCE_ENABLE"]);
305
+ }
306
+ /**
307
+ * Build-time-derived release slug. The build step
308
+ * (`@interfere/next/withInterfere`, `@interfere/vite/plugin`) takes the
309
+ * leading 16 hex chars of the commit SHA (`rel_<16hex>`) and stamps it into
310
+ * the bundle as `__INTERFERE_RELEASE_SLUG__`. The collector derives the same
311
+ * value from the create-release request, so the bundle's runtime slug is
312
+ * guaranteed to match the row without a round-trip.
313
+ *
314
+ * Returns `null` when no slug was injected (dev builds, customers who
315
+ * haven't wired up the plugin).
316
+ */
317
+ function readReleaseSlugFromGlobals() {
318
+ const value = getGlobal("__INTERFERE_RELEASE_SLUG__");
319
+ return typeof value === "string" ? releaseSlugSchema.parse(value) : null;
320
+ }
321
+ //#endregion
322
+ export { Kernel, buildSdkStack, createKernel, isEnabledByEnvironment, readReleaseSlugFromGlobals };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kernel.mjs","names":[],"sources":["../../src/internal/kernel.ts"],"sourcesContent":["import type { IngestedFrame } from \"@interfere/types/data/frame\";\nimport type { SessionId } from \"@interfere/types/data/session\";\nimport {\n type ReleaseSlug,\n releaseSlugSchema,\n} from \"@interfere/types/releases/slug\";\nimport {\n isNonErrorException,\n type NonErrorException,\n shouldDropBrowserExtensionNoise,\n shouldDropUnresolvableStack,\n toExceptions,\n} from \"@interfere/types/sdk/errors\";\nimport type { IdentifyParams } from \"@interfere/types/sdk/identify\";\nimport type { ConsentState } from \"@interfere/types/sdk/plugins/manifest\";\nimport type { ErrorMechanism } from \"@interfere/types/sdk/plugins/payload/errors\";\nimport type { RemoteConfig } from \"@interfere/types/sdk/remote-config\";\n\nimport { trace } from \"@opentelemetry/api\";\nimport type { LogRecordProcessor } from \"@opentelemetry/sdk-logs\";\nimport type { MetricReader } from \"@opentelemetry/sdk-metrics\";\nimport type { SpanProcessor } from \"@opentelemetry/sdk-trace-base\";\n\nimport type { PluginOverrides } from \"../plugins/lib/loader.js\";\nimport type { PluginContext } from \"../plugins/lib/types.js\";\nimport { SessionTracker } from \"../tracking/api.js\";\nimport { getGlobal } from \"../util/global.js\";\nimport { createLogger } from \"../util/log.js\";\nimport { safeStringify } from \"../util/stringify.js\";\nimport type { AuthHeaders, IngestTarget } from \"./config.js\";\nimport { resolveTargets } from \"./config.js\";\nimport type { OtelProviderHandle } from \"./otel/index.js\";\nimport { onPageHidden } from \"./page-lifecycle.js\";\nimport { PluginRuntime } from \"./plugin-runtime.js\";\nimport { registerServiceWorker, type SwMessage } from \"./sw.js\";\nimport { PRODUCER_VERSION } from \"./version.js\";\n\nconst log = createLogger(\"kernel\");\n\nconst TRAILING_SLASH_RE = /\\/$/;\n\nexport function buildSdkStack(wrapperVersions?: string[]): string[] {\n return [...(wrapperVersions ?? []), PRODUCER_VERSION];\n}\n\nfunction buildExceptionEventAttrs(\n value: Error | NonErrorException,\n opts: RecordExceptionOpts\n): Record<string, string> {\n const attrs: Record<string, string> = {\n \"interfere.exception.mechanism\": opts.mechanism.type,\n \"interfere.exception.handled\": String(opts.mechanism.handled),\n };\n if (opts.errorDigest) {\n attrs[\"interfere.error.digest\"] = opts.errorDigest;\n }\n if (isNonErrorException(value)) {\n attrs[\"exception.type\"] = value.type;\n attrs[\"exception.message\"] = value.value;\n attrs[\"interfere.exception.kind\"] = \"non-error\";\n // OTel attrs are scalars; serialize the structured payload so the\n // OTLP mapper can reconstruct it server-side.\n attrs[\"interfere.exception.serialized\"] = safeStringify(value.serialized);\n return attrs;\n }\n attrs[\"exception.type\"] = value.name;\n attrs[\"exception.message\"] = value.message;\n attrs[\"interfere.exception.kind\"] = \"error\";\n if (value.stack) {\n attrs[\"exception.stacktrace\"] = value.stack;\n }\n // Walk Error.cause and ship every entry on a single attr. Each entry\n // carries `{type, value, mechanism?, kind, stack}` — the SDK does not\n // parse stacks; the collector parses `stack` server-side.\n attrs[\"interfere.exception.chain\"] = safeStringify(\n toExceptions(value, opts.mechanism)\n );\n // Supplementary frames the SDK has runtime context to assemble that\n // the server can't synthesise from the raw stack:\n // - `appendFrames`: parsed React component-stack frames (regex\n // parser stays in the SDK — React-specific format, tiny).\n // - `fallbackFrames`: source/line/col tuple from `window.onerror`\n // when the Error's `.stack` is degenerate (CORS-masked, etc.).\n // The collector mapper merges these into the `frames` column after\n // parsing the raw JS stack.\n if (opts.appendFrames && opts.appendFrames.length > 0) {\n attrs[\"interfere.exception.appended_frames\"] = safeStringify(\n opts.appendFrames\n );\n }\n if (opts.fallbackFrames && opts.fallbackFrames.length > 0) {\n attrs[\"interfere.exception.fallback_frames\"] = safeStringify(\n opts.fallbackFrames\n );\n }\n return attrs;\n}\n\nexport interface KernelOptions {\n consent?: ConsentState;\n /**\n * Override the automatic dev-mode guard. When `undefined`, the SDK\n * auto-detects: it disables itself if `process.env[\"NODE_ENV\"]` is not\n * `\"production\"` (Node / webpack / Next.js). In environments where\n * `process` does not exist (Vite, CRA, plain browser) the SDK\n * defaults to **enabled** — pass `false` to disable explicitly.\n */\n enabled?: boolean;\n /** Override `globalThis.fetch` for tests / non-browser hosts. */\n fetch?: typeof globalThis.fetch;\n plugins?: PluginOverrides;\n /**\n * Override the OTel `service.name` resource attribute. Defaults to\n * `\"interfere-sdk\"`. Customers running multiple frontends in one\n * monitoring backend (e.g. `\"@interfere/homepage\"` vs\n * `\"@interfere/dashboard\"`) set this so spans/metrics/logs slice\n * cleanly by surface.\n */\n serviceName?: string;\n /**\n * `false` skips the OTel module entirely — the bundler code-splits\n * `internal/otel/*` out of this path so the error-only bundle never\n * imports the OTel SDK. `true` (default) loads it with default\n * instrumentation config. An object loads it with the supplied\n * config.\n */\n tracing?: boolean | TracingOptions;\n}\n\n/**\n * Per-instrumentation tuning. Every field is optional; defaults match\n * what `interfere/homepage` and `interfere/dashboard` use today via\n * the internal `observability/browser/rum.ts`. New customers don't\n * need to touch any of these — sensible defaults ship out of the box.\n */\nexport interface TracingOptions {\n /**\n * Additional URL patterns to skip when creating fetch / XHR spans.\n * The SDK's own collector endpoint is always ignored; customer-side\n * third-party SDKs (Clerk, Sentry, GTM, etc.) are also skipped by\n * default — supplement with anything specific to your app.\n */\n ignoreUrls?: (string | RegExp)[];\n /**\n * URL patterns that should receive W3C `traceparent` + `baggage`\n * headers on outgoing fetch / XHR. Same-origin requests always\n * propagate; this allowlist enables cross-origin propagation\n * (separate API origin, internal service mesh, etc.).\n */\n propagateContextUrls?: (string | RegExp)[];\n /**\n * Resolve a URL pathname to a low-cardinality route template\n * (e.g. `/blog/[slug]`). Without this, dynamic routes produce one\n * unique span / metric label per visited slug — a cardinality\n * hazard. Used by fetch, document-load resource enrichment,\n * user-interaction, long-task, and web-vitals.\n */\n resolveRoute?: (pathname: string) => string | undefined;\n /**\n * `false` disables Core Web Vitals capture (LCP/FCP/TTFB/INP/CLS).\n * Defaults to enabled; customers who run their own vitals reporting\n * can opt out.\n */\n webVitals?: boolean;\n}\n\n/**\n * @internal\n * Framework wrappers (`@interfere/next`, `@interfere/vite`) pass extra\n * fields through `createKernel` that customers never set themselves.\n * Kept off the customer-facing `KernelOptions` to make the public surface\n * clean.\n */\nexport interface KernelInternalOptions extends KernelOptions {\n /**\n * Extra log-record processors fanned into the LoggerProvider's\n * processor list. Used by `@interfere/observability` for internal-only\n * dual-write to BetterStack from `interfere/homepage` +\n * `interfere/dashboard`. **Not part of the customer surface** — the\n * SDK doesn't expose a way to fan-out to other observability vendors.\n */\n _internalAdditionalLogRecordProcessors?: LogRecordProcessor[];\n /**\n * Extra metric readers fanned into the MeterProvider's reader list.\n * See `_internalAdditionalLogRecordProcessors`. Used to keep web\n * vitals histograms landing on the BetterStack-fronting OTel\n * collector after the rum.ts → SDK migration moved the primary path\n * to `/v2/sink` (Tinybird-only).\n */\n _internalAdditionalMetricReaders?: MetricReader[];\n /**\n * Extra span processors fanned into the WebTracerProvider's processor\n * list. See `_internalAdditionalLogRecordProcessors`.\n */\n _internalAdditionalSpanProcessors?: SpanProcessor[];\n /** Wrapper SDK versions (e.g. `@interfere/next@10.0.0`). */\n _wrapperVersions?: string[];\n}\n\nexport interface KernelConsent {\n get(): ConsentState | null;\n set(value?: ConsentState): void;\n}\n\nexport interface KernelIdentity {\n get(): IdentifyParams | null;\n set(params: IdentifyParams): Promise<void>;\n}\n\nexport interface KernelDevice {\n getDeviceId(): string | null;\n getFpHash(): string | null;\n}\n\nexport interface KernelSession {\n getId(): SessionId | null;\n getWindowId(): string | null;\n}\n\nexport interface RecordExceptionOpts {\n /**\n * Frames always appended to the first exception's stack. Used by\n * `<ErrorBoundary>` to inject React's component-stack frames as\n * supplementary context — these add on top of the real JS frames, they\n * don't replace them.\n */\n readonly appendFrames?: readonly IngestedFrame[];\n /**\n * `error.digest` from React when an RSC server-side throw is\n * sanitised and rebuilt as a synthetic Error on the client. The only\n * identifier that pairs the redacted client capture with the\n * unredacted server capture — enrichment mixes this into the\n * fingerprint so both events group as one problem.\n */\n readonly errorDigest?: string;\n /**\n * Frames adopted only when the first exception's parsed stack is empty.\n * Used by the `window.onerror` handler when the browser hands us\n * source/line/col but the `Error` itself has a degenerate stack — never\n * glued on top of a real stack.\n */\n readonly fallbackFrames?: readonly IngestedFrame[];\n readonly mechanism: ErrorMechanism;\n}\n\n/**\n * The replacement for the old `Client` god-object. Pure construction graph\n * via `createKernel`; no module-level mutable state, no global lookups.\n * Framework wrappers (`@interfere/next`, `@interfere/vite`) own the\n * singleton lifecycle.\n *\n * Implements `PluginContext` directly: plugins are wired with the kernel\n * itself rather than a closure adapter. The kernel⇄runtime cycle is\n * resolved by constructing the kernel first and binding the runtime via\n * `attachRuntime` immediately after.\n */\nexport class Kernel implements PluginContext {\n readonly consent: KernelConsent;\n readonly identity: KernelIdentity;\n readonly device: KernelDevice;\n readonly session: KernelSession;\n\n private readonly tracker: SessionTracker;\n // Single dedup authority for `recordException`. WeakSet so GC'd errors\n // don't pin memory. Upstream callers do not pre-filter.\n private readonly seen = new WeakSet<Error>();\n // Lateinit. `createKernel` constructs the kernel first, then the runtime\n // (passing the kernel as `PluginContext`), then calls `attachRuntime`.\n // Anything kernel-side that touches `runtime` (consent / dispose) runs\n // after the bind.\n private runtime: PluginRuntime | null = null;\n private otel: OtelProviderHandle | null = null;\n private otelDispose: (() => void) | null = null;\n private unsubscribePageHidden: (() => void) | null = null;\n\n constructor(tracker: SessionTracker) {\n this.tracker = tracker;\n\n this.consent = {\n get: () => this.requireRuntime().getConsent(),\n set: (value) => {\n const runtime = this.requireRuntime();\n if (value) {\n runtime.setConsent(value);\n } else {\n runtime.resetConsent();\n }\n },\n };\n\n this.identity = {\n get: () => this.tracker.getIdentity(),\n set: (params) => this.tracker.identify(params),\n };\n\n this.device = {\n getDeviceId: () => this.tracker.getDeviceId(),\n getFpHash: () => this.tracker.getFpHash(),\n };\n\n this.session = {\n getId: () => this.tracker.sessionId(),\n getWindowId: () => this.tracker.windowId(),\n };\n\n // Force-flush the OTel exporters when the tab is about to be hidden.\n // Tail spans + web vitals (CLS/INP fire on visibility hidden) are\n // otherwise lost when the user navigates away. Subscription removed\n // in `dispose()`.\n this.unsubscribePageHidden = onPageHidden(() => {\n // Fire-and-forget — we can't block the unload path.\n this.flush().catch(() => undefined);\n });\n }\n\n /** @internal Bound by `createKernel` immediately after construction. */\n attachRuntime(runtime: PluginRuntime): void {\n this.runtime = runtime;\n }\n\n private requireRuntime(): PluginRuntime {\n if (!this.runtime) {\n throw new Error(\n \"Kernel runtime not attached. createKernel must call attachRuntime before any consent/dispose path runs.\"\n );\n }\n return this.runtime;\n }\n\n /** PluginContext shim: plugins expect a non-null string. */\n getSessionId(): string {\n return this.tracker.sessionId() ?? \"\";\n }\n\n /**\n * Records an exception event on the active OTel span. No envelope\n * path — the SDK is OTel-only. Single dedup boundary: drops repeats\n * of the same `Error` instance, plus browser-extension noise and\n * unresolvable stacks. Upstream callers (the errors plugin's\n * `window.onerror` / `console.error` / `unhandledrejection`, the\n * React error boundary) just call this — they don't pre-filter.\n *\n * Does NOT mark the active span as failed. A console.error inside a\n * customer-owned `span()` block would otherwise taint the customer's\n * span even when the customer caught the error explicitly. The\n * customer's `span()` helper sets status itself when it observes a\n * throw.\n *\n * `appendFrames` are always appended (component-stack supplements);\n * `fallbackFrames` are only adopted when the parsed stack is empty\n * (`window.onerror` source/line/col when the Error has no usable\n * stack). Both ride on the serialised `interfere.exception.chain`\n * attribute when the chain is non-trivial.\n */\n recordException(\n value: Error | NonErrorException,\n opts: RecordExceptionOpts\n ): void {\n // Dedup applies only to real Errors. NonErrorException is a fresh\n // object on every capture site (`toException(event.reason)` allocates\n // it inline) so the WeakSet would never see a repeat anyway.\n if (!isNonErrorException(value)) {\n if (this.seen.has(value)) {\n return;\n }\n this.seen.add(value);\n }\n\n // Noise filters operate on the raw stack strings — Error variants\n // only. Non-Error rejections carry no stack to inspect, but they\n // ship structured payloads the agent can reason over directly, so\n // they're never dropped here.\n if (!isNonErrorException(value)) {\n const stacks: string[] = [];\n let current: Error | undefined = value;\n for (let depth = 0; current && depth < 5; depth += 1) {\n if (current.stack) {\n stacks.push(current.stack);\n }\n current = current.cause instanceof Error ? current.cause : undefined;\n }\n if (\n shouldDropBrowserExtensionNoise(stacks) ||\n shouldDropUnresolvableStack(stacks)\n ) {\n return;\n }\n }\n\n // Stamp our mechanism taxonomy onto the OTel exception event so the\n // collector mapper can recover handled / unhandled and the\n // originating mechanism (browser.onerror / unhandledrejection /\n // manual.api.capture / etc.). OTel's standard `recordException`\n // only carries type/message/stack; mechanism is our enrichment, so\n // we emit the event ourselves via `addEvent` with the full attr\n // set.\n const span = trace.getActiveSpan();\n if (span) {\n span.addEvent(\"exception\", buildExceptionEventAttrs(value, opts));\n }\n }\n\n /**\n * Emits an OTel `LogRecord` via the kernel's logger provider. Used by\n * `plugins/logs.ts` to capture string-only `console.*` calls (the\n * errors plugin still owns Error-bearing console calls — class\n * boundary). No-ops when OTel is not wired (`tracing: false`) since\n * there's no logger provider to emit through.\n */\n recordLog(input: {\n severityText: string;\n severityNumber: number;\n body: string;\n attributes?: Record<string, string>;\n }): void {\n if (!this.otel) {\n return;\n }\n this.otel.loggerProvider.getLogger(\"@interfere/react\").emit({\n severityText: input.severityText,\n severityNumber: input.severityNumber,\n body: input.body,\n ...(input.attributes ? { attributes: input.attributes } : {}),\n });\n }\n\n /**\n * Force-flushes the OTel exporters. Returns once they've settled\n * (success or failure) so callers driving unload — page-lifecycle\n * handlers, integration test teardown — can actually await\n * completion. The OTel SDK swallows export errors internally; we\n * surface the resolution either way.\n */\n async flush(): Promise<void> {\n if (this.otel) {\n await this.otel.flush();\n }\n }\n\n /**\n * @internal Wired by `createKernel` after the OTel module has been\n * lazy-loaded. Held on the kernel so `flush()` and `dispose()` can fan\n * out to the providers. The tracer/meter providers stay private —\n * customers don't get raw OTel access; the public surface is the\n * `span()` / `capture()` helpers in `api.ts`.\n */\n attachOtel(handle: OtelProviderHandle, dispose: () => void): void {\n this.otel = handle;\n this.otelDispose = dispose;\n }\n\n /**\n * Provider-driven consent sync. Pass `undefined` to clear consent (treats\n * the prop as \"unmanaged\"); pass a state to apply it.\n */\n syncConsent(value: ConsentState | undefined): void {\n const runtime = this.requireRuntime();\n if (value) {\n runtime.setConsent(value);\n return;\n }\n runtime.resetConsent();\n }\n\n async dispose(): Promise<void> {\n this.unsubscribePageHidden?.();\n this.unsubscribePageHidden = null;\n if (this.runtime) {\n await this.runtime.dispose();\n }\n this.tracker.dispose();\n\n this.otelDispose?.();\n this.otelDispose = null;\n if (this.otel) {\n await this.otel.shutdown();\n this.otel = null;\n }\n }\n}\n\ninterface CreateKernelInput {\n opts?: KernelInternalOptions;\n}\n\n/**\n * Pure construction graph. Awaits the remote-config fetch before resolving,\n * so the runtime sees a fully-applied config before any capture is attempted\n * by the caller. Removes the capture-before-config race the old `Client`\n * had.\n */\nexport async function createKernel(\n input: CreateKernelInput = {}\n): Promise<Kernel> {\n const { opts = {} } = input;\n const fetcher = opts.fetch ?? globalThis.fetch.bind(globalThis);\n const targets = resolveTargets();\n\n log.info(\"collector: %s\", targets.collectorBaseUrl);\n\n // Misconfigured prod (NODE_ENV missing/wrong + force-enable on) would\n // happily ship the header to a prod collector and have every event\n // silently dropped. The collector returns a structured `dropped` reason\n // so dev tools can surface it, but the warning here is the first line\n // of defense — fires at most once per kernel construction.\n if (getGlobal<boolean>(\"__INTERFERE_FORCE_ENABLE__\")) {\n log.warn(\n \"FORCE_ENABLE is active. Events will be dropped by production collectors. This should only be set during local development.\"\n );\n }\n\n const tracker = new SessionTracker({ target: targets.session, fetcher });\n\n // Single write site for the SDK-stack global. The OTLP exporter\n // headers and the OTel resource attribute both attribute events to\n // the producer chain (`@interfere/next@10.0.0`,\n // `@interfere/react@10.0.0`, …). Wrapper passes `_wrapperVersions`;\n // the kernel publishes the resulting stack here.\n if (opts._wrapperVersions?.length) {\n (globalThis as Record<string, unknown>)[\"__INTERFERE_SDK_STACK__\"] =\n opts._wrapperVersions;\n }\n\n // Resolve the kernel⇄runtime cycle by constructing the kernel first\n // (without runtime), then the runtime with the kernel as PluginContext,\n // then binding. No closure adapter — plugins call the kernel directly.\n const kernel = new Kernel(tracker);\n const runtime = new PluginRuntime(kernel, opts.plugins, opts.consent);\n kernel.attachRuntime(runtime);\n\n tracker.start();\n runtime.start();\n\n if (opts.tracing !== false) {\n const tracingOpts =\n typeof opts.tracing === \"object\" ? opts.tracing : undefined;\n await wireOtel(kernel, targets, opts._wrapperVersions, {\n ...(tracingOpts ?? {}),\n ...(opts.serviceName ? { serviceName: opts.serviceName } : {}),\n ...(opts._internalAdditionalSpanProcessors\n ? {\n additionalSpanProcessors: opts._internalAdditionalSpanProcessors,\n }\n : {}),\n ...(opts._internalAdditionalLogRecordProcessors\n ? {\n additionalLogRecordProcessors:\n opts._internalAdditionalLogRecordProcessors,\n }\n : {}),\n ...(opts._internalAdditionalMetricReaders\n ? {\n additionalMetricReaders: opts._internalAdditionalMetricReaders,\n }\n : {}),\n });\n }\n\n await fetchRemoteConfig(targets.config, fetcher, runtime);\n\n // Service-worker registration is best-effort and never blocks\n // kernel resolution. The SW intercepts `/api/interfere/*` POSTs\n // from the page and queues failures in IndexedDB so OTel batches\n // survive page-hide / offline / >30s collector blips. See\n // `internal/sw.ts`.\n registerServiceWorker({\n onMessage: (msg) => recordSwMessage(kernel, msg),\n }).catch(() => {\n // registerServiceWorker is best-effort and swallows its own\n // failures; this catch is just to satisfy biome's no-floating-\n // promise rule without using `void`.\n });\n\n return kernel;\n}\n\nfunction recordSwMessage(\n kernel: { recordLog: Kernel[\"recordLog\"] },\n msg: SwMessage\n): void {\n // SW activity rides as INFO LogRecords so dashboards can rank\n // queued vs replayed vs dropped without a new metric surface. The\n // SW runs in its own context — we only see what gets posted back\n // when a page is open, which is fine: \"did the SW catch anything\"\n // is the question dashboards need to answer.\n kernel.recordLog({\n severityText: \"info\",\n severityNumber: 9,\n body: msg.type,\n attributes: {\n \"url.full\": msg.url,\n ...(msg.reason ? { \"interfere.sw.reason\": msg.reason } : {}),\n },\n });\n}\n\n/**\n * Lazy-imports the OTel module so `init({ tracing: false })` keeps the\n * error-only bundle path light — the dynamic `import()` is what tells the\n * bundler to code-split. Inherits the kernel's resolved auth headers so\n * OTLP exports use the same `x-interfere-pub-token` (or proxy-mode auth)\n * the rest of the SDK uses; no separate auth knob.\n *\n * Module-load failures are NOT caught — a bundled dynamic import only\n * fails on OOM or module-federation split-brain, both real problems we\n * want to surface, not silently flip into \"tracing disabled.\"\n */\ninterface ResolvedTracingOptions extends TracingOptions {\n additionalLogRecordProcessors?: LogRecordProcessor[];\n additionalMetricReaders?: MetricReader[];\n additionalSpanProcessors?: SpanProcessor[];\n serviceName?: string;\n}\n\nasync function wireOtel(\n kernel: Kernel,\n targets: {\n collectorBaseUrl: string;\n config: IngestTarget;\n ingest: AuthHeaders;\n session: IngestTarget;\n },\n wrapperVersions: string[] | undefined,\n tracing: ResolvedTracingOptions\n): Promise<void> {\n const {\n buildOtelProvider,\n captureWebVitals,\n readPropagationFromDocument,\n registerBundledInstrumentations,\n } = await import(\"./otel/index.js\");\n\n const collectorUrl = targets.collectorBaseUrl.replace(TRAILING_SLASH_RE, \"\");\n const releaseSlug = readReleaseSlugFromGlobals();\n\n const handle = buildOtelProvider({\n collectorUrl,\n authHeaders: targets.ingest.headers,\n sdkStack: buildSdkStack(wrapperVersions),\n // Session id is read per-span by the session span processor, so a\n // mid-page session rotation is reflected on subsequent spans\n // without rebuilding the provider.\n getSessionId: () => kernel.session.getId(),\n // `device.id` is stable for the page lifetime — `DeviceManager.init()`\n // ran during `createKernel` before this point, so the id is in\n // localStorage / cookie / memory by the time the provider boots.\n // Stamped as a resource attribute so every span / log / metric\n // carries it without per-record re-stamping; the collector projects\n // it into `spans.deviceId`. Without this the column is permanently\n // null in production (which is exactly what we observed in\n // 2026-05-12 telemetry — 0/50,632 spans had a device id).\n deviceId: kernel.device.getDeviceId(),\n releaseSlug,\n ...(tracing.serviceName ? { serviceName: tracing.serviceName } : {}),\n ...(tracing.additionalSpanProcessors\n ? { additionalSpanProcessors: tracing.additionalSpanProcessors }\n : {}),\n ...(tracing.additionalLogRecordProcessors\n ? {\n additionalLogRecordProcessors: tracing.additionalLogRecordProcessors,\n }\n : {}),\n ...(tracing.additionalMetricReaders\n ? { additionalMetricReaders: tracing.additionalMetricReaders }\n : {}),\n });\n\n // Phase 4: stitch the client trace onto whatever the SSR renderer was\n // already in. SSR injects a `traceparent` meta tag; we extract it into a\n // Context and seed the page-scope context manager with it. Spans created\n // outside any zone (DocumentLoad, Fetch listeners attached by OTel\n // instrumentations, app code in `useEffect`) fall back to that context\n // and become children of the server-side parent. No meta tag → page\n // scope stays at ROOT_CONTEXT; spans default to fresh roots.\n const extracted = readPropagationFromDocument();\n if (extracted) {\n handle.contextManager.setPageScope(extracted);\n }\n\n const dispose = registerBundledInstrumentations({\n tracerProvider: handle.tracerProvider,\n // Skip auto-instrumenting every fetch the SDK itself makes.\n // Without this we trace OTLP sink POSTs (infinite loop) and the\n // SDK's own config / session-sync requests — and worse, those\n // self-fetches' transient failures (DOMException timeouts on\n // flaky networks, ECONNRESET on the homepage's outbound\n // collector calls) get recorded as `exception` events and end up\n // in `events_pending`, burning enrichment cycles on\n // pipeline-internal noise that has nothing to do with the\n // customer's app. Measured 2026-05-12: 100% of `events_pending`\n // exceptions were children of `fetch GET …/v2/config` / `…/v2/sink`\n // spans.\n //\n // Exact-string ignore on the resolved target URLs (set at SDK\n // boot from `resolveTargets`) so customer proxies on\n // `/api/interfere/v2/*` are caught too. The OTLP sink lives at\n // `${collectorBaseUrl}/v2/sink` (`COLLECTOR_SINK_PATH` in the\n // exporter); config and session land on full URLs already.\n ignoreUrls: [\n `${collectorUrl}/v2/sink`,\n targets.config.url,\n targets.session.url,\n ],\n ...(tracing.propagateContextUrls\n ? { propagateContextUrls: tracing.propagateContextUrls }\n : {}),\n ...(tracing.resolveRoute ? { resolveRoute: tracing.resolveRoute } : {}),\n });\n\n if (tracing.webVitals !== false) {\n captureWebVitals({\n meter: handle.meterProvider.getMeter(\"@interfere/react/web-vitals\"),\n flush: () => handle.flush(),\n ...(tracing.resolveRoute ? { resolveRoute: tracing.resolveRoute } : {}),\n });\n }\n\n kernel.attachOtel(handle, dispose);\n}\n\nasync function fetchRemoteConfig(\n configTarget: IngestTarget,\n fetcher: typeof globalThis.fetch,\n runtime: PluginRuntime\n): Promise<void> {\n try {\n const headers = Object.fromEntries(configTarget.headers.entries());\n const res = await fetcher(configTarget.url, {\n method: \"GET\",\n headers,\n signal: AbortSignal.timeout(10_000),\n });\n if (!res.ok) {\n return;\n }\n const config = (await res.json()) as RemoteConfig;\n if (config?.plugins) {\n runtime.applyRemoteConfig(config.plugins);\n log.debug(\"applied remote config\");\n }\n } catch {\n log.warn(\"remote config fetch failed, using local defaults\");\n }\n}\n\nexport function isEnabledByEnvironment(): boolean {\n if (typeof process === \"undefined\" || !process.env) {\n return true;\n }\n const nodeEnv = process.env[\"NODE_ENV\"];\n if (nodeEnv === \"production\" || nodeEnv === undefined) {\n return true;\n }\n return !!(\n getGlobal<boolean>(\"__INTERFERE_FORCE_ENABLE__\") ||\n process.env[\"INTERFERE_FORCE_ENABLE\"]\n );\n}\n\n/**\n * Build-time-derived release slug. The build step\n * (`@interfere/next/withInterfere`, `@interfere/vite/plugin`) takes the\n * leading 16 hex chars of the commit SHA (`rel_<16hex>`) and stamps it into\n * the bundle as `__INTERFERE_RELEASE_SLUG__`. The collector derives the same\n * value from the create-release request, so the bundle's runtime slug is\n * guaranteed to match the row without a round-trip.\n *\n * Returns `null` when no slug was injected (dev builds, customers who\n * haven't wired up the plugin).\n */\nexport function readReleaseSlugFromGlobals(): ReleaseSlug | null {\n const value = getGlobal<unknown>(\"__INTERFERE_RELEASE_SLUG__\");\n\n return typeof value === \"string\" ? releaseSlugSchema.parse(value) : null;\n}\n"],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ import { ExportResult } from "@opentelemetry/core";
2
+ import { AggregationTemporality, PushMetricExporter, ResourceMetrics } from "@opentelemetry/sdk-metrics";
3
+ import { ReadableLogRecord } from "@opentelemetry/sdk-logs";
4
+ import { ReadableSpan, SpanExporter } from "@opentelemetry/sdk-trace-base";
5
+
6
+ //#region src/internal/otel/exporter.d.ts
7
+ interface ExporterCommon {
8
+ /**
9
+ * Auth identity from the kernel's resolved ingest target — currently
10
+ * `x-interfere-pub-token` only (proxy mode sets nothing because the
11
+ * proxy server stamps `x-api-key` upstream). Encoded into the beacon
12
+ * URL via `buildBeaconUrl` rather than into request headers because
13
+ * `sendBeacon` doesn't carry custom headers.
14
+ */
15
+ authHeaders: Headers;
16
+ /** Base collector URL — the opaque sink path is appended. */
17
+ collectorUrl: string;
18
+ }
19
+ /** Exported for direct unit testing. */
20
+ declare function buildSinkUrl(collectorUrl: string): string;
21
+ /**
22
+ * Beacon-flavoured sink URL — encodes the producer-version (always)
23
+ * and pub-token (when present in `authHeaders`) as query parameters
24
+ * so `navigator.sendBeacon` can authenticate without setting custom
25
+ * request headers.
26
+ *
27
+ * Exported for direct unit testing.
28
+ */
29
+ declare function buildBeaconUrl(input: ExporterCommon): string;
30
+ /**
31
+ * Browser-side OTLP trace exporter.
32
+ *
33
+ * Dispatches every export via `navigator.sendBeacon` — which is the
34
+ * only browser transport that reliably commits a request the page is
35
+ * also tearing down (`visibilitychange→hidden`, hard navigation). The
36
+ * `keepalive: true` fetch path the OTLP HTTP exporter ships with
37
+ * works for small payloads but falls back to ordinary fetch (which
38
+ * the renderer aborts on unload) once the cumulative 64KiB / 9-
39
+ * concurrent budget is exhausted. Production data on 2026-05-11
40
+ * attributed ~15% browser-fetch span loss to that fallback; the
41
+ * beacon path closes that hole by design.
42
+ *
43
+ * Identity (`x-interfere-producer-version`, `x-interfere-pub-token`)
44
+ * rides the URL because beacons can't carry custom headers. The
45
+ * collector accepts both query and header paths — see
46
+ * `services/collector/src/{modules/v2/middleware,auth/surface}.ts`.
47
+ *
48
+ * No retry on failure. The service worker backstop captures 5xx /
49
+ * network failures separately by intercepting the same beacon POST
50
+ * and queueing into IndexedDB for replay (`internal/sw.ts`).
51
+ */
52
+ declare class BeaconTraceExporter implements SpanExporter {
53
+ private readonly url;
54
+ constructor(url: string);
55
+ export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void;
56
+ shutdown(): Promise<void>;
57
+ forceFlush(): Promise<void>;
58
+ }
59
+ /**
60
+ * Browser-side OTLP log exporter — same beacon transport, log
61
+ * payload. See `BeaconTraceExporter` for the design rationale.
62
+ */
63
+ declare class BeaconLogExporter {
64
+ private readonly url;
65
+ constructor(url: string);
66
+ export(logs: ReadableLogRecord[], resultCallback: (result: ExportResult) => void): void;
67
+ shutdown(): Promise<void>;
68
+ forceFlush(): Promise<void>;
69
+ }
70
+ /**
71
+ * Browser-side OTLP metric exporter — beacon transport, metric
72
+ * payload, `DELTA` temporality.
73
+ *
74
+ * `DELTA` matches what the OTLP HTTP exporter the kit's previous
75
+ * `OTLPMetricExporter` was configured with (see the
76
+ * `temporalityPreference: AggregationTemporalityPreference.DELTA`
77
+ * arg `provider.ts` used to pass). Returning the same temporality
78
+ * for every instrument type keeps the wire shape downstream
79
+ * consumers receive unchanged across the migration.
80
+ */
81
+ declare class BeaconMetricExporter implements PushMetricExporter {
82
+ private readonly url;
83
+ constructor(url: string);
84
+ export(metrics: ResourceMetrics, resultCallback: (result: ExportResult) => void): void;
85
+ selectAggregationTemporality(): AggregationTemporality;
86
+ forceFlush(): Promise<void>;
87
+ shutdown(): Promise<void>;
88
+ }
89
+ declare function createBeaconTraceExporter(input: ExporterCommon): BeaconTraceExporter;
90
+ declare function createBeaconMetricExporter(input: ExporterCommon): BeaconMetricExporter;
91
+ declare function createBeaconLogExporter(input: ExporterCommon): BeaconLogExporter;
92
+ //#endregion
93
+ export { buildBeaconUrl, buildSinkUrl, createBeaconLogExporter, createBeaconMetricExporter, createBeaconTraceExporter };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exporter.d.mts","names":[],"sources":["../../../src/internal/otel/exporter.ts"],"mappings":";;;;;;UA+EU,cAAA;EAAA;;;;;;;EAQR,WAAA,EAAa,OAAA;EAED;EAAZ,YAAA;AAAA;;iBAIc,YAAA,CAAa,YAAA;;AAe7B;;;;;AASC;;iBATe,cAAA,CAAe,KAAA,EAAO,cAAA;;;;;;;;;;;;;;;;;;;;;;;cAgHhC,mBAAA,YAA+B,YAAA;EAAA,iBAClB,GAAA;cAEL,GAAA;EAIZ,MAAA,CACE,KAAA,EAAO,YAAA,IACP,cAAA,GAAiB,MAAA,EAAQ,YAAA;EAc3B,QAAA,CAAA,GAAY,OAAA;EAIZ,UAAA,CAAA,GAAc,OAAA;AAAA;;;;;cASV,iBAAA;EAAA,iBACa,GAAA;cAEL,GAAA;EAIZ,MAAA,CACE,IAAA,EAAM,iBAAA,IACN,cAAA,GAAiB,MAAA,EAAQ,YAAA;EAc3B,QAAA,CAAA,GAAY,OAAA;EAIZ,UAAA,CAAA,GAAc,OAAA;AAAA;;;;;;;;AAAO;;;;cAgBjB,oBAAA,YAAgC,kBAAA;EAAA,iBACnB,GAAA;cAEL,GAAA;EAIZ,MAAA,CACE,OAAA,EAAS,eAAA,EACT,cAAA,GAAiB,MAAA,EAAQ,YAAA;EAU3B,4BAAA,CAAA,GAAgC,sBAAA;EAIhC,UAAA,CAAA,GAAc,OAAA;EAId,QAAA,CAAA,GAAY,OAAA;AAAA;AAAA,iBAKE,yBAAA,CACd,KAAA,EAAO,cAAA,GACN,mBAAA;AAAA,iBAIa,0BAAA,CACd,KAAA,EAAO,cAAA,GACN,oBAAA;AAAA,iBAIa,uBAAA,CACd,KAAA,EAAO,cAAA,GACN,iBAAA"}