@interfere/react 11.0.0-canary.2 → 11.0.0-canary.4

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 (131) hide show
  1. package/dist/internal/activity.d.mts +38 -0
  2. package/dist/internal/activity.mjs +1 -0
  3. package/dist/internal/capture-boundary.mjs +1 -1
  4. package/dist/internal/capture.mjs +1 -1
  5. package/dist/internal/hydration.d.mts +10 -0
  6. package/dist/internal/hydration.mjs +1 -0
  7. package/dist/internal/kernel.d.mts +31 -11
  8. package/dist/internal/kernel.mjs +1 -1
  9. package/dist/internal/otel/initial-navigation.d.mts +13 -0
  10. package/dist/internal/otel/initial-navigation.mjs +1 -0
  11. package/dist/internal/otel/instrumentations.d.mts +25 -5
  12. package/dist/internal/otel/instrumentations.mjs +1 -1
  13. package/dist/internal/otel/propagation.d.mts +2 -2
  14. package/dist/internal/otel/propagation.mjs +1 -1
  15. package/dist/internal/plugin-runtime.d.mts +8 -2
  16. package/dist/internal/plugin-runtime.mjs +1 -1
  17. package/dist/internal/react-context.d.mts +1 -0
  18. package/dist/internal/react-context.mjs +1 -1
  19. package/dist/internal/server-digest.d.mts +9 -0
  20. package/dist/internal/server-digest.mjs +1 -0
  21. package/dist/internal/sw-queue.d.mts +26 -0
  22. package/dist/internal/sw-queue.mjs +1 -0
  23. package/dist/internal/third-party-hosts.d.mts +14 -0
  24. package/dist/internal/third-party-hosts.mjs +1 -0
  25. package/dist/internal/wrapper-singleton.d.mts +1 -0
  26. package/dist/internal/wrapper-singleton.mjs +1 -1
  27. package/dist/package.mjs +1 -1
  28. package/dist/plugins/errors.d.mts +1 -1
  29. package/dist/plugins/errors.mjs +1 -1
  30. package/dist/plugins/lib/types.d.mts +8 -1
  31. package/dist/plugins/logs.d.mts +1 -1
  32. package/dist/plugins/logs.mjs +1 -1
  33. package/dist/plugins/rage-clicks.d.mts +1 -1
  34. package/dist/plugins/rage-clicks.mjs +1 -1
  35. package/dist/plugins/replay/index.d.mts +1 -1
  36. package/dist/plugins/replay/index.mjs +1 -1
  37. package/dist/react-error-handler.d.mts +1 -1
  38. package/dist/sw.mjs +1 -1
  39. package/dist/tracking/api.d.mts +8 -0
  40. package/dist/tracking/api.mjs +1 -1
  41. package/dist/tracking/session.d.mts +4 -0
  42. package/dist/tracking/session.mjs +1 -1
  43. package/package.json +32 -33
  44. package/dist/api.d.mts.map +0 -1
  45. package/dist/api.mjs.map +0 -1
  46. package/dist/error-boundary.d.mts.map +0 -1
  47. package/dist/error-boundary.mjs.map +0 -1
  48. package/dist/internal/browser-context.d.mts.map +0 -1
  49. package/dist/internal/browser-context.mjs.map +0 -1
  50. package/dist/internal/capture-boundary.d.mts.map +0 -1
  51. package/dist/internal/capture-boundary.mjs.map +0 -1
  52. package/dist/internal/capture.d.mts.map +0 -1
  53. package/dist/internal/capture.mjs.map +0 -1
  54. package/dist/internal/config.d.mts.map +0 -1
  55. package/dist/internal/config.mjs.map +0 -1
  56. package/dist/internal/consent.d.mts.map +0 -1
  57. package/dist/internal/consent.mjs.map +0 -1
  58. package/dist/internal/console-patch.d.mts.map +0 -1
  59. package/dist/internal/console-patch.mjs.map +0 -1
  60. package/dist/internal/dom/actionable.d.mts.map +0 -1
  61. package/dist/internal/dom/actionable.mjs.map +0 -1
  62. package/dist/internal/kernel-registry.d.mts.map +0 -1
  63. package/dist/internal/kernel-registry.mjs.map +0 -1
  64. package/dist/internal/kernel.d.mts.map +0 -1
  65. package/dist/internal/kernel.mjs.map +0 -1
  66. package/dist/internal/otel/exporter.d.mts.map +0 -1
  67. package/dist/internal/otel/exporter.mjs.map +0 -1
  68. package/dist/internal/otel/instrumentations.d.mts.map +0 -1
  69. package/dist/internal/otel/instrumentations.mjs.map +0 -1
  70. package/dist/internal/otel/page-scope-context-manager.d.mts.map +0 -1
  71. package/dist/internal/otel/page-scope-context-manager.mjs.map +0 -1
  72. package/dist/internal/otel/propagation.d.mts.map +0 -1
  73. package/dist/internal/otel/propagation.mjs.map +0 -1
  74. package/dist/internal/otel/provider.d.mts.map +0 -1
  75. package/dist/internal/otel/provider.mjs.map +0 -1
  76. package/dist/internal/otel/web-vitals.d.mts.map +0 -1
  77. package/dist/internal/otel/web-vitals.mjs.map +0 -1
  78. package/dist/internal/page-lifecycle.d.mts.map +0 -1
  79. package/dist/internal/page-lifecycle.mjs.map +0 -1
  80. package/dist/internal/plugin-runtime.d.mts.map +0 -1
  81. package/dist/internal/plugin-runtime.mjs.map +0 -1
  82. package/dist/internal/react-context.d.mts.map +0 -1
  83. package/dist/internal/react-context.mjs.map +0 -1
  84. package/dist/internal/sw.d.mts.map +0 -1
  85. package/dist/internal/sw.mjs.map +0 -1
  86. package/dist/internal/version.d.mts.map +0 -1
  87. package/dist/internal/version.mjs.map +0 -1
  88. package/dist/internal/wrapper-singleton.d.mts.map +0 -1
  89. package/dist/internal/wrapper-singleton.mjs.map +0 -1
  90. package/dist/package.mjs.map +0 -1
  91. package/dist/plugins/errors.d.mts.map +0 -1
  92. package/dist/plugins/errors.mjs.map +0 -1
  93. package/dist/plugins/lib/loader.d.mts.map +0 -1
  94. package/dist/plugins/lib/loader.mjs.map +0 -1
  95. package/dist/plugins/lib/types.d.mts.map +0 -1
  96. package/dist/plugins/logs.d.mts.map +0 -1
  97. package/dist/plugins/logs.mjs.map +0 -1
  98. package/dist/plugins/rage-clicks.d.mts.map +0 -1
  99. package/dist/plugins/rage-clicks.mjs.map +0 -1
  100. package/dist/plugins/replay/buffer.d.mts.map +0 -1
  101. package/dist/plugins/replay/buffer.mjs.map +0 -1
  102. package/dist/plugins/replay/chunking.d.mts.map +0 -1
  103. package/dist/plugins/replay/chunking.mjs.map +0 -1
  104. package/dist/plugins/replay/compressor-host.d.mts.map +0 -1
  105. package/dist/plugins/replay/compressor-host.mjs.map +0 -1
  106. package/dist/plugins/replay/delivery.d.mts.map +0 -1
  107. package/dist/plugins/replay/delivery.mjs.map +0 -1
  108. package/dist/plugins/replay/index.d.mts.map +0 -1
  109. package/dist/plugins/replay/index.mjs.map +0 -1
  110. package/dist/plugins/replay/recorder.d.mts.map +0 -1
  111. package/dist/plugins/replay/recorder.mjs.map +0 -1
  112. package/dist/provider.d.mts.map +0 -1
  113. package/dist/provider.mjs.map +0 -1
  114. package/dist/react-error-handler.d.mts.map +0 -1
  115. package/dist/react-error-handler.mjs.map +0 -1
  116. package/dist/tracking/api.d.mts.map +0 -1
  117. package/dist/tracking/api.mjs.map +0 -1
  118. package/dist/tracking/device.d.mts.map +0 -1
  119. package/dist/tracking/device.mjs.map +0 -1
  120. package/dist/tracking/geo.d.mts.map +0 -1
  121. package/dist/tracking/geo.mjs.map +0 -1
  122. package/dist/tracking/session.d.mts.map +0 -1
  123. package/dist/tracking/session.mjs.map +0 -1
  124. package/dist/util/bot.d.mts.map +0 -1
  125. package/dist/util/bot.mjs.map +0 -1
  126. package/dist/util/global.d.mts.map +0 -1
  127. package/dist/util/global.mjs.map +0 -1
  128. package/dist/util/log.d.mts.map +0 -1
  129. package/dist/util/log.mjs.map +0 -1
  130. package/dist/util/stringify.d.mts.map +0 -1
  131. package/dist/util/stringify.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"page-scope-context-manager.mjs","names":[],"sources":["../../../src/internal/otel/page-scope-context-manager.ts"],"sourcesContent":["import type { Context } from \"@opentelemetry/api\";\nimport { ROOT_CONTEXT } from \"@opentelemetry/api\";\nimport { ZoneContextManager } from \"@opentelemetry/context-zone\";\n\n/**\n * `ZoneContextManager` returns `ROOT_CONTEXT` whenever `context.active()` is\n * called outside a zone the manager itself created — which in practice is\n * most of the time:\n *\n * - app code calling `fetch(...)` from a `useEffect`\n * - `PerformanceObserver` callbacks (long-task, paint timing, …)\n * - top-level await / module init code\n * - listeners attached before the SDK booted\n *\n * Spans created from those code paths default to \"no parent\" → root, which\n * is why a single page load produces N disjoint traces (one per\n * instrumentation) instead of one waterfall.\n *\n * This subclass adds a single fallback: if the underlying zone lookup yields\n * `ROOT_CONTEXT`, return the page-scope context instead. The page-scope\n * context is built once at SDK init from the `<meta name=\"traceparent\">`\n * tag emitted by the SSR layout. If the meta tag is absent the page scope\n * stays at `ROOT_CONTEXT` and the manager behaves like a stock\n * `ZoneContextManager`.\n */\nexport class PageScopeContextManager extends ZoneContextManager {\n private pageScope: Context = ROOT_CONTEXT;\n\n setPageScope(ctx: Context): void {\n this.pageScope = ctx;\n }\n\n override active(): Context {\n const ctx = super.active();\n return ctx === ROOT_CONTEXT ? this.pageScope : ctx;\n }\n}\n"],"mappings":"yGAyBA,IAAa,wBAAb,cAA6C,kBAAmB,CAC9D,UAA6B,aAE7B,aAAa,IAAoB,CAC/B,KAAK,UAAY,GACnB,CAEA,QAA2B,CACzB,IAAM,IAAM,MAAM,OAAO,EACzB,OAAO,MAAQ,aAAe,KAAK,UAAY,GACjD,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"propagation.d.mts","names":[],"sources":["../../../src/internal/otel/propagation.ts"],"mappings":";;;;;AAsCA;;;;AAAsD;;;;;;;;;iBAAtC,2BAAA,IAA+B,OAAO"}
@@ -1 +0,0 @@
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\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\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":"mIAQA,MACM,gBAAkB,aAIlB,eAAiB,IAAI,0BAE3B,SAAS,SAAS,KAA6B,CAK7C,OAJI,OAAO,SAAa,IACf,KAEE,SAAS,cAAc,cAAc,KAAK,GAC7C,GAAG,aAAa,SAAS,GAAK,IACxC,CAiBA,SAAgB,6BAA8C,CAC5D,IAAM,YAAc,SAAS,aAAgB,EAC7C,GAAI,CAAC,YACH,OAAO,KAGT,IAAM,QAAkC,CAAE,WAAY,EAChD,WAAa,SAAS,eAAe,EACvC,aACF,QAAQ,iBAAmB,YAG7B,IAAM,UAAY,eAAe,QAC/B,aACA,QACA,oBACF,EACM,QAAU,MAAM,eAAe,SAAS,EAU9C,MAPE,CAAC,SACD,QAAQ,UAAY,oCAAA,EAEnB,QAAQ,WAAa,GAEf,KAEF,SACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"provider.d.mts","names":[],"sources":["../../../src/internal/otel/provider.ts"],"mappings":";;;;;;;;;UAoGiB,iBAAA;;AAAjB;;;;;;;;;EAWE,6BAAA,GAAgC,kBAAA;EA8CP;;;;;;;;;;EAnCzB,uBAAA,GAA0B,YAAA;EAiC1B;;;;;;EA1BA,wBAAA,GAA2B,aAAA;EAuChB;AAGb;;;;EApCE,WAAA,EAAa,OAAA;EA6CG;EA3ChB,YAAA;EA6Cc;;;;;;;;;EAnCd,QAAA;EAiCgB;;;;;;;EAzBhB,YAAA,QAAoB,SAAA;EA8BJ;EA5BhB,WAAA,GAAc,WAAA;EA4BmB;AAWnC;;;;EAjCE,QAAA;EAkCA;;;AACmB;EA9BnB,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,kBAAkB"}
@@ -1 +0,0 @@
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 { resolveEnvironment } 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 { getGlobal } from \"../../util/global.js\";\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\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\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 * transport metadata the rest of the SDK 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 `process.env.X` member expressions only — bundlers (Turbopack,\n // webpack DefinePlugin, Vite) inline env vars by replacing the exact member\n // expression. Passing the whole `process.env` object reads the browser\n // process shim's empty `env` at runtime instead, so every value comes back\n // undefined and the environment silently falls back to `development`.\n const environment = resolveEnvironment({\n // `withInterfere()` stamps the build-time deploy environment\n // (INTERFERE_ENVIRONMENT ?? VERCEL_ENV) onto globalThis — neither var is\n // ever inlined into browser bundles, so this is the only way they can\n // outrank the env chain below.\n explicit: getGlobal<string>(\"__INTERFERE_ENVIRONMENT__\"),\n env:\n typeof process === \"undefined\"\n ? {}\n : {\n INTERFERE_ENVIRONMENT: process.env[\"INTERFERE_ENVIRONMENT\"],\n VERCEL_ENV: process.env[\"VERCEL_ENV\"],\n NODE_ENV: process.env[\"NODE_ENV\"],\n },\n });\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 const getSessionId = input.getSessionId;\n\n const spanProcessors: SpanProcessor[] = [\n createSessionSpanProcessor({ getSessionId }),\n\n new BatchSpanProcessor(traceExporter, BROWSER_BATCH_OPTIONS),\n\n ...(input.additionalSpanProcessors ?? []),\n ];\n\n const tracerProvider = new WebTracerProvider({ resource, spanProcessors });\n\n const contextManager = new PageScopeContextManager();\n tracerProvider.register({ contextManager });\n\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 const logExporter = createBeaconLogExporter({\n collectorUrl: input.collectorUrl,\n authHeaders: input.authHeaders,\n });\n const loggerProvider = new LoggerProvider({\n resource,\n processors: [\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":"8+BA4CA,MAiDM,sBAAwB,CAC5B,aAAc,GACd,mBAAoB,GACpB,qBAAsB,IACtB,oBAAqB,GACvB,EAkGA,SAAgB,kBACd,MACoB,CAMpB,IAAM,YAAc,mBAAmB,CAKrC,SAAU,UAAkB,2BAA2B,EACvD,IACE,OAAO,QAAY,IACf,CAAC,EACD,CACE,sBAAuB,QAAQ,IAAI,sBACnC,WAAY,QAAQ,IAAI,WACxB,SAAU,QAAQ,IAAI,QACxB,CACR,CAAC,EAEK,aAAe,uBAAuB,CACzC,eAAoB,MAAM,aAAe,gBACzC,oBAAyB,YACzB,8BAAmC,YACnC,yBAA8B,QAC9B,qBAA0B,SAC1B,wBAA6B,YAC7B,sBAA2B,MAAM,SAAS,KAAK,IAAI,EACpD,GAAI,MAAM,YAAc,CAAG,eAAoB,MAAM,WAAY,EAAI,CAAC,EACtE,GAAI,MAAM,SAAW,CAAG,YAAiB,MAAM,QAAS,EAAI,CAAC,CAC/D,CAAC,EACK,SAAW,gBAAgB,CAAE,UAAW,CAAC,eAAe,CAAE,CAAC,EAC3D,SAAW,aAAa,MAAM,QAAQ,EAEtC,cAAgB,0BAA0B,CAC9C,aAAc,MAAM,aACpB,YAAa,MAAM,WACrB,CAAC,EAEK,aAAe,MAAM,aAUrB,eAAiB,IAAI,kBAAkB,CAAE,SAAU,eAAA,CAPvD,2BAA2B,CAAE,YAAa,CAAC,EAE3C,IAAI,mBAAmB,cAAe,qBAAqB,EAE3D,GAAI,MAAM,0BAA4B,CAAC,CAG6B,CAAE,CAAC,EAEnE,eAAiB,IAAI,wBAC3B,eAAe,SAAS,CAAE,cAAe,CAAC,EAEtC,YAAY,OAAO,EAAE,SAAW,GAClC,YAAY,oBACV,IAAI,oBAAoB,CACtB,YAAa,CACX,IAAI,0BACJ,IAAI,oBACN,CACF,CAAC,CACH,EAQF,IAAM,aAAe,IAAI,8BAA8B,CACrD,SANqB,2BAA2B,CAChD,aAAc,MAAM,aACpB,YAAa,MAAM,WACrB,CAGyB,EACvB,qBAAsB,GACxB,CAAC,EAEK,cAAgB,IAAI,cAAc,CACtC,SACA,QAAS,CAAC,aAAc,GAAI,MAAM,yBAA2B,CAAC,CAAE,CAClE,CAAC,EAEK,YAAc,wBAAwB,CAC1C,aAAc,MAAM,aACpB,YAAa,MAAM,WACrB,CAAC,EACK,eAAiB,IAAI,eAAe,CACxC,SACA,WAAY,CACV,gCAAgC,CAAE,YAAa,CAAC,EAChD,IAAI,wBAAwB,YAAa,qBAAqB,EAC9D,GAAI,MAAM,+BAAiC,CAAC,CAC9C,CACF,CAAC,EAED,MAAO,CACL,eACA,eACA,cACA,aACA,eACA,MAAO,SAAY,CACjB,MAAM,QAAQ,IAAI,CAChB,eAAe,WAAW,EAC1B,aAAa,WAAW,EACxB,eAAe,WAAW,CAC5B,CAAC,CACH,EACA,SAAU,SAAY,CACpB,MAAM,QAAQ,IAAI,CAChB,eAAe,SAAS,EACxB,cAAc,SAAS,EACvB,eAAe,SAAS,CAC1B,CAAC,CACH,CACF,CACF"}
@@ -1 +0,0 @@
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,KAAK;EAAL;;;;AAMyB;EAAhC,YAAA,IAAgB,QAAA;AAAA;;;AAgBoC;;;;;;;;;;;iBAAtC,gBAAA,CAAiB,KAAqB,EAAd,cAAc"}
@@ -1 +0,0 @@
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":"uDAGA,MASM,YAAc,IAAI,IAAI,CAAC,OAAQ,MAAO,KAAK,CAAC,EAqClD,SAAgB,iBAAiB,MAA6B,CAC5D,GAAI,OAAO,OAAW,IACpB,OAGF,GAAM,CAAE,MAAO,aAAc,OAAU,MAEjC,IAAM,MAAM,gBAAgB,iBAAkB,CAClD,YAAa,2BACb,KAAM,IACN,OAAQ,CACN,yBAA0B,CAAC,GAAK,EAAG,IAAK,EAAG,IAAK,EAAG,EAAG,EAAG,IAAK,EAAE,CAClE,CACF,CAAC,EACK,IAAM,MAAM,gBAAgB,iBAAkB,CAClD,YAAa,yBACb,KAAM,IACN,OAAQ,CACN,yBAA0B,CAAC,GAAK,EAAG,IAAK,IAAK,EAAG,IAAK,EAAG,EAAG,EAAG,GAAG,CACnE,CACF,CAAC,EACK,KAAO,MAAM,gBAAgB,kBAAmB,CACpD,YAAa,qBACb,KAAM,IACN,OAAQ,CACN,yBAA0B,CAAC,GAAK,GAAK,GAAK,GAAK,GAAK,EAAG,IAAK,EAAG,EAAG,CAAC,CACrE,CACF,CAAC,EACK,IAAM,MAAM,gBAAgB,iBAAkB,CAClD,YAAa,4BACb,KAAM,IACN,OAAQ,CACN,yBAA0B,CACxB,IAAM,GAAK,IAAM,GAAK,GAAK,GAAK,GAAK,IAAM,EAAG,CAChD,CACF,CACF,CAAC,EACK,IAAM,MAAM,gBAAgB,iBAAkB,CAClD,YAAa,0BACb,KAAM,IACN,OAAQ,CACN,yBAA0B,CACxB,IAAM,KAAO,IAAM,KAAO,GAAK,IAAM,GAAK,IAAM,GAAK,CACvD,CACF,CACF,CAAC,EAEK,OAAqC,CACzC,IAAK,CAAE,UAAW,IAAK,UAAW,EAAK,EACvC,IAAK,CAAE,UAAW,IAAK,UAAW,EAAK,EACvC,KAAM,CAAE,UAAW,KAAM,UAAW,EAAK,EACzC,IAAK,CAAE,UAAW,IAAK,UAAW,EAAK,EACvC,IAAK,CAAE,UAAW,IAAK,UAAW,EAAM,CAC1C,EAEM,SAAW,IAAI,IACjB,QAAU,GAEd,SAAS,OAAO,OAKP,CACP,IAAM,MAAQ,OAAO,OAAO,MAC5B,GAAI,CAAC,MACH,OAEF,IAAM,MAAQ,MAAM,UAAY,OAAO,MAAQ,KAAU,OAAO,MAC1D,SAAW,OAAO,SAAS,SAC3B,MAAQ,eAAe,QAAQ,GAAK,SAE1C,MAAM,UAAU,OAAO,MAAO,CAC5B,GAAI,OAAO,OAAS,CAAE,mBAAoB,OAAO,MAAO,EAAI,CAAC,EAC7D,GAAI,OAAO,eACP,CAAE,4BAA6B,OAAO,cAAe,EACrD,CAAC,EACJ,WAAgB,KACnB,CAAC,EAEG,CAAC,SAAW,YAAY,IAAI,OAAO,IAAI,IACzC,SAAS,IAAI,OAAO,IAAI,EACpB,SAAS,OAAS,YAAY,OAChC,QAAU,GACV,MAAM,EAAE,UAAY,IAAA,EAAS,GAGnC,CAEA,MAAM,MAAM,EACZ,MAAM,MAAM,EACZ,MAAM,MAAM,EACZ,OAAO,MAAM,EACb,MAAM,MAAM,CACd"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"page-lifecycle.d.mts","names":[],"sources":["../../src/internal/page-lifecycle.ts"],"mappings":";KACK,OAAA;AAAA,iBA0CW,YAAA,CAAa,OAAgB,EAAP,OAAO;AAAA,iBAQ7B,WAAA,CAAY,OAAgB,EAAP,OAAO;;iBAS5B,2BAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"page-lifecycle.mjs","names":[],"sources":["../../src/internal/page-lifecycle.ts"],"sourcesContent":["// visibilitychange and beforeunload can both fire on hide — handlers must be idempotent.\ntype Handler = () => void;\n\nconst hiddenHandlers = new Set<Handler>();\nconst shownHandlers = new Set<Handler>();\nlet installed = false;\n\nfunction fire(set: Set<Handler>): void {\n for (const handler of set) {\n handler();\n }\n}\n\nfunction fireHidden(): void {\n fire(hiddenHandlers);\n}\n\nfunction fireShown(): void {\n fire(shownHandlers);\n}\n\nfunction onVisibilityChange(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n if (document.visibilityState === \"hidden\") {\n fireHidden();\n return;\n }\n if (document.visibilityState === \"visible\") {\n fireShown();\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\", fireHidden);\n}\n\nexport function onPageHidden(handler: Handler): () => void {\n install();\n hiddenHandlers.add(handler);\n return () => {\n hiddenHandlers.delete(handler);\n };\n}\n\nexport function onPageShown(handler: Handler): () => void {\n install();\n shownHandlers.add(handler);\n return () => {\n shownHandlers.delete(handler);\n };\n}\n\n/** Test hook — drops every handler and unbinds the listeners. */\nexport function _resetPageLifecycleForTests(): void {\n hiddenHandlers.clear();\n shownHandlers.clear();\n if (installed && typeof globalThis.removeEventListener === \"function\") {\n globalThis.removeEventListener(\"visibilitychange\", onVisibilityChange);\n globalThis.removeEventListener(\"beforeunload\", fireHidden);\n }\n installed = false;\n}\n"],"mappings":"AAGA,MAAM,eAAiB,IAAI,IACrB,cAAgB,IAAI,IAC1B,IAAI,UAAY,GAEhB,SAAS,KAAK,IAAyB,CACrC,IAAK,IAAM,WAAW,IACpB,QAAQ,CAEZ,CAEA,SAAS,YAAmB,CAC1B,KAAK,cAAc,CACrB,CAEA,SAAS,WAAkB,CACzB,KAAK,aAAa,CACpB,CAEA,SAAS,oBAA2B,CAC9B,YAAO,SAAa,KAGxB,IAAI,SAAS,kBAAoB,SAAU,CACzC,WAAW,EACX,MACF,CACI,SAAS,kBAAoB,WAC/B,UAAU,CAFZ,CAIF,CAEA,SAAS,SAAgB,CACnB,WAAa,OAAO,WAAW,kBAAqB,aAGxD,UAAY,GACZ,WAAW,iBAAiB,mBAAoB,kBAAkB,EAClE,WAAW,iBAAiB,eAAgB,UAAU,EACxD,CAEA,SAAgB,aAAa,QAA8B,CAGzD,OAFA,QAAQ,EACR,eAAe,IAAI,OAAO,MACb,CACX,eAAe,OAAO,OAAO,CAC/B,CACF,CAEA,SAAgB,YAAY,QAA8B,CAGxD,OAFA,QAAQ,EACR,cAAc,IAAI,OAAO,MACZ,CACX,cAAc,OAAO,OAAO,CAC9B,CACF,CAGA,SAAgB,6BAAoC,CAClD,eAAe,MAAM,EACrB,cAAc,MAAM,EAChB,WAAa,OAAO,WAAW,qBAAwB,aACzD,WAAW,oBAAoB,mBAAoB,kBAAkB,EACrE,WAAW,oBAAoB,eAAgB,UAAU,GAE3D,UAAY,EACd"}
@@ -1 +0,0 @@
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,IAAc,YAAA;EAId,UAAA,CAAW,WAAA,GAAc,YAAA;EAUzB,YAAA;EASA,iBAAA,CAAkB,MAAA,EAAQ,kBAAA;EAK1B,KAAA;EAWM,OAAA,IAAW,OAAA;EAAA,QAWT,kBAAA;EAAA,QAQA,UAAA;EAAA,QAeM,QAAA;EAAA,QAoBN,IAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugin-runtime.mjs","names":[],"sources":["../../src/internal/plugin-runtime.ts"],"sourcesContent":["import {\n type ConsentState,\n PLUGIN_MANIFEST,\n type PluginKey,\n} from \"@interfere/types/sdk/plugins/manifest\";\nimport type { RemotePluginConfig } from \"@interfere/types/sdk/remote-config\";\n\nimport errorsPlugin from \"../plugins/errors.js\";\nimport {\n loadPlugin,\n type PluginOverrides,\n resolveFeatures,\n} from \"../plugins/lib/loader.js\";\nimport type { PluginCleanup, PluginContext } from \"../plugins/lib/types.js\";\nimport { createLogger } from \"../util/log.js\";\nimport {\n getPluginConsentCategory,\n hasConsentChanged,\n isConsentAllowed,\n resolveGrantedConsent,\n} from \"./consent.js\";\n\nconst log = createLogger(\"plugin-runtime\");\n\nexport class PluginRuntime {\n private readonly activeCleanups = new Map<PluginKey, PluginCleanup>();\n private readonly pending = new Set<Promise<void>>();\n private readonly context: PluginContext;\n private readonly features: Record<PluginKey, boolean>;\n private remoteConfig: RemotePluginConfig = {};\n private consentState: ConsentState | null;\n private syncVersion = 0;\n private disposed = false;\n\n constructor(\n context: PluginContext,\n overrides: PluginOverrides | undefined,\n initialConsent: ConsentState | undefined\n ) {\n this.context = context;\n this.features = resolveFeatures(overrides);\n this.consentState = initialConsent ?? null;\n }\n\n getConsent(): ConsentState | null {\n return this.consentState;\n }\n\n setConsent(nextConsent?: ConsentState): void {\n const nextState = resolveGrantedConsent(nextConsent);\n if (!hasConsentChanged(this.consentState, nextState)) {\n return;\n }\n\n this.consentState = nextState;\n this.sync();\n }\n\n resetConsent(): void {\n if (!hasConsentChanged(this.consentState, null)) {\n return;\n }\n\n this.consentState = null;\n this.sync();\n }\n\n applyRemoteConfig(config: RemotePluginConfig): void {\n this.remoteConfig = config;\n this.sync();\n }\n\n start(): void {\n if (this.shouldEnablePlugin(\"errors\")) {\n const cleanup = errorsPlugin.setup(this.context);\n if (cleanup) {\n this.activeCleanups.set(\"errors\", cleanup);\n }\n }\n\n this.sync();\n }\n\n async dispose(): Promise<void> {\n this.disposed = true;\n this.syncVersion += 1;\n\n for (const key of this.activeCleanups.keys()) {\n this.deactivate(key);\n }\n\n await Promise.allSettled(this.pending);\n }\n\n private shouldEnablePlugin(key: PluginKey): boolean {\n return (\n this.features[key] &&\n this.remoteConfig[key] !== false &&\n isConsentAllowed(getPluginConsentCategory(key), this.consentState)\n );\n }\n\n private deactivate(key: PluginKey): void {\n const cleanup = this.activeCleanups.get(key);\n if (!cleanup) {\n return;\n }\n\n try {\n cleanup();\n } catch {\n log.warn(\"cleanup failed for %s\", key);\n }\n\n this.activeCleanups.delete(key);\n }\n\n private async activate(key: PluginKey): Promise<void> {\n if (this.activeCleanups.has(key) || !this.shouldEnablePlugin(key)) {\n return;\n }\n\n const version = this.syncVersion;\n const cleanup = await loadPlugin(key, this.context);\n if (!cleanup) {\n return;\n }\n\n const staleSync = version !== this.syncVersion;\n if (staleSync || !this.shouldEnablePlugin(key)) {\n cleanup();\n return;\n }\n\n this.activeCleanups.set(key, cleanup);\n }\n\n private sync(): void {\n if (this.disposed) {\n return;\n }\n\n this.syncVersion += 1;\n\n for (const plugin of PLUGIN_MANIFEST) {\n if (this.shouldEnablePlugin(plugin.name)) {\n if (plugin.name === \"errors\") {\n if (!this.activeCleanups.has(\"errors\")) {\n const cleanup = errorsPlugin.setup(this.context);\n if (cleanup) {\n this.activeCleanups.set(\"errors\", cleanup);\n }\n }\n continue;\n }\n\n const p = this.activate(plugin.name).catch(() => {\n log.warn(\"non-critical plugin loading failed\");\n });\n this.pending.add(p);\n p.finally(() => this.pending.delete(p));\n continue;\n }\n\n this.deactivate(plugin.name);\n }\n }\n}\n"],"mappings":"4UAsBA,MAAM,IAAM,aAAa,gBAAgB,EAEzC,IAAa,cAAb,KAA2B,CACzB,eAAkC,IAAI,IACtC,QAA2B,IAAI,IAC/B,QACA,SACA,aAA2C,CAAC,EAC5C,aACA,YAAsB,EACtB,SAAmB,GAEnB,YACE,QACA,UACA,eACA,CACA,KAAK,QAAU,QACf,KAAK,SAAW,gBAAgB,SAAS,EACzC,KAAK,aAAe,gBAAkB,IACxC,CAEA,YAAkC,CAChC,OAAO,KAAK,YACd,CAEA,WAAW,YAAkC,CAC3C,IAAM,UAAY,sBAAsB,WAAW,EAC9C,kBAAkB,KAAK,aAAc,SAAS,IAInD,KAAK,aAAe,UACpB,KAAK,KAAK,EACZ,CAEA,cAAqB,CACd,kBAAkB,KAAK,aAAc,IAAI,IAI9C,KAAK,aAAe,KACpB,KAAK,KAAK,EACZ,CAEA,kBAAkB,OAAkC,CAClD,KAAK,aAAe,OACpB,KAAK,KAAK,CACZ,CAEA,OAAc,CACZ,GAAI,KAAK,mBAAmB,QAAQ,EAAG,CACrC,IAAM,QAAU,aAAa,MAAM,KAAK,OAAO,EAC3C,SACF,KAAK,eAAe,IAAI,SAAU,OAAO,CAE7C,CAEA,KAAK,KAAK,CACZ,CAEA,MAAM,SAAyB,CAC7B,KAAK,SAAW,GAChB,KAAK,aAAe,EAEpB,IAAK,IAAM,OAAO,KAAK,eAAe,KAAK,EACzC,KAAK,WAAW,GAAG,EAGrB,MAAM,QAAQ,WAAW,KAAK,OAAO,CACvC,CAEA,mBAA2B,IAAyB,CAClD,OACE,KAAK,SAAS,MACd,KAAK,aAAa,OAAS,IAC3B,iBAAiB,yBAAyB,GAAG,EAAG,KAAK,YAAY,CAErE,CAEA,WAAmB,IAAsB,CACvC,IAAM,QAAU,KAAK,eAAe,IAAI,GAAG,EACtC,WAIL,IAAI,CACF,QAAQ,CACV,MAAQ,CACN,IAAI,KAAK,wBAAyB,GAAG,CACvC,CAEA,KAAK,eAAe,OAAO,GAAG,CAF9B,CAGF,CAEA,MAAc,SAAS,IAA+B,CACpD,GAAI,KAAK,eAAe,IAAI,GAAG,GAAK,CAAC,KAAK,mBAAmB,GAAG,EAC9D,OAGF,IAAM,QAAU,KAAK,YACf,QAAU,MAAM,WAAW,IAAK,KAAK,OAAO,EAC7C,WAKL,IADkB,UAAY,KAAK,aAClB,CAAC,KAAK,mBAAmB,GAAG,EAAG,CAC9C,QAAQ,EACR,MACF,CAEA,KAAK,eAAe,IAAI,IAAK,OAAO,CAFpC,CAGF,CAEA,MAAqB,CACf,SAAK,SAIT,MAAK,aAAe,EAEpB,IAAK,IAAM,UAAU,gBAAiB,CACpC,GAAI,KAAK,mBAAmB,OAAO,IAAI,EAAG,CACxC,GAAI,OAAO,OAAS,SAAU,CAC5B,GAAI,CAAC,KAAK,eAAe,IAAI,QAAQ,EAAG,CACtC,IAAM,QAAU,aAAa,MAAM,KAAK,OAAO,EAC3C,SACF,KAAK,eAAe,IAAI,SAAU,OAAO,CAE7C,CACA,QACF,CAEA,IAAM,EAAI,KAAK,SAAS,OAAO,IAAI,EAAE,UAAY,CAC/C,IAAI,KAAK,oCAAoC,CAC/C,CAAC,EACD,KAAK,QAAQ,IAAI,CAAC,EAClB,EAAE,YAAc,KAAK,QAAQ,OAAO,CAAC,CAAC,EACtC,QACF,CAEA,KAAK,WAAW,OAAO,IAAI,CAC7B,CAvBoB,CAwBtB,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"react-context.d.mts","names":[],"sources":["../../src/internal/react-context.ts"],"mappings":";;;;;UASiB,qBAAA;EACf,OAAA;IACE,GAAA,IAAO,YAAA;IACP,GAAA,CAAI,KAAA,GAAQ,YAAA;EAAA;EAEd,MAAA;IACE,WAAA;IACA,SAAA;EAAA;EAEF,QAAA;IACE,GAAA,IAAO,cAAA;IACP,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA;EAAA;EAV/B;;;;;;EAkBA,MAAA,EAAQ,MAAA;EACR,OAAA;IACE,KAAA;IACA,WAAA;EAAA;AAAA;;;;;;;cAkBS,kBAAA,EAAoB,qBAMhC;;;;AAxBc;cA8BF,gBAAA,kBAAgB,OAAA,CAAA,qBAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"react-context.mjs","names":[],"sources":["../../src/internal/react-context.ts"],"sourcesContent":["\"use client\";\n\nimport type { IdentifyParams } from \"@interfere/types/sdk/identify\";\nimport type { ConsentState } from \"@interfere/types/sdk/plugins/manifest\";\n\nimport { createContext } from \"react\";\n\nimport type { Kernel } from \"./kernel.js\";\n\nexport interface InterfereContextValue {\n consent: {\n get(): ConsentState | null;\n set(state?: ConsentState): void;\n };\n device: {\n getDeviceId(): string | null;\n getFpHash(): string | null;\n };\n identity: {\n get(): IdentifyParams | null;\n set(params: IdentifyParams): Promise<void>;\n };\n /**\n * The active kernel, or `null` when the SDK isn't initialized yet — for\n * example during server-side prerender, when `init()` is client-only.\n * Accessor objects (consent/device/identity/session) above stay safe to\n * call in either state; they no-op when the kernel is null.\n */\n kernel: Kernel | null;\n session: {\n getId(): string | null;\n getWindowId(): string | null;\n };\n}\n\nconst NULL_CONSENT = { get: () => null, set: () => undefined };\nconst NULL_DEVICE = { getDeviceId: () => null, getFpHash: () => null };\nconst NULL_IDENTITY = {\n get: () => null,\n set: () => Promise.resolve(),\n};\nconst NULL_SESSION = { getId: () => null, getWindowId: () => null };\n\n/**\n * Context value used when the provider is mounted without a kernel — every\n * accessor returns `null` and mutations are no-ops. Lets `useInterfere()`\n * work seamlessly across SSR/CSR boundaries (server gets nulls, client\n * gets real values once the kernel resolves).\n */\nexport const NULL_CONTEXT_VALUE: InterfereContextValue = {\n consent: NULL_CONSENT,\n device: NULL_DEVICE,\n identity: NULL_IDENTITY,\n kernel: null,\n session: NULL_SESSION,\n};\n\n/**\n * Lives in its own module so `provider.tsx` and `internal/capture-boundary.tsx`\n * can both import it without forming an import cycle.\n */\nexport const InterfereContext = createContext<InterfereContextValue | null>(\n null\n);\n"],"mappings":"8CAiDA,MAAa,mBAA4C,CACvD,QAAS,CAfY,QAAW,KAAM,QAAW,IAAA,EAexC,EACT,OAAQ,CAfY,gBAAmB,KAAM,cAAiB,IAetD,EACR,SAAU,CAdV,QAAW,KACX,QAAW,QAAQ,QAAQ,CAajB,EACV,OAAQ,KACR,QAAS,CAbY,UAAa,KAAM,gBAAmB,IAalD,CACX,EAMa,iBAAmB,cAC9B,IACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"sw.d.mts","names":[],"sources":["../../src/internal/sw.ts"],"mappings":";UAMiB,qBAAA;EAAA;;;;;;;EAQf,SAAA,IAAa,OAAA,EAAS,SAAS;AAAA;AAAA,UAGhB,SAAA;EACf,MAAA;EACA,IAAA;EAIA,GAAA;AAAA;;;;AAAG;AAQL;iBAAsB,qBAAA,CACpB,IAAA,GAAM,qBAAA,GACL,OAAO"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"sw.mjs","names":[],"sources":["../../src/internal/sw.ts"],"sourcesContent":["import { resolveRoutePrefix } from \"@interfere/constants/route-prefix\";\n\nimport { createLogger } from \"../util/log.js\";\n\nconst log = createLogger(\"sw\");\n\nexport interface SwRegistrationOptions {\n /**\n * Optional hook to bridge SW → kernel telemetry. The SW posts\n * `{ type: \"interfere.sw.queued\" | ... }` messages back to the\n * page when it queues, replays, or drops a request; if provided,\n * this fires for each one so the kernel can record an OTel span\n * event.\n */\n onMessage?: (message: SwMessage) => void;\n}\n\nexport interface SwMessage {\n reason?: string;\n type:\n | \"interfere.sw.queued\"\n | \"interfere.sw.replayed\"\n | \"interfere.sw.dropped\";\n url: string;\n}\n\n/**\n * Best-effort: never throws, never blocks. SW registration is a\n * page-load-time durability bonus; if it fails, the SDK falls back to\n * direct fetch and behaves exactly like 9.x did.\n */\nexport async function registerServiceWorker(\n opts: SwRegistrationOptions = {}\n): Promise<void> {\n if (typeof navigator === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return;\n }\n\n const prefix = resolveRoutePrefix();\n const swPath = `${prefix}/sw`;\n const swScope = `${prefix}/`;\n\n try {\n await navigator.serviceWorker.register(swPath, { scope: swScope });\n log.debug(\"registered at %s (scope %s)\", swPath, swScope);\n } catch (err) {\n log.warn(\"registration failed, falling back to direct fetch\", err);\n return;\n }\n\n if (opts.onMessage) {\n const handler = (event: MessageEvent<unknown>) => {\n const data = event.data;\n if (!isSwMessage(data)) {\n return;\n }\n opts.onMessage?.(data);\n };\n navigator.serviceWorker.addEventListener(\"message\", handler);\n }\n}\n\nfunction isSwMessage(value: unknown): value is SwMessage {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n const v = value as Record<string, unknown>;\n return (\n (v[\"type\"] === \"interfere.sw.queued\" ||\n v[\"type\"] === \"interfere.sw.replayed\" ||\n v[\"type\"] === \"interfere.sw.dropped\") &&\n typeof v[\"url\"] === \"string\"\n );\n}\n"],"mappings":"4GAIA,MAAM,IAAM,aAAa,IAAI,EA2B7B,eAAsB,sBACpB,KAA8B,CAAC,EAChB,CACf,GAAI,OAAO,UAAc,KAAe,EAAE,kBAAmB,WAC3D,OAGF,IAAM,OAAS,mBAAmB,EAC5B,OAAS,GAAG,OAAO,KACnB,QAAU,GAAG,OAAO,GAE1B,GAAI,CACF,MAAM,UAAU,cAAc,SAAS,OAAQ,CAAE,MAAO,OAAQ,CAAC,EACjE,IAAI,MAAM,8BAA+B,OAAQ,OAAO,CAC1D,OAAS,IAAK,CACZ,IAAI,KAAK,oDAAqD,GAAG,EACjE,MACF,CAEI,KAAK,WAQP,UAAU,cAAc,iBAAiB,UAPxB,OAAiC,CAChD,IAAM,KAAO,MAAM,KACd,YAAY,IAAI,GAGrB,KAAK,YAAY,IAAI,CACvB,CAC2D,CAE/D,CAEA,SAAS,YAAY,MAAoC,CACvD,GAAI,CAAC,OAAS,OAAO,OAAU,SAC7B,MAAO,GAET,IAAM,EAAI,MACV,OACG,EAAE,OAAY,uBACb,EAAE,OAAY,yBACd,EAAE,OAAY,yBAChB,OAAO,EAAE,KAAW,QAExB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"version.d.mts","names":[],"sources":["../../src/internal/version.ts"],"mappings":";cAEa,QAAA;AAAA,cACA,WAAA;AAAA,cACA,gBAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"version.mjs","names":["pkg.name","pkg.version"],"sources":["../../src/internal/version.ts"],"sourcesContent":["import pkg from \"../../package.json\" with { type: \"json\" };\n\nexport const SDK_NAME = pkg.name;\nexport const SDK_VERSION = pkg.version;\nexport const PRODUCER_VERSION = `${SDK_NAME}@${SDK_VERSION}`;\n"],"mappings":"yCAEA,MAAa,SAAWA,KACX,YAAcC,QACd,iBAAmB,GAAG,SAAS,GAAG"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrapper-singleton.d.mts","names":[],"sources":["../../src/internal/wrapper-singleton.ts"],"mappings":";;;;;UAaiB,gBAAA;EACf,KAAA,IAAS,OAAA;EACT,OAAA;IACE,GAAA,IAAO,YAAA;IACP,GAAA,CAAI,KAAA,GAAQ,YAAA;EAAA;EAEd,SAAA,IAAa,MAAA;EACb,eAAA,IAAmB,MAAA;EACnB,QAAA;IACE,GAAA,IAAO,cAAA;IACP,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA;EAAA;EAE/B,IAAA,CAAK,IAAA,GAAO,aAAA,GAAgB,OAAA,CAAQ,MAAA;EACpC,iBAAA,CAAkB,QAAA;AAAA;AAAA,UAGH,qBAAA;EAJoB;;;;;EAUnC,aAAA;EAnBS;;;;;EAyBT,eAAe;AAAA;;;;;;;;;;;iBAaD,sBAAA,CACd,KAAA,EAAO,qBAAA,GACN,gBAAgB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrapper-singleton.mjs","names":[],"sources":["../../src/internal/wrapper-singleton.ts"],"sourcesContent":["import type { IdentifyParams } from \"@interfere/types/sdk/identify\";\nimport type { ConsentState } from \"@interfere/types/sdk/plugins/manifest\";\n\nimport { isBotUserAgent } from \"../util/bot.js\";\nimport {\n createKernel,\n isEnabledByEnvironment,\n type Kernel,\n type KernelInternalOptions,\n type KernelOptions,\n} from \"./kernel.js\";\nimport { registerKernel, unregisterKernel } from \"./kernel-registry.js\";\n\nexport interface WrapperSingleton {\n close(): Promise<void>;\n consent: {\n get(): ConsentState | null;\n set(value?: ConsentState): void;\n };\n getKernel(): Kernel;\n getKernelOrNull(): Kernel | null;\n identity: {\n get(): IdentifyParams | null;\n set(params: IdentifyParams): Promise<void>;\n };\n init(opts?: KernelOptions): Promise<Kernel | null>;\n subscribeToKernel(listener: () => void): () => void;\n}\n\nexport interface WrapperSingletonInput {\n /**\n * Surfaced in the \"kernel not initialized\" error so the message points\n * customers at the right entry file (e.g. \"instrumentation-client\" /\n * \"main.ts\"). Single concrete fact — no other behavior change.\n */\n initEntryName: string;\n /**\n * Wrapper SDK identifier injected as the first entry in\n * `__INTERFERE_SDK_STACK__` (e.g. `@interfere/next@10.0.0`). Threaded\n * through to the kernel so the OTel resource carries the full stack.\n */\n producerVersion: string;\n}\n\n/**\n * Per-wrapper kernel-singleton lifecycle. The next/vite/etc wrappers used\n * to copy the same ~100 lines verbatim (init/close/getKernel/subscribe\n * /consent/identity); this is the single source of truth so a fix once\n * applies everywhere.\n *\n * Each wrapper holds its own `WrapperSingleton` in module scope so the\n * kernel is per-wrapper, not per-process. (Microfrontends can run two\n * different wrappers on the same page; each gets its own kernel.)\n */\nexport function createWrapperSingleton(\n input: WrapperSingletonInput\n): WrapperSingleton {\n let kernel: Kernel | null = null;\n let pending: Promise<Kernel | null> | null = null;\n const listeners = new Set<() => void>();\n\n function emit(): void {\n for (const listener of listeners) {\n listener();\n }\n }\n\n return {\n init(opts: KernelOptions = {}): Promise<Kernel | null> {\n if (kernel) {\n return Promise.resolve(kernel);\n }\n if (pending) {\n return pending;\n }\n if (!(opts.enabled ?? isEnabledByEnvironment())) {\n return Promise.resolve(null);\n }\n if (isBotUserAgent()) {\n return Promise.resolve(null);\n }\n\n const internalOpts: KernelInternalOptions = {\n ...opts,\n _wrapperVersions: [input.producerVersion],\n };\n pending = createKernel({ opts: internalOpts })\n .then((k) => {\n kernel = k;\n if (k) {\n registerKernel(k);\n }\n emit();\n return k;\n })\n .finally(() => {\n pending = null;\n });\n\n return pending;\n },\n\n getKernel(): Kernel {\n if (!kernel) {\n throw new Error(\n `Interfere SDK not initialized. Call init() from your ${input.initEntryName} entrypoint.`\n );\n }\n return kernel;\n },\n\n getKernelOrNull(): Kernel | null {\n return kernel;\n },\n\n async close(): Promise<void> {\n if (!kernel) {\n return;\n }\n const previous = kernel;\n await previous.dispose();\n kernel = null;\n unregisterKernel(previous);\n emit();\n },\n\n subscribeToKernel(listener: () => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n\n consent: {\n get: () => kernel?.consent.get() ?? null,\n set: (value) => kernel?.consent.set(value),\n },\n\n identity: {\n get: () => kernel?.identity.get() ?? null,\n set: (params) => kernel?.identity.set(params) ?? Promise.resolve(),\n },\n };\n}\n"],"mappings":"6KAsDA,SAAgB,uBACd,MACkB,CAClB,IAAI,OAAwB,KACxB,QAAyC,KACvC,UAAY,IAAI,IAEtB,SAAS,MAAa,CACpB,IAAK,IAAM,YAAY,UACrB,SAAS,CAEb,CAEA,MAAO,CACL,KAAK,KAAsB,CAAC,EAA2B,CA+BrD,OA9BI,OACK,QAAQ,QAAQ,MAAM,EAE3B,UAGA,EAAE,KAAK,SAAW,uBAAuB,IAGzC,eAAe,EACV,QAAQ,QAAQ,IAAI,GAO7B,QAAU,aAAa,CAAE,KAAM,CAH7B,GAAG,KACH,iBAAkB,CAAC,MAAM,eAAe,CAEA,CAAE,CAAC,EAC1C,KAAM,IACL,OAAS,EACL,GACF,eAAe,CAAC,EAElB,KAAK,EACE,EACR,EACA,YAAc,CACb,QAAU,IACZ,CAAC,EAEI,SACT,EAEA,WAAoB,CAClB,GAAI,CAAC,OACH,MAAU,MACR,wDAAwD,MAAM,cAAc,aAC9E,EAEF,OAAO,MACT,EAEA,iBAAiC,CAC/B,OAAO,MACT,EAEA,MAAM,OAAuB,CAC3B,GAAI,CAAC,OACH,OAEF,IAAM,SAAW,OACjB,MAAM,SAAS,QAAQ,EACvB,OAAS,KACT,iBAAiB,QAAQ,EACzB,KAAK,CACP,EAEA,kBAAkB,SAAkC,CAElD,OADA,UAAU,IAAI,QAAQ,MACT,CACX,UAAU,OAAO,QAAQ,CAC3B,CACF,EAEA,QAAS,CACP,QAAW,QAAQ,QAAQ,IAAI,GAAK,KACpC,IAAM,OAAU,QAAQ,QAAQ,IAAI,KAAK,CAC3C,EAEA,SAAU,CACR,QAAW,QAAQ,SAAS,IAAI,GAAK,KACrC,IAAM,QAAW,QAAQ,SAAS,IAAI,MAAM,GAAK,QAAQ,QAAQ,CACnE,CACF,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"package.mjs","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.d.mts","names":[],"sources":["../../src/plugins/errors.ts"],"mappings":";;;cAwDa,YAAA,EAAc,MAgF1B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.mjs","names":[],"sources":["../../src/plugins/errors.ts"],"sourcesContent":["import type { IngestedFrame } from \"@interfere/types/data/frame\";\nimport {\n isNonErrorException,\n MECHANISM_TYPE,\n type NonErrorException,\n toException,\n} from \"@interfere/types/sdk/errors\";\nimport type { ErrorMechanism } from \"@interfere/types/sdk/plugins/payload/errors\";\n\nimport { onConsoleCall } from \"../internal/console-patch.js\";\nimport type { Plugin, PluginContext } from \"./lib/types.js\";\n\n/**\n * V8's default stack limit is 10. Deep React trees routinely exceed that\n * before reaching the actual application frames, leaving only react-dom\n * internals in the captured stack. Matches Sentry's browser SDK.\n */\nconst STACK_TRACE_LIMIT = 50;\n\ninterface CaptureOpts {\n readonly error: Error | NonErrorException;\n /**\n * Fallback frame to inject when the parsed stack of the root exception is\n * empty. Used for `window.onerror` calls where the browser provides\n * `source`/`line`/`col` even though the Error object itself has a\n * degenerate stack. Ignored for non-Error captures — there's no stack\n * to fall back into.\n */\n readonly fallbackFrame?: IngestedFrame;\n readonly mechanism: ErrorMechanism;\n}\n\nfunction capture(ctx: PluginContext, opts: CaptureOpts) {\n ctx.recordException(opts.error, {\n mechanism: opts.mechanism,\n ...(opts.fallbackFrame && !isNonErrorException(opts.error)\n ? { fallbackFrames: [opts.fallbackFrame] }\n : {}),\n });\n}\n\n/**\n * Finds the first `Error` instance in a list of `console.error` arguments.\n * React 18+ in production logs errors as\n * `console.error(\"The above error occurred in ...\", error, componentStack)`,\n * so scanning only `args[0]` misses React's own uncaught-error reports.\n */\nfunction findErrorArg(args: readonly unknown[]): Error | null {\n for (const arg of args) {\n if (arg instanceof Error) {\n return arg;\n }\n }\n return null;\n}\n\nexport const errorsPlugin: Plugin = {\n name: \"errors\",\n\n setup(ctx) {\n const originalOnError = globalThis.onerror;\n const originalStackTraceLimit = Error.stackTraceLimit;\n if (Error.stackTraceLimit < STACK_TRACE_LIMIT) {\n Error.stackTraceLimit = STACK_TRACE_LIMIT;\n }\n\n globalThis.onerror = (msg, source, line, col, error) => {\n if (error instanceof Error) {\n const fallbackFrame =\n typeof source === \"string\"\n ? {\n id: \"fallback-0\",\n file: source,\n ...(typeof line === \"number\" ? { line } : {}),\n ...(typeof col === \"number\" ? { column: col } : {}),\n }\n : null;\n\n capture(ctx, {\n error,\n mechanism: { type: MECHANISM_TYPE.browser.onerror, handled: false },\n ...(fallbackFrame ? { fallbackFrame } : {}),\n });\n }\n if (typeof originalOnError === \"function\") {\n return originalOnError.call(globalThis, msg, source, line, col, error);\n }\n return false;\n };\n\n const onUnhandledRejection = (event: PromiseRejectionEvent) => {\n capture(ctx, {\n error: toException(event.reason),\n mechanism: {\n type: MECHANISM_TYPE.browser.onunhandledrejection,\n handled: false,\n ...(event.reason instanceof Error ? {} : { synthetic: true }),\n },\n });\n };\n\n const supportsEventTarget =\n typeof globalThis.addEventListener === \"function\" &&\n typeof globalThis.removeEventListener === \"function\";\n if (supportsEventTarget) {\n globalThis.addEventListener(\"unhandledrejection\", onUnhandledRejection);\n }\n\n const unsubscribeConsole = onConsoleCall((level, args) => {\n if (level !== \"error\") {\n return;\n }\n const error = findErrorArg(args);\n if (error) {\n capture(ctx, {\n error,\n mechanism: {\n type: MECHANISM_TYPE.browser.consoleError,\n handled: true,\n },\n });\n }\n });\n\n return () => {\n Error.stackTraceLimit = originalStackTraceLimit;\n globalThis.onerror = originalOnError;\n if (supportsEventTarget) {\n globalThis.removeEventListener(\n \"unhandledrejection\",\n onUnhandledRejection\n );\n }\n unsubscribeConsole();\n };\n },\n};\n\nexport default errorsPlugin;\n"],"mappings":"iJAgCA,SAAS,QAAQ,IAAoB,KAAmB,CACtD,IAAI,gBAAgB,KAAK,MAAO,CAC9B,UAAW,KAAK,UAChB,GAAI,KAAK,eAAiB,CAAC,oBAAoB,KAAK,KAAK,EACrD,CAAE,eAAgB,CAAC,KAAK,aAAa,CAAE,EACvC,CAAC,CACP,CAAC,CACH,CAQA,SAAS,aAAa,KAAwC,CAC5D,IAAK,IAAM,OAAO,KAChB,GAAI,eAAe,MACjB,OAAO,IAGX,OAAO,IACT,CAEA,MAAa,aAAuB,CAClC,KAAM,SAEN,MAAM,IAAK,CACT,IAAM,gBAAkB,WAAW,QAC7B,wBAA0B,MAAM,gBAClC,MAAM,gBAAkB,KAC1B,MAAM,gBAAkB,IAG1B,WAAW,SAAW,IAAK,OAAQ,KAAM,IAAK,QAAU,CACtD,GAAI,iBAAiB,MAAO,CAC1B,IAAM,cACJ,OAAO,QAAW,SACd,CACE,GAAI,aACJ,KAAM,OACN,GAAI,OAAO,MAAS,SAAW,CAAE,IAAK,EAAI,CAAC,EAC3C,GAAI,OAAO,KAAQ,SAAW,CAAE,OAAQ,GAAI,EAAI,CAAC,CACnD,EACA,KAEN,QAAQ,IAAK,CACX,MACA,UAAW,CAAE,KAAM,eAAe,QAAQ,QAAS,QAAS,EAAM,EAClE,GAAI,cAAgB,CAAE,aAAc,EAAI,CAAC,CAC3C,CAAC,CACH,CAIA,OAHI,OAAO,iBAAoB,WACtB,gBAAgB,KAAK,WAAY,IAAK,OAAQ,KAAM,IAAK,KAAK,EAEhE,EACT,EAEA,IAAM,qBAAwB,OAAiC,CAC7D,QAAQ,IAAK,CACX,MAAO,YAAY,MAAM,MAAM,EAC/B,UAAW,CACT,KAAM,eAAe,QAAQ,qBAC7B,QAAS,GACT,GAAI,MAAM,kBAAkB,MAAQ,CAAC,EAAI,CAAE,UAAW,EAAK,CAC7D,CACF,CAAC,CACH,EAEM,oBACJ,OAAO,WAAW,kBAAqB,YACvC,OAAO,WAAW,qBAAwB,WACxC,qBACF,WAAW,iBAAiB,qBAAsB,oBAAoB,EAGxE,IAAM,mBAAqB,eAAe,MAAO,OAAS,CACxD,GAAI,QAAU,QACZ,OAEF,IAAM,MAAQ,aAAa,IAAI,EAC3B,OACF,QAAQ,IAAK,CACX,MACA,UAAW,CACT,KAAM,eAAe,QAAQ,aAC7B,QAAS,EACX,CACF,CAAC,CAEL,CAAC,EAED,UAAa,CACX,MAAM,gBAAkB,wBACxB,WAAW,QAAU,gBACjB,qBACF,WAAW,oBACT,qBACA,oBACF,EAEF,mBAAmB,CACrB,CACF,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"loader.d.mts","names":[],"sources":["../../../src/plugins/lib/loader.ts"],"mappings":";;;;KAuBY,eAAA,GAAkB,OAAA,CAAQ,MAAA,CAAO,SAAA;AAAA,iBAE7B,eAAA,CACd,SAAA,GAAY,eAAA,GACX,MAAA,CAAO,SAAA;AAAA,iBAUY,UAAA,CACpB,GAAA,EAAK,SAAA,EACL,OAAA,EAAS,aAAA,GACR,OAAA,CAAQ,aAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"loader.mjs","names":[],"sources":["../../../src/plugins/lib/loader.ts"],"sourcesContent":["import {\n PLUGIN_MANIFEST,\n type PluginKey,\n} from \"@interfere/types/sdk/plugins/manifest\";\n\nimport { createLogger } from \"../../util/log.js\";\nimport type { Plugin, PluginCleanup, PluginContext } from \"./types.js\";\n\nconst log = createLogger(\"plugins\");\n\ntype PluginLoader = () => Promise<{ default: Plugin } | Plugin>;\n\nconst LOADERS: Partial<Record<PluginKey, PluginLoader>> = {\n errors: () => import(\"../errors.js\"),\n logs: () => import(\"../logs.js\"),\n rageClick: () => import(\"../rage-clicks.js\"),\n replay: () => import(\"../replay/index.js\"),\n};\n\nconst DEFAULTS: Record<PluginKey, boolean> = Object.fromEntries(\n PLUGIN_MANIFEST.map((p) => [p.name, p.defaultEnabled])\n) as Record<PluginKey, boolean>;\n\nexport type PluginOverrides = Partial<Record<PluginKey, boolean>>;\n\nexport function resolveFeatures(\n overrides?: PluginOverrides\n): Record<PluginKey, boolean> {\n return { ...DEFAULTS, ...overrides };\n}\n\nfunction resolvePlugin(mod: { default: Plugin } | Plugin): Plugin {\n return \"default\" in mod && typeof (mod.default as Plugin).setup === \"function\"\n ? mod.default\n : (mod as Plugin);\n}\n\nexport async function loadPlugin(\n key: PluginKey,\n context: PluginContext\n): Promise<PluginCleanup | null> {\n const loader = LOADERS[key];\n if (!loader) {\n return null;\n }\n\n try {\n const mod = await loader();\n const plugin = resolvePlugin(mod);\n const cleanup = plugin.setup(context);\n log.debug(\"loaded %s\", key);\n return typeof cleanup === \"function\" ? cleanup : null;\n } catch {\n log.error(\"failed to load plugin %s\", key);\n return null;\n }\n}\n"],"mappings":"gHAQA,MAAM,IAAM,aAAa,SAAS,EAI5B,QAAoD,CACxD,WAAc,OAAO,iBACrB,SAAY,OAAO,eACnB,cAAiB,OAAO,sBACxB,WAAc,OAAO,sBACvB,EAEM,SAAuC,OAAO,YAClD,gBAAgB,IAAK,GAAM,CAAC,EAAE,KAAM,EAAE,cAAc,CAAC,CACvD,EAIA,SAAgB,gBACd,UAC4B,CAC5B,MAAO,CAAE,GAAG,SAAU,GAAG,SAAU,CACrC,CAEA,SAAS,cAAc,IAA2C,CAChE,MAAO,YAAa,KAAO,OAAQ,IAAI,QAAmB,OAAU,WAChE,IAAI,QACH,GACP,CAEA,eAAsB,WACpB,IACA,QAC+B,CAC/B,IAAM,OAAS,QAAQ,KACvB,GAAI,CAAC,OACH,OAAO,KAGT,GAAI,CAGF,IAAM,QADS,cAAc,MADX,OAAO,CAEJ,EAAE,MAAM,OAAO,EAEpC,OADA,IAAI,MAAM,YAAa,GAAG,EACnB,OAAO,SAAY,WAAa,QAAU,IACnD,MAAQ,CAEN,OADA,IAAI,MAAM,2BAA4B,GAAG,EAClC,IACT,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../../src/plugins/lib/types.ts"],"mappings":";;;;KAIY,aAAA;AAAA,UAEK,aAAA;EACf,YAAA;EACA,eAAA,CACE,KAAA,EAAO,KAAA,GAAQ,iBAAA,EACf,IAAA,EAAM,mBAAA;AAAA;AAAA,UAIO,MAAA;EAAA,SACN,IAAA;EACT,KAAA,CAAM,GAAA,EAAK,aAAA,GAAgB,aAAa;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"logs.d.mts","names":[],"sources":["../../src/plugins/logs.ts"],"mappings":";;;;;AAyBA;;;;AA0BC;cA1BY,UAAA,EAAY,MA0BxB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"logs.mjs","names":[],"sources":["../../src/plugins/logs.ts"],"sourcesContent":["import { SeverityNumber } from \"@opentelemetry/api-logs\";\n\nimport { type ConsoleLevel, onConsoleCall } from \"../internal/console-patch.js\";\nimport { activeKernel } from \"../internal/kernel-registry.js\";\nimport { safeStringify } from \"../util/stringify.js\";\nimport type { Plugin } from \"./lib/types.js\";\n\nconst LEVEL_TO_SEVERITY: Record<\n ConsoleLevel,\n { number: SeverityNumber; text: string }\n> = {\n debug: { number: SeverityNumber.DEBUG, text: \"DEBUG\" },\n info: { number: SeverityNumber.INFO, text: \"INFO\" },\n log: { number: SeverityNumber.INFO, text: \"INFO\" },\n warn: { number: SeverityNumber.WARN, text: \"WARN\" },\n error: { number: SeverityNumber.ERROR, text: \"ERROR\" },\n};\n\n/**\n * Subscribes to the central console patch so string-only console calls\n * land as OTel `LogRecord`s. Error-bearing calls are skipped: the\n * `errors` plugin's subscriber on the same patch handles those as\n * exceptions. Class boundary stays firm: errors flow as exceptions,\n * strings flow as logs.\n */\nexport const logsPlugin: Plugin = {\n name: \"logs\",\n\n setup() {\n const unsubscribe = onConsoleCall((level, args) => {\n if (args.some((a) => a instanceof Error)) {\n return;\n }\n const kernel = activeKernel();\n if (!kernel) {\n return;\n }\n const sev = LEVEL_TO_SEVERITY[level];\n kernel.recordLog({\n severityText: sev.text,\n severityNumber: sev.number,\n body:\n args.length === 1\n ? safeStringify(args[0])\n : args.map(safeStringify).join(\" \"),\n attributes: { \"console.level\": level },\n });\n });\n\n return unsubscribe;\n },\n};\n\nexport default logsPlugin;\n"],"mappings":"wNAOA,MAAM,kBAGF,CACF,MAAO,CAAE,OAAQ,eAAe,MAAO,KAAM,OAAQ,EACrD,KAAM,CAAE,OAAQ,eAAe,KAAM,KAAM,MAAO,EAClD,IAAK,CAAE,OAAQ,eAAe,KAAM,KAAM,MAAO,EACjD,KAAM,CAAE,OAAQ,eAAe,KAAM,KAAM,MAAO,EAClD,MAAO,CAAE,OAAQ,eAAe,MAAO,KAAM,OAAQ,CACvD,EASa,WAAqB,CAChC,KAAM,OAEN,OAAQ,CAqBN,OApBoB,eAAe,MAAO,OAAS,CACjD,GAAI,KAAK,KAAM,GAAM,aAAa,KAAK,EACrC,OAEF,IAAM,OAAS,aAAa,EAC5B,GAAI,CAAC,OACH,OAEF,IAAM,IAAM,kBAAkB,OAC9B,OAAO,UAAU,CACf,aAAc,IAAI,KAClB,eAAgB,IAAI,OACpB,KACE,KAAK,SAAW,EACZ,cAAc,KAAK,EAAE,EACrB,KAAK,IAAI,aAAa,EAAE,KAAK,GAAG,EACtC,WAAY,CAAE,gBAAiB,KAAM,CACvC,CAAC,CACH,CAEiB,CACnB,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"rage-clicks.d.mts","names":[],"sources":["../../src/plugins/rage-clicks.ts"],"mappings":";;;cAgCa,gBAAA,EAAkB,MA8D9B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"rage-clicks.mjs","names":[],"sources":["../../src/plugins/rage-clicks.ts"],"sourcesContent":["import { trace } from \"@opentelemetry/api\";\n\nimport type { Plugin } from \"./lib/types.js\";\n\nconst CLICK_THRESHOLD = 3;\nconst TIME_WINDOW_MS = 800;\nconst PROXIMITY_PX = 30;\nconst TRACER_NAME = \"@interfere/react/rage-clicks\";\n\ninterface Click {\n target: Element | null;\n ts: number;\n x: number;\n y: number;\n}\n\nfunction distance(a: Click, b: Click): number {\n return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2);\n}\n\nfunction selectorFor(el: Element | null): string {\n if (!el) {\n return \"unknown\";\n }\n if (el.id) {\n return `#${el.id}`;\n }\n const classes = [...el.classList].slice(0, 3).join(\".\");\n const tag = el.tagName.toLowerCase();\n return classes ? `${tag}.${classes}` : tag;\n}\n\nexport const rageClicksPlugin: Plugin = {\n name: \"rage-clicks\",\n\n setup() {\n const clicks: Click[] = [];\n\n const onClick = (event: MouseEvent) => {\n const now = Date.now();\n clicks.push({\n x: event.clientX,\n y: event.clientY,\n ts: now,\n target: event.target instanceof Element ? event.target : null,\n });\n\n while (clicks.length > 0 && now - (clicks[0]?.ts ?? 0) > TIME_WINDOW_MS) {\n clicks.shift();\n }\n\n if (clicks.length < CLICK_THRESHOLD) {\n return;\n }\n\n const anchor = clicks[0];\n if (!anchor) {\n return;\n }\n const clustered = clicks.filter(\n (c) => distance(anchor, c) <= PROXIMITY_PX\n );\n if (clustered.length < CLICK_THRESHOLD) {\n return;\n }\n\n const last = clustered.at(-1);\n if (!last) {\n return;\n }\n\n const span = trace.getTracer(TRACER_NAME).startSpan(\"rage_click\", {\n attributes: {\n \"ui.event_type\": \"rage_click\",\n \"ui.rage_click.count\": clustered.length,\n \"ui.rage_click.time_window_ms\": last.ts - anchor.ts,\n \"ui.rage_click.selector\": selectorFor(last.target),\n \"ui.rage_click.text\":\n last.target?.textContent?.trim().slice(0, 120) ?? \"\",\n \"ui.rage_click.x\": last.x,\n \"ui.rage_click.y\": last.y,\n },\n });\n span.end();\n\n clicks.length = 0;\n };\n\n document.addEventListener(\"click\", onClick, { capture: true });\n\n return () => {\n document.removeEventListener(\"click\", onClick, { capture: true });\n };\n },\n};\n\nexport default rageClicksPlugin;\n"],"mappings":"sCAgBA,SAAS,SAAS,EAAU,EAAkB,CAC5C,OAAO,KAAK,MAAM,EAAE,EAAI,EAAE,IAAM,GAAK,EAAE,EAAI,EAAE,IAAM,CAAC,CACtD,CAEA,SAAS,YAAY,GAA4B,CAC/C,GAAI,CAAC,GACH,MAAO,UAET,GAAI,GAAG,GACL,MAAO,IAAI,GAAG,KAEhB,IAAM,QAAU,CAAC,GAAG,GAAG,SAAS,EAAE,MAAM,EAAG,CAAC,EAAE,KAAK,GAAG,EAChD,IAAM,GAAG,QAAQ,YAAY,EACnC,OAAO,QAAU,GAAG,IAAI,GAAG,UAAY,GACzC,CAEA,MAAa,iBAA2B,CACtC,KAAM,cAEN,OAAQ,CACN,IAAM,OAAkB,CAAC,EAEnB,QAAW,OAAsB,CACrC,IAAM,IAAM,KAAK,IAAI,EAQrB,IAPA,OAAO,KAAK,CACV,EAAG,MAAM,QACT,EAAG,MAAM,QACT,GAAI,IACJ,OAAQ,MAAM,kBAAkB,QAAU,MAAM,OAAS,IAC3D,CAAC,EAEM,OAAO,OAAS,GAAK,KAAO,OAAO,IAAI,IAAM,GAAK,KACvD,OAAO,MAAM,EAGf,GAAI,OAAO,OAAS,EAClB,OAGF,IAAM,OAAS,OAAO,GACtB,GAAI,CAAC,OACH,OAEF,IAAM,UAAY,OAAO,OACtB,GAAM,SAAS,OAAQ,CAAC,GAAK,EAChC,EACA,GAAI,UAAU,OAAS,EACrB,OAGF,IAAM,KAAO,UAAU,GAAG,EAAE,EACvB,OAgBL,MAZmB,UAAU,8BAAW,EAAE,UAAU,aAAc,CAChE,WAAY,CACV,gBAAiB,aACjB,sBAAuB,UAAU,OACjC,+BAAgC,KAAK,GAAK,OAAO,GACjD,yBAA0B,YAAY,KAAK,MAAM,EACjD,qBACE,KAAK,QAAQ,aAAa,KAAK,EAAE,MAAM,EAAG,GAAG,GAAK,GACpD,kBAAmB,KAAK,EACxB,kBAAmB,KAAK,CAC1B,CACF,CACG,EAAE,IAAI,EAET,OAAO,OAAS,EAClB,EAIA,OAFA,SAAS,iBAAiB,QAAS,QAAS,CAAE,QAAS,EAAK,CAAC,MAEhD,CACX,SAAS,oBAAoB,QAAS,QAAS,CAAE,QAAS,EAAK,CAAC,CAClE,CACF,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"buffer.d.mts","names":[],"sources":["../../../src/plugins/replay/buffer.ts"],"mappings":";;;UAEiB,YAAA;EACf,KAAA,IAAS,mBAAmB;EAC5B,IAAA,CAAK,KAAA;AAAA;AAAA,iBAGS,kBAAA,CAAmB,IAAA;EACjC,WAAA;EACA,cAAA;AAAA,IACE,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"buffer.mjs","names":[],"sources":["../../../src/plugins/replay/buffer.ts"],"sourcesContent":["import { type BufferedReplayEvent, utf8ByteLength } from \"./chunking.js\";\n\nexport interface ReplayBuffer {\n drain(): BufferedReplayEvent[];\n push(event: unknown): void;\n}\n\nexport function createReplayBuffer(opts: {\n onSoftLimit: () => void;\n softLimitBytes: number;\n}): ReplayBuffer {\n let entries: BufferedReplayEvent[] = [];\n let totalBytes = 0;\n\n return {\n push(event) {\n const json = JSON.stringify(event);\n const bytes = utf8ByteLength(json);\n entries.push({ json, ts: Date.now(), bytes });\n totalBytes += bytes;\n if (totalBytes >= opts.softLimitBytes) {\n opts.onSoftLimit();\n }\n },\n\n drain() {\n const drained = entries;\n entries = [];\n totalBytes = 0;\n return drained;\n },\n };\n}\n"],"mappings":"2CAOA,SAAgB,mBAAmB,KAGlB,CACf,IAAI,QAAiC,CAAC,EAClC,WAAa,EAEjB,MAAO,CACL,KAAK,MAAO,CACV,IAAM,KAAO,KAAK,UAAU,KAAK,EAC3B,MAAQ,eAAe,IAAI,EACjC,QAAQ,KAAK,CAAE,KAAM,GAAI,KAAK,IAAI,EAAG,KAAM,CAAC,EAC5C,YAAc,MACV,YAAc,KAAK,gBACrB,KAAK,YAAY,CAErB,EAEA,OAAQ,CACN,IAAM,QAAU,QAGhB,MAFA,SAAU,CAAC,EACX,WAAa,EACN,OACT,CACF,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"chunking.d.mts","names":[],"sources":["../../../src/plugins/replay/chunking.ts"],"mappings":";UAOiB,mBAAA;EACf,KAAA;EACA,IAAA;EACA,EAAA;AAAA;AAAA,UAGe,kBAAA;EACf,QAAA;EACA,UAAA;EACA,OAAA;EACA,MAAA;AAAA;AAAA,UAGe,uBAAA;EACf,MAAA,EAAQ,mBAAmB;EAC3B,iBAAA;AAAA;AAAA,iBAGc,oBAAA,CACd,KAAA,EAAO,mBAAA,KACN,kBAAkB;AAAA,iBAaL,iBAAA,CACd,QAAA,EAAU,mBAAA,KACT,uBAAuB;AAAA,iBAmCV,cAAA,CAAe,KAAa"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"chunking.mjs","names":[],"sources":["../../../src/plugins/replay/chunking.ts"],"sourcesContent":["const UPLOAD_LIMIT_BYTES = 8 * 1024 * 1024;\nconst TARGET_CHUNK_BYTES = 6 * 1024 * 1024;\nconst JSON_ARRAY_OVERHEAD_BYTES = 2;\nconst JSON_COMMA_BYTES = 1;\n\nconst textEncoder = new TextEncoder();\n\nexport interface BufferedReplayEvent {\n bytes: number;\n json: string;\n ts: number;\n}\n\nexport interface ReplayChunkPayload {\n bodyJson: string;\n eventCount: number;\n firstTs: number | null;\n lastTs: number | null;\n}\n\nexport interface SplitReplayEventsResult {\n chunks: BufferedReplayEvent[][];\n droppedEventBytes: number[];\n}\n\nexport function toReplayChunkPayload(\n group: BufferedReplayEvent[]\n): ReplayChunkPayload {\n const parts: string[] = [];\n for (const entry of group) {\n parts.push(entry.json);\n }\n return {\n bodyJson: `[${parts.join(\",\")}]`,\n eventCount: group.length,\n firstTs: group[0]?.ts ?? null,\n lastTs: group.at(-1)?.ts ?? null,\n };\n}\n\nexport function splitReplayEvents(\n buffered: BufferedReplayEvent[]\n): SplitReplayEventsResult {\n const chunks: BufferedReplayEvent[][] = [];\n const droppedEventBytes: number[] = [];\n let current: BufferedReplayEvent[] = [];\n let currentBytes = JSON_ARRAY_OVERHEAD_BYTES;\n\n for (const entry of buffered) {\n const singleEventBodyBytes = JSON_ARRAY_OVERHEAD_BYTES + entry.bytes;\n\n if (singleEventBodyBytes > UPLOAD_LIMIT_BYTES) {\n droppedEventBytes.push(singleEventBodyBytes);\n continue;\n }\n\n const commaBytes = current.length === 0 ? 0 : JSON_COMMA_BYTES;\n if (\n current.length > 0 &&\n currentBytes + commaBytes + entry.bytes > TARGET_CHUNK_BYTES\n ) {\n chunks.push(current);\n current = [];\n currentBytes = JSON_ARRAY_OVERHEAD_BYTES;\n }\n\n currentBytes += (current.length === 0 ? 0 : JSON_COMMA_BYTES) + entry.bytes;\n current.push(entry);\n }\n\n if (current.length > 0) {\n chunks.push(current);\n }\n\n return { chunks, droppedEventBytes };\n}\n\nexport function utf8ByteLength(value: string): number {\n return textEncoder.encode(value).length;\n}\n"],"mappings":"AAAA,MAKM,YAAc,IAAI,YAoBxB,SAAgB,qBACd,MACoB,CACpB,IAAM,MAAkB,CAAC,EACzB,IAAK,IAAM,SAAS,MAClB,MAAM,KAAK,MAAM,IAAI,EAEvB,MAAO,CACL,SAAU,IAAI,MAAM,KAAK,GAAG,EAAE,GAC9B,WAAY,MAAM,OAClB,QAAS,MAAM,IAAI,IAAM,KACzB,OAAQ,MAAM,GAAG,EAAE,GAAG,IAAM,IAC9B,CACF,CAEA,SAAgB,kBACd,SACyB,CACzB,IAAM,OAAkC,CAAC,EACnC,kBAA8B,CAAC,EACjC,QAAiC,CAAC,EAClC,aAAe,EAEnB,IAAK,IAAM,SAAS,SAAU,CAC5B,IAAM,qBAAuB,EAA4B,MAAM,MAE/D,GAAI,qBAAuB,QAAoB,CAC7C,kBAAkB,KAAK,oBAAoB,EAC3C,QACF,CAEA,IAAM,WAAa,QAAQ,SAAW,EAAI,EAAI,EAE5C,QAAQ,OAAS,GACjB,aAAe,WAAa,MAAM,MAAQ,UAE1C,OAAO,KAAK,OAAO,EACnB,QAAU,CAAC,EACX,aAAe,GAGjB,eAAiB,QAAQ,SAAW,EAAI,EAAI,GAAoB,MAAM,MACtE,QAAQ,KAAK,KAAK,CACpB,CAMA,OAJI,QAAQ,OAAS,GACnB,OAAO,KAAK,OAAO,EAGd,CAAE,OAAQ,iBAAkB,CACrC,CAEA,SAAgB,eAAe,MAAuB,CACpD,OAAO,YAAY,OAAO,KAAK,EAAE,MACnC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"compressor-host.d.mts","names":[],"sources":["../../../src/plugins/replay/compressor-host.ts"],"mappings":";;UAKiB,UAAA;EAAA,SACN,OAAA;EAAA,SACA,IAAA,IACH,KAAA,EAAO,UAAA,KAAe,OAAA,CAAQ,UAAA,CAAW,WAAA;AAAA;AAAA,iBAMjC,gBAAA,IAAoB,UAAU"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"compressor-host.mjs","names":[],"sources":["../../../src/plugins/replay/compressor-host.ts"],"sourcesContent":["import { createLogger } from \"../../util/log.js\";\n\nconst log = createLogger(\"replay/compressor\");\n\n/** `gzip` is `null` when compression is unavailable — callers must POST the chunk uncompressed. */\nexport interface Compressor {\n readonly dispose: () => void;\n readonly gzip:\n | ((bytes: Uint8Array) => Promise<Uint8Array<ArrayBuffer>>)\n | null;\n}\n\nconst FAIL_OPEN: Compressor = { gzip: null, dispose: (): void => undefined };\n\nexport function createCompressor(): Compressor {\n if (typeof CompressionStream === \"undefined\") {\n log.warn(\n \"CompressionStream unavailable; replay chunks will POST uncompressed\"\n );\n return FAIL_OPEN;\n }\n\n const gzip = async (bytes: Uint8Array): Promise<Uint8Array<ArrayBuffer>> => {\n const cs = new CompressionStream(\"gzip\");\n const compressed = new Response(cs.readable).arrayBuffer();\n const writer = cs.writable.getWriter();\n await writer.write(new Uint8Array(bytes));\n await writer.close();\n return new Uint8Array(await compressed);\n };\n\n return { gzip, dispose: (): void => undefined };\n}\n"],"mappings":"6CAEA,MAAM,IAAM,aAAa,mBAAmB,EAUtC,UAAwB,CAAE,KAAM,KAAM,YAAqB,IAAA,EAAU,EAE3E,SAAgB,kBAA+B,CAiB7C,OAhBI,OAAO,kBAAsB,KAC/B,IAAI,KACF,qEACF,EACO,WAYF,CAAE,UATW,QAAwD,CAC1E,IAAM,GAAK,IAAI,kBAAkB,MAAM,EACjC,WAAa,IAAI,SAAS,GAAG,QAAQ,EAAE,YAAY,EACnD,OAAS,GAAG,SAAS,UAAU,EAGrC,OAFA,MAAM,OAAO,MAAM,IAAI,WAAW,KAAK,CAAC,EACxC,MAAM,OAAO,MAAM,EACZ,IAAI,WAAW,MAAM,UAAU,CACxC,EAEe,YAAqB,IAAA,EAAU,CAChD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"delivery.d.mts","names":[],"sources":["../../../src/plugins/replay/delivery.ts"],"mappings":";;;;;UAoCiB,YAAA;EACf,WAAA,EAAa,MAAA;EACb,UAAA,EAAY,UAAA;EACZ,GAAA,EAAK,aAAA;EACL,OAAA,EAAS,kBAAA;EACT,eAAA;EACA,aAAA;AAAA;AAAA,iBAGoB,YAAA;EACpB,GAAA;EACA,aAAA;EACA,WAAA;EACA,UAAA;EACA,OAAA;EACA;AAAA,GACC,YAAA,GAAe,OAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"delivery.mjs","names":[],"sources":["../../../src/plugins/replay/delivery.ts"],"sourcesContent":["import { trace } from \"@opentelemetry/api\";\n\nimport { appendPathBeforeQuery } from \"../../internal/config.js\";\nimport { createLogger } from \"../../util/log.js\";\nimport type { PluginContext } from \"../lib/types.js\";\nimport type { ReplayChunkPayload } from \"./chunking.js\";\nimport type { Compressor } from \"./compressor-host.js\";\n\nconst log = createLogger(\"replay\");\n\nconst UPLOAD_PATH_PREFIX = \"/v2/replay/upload/\";\nconst TRACER_NAME = \"@interfere/react/replay\";\n\nconst textEncoder = new TextEncoder();\n\ninterface PreparedBody {\n body: Uint8Array<ArrayBuffer>;\n encoding: \"gzip\" | null;\n}\n\nasync function prepareBody(\n compressor: Compressor,\n rawBytes: Uint8Array<ArrayBuffer>\n): Promise<PreparedBody> {\n if (!compressor.gzip) {\n return { body: rawBytes, encoding: null };\n }\n try {\n const compressed = await compressor.gzip(rawBytes);\n return { body: compressed, encoding: \"gzip\" };\n } catch (error) {\n log.warn(\"gzip failed for chunk; sending uncompressed: %o\", error);\n return { body: rawBytes, encoding: null };\n }\n}\n\nexport interface DeliverInput {\n authHeaders: Record<string, string>;\n compressor: Compressor;\n ctx: PluginContext;\n payload: ReplayChunkPayload;\n skipCompression: boolean;\n uploadBaseUrl: string;\n}\n\nexport async function deliverChunk({\n ctx,\n uploadBaseUrl,\n authHeaders,\n compressor,\n payload,\n skipCompression,\n}: DeliverInput): Promise<void> {\n const sessionId = ctx.getSessionId();\n if (!sessionId) {\n return;\n }\n\n const rawBytes = textEncoder.encode(payload.bodyJson);\n const uncompressedBytes = rawBytes.byteLength;\n\n // Worker replies never arrive during unload; the skip path must not await.\n const prepared: PreparedBody = skipCompression\n ? { body: rawBytes, encoding: null }\n : await prepareBody(compressor, rawBytes);\n\n const headers: Record<string, string> = {\n ...authHeaders,\n \"content-type\": \"application/json\",\n ...(prepared.encoding === \"gzip\" ? { \"content-encoding\": \"gzip\" } : {}),\n };\n\n const res = await fetch(\n appendPathBeforeQuery(\n uploadBaseUrl,\n `${UPLOAD_PATH_PREFIX}${encodeURIComponent(sessionId)}`\n ),\n {\n method: \"POST\",\n headers,\n body: prepared.body,\n }\n );\n if (!res.ok) {\n log.warn(\"replay chunk upload rejected (%d)\", res.status);\n return;\n }\n\n const { objectKey } = (await res.json()) as { objectKey: string };\n const span = trace.getTracer(TRACER_NAME).startSpan(\"replay.chunk\", {\n attributes: {\n \"replay.chunk.uri\": objectKey,\n \"replay.chunk.size_bytes\": uncompressedBytes,\n ...(prepared.encoding === \"gzip\"\n ? { \"replay.chunk.compressed_bytes\": prepared.body.byteLength }\n : {}),\n \"replay.chunk.event_count\": payload.eventCount,\n ...(payload.firstTs === null\n ? {}\n : { \"replay.chunk.first_event_ts\": payload.firstTs }),\n ...(payload.lastTs === null\n ? {}\n : { \"replay.chunk.last_event_ts\": payload.lastTs }),\n },\n });\n span.end();\n}\n"],"mappings":"gJAQA,MAAM,IAAM,aAAa,QAAQ,EAK3B,YAAc,IAAI,YAOxB,eAAe,YACb,WACA,SACuB,CACvB,GAAI,CAAC,WAAW,KACd,MAAO,CAAE,KAAM,SAAU,SAAU,IAAK,EAE1C,GAAI,CAEF,MAAO,CAAE,KAAM,MADU,WAAW,KAAK,QAAQ,EACtB,SAAU,MAAO,CAC9C,OAAS,MAAO,CAEd,OADA,IAAI,KAAK,kDAAmD,KAAK,EAC1D,CAAE,KAAM,SAAU,SAAU,IAAK,CAC1C,CACF,CAWA,eAAsB,aAAa,CACjC,IACA,cACA,YACA,WACA,QACA,iBAC8B,CAC9B,IAAM,UAAY,IAAI,aAAa,EACnC,GAAI,CAAC,UACH,OAGF,IAAM,SAAW,YAAY,OAAO,QAAQ,QAAQ,EAC9C,kBAAoB,SAAS,WAG7B,SAAyB,gBAC3B,CAAE,KAAM,SAAU,SAAU,IAAK,EACjC,MAAM,YAAY,WAAY,QAAQ,EAEpC,QAAkC,CACtC,GAAG,YACH,eAAgB,mBAChB,GAAI,SAAS,WAAa,OAAS,CAAE,mBAAoB,MAAO,EAAI,CAAC,CACvE,EAEM,IAAM,MAAM,MAChB,sBACE,cACA,qBAAwB,mBAAmB,SAAS,GACtD,EACA,CACE,OAAQ,OACR,QACA,KAAM,SAAS,IACjB,CACF,EACA,GAAI,CAAC,IAAI,GAAI,CACX,IAAI,KAAK,oCAAqC,IAAI,MAAM,EACxD,MACF,CAEA,GAAM,CAAE,WAAe,MAAM,IAAI,KAAK,EAiBtC,MAhBmB,UAAU,yBAAW,EAAE,UAAU,eAAgB,CAClE,WAAY,CACV,mBAAoB,UACpB,0BAA2B,kBAC3B,GAAI,SAAS,WAAa,OACtB,CAAE,gCAAiC,SAAS,KAAK,UAAW,EAC5D,CAAC,EACL,2BAA4B,QAAQ,WACpC,GAAI,QAAQ,UAAY,KACpB,CAAC,EACD,CAAE,8BAA+B,QAAQ,OAAQ,EACrD,GAAI,QAAQ,SAAW,KACnB,CAAC,EACD,CAAE,6BAA8B,QAAQ,MAAO,CACrD,CACF,CACG,EAAE,IAAI,CACX"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/plugins/replay/index.ts"],"mappings":";;;cAea,YAAA,EAAc,MAwF1B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/replay/index.ts"],"sourcesContent":["import { resolveTargets } from \"../../internal/config.js\";\nimport { onPageHidden, onPageShown } from \"../../internal/page-lifecycle.js\";\nimport { createLogger } from \"../../util/log.js\";\nimport type { Plugin } from \"../lib/types.js\";\nimport { createReplayBuffer } from \"./buffer.js\";\nimport { splitReplayEvents, toReplayChunkPayload } from \"./chunking.js\";\nimport { createCompressor } from \"./compressor-host.js\";\nimport { deliverChunk } from \"./delivery.js\";\nimport { type ReplayRecorder, startReplayRecorder } from \"./recorder.js\";\n\nconst log = createLogger(\"replay\");\n\nconst FLUSH_INTERVAL_MS = 10_000;\nconst SOFT_FLUSH_BYTES = 4 * 1024 * 1024;\n\nexport const replayPlugin: Plugin = {\n name: \"replay\",\n\n setup(ctx) {\n const targets = resolveTargets();\n const uploadBaseUrl = targets.collectorBaseUrl;\n const authHeaders = Object.fromEntries(targets.ingest.headers.entries());\n\n let recorder: ReplayRecorder | null = null;\n let flushTimer: ReturnType<typeof setInterval> | null = null;\n let unsubscribeHidden: (() => void) | null = null;\n let unsubscribeShown: (() => void) | null = null;\n let isHidden = false;\n const compressor = createCompressor();\n\n function flush(opts?: { skipCompression: boolean }): void {\n const drained = buffer.drain();\n if (drained.length === 0) {\n return;\n }\n const { chunks, droppedEventBytes } = splitReplayEvents(drained);\n for (const bytes of droppedEventBytes) {\n log.warn(\n \"dropping replay event of ~%d bytes: too large to upload\",\n bytes\n );\n }\n for (const group of chunks) {\n deliverChunk({\n ctx,\n uploadBaseUrl,\n authHeaders,\n compressor,\n payload: toReplayChunkPayload(group),\n skipCompression: opts?.skipCompression ?? false,\n }).catch((error: unknown) =>\n log.warn(\"replay chunk dropped: %o\", error)\n );\n }\n }\n\n const buffer = createReplayBuffer({\n softLimitBytes: SOFT_FLUSH_BYTES,\n onSoftLimit: flush,\n });\n\n const onHide = () => {\n isHidden = true;\n flush({ skipCompression: true });\n };\n\n const onShow = () => {\n if (!isHidden) {\n return;\n }\n isHidden = false;\n recorder?.takeFullSnapshot();\n };\n\n const init = async () => {\n recorder = await startReplayRecorder((event) => {\n if (isHidden) {\n return;\n }\n buffer.push(event);\n });\n if (!recorder) {\n return;\n }\n flushTimer = setInterval(flush, FLUSH_INTERVAL_MS);\n unsubscribeHidden = onPageHidden(onHide);\n unsubscribeShown = onPageShown(onShow);\n log.debug(\"recording started\");\n };\n\n init();\n\n return () => {\n flush();\n recorder?.stop();\n if (flushTimer) {\n clearInterval(flushTimer);\n }\n unsubscribeHidden?.();\n unsubscribeShown?.();\n compressor.dispose();\n };\n },\n};\n\nexport default replayPlugin;\n"],"mappings":"waAUA,MAAM,IAAM,aAAa,QAAQ,EAKpB,aAAuB,CAClC,KAAM,SAEN,MAAM,IAAK,CACT,IAAM,QAAU,eAAe,EACzB,cAAgB,QAAQ,iBACxB,YAAc,OAAO,YAAY,QAAQ,OAAO,QAAQ,QAAQ,CAAC,EAEnE,SAAkC,KAClC,WAAoD,KACpD,kBAAyC,KACzC,iBAAwC,KACxC,SAAW,GACT,WAAa,iBAAiB,EAEpC,SAAS,MAAM,KAA2C,CACxD,IAAM,QAAU,OAAO,MAAM,EAC7B,GAAI,QAAQ,SAAW,EACrB,OAEF,GAAM,CAAE,OAAQ,mBAAsB,kBAAkB,OAAO,EAC/D,IAAK,IAAM,SAAS,kBAClB,IAAI,KACF,0DACA,KACF,EAEF,IAAK,IAAM,SAAS,OAClB,aAAa,CACX,IACA,cACA,YACA,WACA,QAAS,qBAAqB,KAAK,EACnC,gBAAiB,MAAM,iBAAmB,EAC5C,CAAC,EAAE,MAAO,OACR,IAAI,KAAK,2BAA4B,KAAK,CAC5C,CAEJ,CAEA,IAAM,OAAS,mBAAmB,CAChC,eAAgB,QAChB,YAAa,KACf,CAAC,EAEK,WAAe,CACnB,SAAW,GACX,MAAM,CAAE,gBAAiB,EAAK,CAAC,CACjC,EAEM,WAAe,CACd,WAGL,SAAW,GACX,UAAU,iBAAiB,EAC7B,EAoBA,OAFA,SAhByB,CACvB,SAAW,MAAM,oBAAqB,OAAU,CAC1C,UAGJ,OAAO,KAAK,KAAK,CACnB,CAAC,EACI,WAGL,WAAa,YAAY,MAAO,GAAiB,EACjD,kBAAoB,aAAa,MAAM,EACvC,iBAAmB,YAAY,MAAM,EACrC,IAAI,MAAM,mBAAmB,EAC/B,GAEK,MAEQ,CACX,MAAM,EACN,UAAU,KAAK,EACX,YACF,cAAc,UAAU,EAE1B,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,QAAQ,CACrB,CACF,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"recorder.d.mts","names":[],"sources":["../../../src/plugins/replay/recorder.ts"],"mappings":";UAQiB,cAAA;EACf,IAAA;EACA,gBAAgB;AAAA;AAAA,iBAGI,mBAAA,CACpB,OAAA,GAAU,KAAA,qBACT,OAAO,CAAC,cAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"recorder.mjs","names":[],"sources":["../../../src/plugins/replay/recorder.ts"],"sourcesContent":["import { createLogger } from \"../../util/log.js\";\n\nconst log = createLogger(\"replay\");\n\nconst MOUSEMOVE_SAMPLE_MS = 50;\nconst SCROLL_SAMPLE_MS = 150;\nconst MEDIA_SAMPLE_MS = 800;\n\nexport interface ReplayRecorder {\n stop(): void;\n takeFullSnapshot(): void;\n}\n\nexport async function startReplayRecorder(\n onEvent: (event: unknown) => void\n): Promise<ReplayRecorder | null> {\n try {\n const rrweb = await import(\"rrweb\");\n const stop =\n rrweb.record({\n emit: onEvent,\n recordAfter: \"load\",\n inlineStylesheet: true,\n slimDOMOptions: \"all\",\n sampling: {\n mousemove: MOUSEMOVE_SAMPLE_MS,\n scroll: SCROLL_SAMPLE_MS,\n media: MEDIA_SAMPLE_MS,\n },\n }) ?? null;\n\n return {\n stop: () => {\n stop?.();\n },\n takeFullSnapshot: () => {\n try {\n rrweb.record.takeFullSnapshot?.(true);\n } catch (error) {\n log.warn(\"takeFullSnapshot failed: %o\", error);\n }\n },\n };\n } catch {\n log.error(\"rrweb failed to load, replay disabled\");\n return null;\n }\n}\n"],"mappings":"6CAEA,MAAM,IAAM,aAAa,QAAQ,EAWjC,eAAsB,oBACpB,QACgC,CAChC,GAAI,CACF,IAAM,MAAQ,MAAM,OAAO,SACrB,KACJ,MAAM,OAAO,CACX,KAAM,QACN,YAAa,OACb,iBAAkB,GAClB,eAAgB,MAChB,SAAU,CACR,UAAW,GACX,OAAQ,IACR,MAAO,GACT,CACF,CAAC,GAAK,KAER,MAAO,CACL,SAAY,CACV,OAAO,CACT,EACA,qBAAwB,CACtB,GAAI,CACF,MAAM,OAAO,mBAAmB,EAAI,CACtC,OAAS,MAAO,CACd,IAAI,KAAK,8BAA+B,KAAK,CAC/C,CACF,CACF,CACF,MAAQ,CAEN,OADA,IAAI,MAAM,uCAAuC,EAC1C,IACT,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"provider.d.mts","names":[],"sources":["../src/provider.tsx"],"mappings":";;;;;;UAmBU,sBAAA,SAA+B,iBAAA;EACvC,OAAA,GAAU,YAAA;EADF;;;;;;;;;;;;;;;AAyBM;EAPd,aAAA;EAU+B;;;;;;EAH/B,MAAA,EAAQ,MAAA;AAAA;AAAA,iBAGM,iBAAA;EACd,MAAA;EACA,QAAA;EACA,OAAA;EACA;AAAA,GACC,sBAAA,GAAyB,SAAA;AAAA,iBAwBZ,YAAA,IAAgB,qBAAqB;AAAA,iBAQrC,UAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"provider.mjs","names":[],"sources":["../src/provider.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ConsentState } from \"@interfere/types/sdk/plugins/manifest\";\n\nimport {\n type PropsWithChildren,\n type ReactNode,\n useContext,\n useEffect,\n} from \"react\";\n\nimport { CaptureBoundary } from \"./internal/capture-boundary.js\";\nimport type { Kernel } from \"./internal/kernel.js\";\nimport type { InterfereContextValue } from \"./internal/react-context.js\";\nimport {\n InterfereContext,\n NULL_CONTEXT_VALUE,\n} from \"./internal/react-context.js\";\n\ninterface InterfereProviderProps extends PropsWithChildren {\n consent?: ConsentState | undefined;\n /**\n * Auto-install an internal capture boundary around `children` so\n * render-phase React errors are reported even when the host app has no\n * `ErrorBoundary` / `error.tsx` of its own.\n *\n * The internal boundary is transparent: it captures the error and then\n * re-throws so upstream boundaries (the customer's own, Next.js's\n * `error.tsx` / `global-error.tsx`, or React's default unmount) keep full\n * control of what the user sees. Safe to leave enabled.\n *\n * Pass `false` only if you explicitly don't want automatic capture of\n * render-phase errors — for example, if you've already wired\n * {@link reactErrorHandler} into `createRoot()`.\n *\n * @default true\n */\n errorBoundary?: boolean;\n /**\n * The active kernel from `init()`. Pass `null` (or omit on the server)\n * when the SDK isn't initialized — useful during SSR/SSG where `init()`\n * runs client-side only. The provider mounts with null-safe accessors\n * and switches to the real kernel once it's available.\n */\n kernel: Kernel | null;\n}\n\nexport function InterfereProvider({\n kernel,\n children,\n consent,\n errorBoundary = true,\n}: InterfereProviderProps): ReactNode {\n useEffect(() => {\n kernel?.syncConsent(consent);\n }, [kernel, consent]);\n\n const value: InterfereContextValue = kernel\n ? {\n consent: kernel.consent,\n device: kernel.device,\n identity: kernel.identity,\n kernel,\n session: kernel.session,\n }\n : NULL_CONTEXT_VALUE;\n\n const body = errorBoundary ? (\n <CaptureBoundary>{children}</CaptureBoundary>\n ) : (\n children\n );\n\n return <InterfereContext value={value}>{body}</InterfereContext>;\n}\n\nexport function useInterfere(): InterfereContextValue {\n const ctx = useContext(InterfereContext);\n if (!ctx) {\n throw new Error(\"useInterfere must be used within <InterfereProvider>\");\n }\n return ctx;\n}\n\nexport function useSession(): string | null {\n return useInterfere().session.getId();\n}\n\nexport type { InterfereContextValue } from \"./internal/react-context.js\";\n// biome-ignore lint/performance/noBarrelFile: Re-export the context handle alongside the provider so consumers (custom error boundaries, the `@interfere/next` wrapper) can import everything from one entry.\nexport { InterfereContext } from \"./internal/react-context.js\";\n"],"mappings":"mOA+CA,SAAgB,kBAAkB,CAChC,OACA,SACA,QACA,cAAgB,IACoB,CAqBpC,OApBA,cAAgB,CACd,QAAQ,YAAY,OAAO,CAC7B,EAAG,CAAC,OAAQ,OAAO,CAAC,EAkBb,IAAC,iBAAD,CAAkB,MAhBY,OACjC,CACE,QAAS,OAAO,QAChB,OAAQ,OAAO,OACf,SAAU,OAAO,SACjB,OACA,QAAS,OAAO,OAClB,EACA,4BAES,cACX,IAAC,gBAAD,CAAkB,QAA0B,CAAA,EAE5C,QAG6D,CAAA,CACjE,CAEA,SAAgB,cAAsC,CACpD,IAAM,IAAM,WAAW,gBAAgB,EACvC,GAAI,CAAC,IACH,MAAU,MAAM,sDAAsD,EAExE,OAAO,GACT,CAEA,SAAgB,YAA4B,CAC1C,OAAO,aAAa,EAAE,QAAQ,MAAM,CACtC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"react-error-handler.d.mts","names":[],"sources":["../src/react-error-handler.ts"],"mappings":";;;;;AAKmD;;;;AAUnC;UADN,kBAAA;EACR,cAAc;AAAA;AAAA,KAGX,iBAAA,IAAqB,KAAA,WAAgB,IAAA,EAAM,kBAAkB;AAAA,UAEjD,0BAAA;EAF+B;;;AAAkB;AAElE;EAME,aAAA,GAAgB,iBAAA;;;;;;EAMhB,kBAAA,GAAqB,iBAAA;EANrB;;;;;EAYA,eAAA,GAAkB,iBAAA;AAAA;AAAA,UAGH,wBAAA,SAAiC,0BAAA;EAAjC;;;;;;;;;EAUf,MAAA,GAAS,MAAA,UAAgB,MAAA;AAAA;AAAA,UAGjB,sBAAA;EACR,aAAA,EAAe,iBAAA;EACf,kBAAA,EAAoB,iBAAA;EACpB,eAAA,EAAiB,iBAAA;AAAA;;;;;;;;;;;;;;AAAiB;AAqCpC;;;;;;;;AAEyB;;;iBAFT,iBAAA,CACd,OAAA,GAAS,wBAAA,GACR,sBAAsB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"react-error-handler.mjs","names":[],"sources":["../src/react-error-handler.ts"],"sourcesContent":["\"use client\";\n\nimport { MECHANISM_TYPE } from \"@interfere/types/sdk/errors\";\n\nimport { captureReactError } from \"./internal/capture.js\";\nimport type { Kernel } from \"./internal/kernel.js\";\n\n/**\n * Matches the shape React 19 passes to `onCaughtError`, `onUncaughtError`,\n * and `onRecoverableError` on `createRoot()` / `hydrateRoot()`.\n *\n * We intentionally keep this permissive — React has iterated on the exact\n * fields of `ErrorInfo` across 19.x minors and we only need `componentStack`.\n */\ninterface ReactRootErrorInfo {\n componentStack?: string | null | undefined;\n}\n\ntype RootErrorCallback = (error: unknown, info: ReactRootErrorInfo) => void;\n\nexport interface ReactErrorHandlerCallbacks {\n /**\n * Called when a React error boundary caught and handled an error. Interfere\n * reports it with `mechanism.handled: true`. Your callback runs after the\n * capture.\n */\n onCaughtError?: RootErrorCallback;\n /**\n * Called when concurrent React auto-recovered from an error by retrying\n * the render. Reported with `mechanism.synthetic: true` so the agent can\n * deprioritize — these often aren't user-facing bugs.\n */\n onRecoverableError?: RootErrorCallback;\n /**\n * Called when a render-phase error propagated past every boundary and\n * React unmounted (or fell back to its default UI). Reported with\n * `mechanism.handled: false` — this is the \"real\" uncaught case.\n */\n onUncaughtError?: RootErrorCallback;\n}\n\nexport interface ReactErrorHandlerOptions extends ReactErrorHandlerCallbacks {\n /**\n * The kernel to capture errors against. Pass either the kernel directly\n * or a getter — framework wrappers expose `getKernelOrNull()` for the\n * latter pattern, which lets you wire the handler before `init()`\n * resolves. Pass `getKernelOrNull` (not `getKernel`) — the throwing\n * variant breaks this contract since the handler runs in error-recovery\n * paths where rethrowing isn't useful. Capture is silently skipped when\n * no kernel is available.\n */\n kernel?: Kernel | (() => Kernel | null) | null;\n}\n\ninterface ReactRootErrorHandlers {\n onCaughtError: RootErrorCallback;\n onRecoverableError: RootErrorCallback;\n onUncaughtError: RootErrorCallback;\n}\n\nfunction resolveKernel(\n source: ReactErrorHandlerOptions[\"kernel\"]\n): Kernel | null {\n if (!source) {\n return null;\n }\n return typeof source === \"function\" ? source() : source;\n}\n\n/**\n * Creates the three error callbacks React 19's `createRoot()` / `hydrateRoot()`\n * accept, wired into Interfere's capture pipeline.\n *\n * ```ts\n * import { createRoot } from \"react-dom/client\";\n * import { getKernelOrNull } from \"@interfere/next\";\n * import { reactErrorHandler } from \"@interfere/react/react-error-handler\";\n *\n * createRoot(\n * document.getElementById(\"root\")!,\n * reactErrorHandler({ kernel: getKernelOrNull })\n * ).render(<App />);\n * ```\n *\n * Pass your own callbacks to observe errors without replacing the capture\n * behaviour — user callbacks run after Interfere has captured:\n *\n * ```ts\n * reactErrorHandler({\n * kernel: getKernelOrNull,\n * onUncaughtError: (err) => myLogger.fatal(err),\n * });\n * ```\n */\nexport function reactErrorHandler(\n options: ReactErrorHandlerOptions = {}\n): ReactRootErrorHandlers {\n const { kernel: kernelSource, ...callbacks } = options;\n return {\n onCaughtError(error, info) {\n if (error instanceof Error) {\n captureReactError(\n resolveKernel(kernelSource),\n error,\n info.componentStack,\n {\n type: MECHANISM_TYPE.react.caughtError,\n handled: true,\n }\n );\n }\n callbacks.onCaughtError?.(error, info);\n },\n onUncaughtError(error, info) {\n if (error instanceof Error) {\n captureReactError(\n resolveKernel(kernelSource),\n error,\n info.componentStack,\n {\n type: MECHANISM_TYPE.react.uncaughtError,\n handled: false,\n }\n );\n }\n callbacks.onUncaughtError?.(error, info);\n },\n onRecoverableError(error, info) {\n if (error instanceof Error) {\n captureReactError(\n resolveKernel(kernelSource),\n error,\n info.componentStack,\n {\n type: MECHANISM_TYPE.react.recoverableError,\n handled: true,\n synthetic: true,\n }\n );\n }\n callbacks.onRecoverableError?.(error, info);\n },\n };\n}\n"],"mappings":"2HA4DA,SAAS,cACP,OACe,CAIf,OAHK,OAGE,OAAO,QAAW,WAAa,OAAO,EAAI,OAFxC,IAGX,CA2BA,SAAgB,kBACd,QAAoC,CAAC,EACb,CACxB,GAAM,CAAE,OAAQ,aAAc,GAAG,WAAc,QAC/C,MAAO,CACL,cAAc,MAAO,KAAM,CACrB,iBAAiB,OACnB,kBACE,cAAc,YAAY,EAC1B,MACA,KAAK,eACL,CACE,KAAM,eAAe,MAAM,YAC3B,QAAS,EACX,CACF,EAEF,UAAU,gBAAgB,MAAO,IAAI,CACvC,EACA,gBAAgB,MAAO,KAAM,CACvB,iBAAiB,OACnB,kBACE,cAAc,YAAY,EAC1B,MACA,KAAK,eACL,CACE,KAAM,eAAe,MAAM,cAC3B,QAAS,EACX,CACF,EAEF,UAAU,kBAAkB,MAAO,IAAI,CACzC,EACA,mBAAmB,MAAO,KAAM,CAC1B,iBAAiB,OACnB,kBACE,cAAc,YAAY,EAC1B,MACA,KAAK,eACL,CACE,KAAM,eAAe,MAAM,iBAC3B,QAAS,GACT,UAAW,EACb,CACF,EAEF,UAAU,qBAAqB,MAAO,IAAI,CAC5C,CACF,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"api.d.mts","names":[],"sources":["../../src/tracking/api.ts"],"mappings":";;;;;;;UAgBiB,qBAAA;EACf,MAAA,GAAS,aAAA;EACT,OAAA,UAAiB,UAAA,CAAW,KAAA;EAC5B,GAAA,GAAM,WAAA;EACN,MAAA,EAAQ,YAAA;AAAA;AAAA,cAGG,cAAA;EAAA,iBACM,MAAA;EAAA,iBACA,OAAA;EAAA,iBACA,MAAA;EAAA,iBACA,GAAA;EAAA,QAET,GAAA;EAAA,QACA,eAAA;EAAA,QACA,mBAAA;EAAA,QACA,eAAA;EAAA,QACA,aAAA;EAAA,QACA,UAAA;cAEI,IAAA,EAAM,qBAAA;EAOlB,KAAA;EASA,SAAA,IAAa,SAAA;EAQb,QAAA;EAIA,WAAA;EAIA,SAAA;EA7CyB;;;;;EAsDzB,OAAA,IAAW,MAAA;EAiBX,WAAA,IAAe,cAAA;EAIT,QAAA,CAAS,MAAA,EAAQ,cAAA,GAAiB,OAAA;EA2CxC,aAAA;EAKA,OAAA;EAAA,QAQQ,cAAA;EAAA,QAOA,YAAA;EAAA,QAcA,WAAA;EAAA,QA0BM,QAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"api.mjs","names":[],"sources":["../../src/tracking/api.ts"],"sourcesContent":["import type { SessionId } from \"@interfere/types/data/session\";\nimport type { IdentifyParams } from \"@interfere/types/sdk/identify\";\n\nimport {\n appendPathBeforeQuery,\n type IngestTarget,\n} from \"../internal/config.js\";\nimport { createLogger } from \"../util/log.js\";\nimport { DeviceManager } from \"./device.js\";\nimport { GeoDetector } from \"./geo.js\";\nimport { SessionManager } from \"./session.js\";\n\nconst log = createLogger(\"tracking\");\n\nconst SYNC_COOLDOWN_MS = 5000;\n\nexport interface SessionTrackerOptions {\n device?: DeviceManager;\n fetcher?: typeof globalThis.fetch;\n geo?: GeoDetector;\n target: IngestTarget;\n}\n\nexport class SessionTracker {\n private readonly target: IngestTarget;\n private readonly fetcher: typeof globalThis.fetch;\n private readonly device: DeviceManager;\n private readonly geo: GeoDetector;\n\n private mgr: SessionManager | null = null;\n private currentIdentity: IdentifyParams | null = null;\n private identifiedSessionId: string | null = null;\n private syncedSessionId: string | null = null;\n private syncAttemptMs = 0;\n private generation = 0;\n\n constructor(opts: SessionTrackerOptions) {\n this.target = opts.target;\n this.fetcher = opts.fetcher ?? globalThis.fetch.bind(globalThis);\n this.device = opts.device ?? new DeviceManager();\n this.geo = opts.geo ?? new GeoDetector();\n }\n\n start(): void {\n this.device.init();\n\n this.geo.detect();\n this.mgr = new SessionManager((id) => {\n this.onRotate(id).catch(() => undefined);\n });\n }\n\n sessionId(): SessionId | null {\n const id = this.mgr?.getSessionId() ?? null;\n if (id) {\n this.ensureSynced(id);\n }\n return id;\n }\n\n windowId(): string | null {\n return this.mgr?.getWindowId() ?? null;\n }\n\n getDeviceId(): string | null {\n return this.device.getDeviceId();\n }\n\n getFpHash(): string | null {\n return this.device.getFpHash();\n }\n\n /**\n * Identity headers (session / window / device). Layered onto the\n * session/identify POSTs alongside the target's static headers\n * (content-type + auth + force-enable).\n */\n headers(): Record<string, string> {\n const h: Record<string, string> = {};\n const sid = this.mgr?.getSessionId() ?? null;\n if (sid) {\n h[\"x-interfere-session\"] = sid;\n }\n const wid = this.mgr?.getWindowId() ?? null;\n if (wid) {\n h[\"x-interfere-window\"] = wid;\n }\n const did = this.device.getDeviceId();\n if (did) {\n h[\"x-interfere-device\"] = did;\n }\n return h;\n }\n\n getIdentity(): IdentifyParams | null {\n return this.currentIdentity;\n }\n\n async identify(params: IdentifyParams): Promise<void> {\n if (!this.mgr) {\n return;\n }\n const sessionId = this.mgr.getSessionId();\n if (this.identifiedSessionId === sessionId) {\n log.debug(\"skipped, already identified for session %s\", sessionId);\n return;\n }\n\n this.currentIdentity = params;\n this.identifiedSessionId = sessionId;\n\n const gen = this.generation;\n const [fpHash, country] = await Promise.all([\n this.device.whenFingerprintReady(),\n this.geo.detect(),\n ]);\n const deviceId = this.device.getDeviceId();\n if (gen !== this.generation) {\n this.identifiedSessionId = null;\n return;\n }\n\n log.info(\"POST session %s → identify\", sessionId);\n this.fetcher(appendPathBeforeQuery(this.target.url, \"/identify\"), {\n method: \"POST\",\n headers: this.requestHeaders(),\n body: JSON.stringify({\n sessionId,\n deviceId,\n fpHash,\n ...params,\n ...(country && { country }),\n }),\n keepalive: true,\n signal: AbortSignal.timeout(10_000),\n }).catch(() => {\n this.identifiedSessionId = null;\n log.warn(\"identify failed for session %s\", sessionId);\n });\n }\n\n clearIdentity(): void {\n this.currentIdentity = null;\n this.identifiedSessionId = null;\n }\n\n dispose(): void {\n this.generation += 1;\n this.clearIdentity();\n this.syncedSessionId = null;\n this.syncAttemptMs = 0;\n this.mgr = null;\n }\n\n private requestHeaders(): Record<string, string> {\n return {\n ...Object.fromEntries(this.target.headers.entries()),\n ...this.headers(),\n };\n }\n\n private ensureSynced(sessionId: string): void {\n if (this.syncedSessionId === sessionId) {\n return;\n }\n if (Date.now() - this.syncAttemptMs < SYNC_COOLDOWN_MS) {\n return;\n }\n this.syncSession(\n sessionId,\n this.device.getDeviceId(),\n this.device.getFpHash()\n );\n }\n\n private syncSession(\n sessionId: string,\n deviceId: string | null,\n fpHash: string | null\n ): void {\n this.syncAttemptMs = Date.now();\n this.syncedSessionId = sessionId;\n\n this.fetcher(this.target.url, {\n method: \"POST\",\n headers: this.requestHeaders(),\n body: JSON.stringify({ sessionId, deviceId, fpHash }),\n keepalive: true,\n signal: AbortSignal.timeout(10_000),\n })\n .then((res) => {\n if (!res.ok) {\n this.syncedSessionId = null;\n }\n })\n .catch(() => {\n this.syncedSessionId = null;\n log.warn(\"session sync failed, will retry\");\n });\n }\n\n private async onRotate(sessionId: string): Promise<void> {\n this.syncedSessionId = null;\n this.syncAttemptMs = Date.now();\n const gen = this.generation;\n const fpHash = await this.device.whenFingerprintReady();\n const deviceId = this.device.getDeviceId();\n if (gen !== this.generation) {\n return;\n }\n log.debug(\n \"POST session %s (device=%s fp=%s)\",\n sessionId,\n deviceId ?? \"pending\",\n fpHash ?? \"none\"\n );\n this.syncSession(sessionId, deviceId, fpHash);\n }\n}\n"],"mappings":"yNAYA,MAAM,IAAM,aAAa,UAAU,EAWnC,IAAa,eAAb,KAA4B,CAC1B,OACA,QACA,OACA,IAEA,IAAqC,KACrC,gBAAiD,KACjD,oBAA6C,KAC7C,gBAAyC,KACzC,cAAwB,EACxB,WAAqB,EAErB,YAAY,KAA6B,CACvC,KAAK,OAAS,KAAK,OACnB,KAAK,QAAU,KAAK,SAAW,WAAW,MAAM,KAAK,UAAU,EAC/D,KAAK,OAAS,KAAK,QAAU,IAAI,cACjC,KAAK,IAAM,KAAK,KAAO,IAAI,WAC7B,CAEA,OAAc,CACZ,KAAK,OAAO,KAAK,EAEjB,KAAK,IAAI,OAAO,EAChB,KAAK,IAAM,IAAI,eAAgB,IAAO,CACpC,KAAK,SAAS,EAAE,EAAE,UAAY,IAAA,EAAS,CACzC,CAAC,CACH,CAEA,WAA8B,CAC5B,IAAM,GAAK,KAAK,KAAK,aAAa,GAAK,KAIvC,OAHI,IACF,KAAK,aAAa,EAAE,EAEf,EACT,CAEA,UAA0B,CACxB,OAAO,KAAK,KAAK,YAAY,GAAK,IACpC,CAEA,aAA6B,CAC3B,OAAO,KAAK,OAAO,YAAY,CACjC,CAEA,WAA2B,CACzB,OAAO,KAAK,OAAO,UAAU,CAC/B,CAOA,SAAkC,CAChC,IAAM,EAA4B,CAAC,EAC7B,IAAM,KAAK,KAAK,aAAa,GAAK,KACpC,MACF,EAAE,uBAAyB,KAE7B,IAAM,IAAM,KAAK,KAAK,YAAY,GAAK,KACnC,MACF,EAAE,sBAAwB,KAE5B,IAAM,IAAM,KAAK,OAAO,YAAY,EAIpC,OAHI,MACF,EAAE,sBAAwB,KAErB,CACT,CAEA,aAAqC,CACnC,OAAO,KAAK,eACd,CAEA,MAAM,SAAS,OAAuC,CACpD,GAAI,CAAC,KAAK,IACR,OAEF,IAAM,UAAY,KAAK,IAAI,aAAa,EACxC,GAAI,KAAK,sBAAwB,UAAW,CAC1C,IAAI,MAAM,6CAA8C,SAAS,EACjE,MACF,CAEA,KAAK,gBAAkB,OACvB,KAAK,oBAAsB,UAE3B,IAAM,IAAM,KAAK,WACX,CAAC,OAAQ,SAAW,MAAM,QAAQ,IAAI,CAC1C,KAAK,OAAO,qBAAqB,EACjC,KAAK,IAAI,OAAO,CAClB,CAAC,EACK,SAAW,KAAK,OAAO,YAAY,EACzC,GAAI,MAAQ,KAAK,WAAY,CAC3B,KAAK,oBAAsB,KAC3B,MACF,CAEA,IAAI,KAAK,6BAA8B,SAAS,EAChD,KAAK,QAAQ,sBAAsB,KAAK,OAAO,IAAK,WAAW,EAAG,CAChE,OAAQ,OACR,QAAS,KAAK,eAAe,EAC7B,KAAM,KAAK,UAAU,CACnB,UACA,SACA,OACA,GAAG,OACH,GAAI,SAAW,CAAE,OAAQ,CAC3B,CAAC,EACD,UAAW,GACX,OAAQ,YAAY,QAAQ,GAAM,CACpC,CAAC,EAAE,UAAY,CACb,KAAK,oBAAsB,KAC3B,IAAI,KAAK,iCAAkC,SAAS,CACtD,CAAC,CACH,CAEA,eAAsB,CACpB,KAAK,gBAAkB,KACvB,KAAK,oBAAsB,IAC7B,CAEA,SAAgB,CACd,KAAK,YAAc,EACnB,KAAK,cAAc,EACnB,KAAK,gBAAkB,KACvB,KAAK,cAAgB,EACrB,KAAK,IAAM,IACb,CAEA,gBAAiD,CAC/C,MAAO,CACL,GAAG,OAAO,YAAY,KAAK,OAAO,QAAQ,QAAQ,CAAC,EACnD,GAAG,KAAK,QAAQ,CAClB,CACF,CAEA,aAAqB,UAAyB,CACxC,KAAK,kBAAoB,YAGzB,KAAK,IAAI,EAAI,KAAK,cAAgB,KAGtC,KAAK,YACH,UACA,KAAK,OAAO,YAAY,EACxB,KAAK,OAAO,UAAU,CACxB,EACF,CAEA,YACE,UACA,SACA,OACM,CACN,KAAK,cAAgB,KAAK,IAAI,EAC9B,KAAK,gBAAkB,UAEvB,KAAK,QAAQ,KAAK,OAAO,IAAK,CAC5B,OAAQ,OACR,QAAS,KAAK,eAAe,EAC7B,KAAM,KAAK,UAAU,CAAE,UAAW,SAAU,MAAO,CAAC,EACpD,UAAW,GACX,OAAQ,YAAY,QAAQ,GAAM,CACpC,CAAC,EACE,KAAM,KAAQ,CACR,IAAI,KACP,KAAK,gBAAkB,KAE3B,CAAC,EACA,UAAY,CACX,KAAK,gBAAkB,KACvB,IAAI,KAAK,iCAAiC,CAC5C,CAAC,CACL,CAEA,MAAc,SAAS,UAAkC,CACvD,KAAK,gBAAkB,KACvB,KAAK,cAAgB,KAAK,IAAI,EAC9B,IAAM,IAAM,KAAK,WACX,OAAS,MAAM,KAAK,OAAO,qBAAqB,EAChD,SAAW,KAAK,OAAO,YAAY,EACrC,MAAQ,KAAK,aAGjB,IAAI,MACF,oCACA,UACA,UAAY,UACZ,QAAU,MACZ,EACA,KAAK,YAAY,UAAW,SAAU,MAAM,EAC9C,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"device.d.mts","names":[],"sources":["../../src/tracking/device.ts"],"mappings":";;AAgBA;;;;AAA+C;AAW/C;;KAXY,mBAAA,SAA4B,OAAO;;AAWZ;AAuEnC;;;;;iBAvEgB,QAAA,IAAY,OAAO;AAAA,cAuEtB,aAAA;EAAA,QACH,QAAA;EAAA,QACA,MAAA;EAAA,QACA,SAAA;EAAA,iBACS,mBAAA;cAEL,mBAAA,GAAsB,mBAAA;EAKlC,IAAA;EAAA,QAkCQ,gBAAA;EAgBR,WAAA;EAIA,SAAA;EAIA,oBAAA,IAAwB,OAAO;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"device.mjs","names":[],"sources":["../../src/tracking/device.ts"],"sourcesContent":["import { createLogger } from \"../util/log.js\";\n\nconst log = createLogger(\"device\");\n\nconst LS_KEY = \"interfere:device_id\";\nconst COOKIE_NAME = \"interfere_did\";\nconst COOKIE_MAX_AGE_DAYS = 400;\n\n/**\n * Producer of an opaque, stable per-browser fingerprint hash. The default\n * dynamic-imports the FingerprintJS OSS library and self-defers via\n * `requestIdleCallback` so the ~1.5s of synchronous font / canvas / WebGL\n * probing the library performs on a cold load doesn't land on the\n * hydration tick. Tests inject a fixed-value provider that resolves\n * immediately — they pay nothing for the production deferral.\n */\nexport type FingerprintProvider = () => Promise<string | null>;\n\nconst FP_IDLE_TIMEOUT_MS = 5000;\n\n/**\n * Resolves on the next browser idle frame, capped at 5s. Falls back to\n * `setTimeout(0)` (next macrotask) on hosts without\n * `requestIdleCallback`. Exported only for the unit test that pins the\n * \"default provider gates fingerprint work via RIC\" contract — not part\n * of the package's public surface.\n */\nexport function whenIdle(): Promise<void> {\n return new Promise((resolve) => {\n if (typeof globalThis.requestIdleCallback === \"function\") {\n globalThis.requestIdleCallback(() => resolve(), {\n timeout: FP_IDLE_TIMEOUT_MS,\n });\n return;\n }\n setTimeout(resolve, 0);\n });\n}\n\nconst defaultFingerprintProvider: FingerprintProvider = async () => {\n await whenIdle();\n try {\n const FingerprintJS = await import(\"@fingerprintjs/fingerprintjs\");\n const fp = await FingerprintJS.load();\n const result = await fp.get();\n if (!result.visitorId) {\n log.error(\n \"Fingerprinting returned an empty visitor id; visitor identity will be cookie-only\"\n );\n\n return null;\n }\n return result.visitorId;\n } catch (err) {\n log.error(\n \"fingerprint init failed; visitor identity will be cookie-only\",\n err\n );\n\n return null;\n }\n};\n\nfunction tryLocalStorage(): Storage | null {\n try {\n const s = globalThis.localStorage;\n const key = \"__interfere_device_probe__\";\n s.setItem(key, \"1\");\n s.removeItem(key);\n return s;\n } catch {\n return null;\n }\n}\n\nfunction getCookie(name: string): string | null {\n if (typeof document === \"undefined\") {\n return null;\n }\n const match = document.cookie\n .split(\"; \")\n .find((c) => c.startsWith(`${name}=`));\n return match ? decodeURIComponent(match.split(\"=\")[1] ?? \"\") : null;\n}\n\nfunction setCookie(name: string, value: string): void {\n if (typeof document === \"undefined\") {\n return;\n }\n const maxAge = COOKIE_MAX_AGE_DAYS * 24 * 60 * 60;\n // biome-ignore lint/suspicious/noDocumentCookie: Cookie Store API is async and not universally supported; synchronous access is required here\n document.cookie = `${name}=${encodeURIComponent(value)};path=/;max-age=${maxAge};SameSite=Lax`;\n}\n\nfunction generateId(): string {\n return crypto.randomUUID();\n}\n\nexport class DeviceManager {\n private deviceId: string | null = null;\n private fpHash: string | null = null;\n private fpPending: Promise<string | null> | null = null;\n private readonly fingerprintProvider: FingerprintProvider;\n\n constructor(fingerprintProvider?: FingerprintProvider) {\n this.fingerprintProvider =\n fingerprintProvider ?? defaultFingerprintProvider;\n }\n\n init(): void {\n if (this.deviceId) {\n return;\n }\n\n const ls = tryLocalStorage();\n const fromLs = ls?.getItem(LS_KEY) ?? null;\n const fromCookie = getCookie(COOKIE_NAME);\n\n this.deviceId = fromLs ?? fromCookie ?? generateId();\n\n if (!fromLs && ls) {\n ls.setItem(LS_KEY, this.deviceId);\n }\n if (!fromCookie) {\n setCookie(COOKIE_NAME, this.deviceId);\n }\n if (fromLs && !fromCookie) {\n setCookie(COOKIE_NAME, this.deviceId);\n }\n if (fromCookie && !fromLs && ls) {\n ls.setItem(LS_KEY, this.deviceId);\n }\n\n log.debug(\n \"device %s (ls=%s cookie=%s)\",\n this.deviceId,\n !!fromLs,\n !!fromCookie\n );\n\n this.startFingerprint();\n }\n\n private startFingerprint(): void {\n if (this.fpHash || this.fpPending) {\n return;\n }\n\n this.fpPending = (async () => {\n const hash = await this.fingerprintProvider();\n this.fpHash = hash;\n if (hash) {\n log.debug(\"fpHash %s\", hash);\n }\n\n return hash;\n })();\n }\n\n getDeviceId(): string | null {\n return this.deviceId;\n }\n\n getFpHash(): string | null {\n return this.fpHash;\n }\n\n whenFingerprintReady(): Promise<string | null> {\n if (this.fpHash) {\n return Promise.resolve(this.fpHash);\n }\n return this.fpPending ?? Promise.resolve(null);\n }\n}\n"],"mappings":"0CAEA,MAAM,IAAM,aAAa,QAAQ,EAE3B,OAAS,sBACT,YAAc,gBAsBpB,SAAgB,UAA0B,CACxC,OAAO,IAAI,QAAS,SAAY,CAC9B,GAAI,OAAO,WAAW,qBAAwB,WAAY,CACxD,WAAW,wBAA0B,QAAQ,EAAG,CAC9C,QAAS,GACX,CAAC,EACD,MACF,CACA,WAAW,QAAS,CAAC,CACvB,CAAC,CACH,CAEA,MAAM,2BAAkD,SAAY,CAClE,MAAM,SAAS,EACf,GAAI,CAGF,IAAM,OAAS,MAAM,MADJ,MADW,OAAO,iCACJ,KAAK,GACZ,IAAI,EAQ5B,OAPK,OAAO,UAOL,OAAO,WANZ,IAAI,MACF,mFACF,EAEO,KAGX,OAAS,IAAK,CAMZ,OALA,IAAI,MACF,gEACA,GACF,EAEO,IACT,CACF,EAEA,SAAS,iBAAkC,CACzC,GAAI,CACF,IAAM,EAAI,WAAW,aACf,IAAM,6BAGZ,OAFA,EAAE,QAAQ,IAAK,GAAG,EAClB,EAAE,WAAW,GAAG,EACT,CACT,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAAS,UAAU,KAA6B,CAC9C,GAAI,OAAO,SAAa,IACtB,OAAO,KAET,IAAM,MAAQ,SAAS,OACpB,MAAM,IAAI,EACV,KAAM,GAAM,EAAE,WAAW,GAAG,KAAK,EAAE,CAAC,EACvC,OAAO,MAAQ,mBAAmB,MAAM,MAAM,GAAG,EAAE,IAAM,EAAE,EAAI,IACjE,CAEA,SAAS,UAAU,KAAc,MAAqB,CAChD,OAAO,SAAa,MAKxB,SAAS,OAAS,GAAG,KAAK,GAAG,mBAAmB,KAAK,EAAE,kBAFxC,IAAsB,GAAK,GAAK,GAEiC,eAClF,CAEA,SAAS,YAAqB,CAC5B,OAAO,OAAO,WAAW,CAC3B,CAEA,IAAa,cAAb,KAA2B,CACzB,SAAkC,KAClC,OAAgC,KAChC,UAAmD,KACnD,oBAEA,YAAY,oBAA2C,CACrD,KAAK,oBACH,qBAAuB,0BAC3B,CAEA,MAAa,CACX,GAAI,KAAK,SACP,OAGF,IAAM,GAAK,gBAAgB,EACrB,OAAS,IAAI,QAAQ,MAAM,GAAK,KAChC,WAAa,UAAU,WAAW,EAExC,KAAK,SAAW,QAAU,YAAc,WAAW,EAE/C,CAAC,QAAU,IACb,GAAG,QAAQ,OAAQ,KAAK,QAAQ,EAE7B,YACH,UAAU,YAAa,KAAK,QAAQ,EAElC,QAAU,CAAC,YACb,UAAU,YAAa,KAAK,QAAQ,EAElC,YAAc,CAAC,QAAU,IAC3B,GAAG,QAAQ,OAAQ,KAAK,QAAQ,EAGlC,IAAI,MACF,8BACA,KAAK,SACL,CAAC,CAAC,OACF,CAAC,CAAC,UACJ,EAEA,KAAK,iBAAiB,CACxB,CAEA,kBAAiC,CAC3B,KAAK,QAAU,KAAK,YAIxB,KAAK,WAAa,SAAY,CAC5B,IAAM,KAAO,MAAM,KAAK,oBAAoB,EAM5C,MALA,MAAK,OAAS,KACV,MACF,IAAI,MAAM,YAAa,IAAI,EAGtB,IACT,GAAG,EACL,CAEA,aAA6B,CAC3B,OAAO,KAAK,QACd,CAEA,WAA2B,CACzB,OAAO,KAAK,MACd,CAEA,sBAA+C,CAI7C,OAHI,KAAK,OACA,QAAQ,QAAQ,KAAK,MAAM,EAE7B,KAAK,WAAa,QAAQ,QAAQ,IAAI,CAC/C,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"geo.d.mts","names":[],"sources":["../../src/tracking/geo.ts"],"mappings":";cAea,WAAA;EAAA,QACH,MAAA;EAAA,QACA,OAAA;EAAA,QACA,MAAA;EAAA,iBACS,OAAA;cAEL,OAAA,UAAiB,UAAA,CAAW,KAAA;EAIxC,MAAA,IAAU,OAAO;EAuBjB,UAAA;EAAA,QAIc,gBAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"geo.mjs","names":[],"sources":["../../src/tracking/geo.ts"],"sourcesContent":["import { cloudflareTraceSchema } from \"@interfere/types/sdk/geo\";\n\nconst CF_TRACE_URL = \"https://cloudflare.com/cdn-cgi/trace\";\n\nfunction parseTrace(text: string): Record<string, string> {\n const result: Record<string, string> = {};\n for (const line of text.split(\"\\n\")) {\n const idx = line.indexOf(\"=\");\n if (idx > 0) {\n result[line.slice(0, idx)] = line.slice(idx + 1);\n }\n }\n return result;\n}\n\nexport class GeoDetector {\n private cached: string | null = null;\n private pending: Promise<string | null> | null = null;\n private failed = false;\n private readonly fetcher: typeof globalThis.fetch;\n\n constructor(fetcher?: typeof globalThis.fetch) {\n this.fetcher = fetcher ?? globalThis.fetch.bind(globalThis);\n }\n\n detect(): Promise<string | null> {\n if (this.cached) {\n return Promise.resolve(this.cached);\n }\n\n if (this.failed) {\n return Promise.resolve(null);\n }\n\n if (!this.pending) {\n this.pending = this.fetchCountryCode().then((code) => {\n this.cached = code;\n if (!code) {\n this.failed = true;\n }\n this.pending = null;\n return code;\n });\n }\n\n return this.pending;\n }\n\n getCountry(): string | null {\n return this.cached;\n }\n\n private async fetchCountryCode(): Promise<string | null> {\n try {\n const res = await this.fetcher(CF_TRACE_URL, {\n signal: AbortSignal.timeout(3000),\n });\n if (!res.ok) {\n return null;\n }\n\n const raw = parseTrace(await res.text());\n const trace = cloudflareTraceSchema.safeParse(raw);\n if (!trace.success) {\n return null;\n }\n\n return trace.data.loc.toUpperCase();\n } catch {\n return null;\n }\n }\n}\n"],"mappings":"4DAIA,SAAS,WAAW,KAAsC,CACxD,IAAM,OAAiC,CAAC,EACxC,IAAK,IAAM,QAAQ,KAAK,MAAM;CAAI,EAAG,CACnC,IAAM,IAAM,KAAK,QAAQ,GAAG,EACxB,IAAM,IACR,OAAO,KAAK,MAAM,EAAG,GAAG,GAAK,KAAK,MAAM,IAAM,CAAC,EAEnD,CACA,OAAO,MACT,CAEA,IAAa,YAAb,KAAyB,CACvB,OAAgC,KAChC,QAAiD,KACjD,OAAiB,GACjB,QAEA,YAAY,QAAmC,CAC7C,KAAK,QAAU,SAAW,WAAW,MAAM,KAAK,UAAU,CAC5D,CAEA,QAAiC,CAoB/B,OAnBI,KAAK,OACA,QAAQ,QAAQ,KAAK,MAAM,EAGhC,KAAK,OACA,QAAQ,QAAQ,IAAI,GAG7B,AACE,KAAK,UAAU,KAAK,iBAAiB,EAAE,KAAM,OAC3C,KAAK,OAAS,KACT,OACH,KAAK,OAAS,IAEhB,KAAK,QAAU,KACR,KACR,EAGI,KAAK,QACd,CAEA,YAA4B,CAC1B,OAAO,KAAK,MACd,CAEA,MAAc,kBAA2C,CACvD,GAAI,CACF,IAAM,IAAM,MAAM,KAAK,QAAQ,uCAAc,CAC3C,OAAQ,YAAY,QAAQ,GAAI,CAClC,CAAC,EACD,GAAI,CAAC,IAAI,GACP,OAAO,KAGT,IAAM,IAAM,WAAW,MAAM,IAAI,KAAK,CAAC,EACjC,MAAQ,sBAAsB,UAAU,GAAG,EAKjD,OAJK,MAAM,QAIJ,MAAM,KAAK,IAAI,YAAY,EAHzB,IAIX,MAAQ,CACN,OAAO,IACT,CACF,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"session.d.mts","names":[],"sources":["../../src/tracking/session.ts"],"mappings":";;;KAqBY,QAAA,IAAY,SAAiB;AAAA,cAE5B,cAAA;EACX,SAAA;EACA,QAAA;EAAA,iBACiB,KAAA;EAAA,iBACA,OAAA;EAAA,iBACA,QAAA;EAAA,QACT,cAAA;cAEI,QAAA,GAAW,QAAA;EAOvB,YAAA,IAAgB,SAAS;EAQzB,WAAA;EAAA,QAgBQ,OAAA;EAAA,QAWA,MAAA;EAAA,QAQA,KAAA;EAAA,QAKA,SAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"session.mjs","names":["uuidv7"],"sources":["../../src/tracking/session.ts"],"sourcesContent":["import type { SessionId } from \"@interfere/types/data/session\";\n\nimport { v7 as uuidv7 } from \"uuid\";\n\nconst SESSION_ID_KEY = \"interfere:session_id\";\nconst LAST_ACTIVITY_KEY = \"interfere:last_activity\";\nconst WINDOW_ID_KEY = \"interfere:window_id\";\nconst SESSION_TIMEOUT_MS = 30 * 60 * 1000;\n\nfunction tryStorage(type: \"localStorage\" | \"sessionStorage\"): Storage | null {\n try {\n const s = globalThis[type];\n const key = \"__interfere_probe__\";\n s.setItem(key, \"1\");\n s.removeItem(key);\n return s;\n } catch {\n return null;\n }\n}\n\nexport type OnRotate = (sessionId: string) => void;\n\nexport class SessionManager {\n sessionId: string | null = null;\n windowId: string | null = null;\n private readonly local: Storage | null;\n private readonly session: Storage | null;\n private readonly onRotate: OnRotate | null = null;\n private lastActivityMs = 0;\n\n constructor(onRotate?: OnRotate) {\n this.local = tryStorage(\"localStorage\");\n this.session = tryStorage(\"sessionStorage\");\n this.onRotate = onRotate ?? null;\n this.restore();\n }\n\n getSessionId(): SessionId {\n if (this.sessionId && !this.isExpired()) {\n this.touch();\n return this.sessionId as SessionId;\n }\n return this.rotate() as SessionId;\n }\n\n getWindowId(): string {\n if (this.windowId) {\n return this.windowId;\n }\n\n const stored = this.session?.getItem(WINDOW_ID_KEY);\n if (stored) {\n this.windowId = stored;\n return stored;\n }\n\n this.windowId = `win_${crypto.randomUUID()}`;\n this.session?.setItem(WINDOW_ID_KEY, this.windowId);\n return this.windowId;\n }\n\n private restore(): void {\n const stored = this.local?.getItem(SESSION_ID_KEY);\n\n if (stored && !this.isExpired()) {\n this.sessionId = stored;\n this.touch();\n } else {\n this.rotate();\n }\n }\n\n private rotate(): SessionId {\n this.sessionId = uuidv7();\n this.local?.setItem(SESSION_ID_KEY, this.sessionId);\n this.touch();\n this.onRotate?.(this.sessionId);\n return this.sessionId as SessionId;\n }\n\n private touch(): void {\n this.lastActivityMs = Date.now();\n this.local?.setItem(LAST_ACTIVITY_KEY, String(this.lastActivityMs));\n }\n\n private isExpired(): boolean {\n const raw = this.local?.getItem(LAST_ACTIVITY_KEY);\n const ts = raw ? Number(raw) : this.lastActivityMs;\n\n if (ts === 0) {\n return true;\n }\n return Date.now() - ts > SESSION_TIMEOUT_MS;\n }\n}\n"],"mappings":"qBAIA,MAAM,eAAiB,uBACjB,kBAAoB,0BACpB,cAAgB,sBAGtB,SAAS,WAAW,KAAyD,CAC3E,GAAI,CACF,IAAM,EAAI,WAAW,MACf,IAAM,sBAGZ,OAFA,EAAE,QAAQ,IAAK,GAAG,EAClB,EAAE,WAAW,GAAG,EACT,CACT,MAAQ,CACN,OAAO,IACT,CACF,CAIA,IAAa,eAAb,KAA4B,CAC1B,UAA2B,KAC3B,SAA0B,KAC1B,MACA,QACA,SAA6C,KAC7C,eAAyB,EAEzB,YAAY,SAAqB,CAC/B,KAAK,MAAQ,WAAW,cAAc,EACtC,KAAK,QAAU,WAAW,gBAAgB,EAC1C,KAAK,SAAW,UAAY,KAC5B,KAAK,QAAQ,CACf,CAEA,cAA0B,CAKxB,OAJI,KAAK,WAAa,CAAC,KAAK,UAAU,GACpC,KAAK,MAAM,EACJ,KAAK,WAEP,KAAK,OAAO,CACrB,CAEA,aAAsB,CACpB,GAAI,KAAK,SACP,OAAO,KAAK,SAGd,IAAM,OAAS,KAAK,SAAS,QAAQ,aAAa,EAQlD,OAPI,QACF,KAAK,SAAW,OACT,SAGT,KAAK,SAAW,OAAO,OAAO,WAAW,IACzC,KAAK,SAAS,QAAQ,cAAe,KAAK,QAAQ,EAC3C,KAAK,SACd,CAEA,SAAwB,CACtB,IAAM,OAAS,KAAK,OAAO,QAAQ,cAAc,EAE7C,QAAU,CAAC,KAAK,UAAU,GAC5B,KAAK,UAAY,OACjB,KAAK,MAAM,GAEX,KAAK,OAAO,CAEhB,CAEA,QAA4B,CAK1B,MAJA,MAAK,UAAYA,GAAO,EACxB,KAAK,OAAO,QAAQ,eAAgB,KAAK,SAAS,EAClD,KAAK,MAAM,EACX,KAAK,WAAW,KAAK,SAAS,EACvB,KAAK,SACd,CAEA,OAAsB,CACpB,KAAK,eAAiB,KAAK,IAAI,EAC/B,KAAK,OAAO,QAAQ,kBAAmB,OAAO,KAAK,cAAc,CAAC,CACpE,CAEA,WAA6B,CAC3B,IAAM,IAAM,KAAK,OAAO,QAAQ,iBAAiB,EAC3C,GAAK,IAAM,OAAO,GAAG,EAAI,KAAK,eAKpC,OAHI,KAAO,EACF,GAEF,KAAK,IAAI,EAAI,GAAK,IAC3B,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"bot.d.mts","names":[],"sources":["../../src/util/bot.ts"],"mappings":";;AAQA;;;;AAA8B;iBAAd,cAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"bot.mjs","names":[],"sources":["../../src/util/bot.ts"],"sourcesContent":["import { isBot } from \"@ua-parser-js/pro-enterprise/bot-detection\";\n\n/**\n * `isBot` covers the union of the Crawlers, Fetchers, CLIs, and Libraries\n * extensions, so `isAICrawler` / `isAIAssistant` are strict subsets that\n * don't need separate checks. Returns `false` outside the browser (no UA\n * to inspect).\n */\nexport function isBotUserAgent(): boolean {\n if (typeof navigator === \"undefined\") {\n return false;\n }\n return isBot(navigator.userAgent);\n}\n"],"mappings":"8DAQA,SAAgB,gBAA0B,CAIxC,OAHI,OAAO,UAAc,IAChB,GAEF,MAAM,UAAU,SAAS,CAClC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"global.d.mts","names":[],"sources":["../../src/util/global.ts"],"mappings":";;AAMA;;;;;iBAAgB,SAAA,IAAa,GAAA,WAAc,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"global.mjs","names":[],"sources":["../../src/util/global.ts"],"sourcesContent":["/**\n * Typed read of a build-time-injected global. The Vite plugin and\n * `withInterfere()` stamp values like `__INTERFERE_RELEASE_SLUG__` onto\n * `globalThis`; this is the single read site so the unsafe cast lives in\n * one place.\n */\nexport function getGlobal<T>(key: string): T | undefined {\n return (globalThis as Record<string, unknown>)[key] as T | undefined;\n}\n"],"mappings":"AAMA,SAAgB,UAAa,IAA4B,CACvD,OAAQ,WAAuC,IACjD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"log.d.mts","names":[],"sources":["../../src/util/log.ts"],"mappings":";KAAY,QAAA;AAAA,iBA6BI,WAAA,CAAY,KAAe,EAAR,QAAQ;AAAA,iBAI3B,WAAA,IAAe,QAAQ;AAAA,UAKtB,MAAA;EACf,KAAA,IAAS,IAAA;EACT,KAAA,IAAS,IAAA;EACT,IAAA,IAAQ,IAAA;EACR,IAAA,IAAQ,IAAA;AAAA;AAAA,iBAeM,YAAA,CAAa,KAAA,WAAgB,MAAM"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"log.mjs","names":[],"sources":["../../src/util/log.ts"],"sourcesContent":["export type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\" | \"none\";\n\nconst PRIORITY: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n none: 4,\n};\n\nconst CONSOLE_FN: Record<\n Exclude<LogLevel, \"none\">,\n \"debug\" | \"info\" | \"warn\" | \"error\"\n> = {\n debug: \"debug\",\n info: \"info\",\n warn: \"warn\",\n error: \"error\",\n};\n\n/**\n * Customer apps see `warn` and above by default. Tests opt into silence by\n * calling `setLogLevel(\"none\")` in their setup file (we don't auto-detect\n * the test runtime — verified empirically that vitest browser mode exposes\n * neither `import.meta.env`, `process.env`, nor `__VITEST_*` globals to\n * library code, so any \"is this a test?\" probe is unreliable).\n */\nlet threshold = PRIORITY.warn;\n\nexport function setLogLevel(level: LogLevel): void {\n threshold = PRIORITY[level];\n}\n\nexport function getLogLevel(): LogLevel {\n const entry = Object.entries(PRIORITY).find(([, v]) => v === threshold);\n return (entry?.[0] ?? \"warn\") as LogLevel;\n}\n\nexport interface Logger {\n debug(...args: unknown[]): void;\n error(...args: unknown[]): void;\n info(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n}\n\nfunction emit(\n level: Exclude<LogLevel, \"none\">,\n prefix: string,\n args: unknown[]\n): void {\n if (PRIORITY[level] < threshold) {\n return;\n }\n const fn = CONSOLE_FN[level];\n globalThis.console[fn](prefix, ...args);\n}\n\nexport function createLogger(scope: string): Logger {\n const prefix = `[Interfere:${scope}]`;\n return {\n debug: (...args) => emit(\"debug\", prefix, args),\n info: (...args) => emit(\"info\", prefix, args),\n warn: (...args) => emit(\"warn\", prefix, args),\n error: (...args) => emit(\"error\", prefix, args),\n };\n}\n"],"mappings":"AAEA,MAAM,SAAqC,CACzC,MAAO,EACP,KAAM,EACN,KAAM,EACN,MAAO,EACP,KAAM,CACR,EAEM,WAGF,CACF,MAAO,QACP,KAAM,OACN,KAAM,OACN,MAAO,OACT,EASA,IAAI,UAAY,SAAS,KAEzB,SAAgB,YAAY,MAAuB,CACjD,UAAY,SAAS,MACvB,CAEA,SAAgB,aAAwB,CAEtC,OADc,OAAO,QAAQ,QAAQ,EAAE,MAAM,EAAG,KAAO,IAAM,SACjD,IAAI,IAAM,MACxB,CASA,SAAS,KACP,MACA,OACA,KACM,CACN,GAAI,SAAS,OAAS,UACpB,OAEF,IAAM,GAAK,WAAW,OACtB,WAAW,QAAQ,IAAI,OAAQ,GAAG,IAAI,CACxC,CAEA,SAAgB,aAAa,MAAuB,CAClD,IAAM,OAAS,cAAc,MAAM,GACnC,MAAO,CACL,OAAQ,GAAG,OAAS,KAAK,QAAS,OAAQ,IAAI,EAC9C,MAAO,GAAG,OAAS,KAAK,OAAQ,OAAQ,IAAI,EAC5C,MAAO,GAAG,OAAS,KAAK,OAAQ,OAAQ,IAAI,EAC5C,OAAQ,GAAG,OAAS,KAAK,QAAS,OAAQ,IAAI,CAChD,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"stringify.d.mts","names":[],"sources":["../../src/util/stringify.ts"],"mappings":";;AAKA;;;;iBAAgB,aAAA,CAAc,KAAc"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"stringify.mjs","names":[],"sources":["../../src/util/stringify.ts"],"sourcesContent":["/**\n * `JSON.stringify` with a `String(value)` fallback for circular refs and\n * non-serialisable values (BigInt, …). Strings pass through unchanged so\n * single-arg log calls don't get re-quoted.\n */\nexport function safeStringify(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n return String(value);\n }\n}\n"],"mappings":"AAKA,SAAgB,cAAc,MAAwB,CACpD,GAAI,OAAO,OAAU,SACnB,OAAO,MAET,GAAI,CACF,OAAO,KAAK,UAAU,KAAK,GAAK,OAAO,KAAK,CAC9C,MAAQ,CACN,OAAO,OAAO,KAAK,CACrB,CACF"}