@interfere/react 9.0.1 → 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 -21
  181. package/dist/transport/http.d.mts.map +0 -1
  182. package/dist/transport/http.mjs +0 -72
  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 -95
  187. package/dist/transport/queue.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"propagation.mjs","names":[],"sources":["../../../src/internal/otel/propagation.ts"],"sourcesContent":["import {\n type Context,\n defaultTextMapGetter,\n ROOT_CONTEXT,\n trace,\n} from \"@opentelemetry/api\";\nimport { W3CTraceContextPropagator } from \"@opentelemetry/core\";\n\nconst TRACEPARENT_META = \"traceparent\";\nconst TRACESTATE_META = \"tracestate\";\nconst INVALID_TRACE_ID = \"00000000000000000000000000000000\";\nconst TRACE_FLAGS_SAMPLED = 0x1;\n// Local instance — extraction must not depend on whether the customer (or\n// our own bootstrap) set a global propagator.\nconst W3C_PROPAGATOR = new W3CTraceContextPropagator();\n\nfunction readMeta(name: string): string | null {\n if (typeof document === \"undefined\") {\n return null;\n }\n const el = document.querySelector(`meta[name=\"${name}\"]`);\n return el?.getAttribute(\"content\") ?? null;\n}\n\n/**\n * Reads the W3C `traceparent` (and optional `tracestate`) meta tag from the\n * document head and extracts an OTel `Context`. SSR renderers (Next, Vite,\n * …) inject the server-side request span's context so the client SDK can\n * stitch its spans onto the same trace.\n *\n * Returns `null` when:\n * - called server-side (no `document`)\n * - no `traceparent` meta tag is present\n * - the extracted context has an invalid trace id\n * - the `sampled` flag is unset — the browser doesn't honor the bit when\n * creating child spans, so an unsampled parent would orphan every\n * browser span under a trace with no recorded server segments. Drop\n * instead so spans fall back to a fresh root.\n */\nexport function readPropagationFromDocument(): Context | null {\n const traceparent = readMeta(TRACEPARENT_META);\n if (!traceparent) {\n return null;\n }\n\n const carrier: Record<string, string> = { traceparent };\n const tracestate = readMeta(TRACESTATE_META);\n if (tracestate) {\n carrier[TRACESTATE_META] = tracestate;\n }\n\n const extracted = W3C_PROPAGATOR.extract(\n ROOT_CONTEXT,\n carrier,\n defaultTextMapGetter\n );\n const spanCtx = trace.getSpanContext(extracted);\n // octet of flag bits per the W3C trace-context spec; bit 0 is the sampled\n // bit. Bitwise AND is the canonical extraction.\n if (\n !spanCtx ||\n spanCtx.traceId === INVALID_TRACE_ID ||\n // biome-ignore lint/suspicious/noBitwiseOperators: traceFlags is a single\n (spanCtx.traceFlags & TRACE_FLAGS_SAMPLED) === 0\n ) {\n return null;\n }\n return extracted;\n}\n"],"mappings":";;;AAQA,MAAM,mBAAmB;AACzB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAG5B,MAAM,iBAAiB,IAAI,2BAA2B;AAEtD,SAAS,SAAS,MAA6B;CAC7C,IAAI,OAAO,aAAa,aACtB,OAAO;CAGT,OADW,SAAS,cAAc,cAAc,KAAK,IAC5C,EAAE,aAAa,UAAU,IAAI;;;;;;;;;;;;;;;;;AAkBxC,SAAgB,8BAA8C;CAC5D,MAAM,cAAc,SAAS,iBAAiB;CAC9C,IAAI,CAAC,aACH,OAAO;CAGT,MAAM,UAAkC,EAAE,aAAa;CACvD,MAAM,aAAa,SAAS,gBAAgB;CAC5C,IAAI,YACF,QAAQ,mBAAmB;CAG7B,MAAM,YAAY,eAAe,QAC/B,cACA,SACA,qBACD;CACD,MAAM,UAAU,MAAM,eAAe,UAAU;CAG/C,IACE,CAAC,WACD,QAAQ,YAAY,qBAEnB,QAAQ,aAAa,yBAAyB,GAE/C,OAAO;CAET,OAAO"}
@@ -0,0 +1,107 @@
1
+ import { PageScopeContextManager } from "./page-scope-context-manager.mjs";
2
+ import { ReleaseSlug } from "@interfere/types/releases/slug";
3
+ import { MeterProvider, MetricReader } from "@opentelemetry/sdk-metrics";
4
+ import { LogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs";
5
+ import { SpanProcessor } from "@opentelemetry/sdk-trace-base";
6
+ import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
7
+ import { SessionId } from "@interfere/types/data/session";
8
+
9
+ //#region src/internal/otel/provider.d.ts
10
+ interface OtelProviderInput {
11
+ /**
12
+ * @internal
13
+ *
14
+ * Extra log-record processors fanned into the LoggerProvider's
15
+ * processor list at construction. Used by `@interfere/observability`
16
+ * for internal-only dual-write to BetterStack — keeps a parallel
17
+ * destination's queue/exporter independent of the SDK's own.
18
+ * Customers don't get a fan-out hook on the SDK surface; this is a
19
+ * private bridge for our own dogfood apps.
20
+ */
21
+ additionalLogRecordProcessors?: LogRecordProcessor[];
22
+ /**
23
+ * @internal
24
+ *
25
+ * Extra metric readers fanned into the MeterProvider's reader list
26
+ * at construction. Used by `@interfere/observability` for internal-
27
+ * only dual-write of web vitals histograms (`web_vitals.{ttfb,fcp,
28
+ * lcp,inp,cls}`) to the BetterStack-fronting OTel collector. Each
29
+ * reader owns its own queue + exporter so a 5xx / network outage on
30
+ * the parallel destination backs off only its own batch.
31
+ */
32
+ additionalMetricReaders?: MetricReader[];
33
+ /**
34
+ * @internal
35
+ *
36
+ * Extra span processors fanned into the WebTracerProvider's processor
37
+ * list at construction. See `additionalLogRecordProcessors`.
38
+ */
39
+ additionalSpanProcessors?: SpanProcessor[];
40
+ /**
41
+ * Auth + content headers for OTLP exports. Sourced from the kernel's
42
+ * already-resolved ingest target so the OTLP path inherits the same
43
+ * `x-interfere-pub-token` (or proxy-mode auth) the rest of the SDK
44
+ * uses — no separate auth knob.
45
+ */
46
+ authHeaders: Headers;
47
+ /** OTLP base URL — the opaque sink path is appended by the exporters. */
48
+ collectorUrl: string;
49
+ /**
50
+ * Stable per-device identifier from `DeviceManager`. Resolved from
51
+ * localStorage / cookie at kernel boot, so it's synchronously
52
+ * available before the providers are constructed. Stamped as the
53
+ * OTel-semconv `device.id` resource attribute so every span / log /
54
+ * metric carries it without per-record re-stamping. Optional —
55
+ * tests and SSR boot paths that build the provider before the
56
+ * device manager has run pass `null`.
57
+ */
58
+ deviceId?: string | null | undefined;
59
+ /**
60
+ * Returns the kernel's current session id. Wired into per-span /
61
+ * per-log session processors so a mid-page session rotation (30-min
62
+ * idle expiry, manual reset) lands on subsequent spans without the
63
+ * kernel having to rebuild the providers. Returns `null` before the
64
+ * `SessionTracker` has bootstrapped.
65
+ */
66
+ getSessionId: () => SessionId | null;
67
+ /** Optional release slug — top-level `release.slug` attr (Phase 6 wire identity). */
68
+ releaseSlug?: ReleaseSlug | null | undefined;
69
+ /**
70
+ * Producer chain for the `interfere.sdk.stack` resource attribute —
71
+ * e.g. `["@interfere/next@10.0.0", "@interfere/react@10.0.0"]`.
72
+ * Wrapper-injected; the kernel just forwards.
73
+ */
74
+ sdkStack: string[];
75
+ /**
76
+ * Override the OTel `service.name` resource attribute. Defaults to
77
+ * `"interfere-sdk"`.
78
+ */
79
+ serviceName?: string;
80
+ }
81
+ interface OtelProviderHandle {
82
+ /**
83
+ * Page-scope context manager. The kernel calls `setPageScope(ctx)` after
84
+ * extracting the SSR `traceparent` meta tag so spans without a more
85
+ * specific zone descend from the server-side parent.
86
+ */
87
+ contextManager: PageScopeContextManager;
88
+ /** Force-flush all three providers — call from kernel.flush() / on visibility hidden. */
89
+ flush(): Promise<void>;
90
+ loggerProvider: LoggerProvider;
91
+ meterProvider: MeterProvider;
92
+ metricReader: MetricReader;
93
+ /** Tear down all three providers — called from kernel.dispose(). */
94
+ shutdown(): Promise<void>;
95
+ tracerProvider: WebTracerProvider;
96
+ }
97
+ /**
98
+ * Constructs the SDK's **private** OTel providers — never registers globally
99
+ * via `provider.register()` or `trace.setGlobalTracerProvider()`. Customers
100
+ * with their own OTel setup (DataDog, Vercel, etc.) cohabit cleanly.
101
+ *
102
+ * Splitting this out from `Kernel` keeps the OTel module lazy-loadable so
103
+ * the error-only bundle path (no tracing) never imports the OTel SDK.
104
+ */
105
+ declare function buildOtelProvider(input: OtelProviderInput): OtelProviderHandle;
106
+ //#endregion
107
+ export { OtelProviderHandle, OtelProviderInput, buildOtelProvider };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.mts","names":[],"sources":["../../../src/internal/otel/provider.ts"],"mappings":";;;;;;;;;UA0GiB,iBAAA;;AAAjB;;;;;;;;;EAWE,6BAAA,GAAgC,kBAAA;EA+CP;;;;;;;;;;EApCzB,uBAAA,GAA0B,YAAA;EAkC1B;;;;;;EA3BA,wBAAA,GAA2B,aAAA;EAwChB;AAGb;;;;;EApCE,WAAA,EAAa,OAAA;EA8CE;EA5Cf,YAAA;EA+CY;;;;;;;;;EArCZ,QAAA;EAkCA;;;;;;;EA1BA,YAAA,QAAoB,SAAA;EA8Ba;EA5BjC,WAAA,GAAc,WAAA;EAuCA;;;;;EAjCd,QAAA;EAmCC;;;;EA9BD,WAAA;AAAA;AAAA,UAGe,kBAAA;;;;;;EAMf,cAAA,EAAgB,uBAAA;;EAEhB,KAAA,IAAS,OAAA;EACT,cAAA,EAAgB,cAAA;EAChB,aAAA,EAAe,aAAA;EACf,YAAA,EAAc,YAAA;;EAEd,QAAA,IAAY,OAAA;EACZ,cAAA,EAAgB,iBAAA;AAAA;;;;;;;;;iBAWF,iBAAA,CACd,KAAA,EAAO,iBAAA,GACN,kBAAA"}
@@ -0,0 +1,151 @@
1
+ import { SDK_NAME, SDK_VERSION } from "../version.mjs";
2
+ import { createBeaconLogExporter, createBeaconMetricExporter, createBeaconTraceExporter } from "./exporter.mjs";
3
+ import { PageScopeContextManager } from "./page-scope-context-manager.mjs";
4
+ import { propagation } from "@opentelemetry/api";
5
+ import { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator } from "@opentelemetry/core";
6
+ import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
7
+ import { normalizeEnv } from "@interfere/types/sdk/runtime";
8
+ import { browserDetector } from "@opentelemetry/opentelemetry-browser-detector";
9
+ import { detectResources, resourceFromAttributes } from "@opentelemetry/resources";
10
+ import { BatchLogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs";
11
+ import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
12
+ import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
13
+ import { createSessionLogRecordProcessor, createSessionSpanProcessor } from "@opentelemetry/web-common";
14
+ //#region src/internal/otel/provider.ts
15
+ const SERVICE_NAMESPACE = "interfere";
16
+ const ATTR_SERVICE_NAME = "service.name";
17
+ const ATTR_SERVICE_NAMESPACE = "service.namespace";
18
+ const ATTR_DEPLOYMENT_ENVIRONMENT_NAME = "deployment.environment.name";
19
+ const ATTR_TELEMETRY_SDK_LANGUAGE = "telemetry.sdk.language";
20
+ const ATTR_RELEASE_SLUG = "release.slug";
21
+ const ATTR_INTERFERE_SDK_STACK = "interfere.sdk.stack";
22
+ const ATTR_INTERFERE_SDK_NAME = "interfere.sdk.name";
23
+ const ATTR_INTERFERE_SDK_VERSION = "interfere.sdk.version";
24
+ const ATTR_DEVICE_ID = "device.id";
25
+ const DEFAULT_SERVICE_NAME = "interfere-sdk";
26
+ /**
27
+ * Tuned for `navigator.sendBeacon`'s per-call payload ceiling.
28
+ *
29
+ * The browser primary exporters dispatch every export via beacon
30
+ * (see `BeaconTraceExporter` / `BeaconLogExporter` /
31
+ * `BeaconMetricExporter` in `exporter.ts`) — sendBeacon is the only
32
+ * browser transport that reliably commits a request the page is also
33
+ * tearing down. It's not free though: most browsers cap each beacon
34
+ * payload at 64KiB (Chrome / Firefox) and reject anything larger
35
+ * with a `false` return from `navigator.sendBeacon` — at which point
36
+ * the BSP just drops the batch (no retry: the SW backstop intercepts
37
+ * 5xx / network failures, not user-agent rejections).
38
+ *
39
+ * The numbers below keep payloads comfortably under that ceiling,
40
+ * even on jank-heavy pages (production data on 2026-05-11 saw 831
41
+ * `longtask` spans piled up on a single page-hide, observed via the
42
+ * BetterStack dual-write) where a 100-span batch would already top
43
+ * 100KiB:
44
+ *
45
+ * - `maxExportBatchSize: 10` — caps each beacon at ~10–20KiB,
46
+ * leaving headroom for span attribute bloat without flirting
47
+ * with the per-call ceiling.
48
+ * - `maxQueueSize: 50` — caps the worst-case unload payload at
49
+ * ~50–100KiB; the BSP drains across 5 beacons when forced-
50
+ * flushed on `visibilitychange→hidden`, all of which queue
51
+ * synchronously inside the user agent.
52
+ * - `scheduledDelayMillis: 2000` — quieter pages keep less in
53
+ * buffer at any moment, so a hidden→visible→hidden bounce
54
+ * doesn't accumulate 5s of telemetry waiting to ship.
55
+ * - `exportTimeoutMillis: 10_000` — unchanged. Beacon dispatches
56
+ * are essentially synchronous (the user agent enqueues into its
57
+ * own send loop), so the timeout is mostly defensive against
58
+ * pathological `JsonTraceSerializer.serializeRequest` calls.
59
+ */
60
+ const BROWSER_BATCH_OPTIONS = {
61
+ maxQueueSize: 50,
62
+ maxExportBatchSize: 10,
63
+ scheduledDelayMillis: 2e3,
64
+ exportTimeoutMillis: 1e4
65
+ };
66
+ /**
67
+ * Constructs the SDK's **private** OTel providers — never registers globally
68
+ * via `provider.register()` or `trace.setGlobalTracerProvider()`. Customers
69
+ * with their own OTel setup (DataDog, Vercel, etc.) cohabit cleanly.
70
+ *
71
+ * Splitting this out from `Kernel` keeps the OTel module lazy-loadable so
72
+ * the error-only bundle path (no tracing) never imports the OTel SDK.
73
+ */
74
+ function buildOtelProvider(input) {
75
+ const environment = normalizeEnv(typeof process === "undefined" ? void 0 : process.env["NODE_ENV"]) ?? "unknown";
76
+ const baseResource = resourceFromAttributes({
77
+ [ATTR_SERVICE_NAME]: input.serviceName ?? DEFAULT_SERVICE_NAME,
78
+ [ATTR_SERVICE_NAMESPACE]: SERVICE_NAMESPACE,
79
+ [ATTR_DEPLOYMENT_ENVIRONMENT_NAME]: environment,
80
+ [ATTR_TELEMETRY_SDK_LANGUAGE]: "webjs",
81
+ [ATTR_INTERFERE_SDK_NAME]: SDK_NAME,
82
+ [ATTR_INTERFERE_SDK_VERSION]: SDK_VERSION,
83
+ [ATTR_INTERFERE_SDK_STACK]: input.sdkStack.join(", "),
84
+ ...input.releaseSlug ? { [ATTR_RELEASE_SLUG]: input.releaseSlug } : {},
85
+ ...input.deviceId ? { [ATTR_DEVICE_ID]: input.deviceId } : {}
86
+ });
87
+ const detected = detectResources({ detectors: [browserDetector] });
88
+ const resource = baseResource.merge(detected);
89
+ const traceExporter = createBeaconTraceExporter({
90
+ collectorUrl: input.collectorUrl,
91
+ authHeaders: input.authHeaders
92
+ });
93
+ const getSessionId = input.getSessionId;
94
+ const tracerProvider = new WebTracerProvider({
95
+ resource,
96
+ spanProcessors: [
97
+ createSessionSpanProcessor({ getSessionId }),
98
+ new BatchSpanProcessor(traceExporter, BROWSER_BATCH_OPTIONS),
99
+ ...input.additionalSpanProcessors ?? []
100
+ ]
101
+ });
102
+ const contextManager = new PageScopeContextManager();
103
+ tracerProvider.register({ contextManager });
104
+ if (propagation.fields().length === 0) propagation.setGlobalPropagator(new CompositePropagator({ propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()] }));
105
+ const metricReader = new PeriodicExportingMetricReader({
106
+ exporter: createBeaconMetricExporter({
107
+ collectorUrl: input.collectorUrl,
108
+ authHeaders: input.authHeaders
109
+ }),
110
+ exportIntervalMillis: 3e4
111
+ });
112
+ const meterProvider = new MeterProvider({
113
+ resource,
114
+ readers: [metricReader, ...input.additionalMetricReaders ?? []]
115
+ });
116
+ const logExporter = createBeaconLogExporter({
117
+ collectorUrl: input.collectorUrl,
118
+ authHeaders: input.authHeaders
119
+ });
120
+ const loggerProvider = new LoggerProvider({
121
+ resource,
122
+ processors: [
123
+ createSessionLogRecordProcessor({ getSessionId }),
124
+ new BatchLogRecordProcessor(logExporter, BROWSER_BATCH_OPTIONS),
125
+ ...input.additionalLogRecordProcessors ?? []
126
+ ]
127
+ });
128
+ return {
129
+ contextManager,
130
+ tracerProvider,
131
+ meterProvider,
132
+ metricReader,
133
+ loggerProvider,
134
+ flush: async () => {
135
+ await Promise.all([
136
+ tracerProvider.forceFlush(),
137
+ metricReader.forceFlush(),
138
+ loggerProvider.forceFlush()
139
+ ]);
140
+ },
141
+ shutdown: async () => {
142
+ await Promise.all([
143
+ tracerProvider.shutdown(),
144
+ meterProvider.shutdown(),
145
+ loggerProvider.shutdown()
146
+ ]);
147
+ }
148
+ };
149
+ }
150
+ //#endregion
151
+ export { buildOtelProvider };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.mjs","names":[],"sources":["../../../src/internal/otel/provider.ts"],"sourcesContent":["import type { SessionId } from \"@interfere/types/data/session\";\nimport type { ReleaseSlug } from \"@interfere/types/releases/slug\";\nimport { normalizeEnv } from \"@interfere/types/sdk/runtime\";\n\nimport { propagation } from \"@opentelemetry/api\";\nimport {\n CompositePropagator,\n W3CBaggagePropagator,\n W3CTraceContextPropagator,\n} from \"@opentelemetry/core\";\nimport { browserDetector } from \"@opentelemetry/opentelemetry-browser-detector\";\nimport {\n detectResources,\n resourceFromAttributes,\n} from \"@opentelemetry/resources\";\nimport {\n BatchLogRecordProcessor,\n LoggerProvider,\n type LogRecordProcessor,\n} from \"@opentelemetry/sdk-logs\";\nimport {\n MeterProvider,\n type MetricReader,\n PeriodicExportingMetricReader,\n} from \"@opentelemetry/sdk-metrics\";\nimport {\n BatchSpanProcessor,\n type SpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\nimport { WebTracerProvider } from \"@opentelemetry/sdk-trace-web\";\nimport {\n createSessionLogRecordProcessor,\n createSessionSpanProcessor,\n} from \"@opentelemetry/web-common\";\n\nimport { SDK_NAME, SDK_VERSION } from \"../version.js\";\nimport {\n createBeaconLogExporter,\n createBeaconMetricExporter,\n createBeaconTraceExporter,\n} from \"./exporter.js\";\nimport { PageScopeContextManager } from \"./page-scope-context-manager.js\";\n\nconst SERVICE_NAMESPACE = \"interfere\";\n\n// Inlined wire keys — matches the pattern in\n// `@interfere/observability/browser/rum` (semconv keeps shifting these\n// between minor versions, but the wire keys themselves are stable).\nconst ATTR_SERVICE_NAME = \"service.name\" as const;\nconst ATTR_SERVICE_NAMESPACE = \"service.namespace\" as const;\nconst ATTR_DEPLOYMENT_ENVIRONMENT_NAME = \"deployment.environment.name\" as const;\nconst ATTR_TELEMETRY_SDK_LANGUAGE = \"telemetry.sdk.language\" as const;\nconst ATTR_RELEASE_SLUG = \"release.slug\" as const;\nconst ATTR_INTERFERE_SDK_STACK = \"interfere.sdk.stack\" as const;\nconst ATTR_INTERFERE_SDK_NAME = \"interfere.sdk.name\" as const;\nconst ATTR_INTERFERE_SDK_VERSION = \"interfere.sdk.version\" as const;\n// OTel semconv-canonical name for a stable per-device identifier.\n// Stamped as a *resource* attribute (not per-span) because it's stable\n// for the lifetime of the page; sessions rotate, devices don't. The\n// collector reads this off `context.resourceAttributes[\"device.id\"]`\n// and projects it into `spans.deviceId`.\nconst ATTR_DEVICE_ID = \"device.id\" as const;\n\nconst DEFAULT_SERVICE_NAME = \"interfere-sdk\";\n\n/**\n * Tuned for `navigator.sendBeacon`'s per-call payload ceiling.\n *\n * The browser primary exporters dispatch every export via beacon\n * (see `BeaconTraceExporter` / `BeaconLogExporter` /\n * `BeaconMetricExporter` in `exporter.ts`) — sendBeacon is the only\n * browser transport that reliably commits a request the page is also\n * tearing down. It's not free though: most browsers cap each beacon\n * payload at 64KiB (Chrome / Firefox) and reject anything larger\n * with a `false` return from `navigator.sendBeacon` — at which point\n * the BSP just drops the batch (no retry: the SW backstop intercepts\n * 5xx / network failures, not user-agent rejections).\n *\n * The numbers below keep payloads comfortably under that ceiling,\n * even on jank-heavy pages (production data on 2026-05-11 saw 831\n * `longtask` spans piled up on a single page-hide, observed via the\n * BetterStack dual-write) where a 100-span batch would already top\n * 100KiB:\n *\n * - `maxExportBatchSize: 10` — caps each beacon at ~10–20KiB,\n * leaving headroom for span attribute bloat without flirting\n * with the per-call ceiling.\n * - `maxQueueSize: 50` — caps the worst-case unload payload at\n * ~50–100KiB; the BSP drains across 5 beacons when forced-\n * flushed on `visibilitychange→hidden`, all of which queue\n * synchronously inside the user agent.\n * - `scheduledDelayMillis: 2000` — quieter pages keep less in\n * buffer at any moment, so a hidden→visible→hidden bounce\n * doesn't accumulate 5s of telemetry waiting to ship.\n * - `exportTimeoutMillis: 10_000` — unchanged. Beacon dispatches\n * are essentially synchronous (the user agent enqueues into its\n * own send loop), so the timeout is mostly defensive against\n * pathological `JsonTraceSerializer.serializeRequest` calls.\n */\nconst BROWSER_BATCH_OPTIONS = {\n maxQueueSize: 50,\n maxExportBatchSize: 10,\n scheduledDelayMillis: 2000,\n exportTimeoutMillis: 10_000,\n} as const;\n\nexport interface OtelProviderInput {\n /**\n * @internal\n *\n * Extra log-record processors fanned into the LoggerProvider's\n * processor list at construction. Used by `@interfere/observability`\n * for internal-only dual-write to BetterStack — keeps a parallel\n * destination's queue/exporter independent of the SDK's own.\n * Customers don't get a fan-out hook on the SDK surface; this is a\n * private bridge for our own dogfood apps.\n */\n additionalLogRecordProcessors?: LogRecordProcessor[];\n /**\n * @internal\n *\n * Extra metric readers fanned into the MeterProvider's reader list\n * at construction. Used by `@interfere/observability` for internal-\n * only dual-write of web vitals histograms (`web_vitals.{ttfb,fcp,\n * lcp,inp,cls}`) to the BetterStack-fronting OTel collector. Each\n * reader owns its own queue + exporter so a 5xx / network outage on\n * the parallel destination backs off only its own batch.\n */\n additionalMetricReaders?: MetricReader[];\n /**\n * @internal\n *\n * Extra span processors fanned into the WebTracerProvider's processor\n * list at construction. See `additionalLogRecordProcessors`.\n */\n additionalSpanProcessors?: SpanProcessor[];\n /**\n * Auth + content headers for OTLP exports. Sourced from the kernel's\n * already-resolved ingest target so the OTLP path inherits the same\n * `x-interfere-pub-token` (or proxy-mode auth) the rest of the SDK\n * uses — no separate auth knob.\n */\n authHeaders: Headers;\n /** OTLP base URL — the opaque sink path is appended by the exporters. */\n collectorUrl: string;\n /**\n * Stable per-device identifier from `DeviceManager`. Resolved from\n * localStorage / cookie at kernel boot, so it's synchronously\n * available before the providers are constructed. Stamped as the\n * OTel-semconv `device.id` resource attribute so every span / log /\n * metric carries it without per-record re-stamping. Optional —\n * tests and SSR boot paths that build the provider before the\n * device manager has run pass `null`.\n */\n deviceId?: string | null | undefined;\n /**\n * Returns the kernel's current session id. Wired into per-span /\n * per-log session processors so a mid-page session rotation (30-min\n * idle expiry, manual reset) lands on subsequent spans without the\n * kernel having to rebuild the providers. Returns `null` before the\n * `SessionTracker` has bootstrapped.\n */\n getSessionId: () => SessionId | null;\n /** Optional release slug — top-level `release.slug` attr (Phase 6 wire identity). */\n releaseSlug?: ReleaseSlug | null | undefined;\n /**\n * Producer chain for the `interfere.sdk.stack` resource attribute —\n * e.g. `[\"@interfere/next@10.0.0\", \"@interfere/react@10.0.0\"]`.\n * Wrapper-injected; the kernel just forwards.\n */\n sdkStack: string[];\n /**\n * Override the OTel `service.name` resource attribute. Defaults to\n * `\"interfere-sdk\"`.\n */\n serviceName?: string;\n}\n\nexport interface OtelProviderHandle {\n /**\n * Page-scope context manager. The kernel calls `setPageScope(ctx)` after\n * extracting the SSR `traceparent` meta tag so spans without a more\n * specific zone descend from the server-side parent.\n */\n contextManager: PageScopeContextManager;\n /** Force-flush all three providers — call from kernel.flush() / on visibility hidden. */\n flush(): Promise<void>;\n loggerProvider: LoggerProvider;\n meterProvider: MeterProvider;\n metricReader: MetricReader;\n /** Tear down all three providers — called from kernel.dispose(). */\n shutdown(): Promise<void>;\n tracerProvider: WebTracerProvider;\n}\n\n/**\n * Constructs the SDK's **private** OTel providers — never registers globally\n * via `provider.register()` or `trace.setGlobalTracerProvider()`. Customers\n * with their own OTel setup (DataDog, Vercel, etc.) cohabit cleanly.\n *\n * Splitting this out from `Kernel` keeps the OTel module lazy-loadable so\n * the error-only bundle path (no tracing) never imports the OTel SDK.\n */\nexport function buildOtelProvider(\n input: OtelProviderInput\n): OtelProviderHandle {\n // Static service identity merged with detected browser attrs\n // (`browser.brands`, `browser.platform`, `browser.mobile`,\n // `browser.language`, `browser.user_agent`). The browser detector\n // reads UA Client Hints synchronously via `navigator.userAgentData`\n // low-entropy properties, so this stays sync — async would race\n // `DocumentLoadInstrumentation` and consistently miss page-load.\n const environment =\n normalizeEnv(\n typeof process === \"undefined\" ? undefined : process.env[\"NODE_ENV\"]\n ) ?? \"unknown\";\n\n const baseResource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: input.serviceName ?? DEFAULT_SERVICE_NAME,\n [ATTR_SERVICE_NAMESPACE]: SERVICE_NAMESPACE,\n [ATTR_DEPLOYMENT_ENVIRONMENT_NAME]: environment,\n [ATTR_TELEMETRY_SDK_LANGUAGE]: \"webjs\",\n [ATTR_INTERFERE_SDK_NAME]: SDK_NAME,\n [ATTR_INTERFERE_SDK_VERSION]: SDK_VERSION,\n [ATTR_INTERFERE_SDK_STACK]: input.sdkStack.join(\", \"),\n ...(input.releaseSlug ? { [ATTR_RELEASE_SLUG]: input.releaseSlug } : {}),\n ...(input.deviceId ? { [ATTR_DEVICE_ID]: input.deviceId } : {}),\n });\n const detected = detectResources({ detectors: [browserDetector] });\n const resource = baseResource.merge(detected);\n\n const traceExporter = createBeaconTraceExporter({\n collectorUrl: input.collectorUrl,\n authHeaders: input.authHeaders,\n });\n\n // The web-common session processor expects `() => string | null`,\n // matching our `KernelSession.getId` shape.\n const getSessionId = input.getSessionId;\n\n const spanProcessors: SpanProcessor[] = [\n // Stamps `session.id` per-span, re-reading on every export. A\n // mid-page session rotation (30-min idle, manual reset) lands on\n // subsequent spans without rebuilding the provider. Resource-level\n // `session.id` would freeze at boot.\n createSessionSpanProcessor({ getSessionId }),\n // Exception attribute defaults (`interfere.exception.{mechanism,\n // handled,kind}`) are no longer stamped here — third-party\n // auto-instrumentations bypass our kernel anyway, so the SDK\n // can't reliably tell what to default. Server-side enrichment\n // (`enrichment/lib/normalize-atom.ts`) is the single chokepoint\n // every exception event flows through, so the defaults live\n // there now and the SDK ships less code.\n new BatchSpanProcessor(traceExporter, BROWSER_BATCH_OPTIONS),\n // Internal-only fan-out (BetterStack dual-write for `interfere/homepage`\n // + `interfere/dashboard`). Each extra processor owns its own queue +\n // exporter, so a 5xx / network outage on the parallel destination\n // backs off only its own batch — the primary collector pipeline keeps\n // draining unaffected.\n ...(input.additionalSpanProcessors ?? []),\n ];\n\n const tracerProvider = new WebTracerProvider({ resource, spanProcessors });\n\n // `PageScopeContextManager` keeps the active context alive across\n // microtasks/promise chains/setTimeout, AND falls back to the page-scope\n // context (the SSR `traceparent`) for code paths that fire outside any\n // zone the manager itself created. The kernel populates the page scope\n // by calling `contextManager.setPageScope(ctx)` after extracting the\n // meta tag.\n //\n // `register({ contextManager })` does call `setGlobalContextManager`\n // internally — that's unavoidable; OTel exposes exactly one global\n // context manager. Customers with their own OTel provider boot last and\n // win. We accept the trade-off for correctness: the alternative\n // (separate per-provider context manager) is not supported by OTel.\n const contextManager = new PageScopeContextManager();\n tracerProvider.register({ contextManager });\n\n // Composed W3C trace context + baggage propagator. `FetchInstrumentation`\n // injects both `traceparent` and `baggage` on outgoing requests so\n // backend Elysia plugins can pick `interfere.*` baggage entries off\n // the request and stamp them on server spans. Only set the global if\n // no propagator is currently registered (`fields()` is `[]` for the\n // noop). Customers who installed their own propagator (B3, composite,\n // …) are preserved. Reading the SSR meta tag uses a local instance,\n // not the global, so our extract path is independent.\n if (propagation.fields().length === 0) {\n propagation.setGlobalPropagator(\n new CompositePropagator({\n propagators: [\n new W3CTraceContextPropagator(),\n new W3CBaggagePropagator(),\n ],\n })\n );\n }\n\n const metricExporter = createBeaconMetricExporter({\n collectorUrl: input.collectorUrl,\n authHeaders: input.authHeaders,\n });\n\n const metricReader = new PeriodicExportingMetricReader({\n exporter: metricExporter,\n exportIntervalMillis: 30_000,\n });\n\n const meterProvider = new MeterProvider({\n resource,\n readers: [metricReader, ...(input.additionalMetricReaders ?? [])],\n });\n\n // Logs path. The `plugins/logs.ts` plugin patches `console.*` and emits\n // a `LogRecord` per call (errorsPlugin still owns Error-bearing console\n // calls — the class boundary is checked there). LoggerProvider is\n // private; the kernel exposes `recordLog` so callers don't have to\n // touch the OTel logs API directly.\n const logExporter = createBeaconLogExporter({\n collectorUrl: input.collectorUrl,\n authHeaders: input.authHeaders,\n });\n const loggerProvider = new LoggerProvider({\n resource,\n processors: [\n // Mirrors the SessionSpanProcessor for traces — every log record\n // (including `BrowserNavigationInstrumentation`'s navigation\n // events) carries the same `session.id` as the spans emitted\n // around it, so trace ↔ log correlation by session is trivial.\n createSessionLogRecordProcessor({ getSessionId }),\n new BatchLogRecordProcessor(logExporter, BROWSER_BATCH_OPTIONS),\n ...(input.additionalLogRecordProcessors ?? []),\n ],\n });\n\n return {\n contextManager,\n tracerProvider,\n meterProvider,\n metricReader,\n loggerProvider,\n flush: async () => {\n await Promise.all([\n tracerProvider.forceFlush(),\n metricReader.forceFlush(),\n loggerProvider.forceFlush(),\n ]);\n },\n shutdown: async () => {\n await Promise.all([\n tracerProvider.shutdown(),\n meterProvider.shutdown(),\n loggerProvider.shutdown(),\n ]);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AA2CA,MAAM,oBAAoB;AAK1B,MAAM,oBAAoB;AAC1B,MAAM,yBAAyB;AAC/B,MAAM,mCAAmC;AACzC,MAAM,8BAA8B;AACpC,MAAM,oBAAoB;AAC1B,MAAM,2BAA2B;AACjC,MAAM,0BAA0B;AAChC,MAAM,6BAA6B;AAMnC,MAAM,iBAAiB;AAEvB,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoC7B,MAAM,wBAAwB;CAC5B,cAAc;CACd,oBAAoB;CACpB,sBAAsB;CACtB,qBAAqB;CACtB;;;;;;;;;AAmGD,SAAgB,kBACd,OACoB;CAOpB,MAAM,cACJ,aACE,OAAO,YAAY,cAAc,KAAA,IAAY,QAAQ,IAAI,YAC1D,IAAI;CAEP,MAAM,eAAe,uBAAuB;GACzC,oBAAoB,MAAM,eAAe;GACzC,yBAAyB;GACzB,mCAAmC;GACnC,8BAA8B;GAC9B,0BAA0B;GAC1B,6BAA6B;GAC7B,2BAA2B,MAAM,SAAS,KAAK,KAAK;EACrD,GAAI,MAAM,cAAc,GAAG,oBAAoB,MAAM,aAAa,GAAG,EAAE;EACvE,GAAI,MAAM,WAAW,GAAG,iBAAiB,MAAM,UAAU,GAAG,EAAE;EAC/D,CAAC;CACF,MAAM,WAAW,gBAAgB,EAAE,WAAW,CAAC,gBAAgB,EAAE,CAAC;CAClE,MAAM,WAAW,aAAa,MAAM,SAAS;CAE7C,MAAM,gBAAgB,0BAA0B;EAC9C,cAAc,MAAM;EACpB,aAAa,MAAM;EACpB,CAAC;CAIF,MAAM,eAAe,MAAM;CAwB3B,MAAM,iBAAiB,IAAI,kBAAkB;EAAE;EAAU,gBAAA;GAjBvD,2BAA2B,EAAE,cAAc,CAAC;GAQ5C,IAAI,mBAAmB,eAAe,sBAAsB;GAM5D,GAAI,MAAM,4BAA4B,EAAE;GAG6B;EAAE,CAAC;CAc1E,MAAM,iBAAiB,IAAI,yBAAyB;CACpD,eAAe,SAAS,EAAE,gBAAgB,CAAC;CAU3C,IAAI,YAAY,QAAQ,CAAC,WAAW,GAClC,YAAY,oBACV,IAAI,oBAAoB,EACtB,aAAa,CACX,IAAI,2BAA2B,EAC/B,IAAI,sBAAsB,CAC3B,EACF,CAAC,CACH;CAQH,MAAM,eAAe,IAAI,8BAA8B;EACrD,UANqB,2BAA2B;GAChD,cAAc,MAAM;GACpB,aAAa,MAAM;GACpB,CAGyB;EACxB,sBAAsB;EACvB,CAAC;CAEF,MAAM,gBAAgB,IAAI,cAAc;EACtC;EACA,SAAS,CAAC,cAAc,GAAI,MAAM,2BAA2B,EAAE,CAAE;EAClE,CAAC;CAOF,MAAM,cAAc,wBAAwB;EAC1C,cAAc,MAAM;EACpB,aAAa,MAAM;EACpB,CAAC;CACF,MAAM,iBAAiB,IAAI,eAAe;EACxC;EACA,YAAY;GAKV,gCAAgC,EAAE,cAAc,CAAC;GACjD,IAAI,wBAAwB,aAAa,sBAAsB;GAC/D,GAAI,MAAM,iCAAiC,EAAE;GAC9C;EACF,CAAC;CAEF,OAAO;EACL;EACA;EACA;EACA;EACA;EACA,OAAO,YAAY;GACjB,MAAM,QAAQ,IAAI;IAChB,eAAe,YAAY;IAC3B,aAAa,YAAY;IACzB,eAAe,YAAY;IAC5B,CAAC;;EAEJ,UAAU,YAAY;GACpB,MAAM,QAAQ,IAAI;IAChB,eAAe,UAAU;IACzB,cAAc,UAAU;IACxB,eAAe,UAAU;IAC1B,CAAC;;EAEL"}
@@ -0,0 +1,35 @@
1
+ import { Meter } from "@opentelemetry/api";
2
+
3
+ //#region src/internal/otel/web-vitals.d.ts
4
+ interface WebVitalsInput {
5
+ /**
6
+ * Called after the page-load vitals have all fired. The kernel passes
7
+ * its own `flush()` so short-visit exports don't depend on the
8
+ * `visibilitychange` listener firing.
9
+ */
10
+ flush: () => Promise<void>;
11
+ /** OTel meter from the kernel's private provider. */
12
+ meter: Meter;
13
+ /**
14
+ * Pathname → low-cardinality route template (e.g. `/blog/[slug]`).
15
+ * Without this, dynamic routes produce one unique `url.path` label
16
+ * per visited slug — a metric cardinality hazard.
17
+ */
18
+ resolveRoute?: (pathname: string) => string | undefined;
19
+ }
20
+ /**
21
+ * Subscribes to Core Web Vitals (LCP/FCP/TTFB/INP/CLS) and records OTel
22
+ * histograms. Time-based vitals are converted to seconds per
23
+ * observability convention; CLS is dimensionless and recorded as-is.
24
+ *
25
+ * Each record carries `web_vital.rating`, `web_vital.navigation_type`,
26
+ * and `url.path` (templated when `resolveRoute` is provided). Browser
27
+ * and device dimensions come from the resource attached to the
28
+ * MeterProvider, so every histogram datapoint already carries them.
29
+ *
30
+ * Bucket boundaries match the rum.ts SDK — same dashboards, same
31
+ * histograms, same percentile shape.
32
+ */
33
+ declare function captureWebVitals(input: WebVitalsInput): void;
34
+ //#endregion
35
+ export { WebVitalsInput, captureWebVitals };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-vitals.d.mts","names":[],"sources":["../../../src/internal/otel/web-vitals.ts"],"mappings":";;;UAmBiB,cAAA;;AAAjB;;;;EAME,KAAA,QAAa,OAAA;EAAA;EAEb,KAAA,EAAO,KAAA;EAAA;;;;;EAMP,YAAA,IAAgB,QAAA;AAAA;;;;;;;;;;;;;;iBAgBF,gBAAA,CAAiB,KAAA,EAAO,cAAA"}
@@ -0,0 +1,162 @@
1
+ import { onCLS, onFCP, onINP, onLCP, onTTFB } from "web-vitals";
2
+ //#region src/internal/otel/web-vitals.ts
3
+ const ATTR_URL_PATH = "url.path";
4
+ const MS_TO_S = 1 / 1e3;
5
+ /**
6
+ * Vitals that fire during the page-load sequence — once we've received
7
+ * all three we issue a one-shot flush so short visits still export
8
+ * data. CLS and INP fire on `visibilitychange` which the kernel
9
+ * already auto-flushes on, so they don't need their own trigger.
10
+ */
11
+ const LOAD_VITALS = new Set([
12
+ "TTFB",
13
+ "FCP",
14
+ "LCP"
15
+ ]);
16
+ /**
17
+ * Subscribes to Core Web Vitals (LCP/FCP/TTFB/INP/CLS) and records OTel
18
+ * histograms. Time-based vitals are converted to seconds per
19
+ * observability convention; CLS is dimensionless and recorded as-is.
20
+ *
21
+ * Each record carries `web_vital.rating`, `web_vital.navigation_type`,
22
+ * and `url.path` (templated when `resolveRoute` is provided). Browser
23
+ * and device dimensions come from the resource attached to the
24
+ * MeterProvider, so every histogram datapoint already carries them.
25
+ *
26
+ * Bucket boundaries match the rum.ts SDK — same dashboards, same
27
+ * histograms, same percentile shape.
28
+ */
29
+ function captureWebVitals(input) {
30
+ if (typeof window === "undefined") return;
31
+ const { meter, resolveRoute, flush } = input;
32
+ const lcp = meter.createHistogram("web_vitals.lcp", {
33
+ description: "Largest Contentful Paint",
34
+ unit: "s",
35
+ advice: { explicitBucketBoundaries: [
36
+ .5,
37
+ 1,
38
+ 1.5,
39
+ 2,
40
+ 2.5,
41
+ 3,
42
+ 4,
43
+ 5,
44
+ 7.5,
45
+ 10
46
+ ] }
47
+ });
48
+ const fcp = meter.createHistogram("web_vitals.fcp", {
49
+ description: "First Contentful Paint",
50
+ unit: "s",
51
+ advice: { explicitBucketBoundaries: [
52
+ .5,
53
+ 1,
54
+ 1.5,
55
+ 1.8,
56
+ 2,
57
+ 2.5,
58
+ 3,
59
+ 4,
60
+ 5,
61
+ 7.5
62
+ ] }
63
+ });
64
+ const ttfb = meter.createHistogram("web_vitals.ttfb", {
65
+ description: "Time to First Byte",
66
+ unit: "s",
67
+ advice: { explicitBucketBoundaries: [
68
+ .1,
69
+ .2,
70
+ .3,
71
+ .5,
72
+ .8,
73
+ 1,
74
+ 1.5,
75
+ 2,
76
+ 3,
77
+ 5
78
+ ] }
79
+ });
80
+ const inp = meter.createHistogram("web_vitals.inp", {
81
+ description: "Interaction to Next Paint",
82
+ unit: "s",
83
+ advice: { explicitBucketBoundaries: [
84
+ .05,
85
+ .1,
86
+ .15,
87
+ .2,
88
+ .3,
89
+ .4,
90
+ .5,
91
+ .75,
92
+ 1,
93
+ 2
94
+ ] }
95
+ });
96
+ const cls = meter.createHistogram("web_vitals.cls", {
97
+ description: "Cumulative Layout Shift",
98
+ unit: "1",
99
+ advice: { explicitBucketBoundaries: [
100
+ .01,
101
+ .025,
102
+ .05,
103
+ .075,
104
+ .1,
105
+ .15,
106
+ .2,
107
+ .25,
108
+ .5,
109
+ 1
110
+ ] }
111
+ });
112
+ const vitals = {
113
+ LCP: {
114
+ histogram: lcp,
115
+ toSeconds: true
116
+ },
117
+ FCP: {
118
+ histogram: fcp,
119
+ toSeconds: true
120
+ },
121
+ TTFB: {
122
+ histogram: ttfb,
123
+ toSeconds: true
124
+ },
125
+ INP: {
126
+ histogram: inp,
127
+ toSeconds: true
128
+ },
129
+ CLS: {
130
+ histogram: cls,
131
+ toSeconds: false
132
+ }
133
+ };
134
+ const received = /* @__PURE__ */ new Set();
135
+ let flushed = false;
136
+ function report(metric) {
137
+ const entry = vitals[metric.name];
138
+ if (!entry) return;
139
+ const value = entry.toSeconds ? metric.value * MS_TO_S : metric.value;
140
+ const pathname = window.location.pathname;
141
+ const route = resolveRoute?.(pathname) ?? pathname;
142
+ entry.histogram.record(value, {
143
+ ...metric.rating ? { "web_vital.rating": metric.rating } : {},
144
+ ...metric.navigationType ? { "web_vital.navigation_type": metric.navigationType } : {},
145
+ [ATTR_URL_PATH]: route
146
+ });
147
+ if (!flushed && LOAD_VITALS.has(metric.name)) {
148
+ received.add(metric.name);
149
+ if (received.size === LOAD_VITALS.size) {
150
+ flushed = true;
151
+ flush().catch(() => void 0);
152
+ }
153
+ }
154
+ }
155
+ onLCP(report);
156
+ onCLS(report);
157
+ onINP(report);
158
+ onTTFB(report);
159
+ onFCP(report);
160
+ }
161
+ //#endregion
162
+ export { captureWebVitals };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-vitals.mjs","names":[],"sources":["../../../src/internal/otel/web-vitals.ts"],"sourcesContent":["import type { Histogram, Meter } from \"@opentelemetry/api\";\nimport { onCLS, onFCP, onINP, onLCP, onTTFB } from \"web-vitals\";\n\nconst ATTR_URL_PATH = \"url.path\" as const;\nconst MS_TO_S = 1 / 1000;\n\n/**\n * Vitals that fire during the page-load sequence — once we've received\n * all three we issue a one-shot flush so short visits still export\n * data. CLS and INP fire on `visibilitychange` which the kernel\n * already auto-flushes on, so they don't need their own trigger.\n */\nconst LOAD_VITALS = new Set([\"TTFB\", \"FCP\", \"LCP\"]);\n\ninterface VitalEntry {\n histogram: Histogram;\n toSeconds: boolean;\n}\n\nexport interface WebVitalsInput {\n /**\n * Called after the page-load vitals have all fired. The kernel passes\n * its own `flush()` so short-visit exports don't depend on the\n * `visibilitychange` listener firing.\n */\n flush: () => Promise<void>;\n /** OTel meter from the kernel's private provider. */\n meter: Meter;\n /**\n * Pathname → low-cardinality route template (e.g. `/blog/[slug]`).\n * Without this, dynamic routes produce one unique `url.path` label\n * per visited slug — a metric cardinality hazard.\n */\n resolveRoute?: (pathname: string) => string | undefined;\n}\n\n/**\n * Subscribes to Core Web Vitals (LCP/FCP/TTFB/INP/CLS) and records OTel\n * histograms. Time-based vitals are converted to seconds per\n * observability convention; CLS is dimensionless and recorded as-is.\n *\n * Each record carries `web_vital.rating`, `web_vital.navigation_type`,\n * and `url.path` (templated when `resolveRoute` is provided). Browser\n * and device dimensions come from the resource attached to the\n * MeterProvider, so every histogram datapoint already carries them.\n *\n * Bucket boundaries match the rum.ts SDK — same dashboards, same\n * histograms, same percentile shape.\n */\nexport function captureWebVitals(input: WebVitalsInput): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n const { meter, resolveRoute, flush } = input;\n\n const lcp = meter.createHistogram(\"web_vitals.lcp\", {\n description: \"Largest Contentful Paint\",\n unit: \"s\",\n advice: {\n explicitBucketBoundaries: [0.5, 1, 1.5, 2, 2.5, 3, 4, 5, 7.5, 10],\n },\n });\n const fcp = meter.createHistogram(\"web_vitals.fcp\", {\n description: \"First Contentful Paint\",\n unit: \"s\",\n advice: {\n explicitBucketBoundaries: [0.5, 1, 1.5, 1.8, 2, 2.5, 3, 4, 5, 7.5],\n },\n });\n const ttfb = meter.createHistogram(\"web_vitals.ttfb\", {\n description: \"Time to First Byte\",\n unit: \"s\",\n advice: {\n explicitBucketBoundaries: [0.1, 0.2, 0.3, 0.5, 0.8, 1, 1.5, 2, 3, 5],\n },\n });\n const inp = meter.createHistogram(\"web_vitals.inp\", {\n description: \"Interaction to Next Paint\",\n unit: \"s\",\n advice: {\n explicitBucketBoundaries: [\n 0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.75, 1, 2,\n ],\n },\n });\n const cls = meter.createHistogram(\"web_vitals.cls\", {\n description: \"Cumulative Layout Shift\",\n unit: \"1\",\n advice: {\n explicitBucketBoundaries: [\n 0.01, 0.025, 0.05, 0.075, 0.1, 0.15, 0.2, 0.25, 0.5, 1,\n ],\n },\n });\n\n const vitals: Record<string, VitalEntry> = {\n LCP: { histogram: lcp, toSeconds: true },\n FCP: { histogram: fcp, toSeconds: true },\n TTFB: { histogram: ttfb, toSeconds: true },\n INP: { histogram: inp, toSeconds: true },\n CLS: { histogram: cls, toSeconds: false },\n };\n\n const received = new Set<string>();\n let flushed = false;\n\n function report(metric: {\n name: string;\n value: number;\n rating?: string;\n navigationType?: string;\n }): void {\n const entry = vitals[metric.name];\n if (!entry) {\n return;\n }\n const value = entry.toSeconds ? metric.value * MS_TO_S : metric.value;\n const pathname = window.location.pathname;\n const route = resolveRoute?.(pathname) ?? pathname;\n\n entry.histogram.record(value, {\n ...(metric.rating ? { \"web_vital.rating\": metric.rating } : {}),\n ...(metric.navigationType\n ? { \"web_vital.navigation_type\": metric.navigationType }\n : {}),\n [ATTR_URL_PATH]: route,\n });\n\n if (!flushed && LOAD_VITALS.has(metric.name)) {\n received.add(metric.name);\n if (received.size === LOAD_VITALS.size) {\n flushed = true;\n flush().catch(() => undefined);\n }\n }\n }\n\n onLCP(report);\n onCLS(report);\n onINP(report);\n onTTFB(report);\n onFCP(report);\n}\n"],"mappings":";;AAGA,MAAM,gBAAgB;AACtB,MAAM,UAAU,IAAI;;;;;;;AAQpB,MAAM,cAAc,IAAI,IAAI;CAAC;CAAQ;CAAO;CAAM,CAAC;;;;;;;;;;;;;;AAqCnD,SAAgB,iBAAiB,OAA6B;CAC5D,IAAI,OAAO,WAAW,aACpB;CAGF,MAAM,EAAE,OAAO,cAAc,UAAU;CAEvC,MAAM,MAAM,MAAM,gBAAgB,kBAAkB;EAClD,aAAa;EACb,MAAM;EACN,QAAQ,EACN,0BAA0B;GAAC;GAAK;GAAG;GAAK;GAAG;GAAK;GAAG;GAAG;GAAG;GAAK;GAAG,EAClE;EACF,CAAC;CACF,MAAM,MAAM,MAAM,gBAAgB,kBAAkB;EAClD,aAAa;EACb,MAAM;EACN,QAAQ,EACN,0BAA0B;GAAC;GAAK;GAAG;GAAK;GAAK;GAAG;GAAK;GAAG;GAAG;GAAG;GAAI,EACnE;EACF,CAAC;CACF,MAAM,OAAO,MAAM,gBAAgB,mBAAmB;EACpD,aAAa;EACb,MAAM;EACN,QAAQ,EACN,0BAA0B;GAAC;GAAK;GAAK;GAAK;GAAK;GAAK;GAAG;GAAK;GAAG;GAAG;GAAE,EACrE;EACF,CAAC;CACF,MAAM,MAAM,MAAM,gBAAgB,kBAAkB;EAClD,aAAa;EACb,MAAM;EACN,QAAQ,EACN,0BAA0B;GACxB;GAAM;GAAK;GAAM;GAAK;GAAK;GAAK;GAAK;GAAM;GAAG;GAC/C,EACF;EACF,CAAC;CACF,MAAM,MAAM,MAAM,gBAAgB,kBAAkB;EAClD,aAAa;EACb,MAAM;EACN,QAAQ,EACN,0BAA0B;GACxB;GAAM;GAAO;GAAM;GAAO;GAAK;GAAM;GAAK;GAAM;GAAK;GACtD,EACF;EACF,CAAC;CAEF,MAAM,SAAqC;EACzC,KAAK;GAAE,WAAW;GAAK,WAAW;GAAM;EACxC,KAAK;GAAE,WAAW;GAAK,WAAW;GAAM;EACxC,MAAM;GAAE,WAAW;GAAM,WAAW;GAAM;EAC1C,KAAK;GAAE,WAAW;GAAK,WAAW;GAAM;EACxC,KAAK;GAAE,WAAW;GAAK,WAAW;GAAO;EAC1C;CAED,MAAM,2BAAW,IAAI,KAAa;CAClC,IAAI,UAAU;CAEd,SAAS,OAAO,QAKP;EACP,MAAM,QAAQ,OAAO,OAAO;EAC5B,IAAI,CAAC,OACH;EAEF,MAAM,QAAQ,MAAM,YAAY,OAAO,QAAQ,UAAU,OAAO;EAChE,MAAM,WAAW,OAAO,SAAS;EACjC,MAAM,QAAQ,eAAe,SAAS,IAAI;EAE1C,MAAM,UAAU,OAAO,OAAO;GAC5B,GAAI,OAAO,SAAS,EAAE,oBAAoB,OAAO,QAAQ,GAAG,EAAE;GAC9D,GAAI,OAAO,iBACP,EAAE,6BAA6B,OAAO,gBAAgB,GACtD,EAAE;IACL,gBAAgB;GAClB,CAAC;EAEF,IAAI,CAAC,WAAW,YAAY,IAAI,OAAO,KAAK,EAAE;GAC5C,SAAS,IAAI,OAAO,KAAK;GACzB,IAAI,SAAS,SAAS,YAAY,MAAM;IACtC,UAAU;IACV,OAAO,CAAC,YAAY,KAAA,EAAU;;;;CAKpC,MAAM,OAAO;CACb,MAAM,OAAO;CACb,MAAM,OAAO;CACb,OAAO,OAAO;CACd,MAAM,OAAO"}
@@ -0,0 +1,21 @@
1
+ //#region src/internal/page-lifecycle.d.ts
2
+ /**
3
+ * Single subscription point for `visibilitychange` (hidden) and
4
+ * `beforeunload`. Modules call `onPageHidden(handler)` and the handler
5
+ * fires once per transition into hidden / unload.
6
+ *
7
+ * Replaces the previous shape where the kernel, the queue, the pages
8
+ * plugin, and the replay plugin each attached their own listeners — four
9
+ * subscribers racing to flush / drain / emit on the same boundary, with
10
+ * no shared lifecycle. One subscription, many handlers, no coordination
11
+ * via the global event subsystem.
12
+ *
13
+ * Both events can fire (visibilitychange first when the user navigates
14
+ * away, then sometimes beforeunload). Handlers must be idempotent.
15
+ */
16
+ type Handler = () => void;
17
+ declare function onPageHidden(handler: Handler): () => void;
18
+ /** Test hook — drops every handler and unbinds the listeners. */
19
+ declare function _resetPageLifecycleForTests(): void;
20
+ //#endregion
21
+ export { _resetPageLifecycleForTests, onPageHidden };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-lifecycle.d.mts","names":[],"sources":["../../src/internal/page-lifecycle.ts"],"mappings":";;;;;;;AA2CA;;;;;AASA;;;KAtCK,OAAA;AAAA,iBA6BW,YAAA,CAAa,OAAA,EAAS,OAAA;;iBAStB,2BAAA,CAAA"}
@@ -0,0 +1,33 @@
1
+ //#region src/internal/page-lifecycle.ts
2
+ const handlers = /* @__PURE__ */ new Set();
3
+ let installed = false;
4
+ function fire() {
5
+ for (const handler of handlers) handler();
6
+ }
7
+ function onVisibilityChange() {
8
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") fire();
9
+ }
10
+ function install() {
11
+ if (installed || typeof globalThis.addEventListener !== "function") return;
12
+ installed = true;
13
+ globalThis.addEventListener("visibilitychange", onVisibilityChange);
14
+ globalThis.addEventListener("beforeunload", fire);
15
+ }
16
+ function onPageHidden(handler) {
17
+ install();
18
+ handlers.add(handler);
19
+ return () => {
20
+ handlers.delete(handler);
21
+ };
22
+ }
23
+ /** Test hook — drops every handler and unbinds the listeners. */
24
+ function _resetPageLifecycleForTests() {
25
+ handlers.clear();
26
+ if (installed && typeof globalThis.removeEventListener === "function") {
27
+ globalThis.removeEventListener("visibilitychange", onVisibilityChange);
28
+ globalThis.removeEventListener("beforeunload", fire);
29
+ }
30
+ installed = false;
31
+ }
32
+ //#endregion
33
+ export { _resetPageLifecycleForTests, onPageHidden };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-lifecycle.mjs","names":[],"sources":["../../src/internal/page-lifecycle.ts"],"sourcesContent":["/**\n * Single subscription point for `visibilitychange` (hidden) and\n * `beforeunload`. Modules call `onPageHidden(handler)` and the handler\n * fires once per transition into hidden / unload.\n *\n * Replaces the previous shape where the kernel, the queue, the pages\n * plugin, and the replay plugin each attached their own listeners — four\n * subscribers racing to flush / drain / emit on the same boundary, with\n * no shared lifecycle. One subscription, many handlers, no coordination\n * via the global event subsystem.\n *\n * Both events can fire (visibilitychange first when the user navigates\n * away, then sometimes beforeunload). Handlers must be idempotent.\n */\ntype Handler = () => void;\n\nconst handlers = new Set<Handler>();\nlet installed = false;\n\nfunction fire(): void {\n for (const handler of handlers) {\n handler();\n }\n}\n\nfunction onVisibilityChange(): void {\n if (\n typeof document !== \"undefined\" &&\n document.visibilityState === \"hidden\"\n ) {\n fire();\n }\n}\n\nfunction install(): void {\n if (installed || typeof globalThis.addEventListener !== \"function\") {\n return;\n }\n installed = true;\n globalThis.addEventListener(\"visibilitychange\", onVisibilityChange);\n globalThis.addEventListener(\"beforeunload\", fire);\n}\n\nexport function onPageHidden(handler: Handler): () => void {\n install();\n handlers.add(handler);\n return () => {\n handlers.delete(handler);\n };\n}\n\n/** Test hook — drops every handler and unbinds the listeners. */\nexport function _resetPageLifecycleForTests(): void {\n handlers.clear();\n if (installed && typeof globalThis.removeEventListener === \"function\") {\n globalThis.removeEventListener(\"visibilitychange\", onVisibilityChange);\n globalThis.removeEventListener(\"beforeunload\", fire);\n }\n installed = false;\n}\n"],"mappings":";AAgBA,MAAM,2BAAW,IAAI,KAAc;AACnC,IAAI,YAAY;AAEhB,SAAS,OAAa;CACpB,KAAK,MAAM,WAAW,UACpB,SAAS;;AAIb,SAAS,qBAA2B;CAClC,IACE,OAAO,aAAa,eACpB,SAAS,oBAAoB,UAE7B,MAAM;;AAIV,SAAS,UAAgB;CACvB,IAAI,aAAa,OAAO,WAAW,qBAAqB,YACtD;CAEF,YAAY;CACZ,WAAW,iBAAiB,oBAAoB,mBAAmB;CACnE,WAAW,iBAAiB,gBAAgB,KAAK;;AAGnD,SAAgB,aAAa,SAA8B;CACzD,SAAS;CACT,SAAS,IAAI,QAAQ;CACrB,aAAa;EACX,SAAS,OAAO,QAAQ;;;;AAK5B,SAAgB,8BAAoC;CAClD,SAAS,OAAO;CAChB,IAAI,aAAa,OAAO,WAAW,wBAAwB,YAAY;EACrE,WAAW,oBAAoB,oBAAoB,mBAAmB;EACtE,WAAW,oBAAoB,gBAAgB,KAAK;;CAEtD,YAAY"}
@@ -2,7 +2,6 @@ import { PluginContext } from "../plugins/lib/types.mjs";
2
2
  import { PluginOverrides } from "../plugins/lib/loader.mjs";
3
3
  import { ConsentState } from "@interfere/types/sdk/plugins/manifest";
4
4
  import { RemotePluginConfig } from "@interfere/types/sdk/remote-config";
5
- import { EventType } from "@interfere/types/sdk/envelope";
6
5
 
7
6
  //#region src/internal/plugin-runtime.d.ts
8
7
  declare class PluginRuntime {
@@ -19,7 +18,6 @@ declare class PluginRuntime {
19
18
  setConsent(nextConsent?: ConsentState): void;
20
19
  resetConsent(): void;
21
20
  applyRemoteConfig(config: RemotePluginConfig): void;
22
- canCapture(type: EventType): boolean;
23
21
  start(): void;
24
22
  dispose(): Promise<void>;
25
23
  private shouldEnablePlugin;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-runtime.d.mts","names":[],"sources":["../../src/internal/plugin-runtime.ts"],"mappings":";;;;;;;cA6Ba,aAAA;EAAA,iBACM,cAAA;EAAA,iBACA,OAAA;EAAA,iBACA,OAAA;EAAA,iBACA,QAAA;EAAA,QACT,YAAA;EAAA,QACA,YAAA;EAAA,QACA,WAAA;EAAA,QACA,QAAA;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,iBAAA,CAAkB,MAAA,EAAQ,kBAAA;EAK1B,UAAA,CAAW,IAAA,EAAM,SAAA;EAQjB,KAAA,CAAA;EAWM,OAAA,CAAA,GAAW,OAAA;EAAA,QAWT,kBAAA;EAAA,QAQA,UAAA;EAAA,QAeM,QAAA;EAAA,QAoBN,IAAA;AAAA"}
1
+ {"version":3,"file":"plugin-runtime.d.mts","names":[],"sources":["../../src/internal/plugin-runtime.ts"],"mappings":";;;;;;cAwBa,aAAA;EAAA,iBACM,cAAA;EAAA,iBACA,OAAA;EAAA,iBACA,OAAA;EAAA,iBACA,QAAA;EAAA,QACT,YAAA;EAAA,QACA,YAAA;EAAA,QACA,WAAA;EAAA,QACA,QAAA;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,iBAAA,CAAkB,MAAA,EAAQ,kBAAA;EAK1B,KAAA,CAAA;EAWM,OAAA,CAAA,GAAW,OAAA;EAAA,QAWT,kBAAA;EAAA,QAQA,UAAA;EAAA,QAeM,QAAA;EAAA,QAoBN,IAAA;AAAA"}
@@ -1,9 +1,8 @@
1
1
  import { createLogger } from "../util/log.mjs";
2
+ import { getPluginConsentCategory, hasConsentChanged, isConsentAllowed, resolveGrantedConsent } from "./consent.mjs";
2
3
  import errorsPlugin from "../plugins/errors.mjs";
3
4
  import { loadPlugin, resolveFeatures } from "../plugins/lib/loader.mjs";
4
- import { getPluginConsentCategory, hasConsentChanged, isConsentAllowed, resolveGrantedConsent, shouldCaptureEvent } from "./consent.mjs";
5
5
  import { PLUGIN_MANIFEST } from "@interfere/types/sdk/plugins/manifest";
6
- import { EVENT_TYPE_TO_PLUGIN } from "@interfere/types/sdk/remote-config";
7
6
  //#region src/internal/plugin-runtime.ts
8
7
  const log = createLogger("plugin-runtime");
9
8
  var PluginRuntime = class {
@@ -38,11 +37,6 @@ var PluginRuntime = class {
38
37
  this.remoteConfig = config;
39
38
  this.sync();
40
39
  }
41
- canCapture(type) {
42
- const plugin = EVENT_TYPE_TO_PLUGIN[type];
43
- if (plugin && this.remoteConfig[plugin] === false) return false;
44
- return shouldCaptureEvent(type, this.consentState);
45
- }
46
40
  start() {
47
41
  if (this.shouldEnablePlugin("errors")) {
48
42
  const cleanup = errorsPlugin.setup(this.context);