@interfere/react 10.0.1-canary.1 → 11.0.0-canary.3

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 (129) hide show
  1. package/README.md +1 -1
  2. package/dist/internal/capture-boundary.mjs +1 -1
  3. package/dist/internal/capture.mjs +1 -1
  4. package/dist/internal/hydration.d.mts +10 -0
  5. package/dist/internal/hydration.mjs +1 -0
  6. package/dist/internal/kernel.d.mts +22 -3
  7. package/dist/internal/kernel.mjs +1 -1
  8. package/dist/internal/otel/index.d.mts +2 -2
  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 +44 -1
  12. package/dist/internal/otel/instrumentations.mjs +1 -1
  13. package/dist/internal/otel/provider.mjs +1 -1
  14. package/dist/internal/page-lifecycle.d.mts +2 -15
  15. package/dist/internal/page-lifecycle.mjs +1 -1
  16. package/dist/internal/react-context.d.mts +1 -0
  17. package/dist/internal/react-context.mjs +1 -1
  18. package/dist/internal/server-digest.d.mts +9 -0
  19. package/dist/internal/server-digest.mjs +1 -0
  20. package/dist/internal/third-party-hosts.d.mts +14 -0
  21. package/dist/internal/third-party-hosts.mjs +1 -0
  22. package/dist/internal/wrapper-singleton.d.mts +1 -0
  23. package/dist/internal/wrapper-singleton.mjs +1 -1
  24. package/dist/package.mjs +1 -1
  25. package/dist/plugins/errors.d.mts +1 -1
  26. package/dist/plugins/errors.mjs +1 -1
  27. package/dist/plugins/lib/loader.mjs +1 -1
  28. package/dist/plugins/logs.d.mts +1 -1
  29. package/dist/plugins/logs.mjs +1 -1
  30. package/dist/plugins/rage-clicks.d.mts +1 -1
  31. package/dist/plugins/rage-clicks.mjs +1 -1
  32. package/dist/plugins/replay/buffer.d.mts +13 -0
  33. package/dist/plugins/replay/buffer.mjs +1 -0
  34. package/dist/plugins/replay/chunking.d.mts +21 -0
  35. package/dist/plugins/replay/chunking.mjs +1 -0
  36. package/dist/plugins/replay/compressor-host.d.mts +9 -0
  37. package/dist/plugins/replay/compressor-host.mjs +1 -0
  38. package/dist/plugins/replay/delivery.d.mts +23 -0
  39. package/dist/plugins/replay/delivery.mjs +1 -0
  40. package/dist/plugins/replay/index.d.mts +6 -0
  41. package/dist/plugins/replay/index.mjs +1 -0
  42. package/dist/plugins/replay/recorder.d.mts +8 -0
  43. package/dist/plugins/replay/recorder.mjs +1 -0
  44. package/dist/sw.mjs +1 -1
  45. package/dist/tracking/api.d.mts +1 -0
  46. package/dist/tracking/api.mjs +1 -1
  47. package/dist/tracking/session.d.mts +1 -0
  48. package/dist/tracking/session.mjs +1 -1
  49. package/package.json +29 -34
  50. package/dist/api.d.mts.map +0 -1
  51. package/dist/api.mjs.map +0 -1
  52. package/dist/error-boundary.d.mts.map +0 -1
  53. package/dist/error-boundary.mjs.map +0 -1
  54. package/dist/internal/browser-context.d.mts.map +0 -1
  55. package/dist/internal/browser-context.mjs.map +0 -1
  56. package/dist/internal/capture-boundary.d.mts.map +0 -1
  57. package/dist/internal/capture-boundary.mjs.map +0 -1
  58. package/dist/internal/capture.d.mts.map +0 -1
  59. package/dist/internal/capture.mjs.map +0 -1
  60. package/dist/internal/config.d.mts.map +0 -1
  61. package/dist/internal/config.mjs.map +0 -1
  62. package/dist/internal/consent.d.mts.map +0 -1
  63. package/dist/internal/consent.mjs.map +0 -1
  64. package/dist/internal/console-patch.d.mts.map +0 -1
  65. package/dist/internal/console-patch.mjs.map +0 -1
  66. package/dist/internal/dom/actionable.d.mts.map +0 -1
  67. package/dist/internal/dom/actionable.mjs.map +0 -1
  68. package/dist/internal/kernel-registry.d.mts.map +0 -1
  69. package/dist/internal/kernel-registry.mjs.map +0 -1
  70. package/dist/internal/kernel.d.mts.map +0 -1
  71. package/dist/internal/kernel.mjs.map +0 -1
  72. package/dist/internal/otel/exporter.d.mts.map +0 -1
  73. package/dist/internal/otel/exporter.mjs.map +0 -1
  74. package/dist/internal/otel/instrumentations.d.mts.map +0 -1
  75. package/dist/internal/otel/instrumentations.mjs.map +0 -1
  76. package/dist/internal/otel/page-scope-context-manager.d.mts.map +0 -1
  77. package/dist/internal/otel/page-scope-context-manager.mjs.map +0 -1
  78. package/dist/internal/otel/propagation.d.mts.map +0 -1
  79. package/dist/internal/otel/propagation.mjs.map +0 -1
  80. package/dist/internal/otel/provider.d.mts.map +0 -1
  81. package/dist/internal/otel/provider.mjs.map +0 -1
  82. package/dist/internal/otel/web-vitals.d.mts.map +0 -1
  83. package/dist/internal/otel/web-vitals.mjs.map +0 -1
  84. package/dist/internal/page-lifecycle.d.mts.map +0 -1
  85. package/dist/internal/page-lifecycle.mjs.map +0 -1
  86. package/dist/internal/plugin-runtime.d.mts.map +0 -1
  87. package/dist/internal/plugin-runtime.mjs.map +0 -1
  88. package/dist/internal/react-context.d.mts.map +0 -1
  89. package/dist/internal/react-context.mjs.map +0 -1
  90. package/dist/internal/sw.d.mts.map +0 -1
  91. package/dist/internal/sw.mjs.map +0 -1
  92. package/dist/internal/version.d.mts.map +0 -1
  93. package/dist/internal/version.mjs.map +0 -1
  94. package/dist/internal/wrapper-singleton.d.mts.map +0 -1
  95. package/dist/internal/wrapper-singleton.mjs.map +0 -1
  96. package/dist/package.mjs.map +0 -1
  97. package/dist/plugins/errors.d.mts.map +0 -1
  98. package/dist/plugins/errors.mjs.map +0 -1
  99. package/dist/plugins/lib/loader.d.mts.map +0 -1
  100. package/dist/plugins/lib/loader.mjs.map +0 -1
  101. package/dist/plugins/lib/types.d.mts.map +0 -1
  102. package/dist/plugins/logs.d.mts.map +0 -1
  103. package/dist/plugins/logs.mjs.map +0 -1
  104. package/dist/plugins/rage-clicks.d.mts.map +0 -1
  105. package/dist/plugins/rage-clicks.mjs.map +0 -1
  106. package/dist/plugins/replay.d.mts +0 -6
  107. package/dist/plugins/replay.d.mts.map +0 -1
  108. package/dist/plugins/replay.mjs +0 -1
  109. package/dist/plugins/replay.mjs.map +0 -1
  110. package/dist/provider.d.mts.map +0 -1
  111. package/dist/provider.mjs.map +0 -1
  112. package/dist/react-error-handler.d.mts.map +0 -1
  113. package/dist/react-error-handler.mjs.map +0 -1
  114. package/dist/tracking/api.d.mts.map +0 -1
  115. package/dist/tracking/api.mjs.map +0 -1
  116. package/dist/tracking/device.d.mts.map +0 -1
  117. package/dist/tracking/device.mjs.map +0 -1
  118. package/dist/tracking/geo.d.mts.map +0 -1
  119. package/dist/tracking/geo.mjs.map +0 -1
  120. package/dist/tracking/session.d.mts.map +0 -1
  121. package/dist/tracking/session.mjs.map +0 -1
  122. package/dist/util/bot.d.mts.map +0 -1
  123. package/dist/util/bot.mjs.map +0 -1
  124. package/dist/util/global.d.mts.map +0 -1
  125. package/dist/util/global.mjs.map +0 -1
  126. package/dist/util/log.d.mts.map +0 -1
  127. package/dist/util/log.mjs.map +0 -1
  128. package/dist/util/stringify.d.mts.map +0 -1
  129. package/dist/util/stringify.mjs.map +0 -1
package/dist/sw.mjs CHANGED
@@ -1,2 +1,2 @@
1
1
  // AUTOGENERATED by scripts/build-sw.ts — do not edit.
2
- export const SW_SCRIPT = "(function(){try{self[`workbox:core:7.4.0`]&&_()}catch{}let e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n};var t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}};let n=(e,t)=>t.some(t=>e instanceof t),r,i;function a(){return r||=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction]}function o(){return i||=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey]}let s=new WeakMap,c=new WeakMap,l=new WeakMap,u=new WeakMap,d=new WeakMap;function f(e){let t=new Promise((t,n)=>{let r=()=>{e.removeEventListener(`success`,i),e.removeEventListener(`error`,a)},i=()=>{t(y(e.result)),r()},a=()=>{n(e.error),r()};e.addEventListener(`success`,i),e.addEventListener(`error`,a)});return t.then(t=>{t instanceof IDBCursor&&s.set(t,e)}).catch(()=>{}),d.set(t,e),t}function p(e){if(c.has(e))return;let t=new Promise((t,n)=>{let r=()=>{e.removeEventListener(`complete`,i),e.removeEventListener(`error`,a),e.removeEventListener(`abort`,a)},i=()=>{t(),r()},a=()=>{n(e.error||new DOMException(`AbortError`,`AbortError`)),r()};e.addEventListener(`complete`,i),e.addEventListener(`error`,a),e.addEventListener(`abort`,a)});c.set(e,t)}let m={get(e,t,n){if(e instanceof IDBTransaction){if(t===`done`)return c.get(e);if(t===`objectStoreNames`)return e.objectStoreNames||l.get(e);if(t===`store`)return n.objectStoreNames[1]?void 0:n.objectStore(n.objectStoreNames[0])}return y(e[t])},set(e,t,n){return e[t]=n,!0},has(e,t){return e instanceof IDBTransaction&&(t===`done`||t===`store`)?!0:t in e}};function h(e){m=e(m)}function g(e){return e===IDBDatabase.prototype.transaction&&!(`objectStoreNames`in IDBTransaction.prototype)?function(t,...n){let r=e.call(b(this),t,...n);return l.set(r,t.sort?t.sort():[t]),y(r)}:o().includes(e)?function(...t){return e.apply(b(this),t),y(s.get(this))}:function(...t){return y(e.apply(b(this),t))}}function v(e){return typeof e==`function`?g(e):(e instanceof IDBTransaction&&p(e),n(e,a())?new Proxy(e,m):e)}function y(e){if(e instanceof IDBRequest)return f(e);if(u.has(e))return u.get(e);let t=v(e);return t!==e&&(u.set(e,t),d.set(t,e)),t}let b=e=>d.get(e);function x(e,t,{blocked:n,upgrade:r,blocking:i,terminated:a}={}){let o=indexedDB.open(e,t),s=y(o);return r&&o.addEventListener(`upgradeneeded`,e=>{r(y(o.result),e.oldVersion,e.newVersion,y(o.transaction),e)}),n&&o.addEventListener(`blocked`,e=>n(e.oldVersion,e.newVersion,e)),s.then(e=>{a&&e.addEventListener(`close`,()=>a()),i&&e.addEventListener(`versionchange`,e=>i(e.oldVersion,e.newVersion,e))}).catch(()=>{}),s}let S=[`get`,`getKey`,`getAll`,`getAllKeys`,`count`],C=[`put`,`add`,`delete`,`clear`],w=new Map;function T(e,t){if(!(e instanceof IDBDatabase&&!(t in e)&&typeof t==`string`))return;if(w.get(t))return w.get(t);let n=t.replace(/FromIndex$/,``),r=t!==n,i=C.includes(n);if(!(n in(r?IDBIndex:IDBObjectStore).prototype)||!(i||S.includes(n)))return;let a=async function(e,...t){let a=this.transaction(e,i?`readwrite`:`readonly`),o=a.store;return r&&(o=o.index(t.shift())),(await Promise.all([o[n](...t),i&&a.done]))[0]};return w.set(t,a),a}h(e=>({...e,get:(t,n,r)=>T(t,n)||e.get(t,n,r),has:(t,n)=>!!T(t,n)||e.has(t,n)}));try{self[`workbox:background-sync:7.4.0`]&&_()}catch{}let E=`requests`,D=`queueName`;var O=class{constructor(){this._db=null}async addEntry(e){let t=(await this.getDb()).transaction(E,`readwrite`,{durability:`relaxed`});await t.store.add(e),await t.done}async getFirstEntryId(){return(await(await this.getDb()).transaction(E).store.openCursor())?.value.id}async getAllEntriesByQueueName(e){return await(await this.getDb()).getAllFromIndex(E,D,IDBKeyRange.only(e))||[]}async getEntryCountByQueueName(e){return(await this.getDb()).countFromIndex(E,D,IDBKeyRange.only(e))}async deleteEntry(e){await(await this.getDb()).delete(E,e)}async getFirstEntryByQueueName(e){return await this.getEndEntryFromIndex(IDBKeyRange.only(e),`next`)}async getLastEntryByQueueName(e){return await this.getEndEntryFromIndex(IDBKeyRange.only(e),`prev`)}async getEndEntryFromIndex(e,t){return(await(await this.getDb()).transaction(E).store.index(D).openCursor(e,t))?.value}async getDb(){return this._db||=await x(`workbox-background-sync`,3,{upgrade:this._upgradeDb}),this._db}_upgradeDb(e,t){t>0&&t<3&&e.objectStoreNames.contains(E)&&e.deleteObjectStore(E),e.createObjectStore(E,{autoIncrement:!0,keyPath:`id`}).createIndex(D,D,{unique:!1})}},k=class{constructor(e){this._queueName=e,this._queueDb=new O}async pushEntry(e){delete e.id,e.queueName=this._queueName,await this._queueDb.addEntry(e)}async unshiftEntry(e){let t=await this._queueDb.getFirstEntryId();t?e.id=t-1:delete e.id,e.queueName=this._queueName,await this._queueDb.addEntry(e)}async popEntry(){return this._removeEntry(await this._queueDb.getLastEntryByQueueName(this._queueName))}async shiftEntry(){return this._removeEntry(await this._queueDb.getFirstEntryByQueueName(this._queueName))}async getAll(){return await this._queueDb.getAllEntriesByQueueName(this._queueName)}async size(){return await this._queueDb.getEntryCountByQueueName(this._queueName)}async deleteEntry(e){await this._queueDb.deleteEntry(e)}async _removeEntry(e){return e&&await this.deleteEntry(e.id),e}};let A=[`method`,`referrer`,`referrerPolicy`,`mode`,`credentials`,`cache`,`redirect`,`integrity`,`keepalive`];var j=class e{static async fromRequest(t){let n={url:t.url,headers:{}};t.method!==`GET`&&(n.body=await t.clone().arrayBuffer());for(let[e,r]of t.headers.entries())n.headers[e]=r;for(let e of A)t[e]!==void 0&&(n[e]=t[e]);return new e(n)}constructor(e){e.mode===`navigate`&&(e.mode=`same-origin`),this._requestData=e}toObject(){let e=Object.assign({},this._requestData);return e.headers=Object.assign({},this._requestData.headers),e.body&&=e.body.slice(0),e}toRequest(){return new Request(this._requestData.url,this._requestData)}clone(){return new e(this.toObject())}};let M=`workbox-background-sync`,N=new Set,P=e=>{let t={request:new j(e.requestData).toRequest(),timestamp:e.timestamp};return e.metadata&&(t.metadata=e.metadata),t};var F=class{constructor(e,{forceSyncFallback:n,onSync:r,maxRetentionTime:i}={}){if(this._syncInProgress=!1,this._requestsAddedDuringSync=!1,N.has(e))throw new t(`duplicate-queue-name`,{name:e});N.add(e),this._name=e,this._onSync=r||this.replayRequests,this._maxRetentionTime=i||10080,this._forceSyncFallback=!!n,this._queueStore=new k(this._name),this._addSyncListener()}get name(){return this._name}async pushRequest(e){await this._addRequest(e,`push`)}async unshiftRequest(e){await this._addRequest(e,`unshift`)}async popRequest(){return this._removeRequest(`pop`)}async shiftRequest(){return this._removeRequest(`shift`)}async getAll(){let e=await this._queueStore.getAll(),t=Date.now(),n=[];for(let r of e){let e=this._maxRetentionTime*60*1e3;t-r.timestamp>e?await this._queueStore.deleteEntry(r.id):n.push(P(r))}return n}async size(){return await this._queueStore.size()}async _addRequest({request:e,metadata:t,timestamp:n=Date.now()},r){let i={requestData:(await j.fromRequest(e.clone())).toObject(),timestamp:n};switch(t&&(i.metadata=t),r){case`push`:await this._queueStore.pushEntry(i);break;case`unshift`:await this._queueStore.unshiftEntry(i);break}this._syncInProgress?this._requestsAddedDuringSync=!0:await this.registerSync()}async _removeRequest(e){let t=Date.now(),n;switch(e){case`pop`:n=await this._queueStore.popEntry();break;case`shift`:n=await this._queueStore.shiftEntry();break}if(n){let r=this._maxRetentionTime*60*1e3;return t-n.timestamp>r?this._removeRequest(e):P(n)}else return}async replayRequests(){let e;for(;e=await this.shiftRequest();)try{await fetch(e.request.clone())}catch{throw await this.unshiftRequest(e),new t(`queue-replay-failed`,{name:this._name})}}async registerSync(){if(`sync`in self.registration&&!this._forceSyncFallback)try{await self.registration.sync.register(`${M}:${this._name}`)}catch{}}_addSyncListener(){`sync`in self.registration&&!this._forceSyncFallback?self.addEventListener(`sync`,e=>{e.tag===`${M}:${this._name}`&&e.waitUntil((async()=>{this._syncInProgress=!0;let t;try{await this._onSync({queue:this})}catch(e){if(e instanceof Error)throw t=e,t}finally{this._requestsAddedDuringSync&&!(t&&!e.lastChance)&&await this.registerSync(),this._syncInProgress=!1,this._requestsAddedDuringSync=!1}})())}):this._onSync({queue:this})}static get _queueNames(){return N}};let I={status:202,statusText:`Queued`};async function L(e){let t=await self.clients.matchAll({includeUncontrolled:!1});for(let n of t)n.postMessage(e)}async function R(e){let t=await e.getAll(),n=0;for(let e of t){let t=e.metadata;typeof t?.bytes==`number`&&(n+=t.bytes)}return n}async function z(e,t){let n=await R(e);for(;n+t>10485760;){let t=await e.shiftRequest();if(!t)break;let r=t.metadata;n-=r?.bytes??0,await L({type:`interfere.sw.dropped`,url:t.request.url,reason:`queue_size_cap`})}}async function B(e,t){let n=t.clone(),r=(await n.clone().arrayBuffer()).byteLength;await z(e,r),await e.pushRequest({request:n,metadata:{bytes:r}}),await L({type:`interfere.sw.queued`,url:t.url})}let V=new F(`interfere-ingest`,{maxRetentionTime:1440,onSync:async({queue:e})=>{let t=await e.shiftRequest();for(;t;){try{let e=await fetch(t.request.clone());if(!e.ok&&(e.status>=500||e.status===429))throw Error(`retry status ${e.status}`);await L({type:`interfere.sw.replayed`,url:t.request.url})}catch(n){throw await e.unshiftRequest(t),n}t=await e.shiftRequest()}}});self.addEventListener(`install`,()=>{self.skipWaiting()}),self.addEventListener(`activate`,e=>{e.waitUntil((async()=>{await self.clients.claim(),V.replayRequests().catch(()=>{})})())}),self.addEventListener(`fetch`,e=>{let{request:t}=e;if(t.method!==`POST`)return;let n;try{n=new URL(t.url)}catch{return}n.pathname.startsWith(`/api/interfere/`)&&e.respondWith((async()=>{try{let e=await fetch(t.clone());return e.ok?e:e.status>=500||e.status===429?(await B(V,t),new Response(null,I)):e}catch{return await B(V,t),new Response(null,I)}})())})})();";
2
+ export const SW_SCRIPT = "(function(){try{self[`workbox:core:7.4.0`]&&_()}catch{}let e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n};var t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}};let n=(e,t)=>t.some(t=>e instanceof t),r,i;function a(){return r||=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction]}function o(){return i||=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey]}let s=new WeakMap,c=new WeakMap,l=new WeakMap,u=new WeakMap,d=new WeakMap;function f(e){let t=new Promise((t,n)=>{let r=()=>{e.removeEventListener(`success`,i),e.removeEventListener(`error`,a)},i=()=>{t(y(e.result)),r()},a=()=>{n(e.error),r()};e.addEventListener(`success`,i),e.addEventListener(`error`,a)});return t.then(t=>{t instanceof IDBCursor&&s.set(t,e)}).catch(()=>{}),d.set(t,e),t}function p(e){if(c.has(e))return;let t=new Promise((t,n)=>{let r=()=>{e.removeEventListener(`complete`,i),e.removeEventListener(`error`,a),e.removeEventListener(`abort`,a)},i=()=>{t(),r()},a=()=>{n(e.error||new DOMException(`AbortError`,`AbortError`)),r()};e.addEventListener(`complete`,i),e.addEventListener(`error`,a),e.addEventListener(`abort`,a)});c.set(e,t)}let m={get(e,t,n){if(e instanceof IDBTransaction){if(t===`done`)return c.get(e);if(t===`objectStoreNames`)return e.objectStoreNames||l.get(e);if(t===`store`)return n.objectStoreNames[1]?void 0:n.objectStore(n.objectStoreNames[0])}return y(e[t])},set(e,t,n){return e[t]=n,!0},has(e,t){return e instanceof IDBTransaction&&(t===`done`||t===`store`)?!0:t in e}};function h(e){m=e(m)}function g(e){return e===IDBDatabase.prototype.transaction&&!(`objectStoreNames`in IDBTransaction.prototype)?function(t,...n){let r=e.call(b(this),t,...n);return l.set(r,t.sort?t.sort():[t]),y(r)}:o().includes(e)?function(...t){return e.apply(b(this),t),y(s.get(this))}:function(...t){return y(e.apply(b(this),t))}}function v(e){return typeof e==`function`?g(e):(e instanceof IDBTransaction&&p(e),n(e,a())?new Proxy(e,m):e)}function y(e){if(e instanceof IDBRequest)return f(e);if(u.has(e))return u.get(e);let t=v(e);return t!==e&&(u.set(e,t),d.set(t,e)),t}let b=e=>d.get(e);function x(e,t,{blocked:n,upgrade:r,blocking:i,terminated:a}={}){let o=indexedDB.open(e,t),s=y(o);return r&&o.addEventListener(`upgradeneeded`,e=>{r(y(o.result),e.oldVersion,e.newVersion,y(o.transaction),e)}),n&&o.addEventListener(`blocked`,e=>n(e.oldVersion,e.newVersion,e)),s.then(e=>{a&&e.addEventListener(`close`,()=>a()),i&&e.addEventListener(`versionchange`,e=>i(e.oldVersion,e.newVersion,e))}).catch(()=>{}),s}let S=[`get`,`getKey`,`getAll`,`getAllKeys`,`count`],C=[`put`,`add`,`delete`,`clear`],w=new Map;function T(e,t){if(!(e instanceof IDBDatabase&&!(t in e)&&typeof t==`string`))return;if(w.get(t))return w.get(t);let n=t.replace(/FromIndex$/,``),r=t!==n,i=C.includes(n);if(!(n in(r?IDBIndex:IDBObjectStore).prototype)||!(i||S.includes(n)))return;let a=async function(e,...t){let a=this.transaction(e,i?`readwrite`:`readonly`),o=a.store;return r&&(o=o.index(t.shift())),(await Promise.all([o[n](...t),i&&a.done]))[0]};return w.set(t,a),a}h(e=>({...e,get:(t,n,r)=>T(t,n)||e.get(t,n,r),has:(t,n)=>!!T(t,n)||e.has(t,n)}));try{self[`workbox:background-sync:7.4.0`]&&_()}catch{}let E=`requests`,D=`queueName`;var O=class{constructor(){this._db=null}async addEntry(e){let t=(await this.getDb()).transaction(E,`readwrite`,{durability:`relaxed`});await t.store.add(e),await t.done}async getFirstEntryId(){return(await(await this.getDb()).transaction(E).store.openCursor())?.value.id}async getAllEntriesByQueueName(e){return await(await this.getDb()).getAllFromIndex(E,D,IDBKeyRange.only(e))||[]}async getEntryCountByQueueName(e){return(await this.getDb()).countFromIndex(E,D,IDBKeyRange.only(e))}async deleteEntry(e){await(await this.getDb()).delete(E,e)}async getFirstEntryByQueueName(e){return await this.getEndEntryFromIndex(IDBKeyRange.only(e),`next`)}async getLastEntryByQueueName(e){return await this.getEndEntryFromIndex(IDBKeyRange.only(e),`prev`)}async getEndEntryFromIndex(e,t){return(await(await this.getDb()).transaction(E).store.index(D).openCursor(e,t))?.value}async getDb(){return this._db||=await x(`workbox-background-sync`,3,{upgrade:this._upgradeDb}),this._db}_upgradeDb(e,t){t>0&&t<3&&e.objectStoreNames.contains(E)&&e.deleteObjectStore(E),e.createObjectStore(E,{autoIncrement:!0,keyPath:`id`}).createIndex(D,D,{unique:!1})}},k=class{constructor(e){this._queueName=e,this._queueDb=new O}async pushEntry(e){delete e.id,e.queueName=this._queueName,await this._queueDb.addEntry(e)}async unshiftEntry(e){let t=await this._queueDb.getFirstEntryId();t?e.id=t-1:delete e.id,e.queueName=this._queueName,await this._queueDb.addEntry(e)}async popEntry(){return this._removeEntry(await this._queueDb.getLastEntryByQueueName(this._queueName))}async shiftEntry(){return this._removeEntry(await this._queueDb.getFirstEntryByQueueName(this._queueName))}async getAll(){return await this._queueDb.getAllEntriesByQueueName(this._queueName)}async size(){return await this._queueDb.getEntryCountByQueueName(this._queueName)}async deleteEntry(e){await this._queueDb.deleteEntry(e)}async _removeEntry(e){return e&&await this.deleteEntry(e.id),e}};let A=[`method`,`referrer`,`referrerPolicy`,`mode`,`credentials`,`cache`,`redirect`,`integrity`,`keepalive`];var j=class e{static async fromRequest(t){let n={url:t.url,headers:{}};t.method!==`GET`&&(n.body=await t.clone().arrayBuffer());for(let[e,r]of t.headers.entries())n.headers[e]=r;for(let e of A)t[e]!==void 0&&(n[e]=t[e]);return new e(n)}constructor(e){e.mode===`navigate`&&(e.mode=`same-origin`),this._requestData=e}toObject(){let e=Object.assign({},this._requestData);return e.headers=Object.assign({},this._requestData.headers),e.body&&=e.body.slice(0),e}toRequest(){return new Request(this._requestData.url,this._requestData)}clone(){return new e(this.toObject())}};let M=`workbox-background-sync`,N=new Set,P=e=>{let t={request:new j(e.requestData).toRequest(),timestamp:e.timestamp};return e.metadata&&(t.metadata=e.metadata),t};var F=class{constructor(e,{forceSyncFallback:n,onSync:r,maxRetentionTime:i}={}){if(this._syncInProgress=!1,this._requestsAddedDuringSync=!1,N.has(e))throw new t(`duplicate-queue-name`,{name:e});N.add(e),this._name=e,this._onSync=r||this.replayRequests,this._maxRetentionTime=i||10080,this._forceSyncFallback=!!n,this._queueStore=new k(this._name),this._addSyncListener()}get name(){return this._name}async pushRequest(e){await this._addRequest(e,`push`)}async unshiftRequest(e){await this._addRequest(e,`unshift`)}async popRequest(){return this._removeRequest(`pop`)}async shiftRequest(){return this._removeRequest(`shift`)}async getAll(){let e=await this._queueStore.getAll(),t=Date.now(),n=[];for(let r of e){let e=this._maxRetentionTime*60*1e3;t-r.timestamp>e?await this._queueStore.deleteEntry(r.id):n.push(P(r))}return n}async size(){return await this._queueStore.size()}async _addRequest({request:e,metadata:t,timestamp:n=Date.now()},r){let i={requestData:(await j.fromRequest(e.clone())).toObject(),timestamp:n};switch(t&&(i.metadata=t),r){case`push`:await this._queueStore.pushEntry(i);break;case`unshift`:await this._queueStore.unshiftEntry(i);break}this._syncInProgress?this._requestsAddedDuringSync=!0:await this.registerSync()}async _removeRequest(e){let t=Date.now(),n;switch(e){case`pop`:n=await this._queueStore.popEntry();break;case`shift`:n=await this._queueStore.shiftEntry();break}if(n){let r=this._maxRetentionTime*60*1e3;return t-n.timestamp>r?this._removeRequest(e):P(n)}else return}async replayRequests(){let e;for(;e=await this.shiftRequest();)try{await fetch(e.request.clone())}catch{throw await this.unshiftRequest(e),new t(`queue-replay-failed`,{name:this._name})}}async registerSync(){if(`sync`in self.registration&&!this._forceSyncFallback)try{await self.registration.sync.register(`${M}:${this._name}`)}catch{}}_addSyncListener(){`sync`in self.registration&&!this._forceSyncFallback?self.addEventListener(`sync`,e=>{e.tag===`${M}:${this._name}`&&e.waitUntil((async()=>{this._syncInProgress=!0;let t;try{await this._onSync({queue:this})}catch(e){if(e instanceof Error)throw t=e,t}finally{this._requestsAddedDuringSync&&!(t&&!e.lastChance)&&await this.registerSync(),this._syncInProgress=!1,this._requestsAddedDuringSync=!1}})())}):this._onSync({queue:this})}static get _queueNames(){return N}};let I={status:202,statusText:`Queued`};async function L(e){let t=await self.clients.matchAll({includeUncontrolled:!1});for(let n of t)n.postMessage(e)}async function R(e){let t=await e.getAll(),n=0;for(let e of t){let t=e.metadata;typeof t?.bytes==`number`&&(n+=t.bytes)}return n}async function z(e,t){let n=await R(e);for(;n+t>10485760;){let t=await e.shiftRequest();if(!t)break;let r=t.metadata;n-=r?.bytes??0,await L({type:`interfere.sw.dropped`,url:t.request.url,reason:`queue_size_cap`})}}async function B(e,t){let n=t.clone(),r=(await n.clone().arrayBuffer()).byteLength;await z(e,r),await e.pushRequest({request:n,metadata:{bytes:r}}),await L({type:`interfere.sw.queued`,url:t.url})}let V=new F(`interfere-ingest`,{maxRetentionTime:1440,onSync:async({queue:e})=>{let t=await e.shiftRequest();for(;t;){try{let e=await fetch(t.request.clone());if(!e.ok&&(e.status>=500||e.status===429))throw Error(`retry status ${e.status}`);await L({type:`interfere.sw.replayed`,url:t.request.url})}catch(n){throw await e.unshiftRequest(t),n}t=await e.shiftRequest()}}});self.addEventListener(`install`,()=>{self.skipWaiting()}),self.addEventListener(`activate`,e=>{e.waitUntil((async()=>{await self.clients.claim(),V.replayRequests().catch(()=>void 0)})())}),self.addEventListener(`fetch`,e=>{let{request:t}=e;if(t.method!==`POST`)return;let n;try{n=new URL(t.url)}catch{return}n.pathname.startsWith(`/api/interfere/`)&&e.respondWith((async()=>{try{let e=await fetch(t.clone());return e.ok?e:e.status>=500||e.status===429?(await B(V,t),new Response(null,I)):e}catch{return await B(V,t),new Response(null,I)}})())})})();";
@@ -37,6 +37,7 @@ declare class SessionTracker {
37
37
  getIdentity(): IdentifyParams | null;
38
38
  identify(params: IdentifyParams): Promise<void>;
39
39
  clearIdentity(): void;
40
+ reset(): void;
40
41
  dispose(): void;
41
42
  private requestHeaders;
42
43
  private ensureSynced;
@@ -1 +1 @@
1
- import{createLogger}from"../util/log.mjs";import{appendPathBeforeQuery}from"../internal/config.mjs";import{DeviceManager}from"./device.mjs";import{GeoDetector}from"./geo.mjs";import{SessionManager}from"./session.mjs";const log=createLogger(`tracking`);var SessionTracker=class{target;fetcher;device;geo;mgr=null;currentIdentity=null;identifiedSessionId=null;syncedSessionId=null;syncAttemptMs=0;generation=0;constructor(opts){this.target=opts.target,this.fetcher=opts.fetcher??globalThis.fetch.bind(globalThis),this.device=opts.device??new DeviceManager,this.geo=opts.geo??new GeoDetector}start(){this.device.init(),this.geo.detect(),this.mgr=new SessionManager(id=>{this.onRotate(id).catch(()=>{})})}sessionId(){let id=this.mgr?.getSessionId()??null;return id&&this.ensureSynced(id),id}windowId(){return this.mgr?.getWindowId()??null}getDeviceId(){return this.device.getDeviceId()}getFpHash(){return this.device.getFpHash()}headers(){let h={},sid=this.mgr?.getSessionId()??null;sid&&(h[`x-interfere-session`]=sid);let wid=this.mgr?.getWindowId()??null;wid&&(h[`x-interfere-window`]=wid);let did=this.device.getDeviceId();return did&&(h[`x-interfere-device`]=did),h}getIdentity(){return this.currentIdentity}async identify(params){if(!this.mgr)return;let sessionId=this.mgr.getSessionId();if(this.identifiedSessionId===sessionId){log.debug(`skipped, already identified for session %s`,sessionId);return}this.currentIdentity=params,this.identifiedSessionId=sessionId;let gen=this.generation,[fpHash,country]=await Promise.all([this.device.whenFingerprintReady(),this.geo.detect()]),deviceId=this.device.getDeviceId();if(gen!==this.generation){this.identifiedSessionId=null;return}log.info(`POST session %s → user %s`,sessionId,params.identifier),this.fetcher(appendPathBeforeQuery(this.target.url,`/identify`),{method:`POST`,headers:this.requestHeaders(),body:JSON.stringify({sessionId,deviceId,fpHash,...params,...country&&{country}}),keepalive:!0,signal:AbortSignal.timeout(1e4)}).catch(()=>{this.identifiedSessionId=null,log.warn(`identify failed for session %s`,sessionId)})}clearIdentity(){this.currentIdentity=null,this.identifiedSessionId=null}dispose(){this.generation+=1,this.clearIdentity(),this.syncedSessionId=null,this.syncAttemptMs=0,this.mgr=null}requestHeaders(){return{...Object.fromEntries(this.target.headers.entries()),...this.headers()}}ensureSynced(sessionId){this.syncedSessionId!==sessionId&&(Date.now()-this.syncAttemptMs<5e3||this.syncSession(sessionId,this.device.getDeviceId(),this.device.getFpHash()))}syncSession(sessionId,deviceId,fpHash){this.syncAttemptMs=Date.now(),this.syncedSessionId=sessionId,this.fetcher(this.target.url,{method:`POST`,headers:this.requestHeaders(),body:JSON.stringify({sessionId,deviceId,fpHash}),keepalive:!0,signal:AbortSignal.timeout(1e4)}).then(res=>{res.ok||(this.syncedSessionId=null)}).catch(()=>{this.syncedSessionId=null,log.warn(`session sync failed, will retry`)})}async onRotate(sessionId){this.syncedSessionId=null,this.syncAttemptMs=Date.now();let gen=this.generation,fpHash=await this.device.whenFingerprintReady(),deviceId=this.device.getDeviceId();gen===this.generation&&(log.debug(`POST session %s (device=%s fp=%s)`,sessionId,deviceId??`pending`,fpHash??`none`),this.syncSession(sessionId,deviceId,fpHash))}};export{SessionTracker};
1
+ import{createLogger}from"../util/log.mjs";import{appendPathBeforeQuery}from"../internal/config.mjs";import{DeviceManager}from"./device.mjs";import{GeoDetector}from"./geo.mjs";import{SessionManager}from"./session.mjs";const log=createLogger(`tracking`);var SessionTracker=class{target;fetcher;device;geo;mgr=null;currentIdentity=null;identifiedSessionId=null;syncedSessionId=null;syncAttemptMs=0;generation=0;constructor(opts){this.target=opts.target,this.fetcher=opts.fetcher??globalThis.fetch.bind(globalThis),this.device=opts.device??new DeviceManager,this.geo=opts.geo??new GeoDetector}start(){this.device.init(),this.geo.detect(),this.mgr=new SessionManager(id=>{this.onRotate(id).catch(()=>void 0)})}sessionId(){let id=this.mgr?.getSessionId()??null;return id&&this.ensureSynced(id),id}windowId(){return this.mgr?.getWindowId()??null}getDeviceId(){return this.device.getDeviceId()}getFpHash(){return this.device.getFpHash()}headers(){let h={},sid=this.mgr?.getSessionId()??null;sid&&(h[`x-interfere-session`]=sid);let wid=this.mgr?.getWindowId()??null;wid&&(h[`x-interfere-window`]=wid);let did=this.device.getDeviceId();return did&&(h[`x-interfere-device`]=did),h}getIdentity(){return this.currentIdentity}async identify(params){if(!this.mgr)return;let sessionId=this.mgr.getSessionId();if(this.identifiedSessionId===sessionId){log.debug(`skipped, already identified for session %s`,sessionId);return}this.currentIdentity=params,this.identifiedSessionId=sessionId;let gen=this.generation,[fpHash,country]=await Promise.all([this.device.whenFingerprintReady(),this.geo.detect()]),deviceId=this.device.getDeviceId();if(gen!==this.generation){this.identifiedSessionId=null;return}log.info(`POST session %s → identify`,sessionId),this.fetcher(appendPathBeforeQuery(this.target.url,`/identify`),{method:`POST`,headers:this.requestHeaders(),body:JSON.stringify({sessionId,deviceId,fpHash,...params,...country&&{country}}),keepalive:!0,signal:AbortSignal.timeout(1e4)}).catch(()=>{this.identifiedSessionId=null,log.warn(`identify failed for session %s`,sessionId)})}clearIdentity(){this.currentIdentity=null,this.identifiedSessionId=null}reset(){this.clearIdentity(),this.mgr?.reset()}dispose(){this.generation+=1,this.clearIdentity(),this.syncedSessionId=null,this.syncAttemptMs=0,this.mgr=null}requestHeaders(){return{...Object.fromEntries(this.target.headers.entries()),...this.headers()}}ensureSynced(sessionId){this.syncedSessionId!==sessionId&&(Date.now()-this.syncAttemptMs<5e3||this.syncSession(sessionId,this.device.getDeviceId(),this.device.getFpHash()))}syncSession(sessionId,deviceId,fpHash){this.syncAttemptMs=Date.now(),this.syncedSessionId=sessionId,this.fetcher(this.target.url,{method:`POST`,headers:this.requestHeaders(),body:JSON.stringify({sessionId,deviceId,fpHash}),keepalive:!0,signal:AbortSignal.timeout(1e4)}).then(res=>{res.ok||(this.syncedSessionId=null)}).catch(()=>{this.syncedSessionId=null,log.warn(`session sync failed, will retry`)})}async onRotate(sessionId){this.syncedSessionId=null,this.syncAttemptMs=Date.now();let gen=this.generation,fpHash=await this.device.whenFingerprintReady(),deviceId=this.device.getDeviceId();gen===this.generation&&(log.debug(`POST session %s (device=%s fp=%s)`,sessionId,deviceId??`pending`,fpHash??`none`),this.syncSession(sessionId,deviceId,fpHash))}};export{SessionTracker};
@@ -12,6 +12,7 @@ declare class SessionManager {
12
12
  constructor(onRotate?: OnRotate);
13
13
  getSessionId(): SessionId;
14
14
  getWindowId(): string;
15
+ reset(): SessionId;
15
16
  private restore;
16
17
  private rotate;
17
18
  private touch;
@@ -1 +1 @@
1
- import{v7}from"uuid";const SESSION_ID_KEY=`interfere:session_id`,LAST_ACTIVITY_KEY=`interfere:last_activity`,WINDOW_ID_KEY=`interfere:window_id`;function tryStorage(type){try{let s=globalThis[type],key=`__interfere_probe__`;return s.setItem(key,`1`),s.removeItem(key),s}catch{return null}}var SessionManager=class{sessionId=null;windowId=null;local;session;onRotate=null;lastActivityMs=0;constructor(onRotate){this.local=tryStorage(`localStorage`),this.session=tryStorage(`sessionStorage`),this.onRotate=onRotate??null,this.restore()}getSessionId(){return this.sessionId&&!this.isExpired()?(this.touch(),this.sessionId):this.rotate()}getWindowId(){if(this.windowId)return this.windowId;let stored=this.session?.getItem(WINDOW_ID_KEY);return stored?(this.windowId=stored,stored):(this.windowId=`win_${crypto.randomUUID()}`,this.session?.setItem(WINDOW_ID_KEY,this.windowId),this.windowId)}restore(){let stored=this.local?.getItem(SESSION_ID_KEY);stored&&!this.isExpired()?(this.sessionId=stored,this.touch()):this.rotate()}rotate(){return this.sessionId=v7(),this.local?.setItem(SESSION_ID_KEY,this.sessionId),this.touch(),this.onRotate?.(this.sessionId),this.sessionId}touch(){this.lastActivityMs=Date.now(),this.local?.setItem(LAST_ACTIVITY_KEY,String(this.lastActivityMs))}isExpired(){let raw=this.local?.getItem(LAST_ACTIVITY_KEY),ts=raw?Number(raw):this.lastActivityMs;return ts===0?!0:Date.now()-ts>18e5}};export{SessionManager};
1
+ import{v7}from"uuid";const SESSION_ID_KEY=`interfere:session_id`,LAST_ACTIVITY_KEY=`interfere:last_activity`,WINDOW_ID_KEY=`interfere:window_id`;function tryStorage(type){try{let s=globalThis[type],key=`__interfere_probe__`;return s.setItem(key,`1`),s.removeItem(key),s}catch{return null}}var SessionManager=class{sessionId=null;windowId=null;local;session;onRotate=null;lastActivityMs=0;constructor(onRotate){this.local=tryStorage(`localStorage`),this.session=tryStorage(`sessionStorage`),this.onRotate=onRotate??null,this.restore()}getSessionId(){return this.sessionId&&!this.isExpired()?(this.touch(),this.sessionId):this.rotate()}getWindowId(){if(this.windowId)return this.windowId;let stored=this.session?.getItem(WINDOW_ID_KEY);return stored?(this.windowId=stored,stored):(this.windowId=`win_${crypto.randomUUID()}`,this.session?.setItem(WINDOW_ID_KEY,this.windowId),this.windowId)}reset(){return this.rotate()}restore(){let stored=this.local?.getItem(SESSION_ID_KEY);stored&&!this.isExpired()?(this.sessionId=stored,this.touch()):this.rotate()}rotate(){return this.sessionId=v7(),this.local?.setItem(SESSION_ID_KEY,this.sessionId),this.touch(),this.onRotate?.(this.sessionId),this.sessionId}touch(){this.lastActivityMs=Date.now(),this.local?.setItem(LAST_ACTIVITY_KEY,String(this.lastActivityMs))}isExpired(){let raw=this.local?.getItem(LAST_ACTIVITY_KEY),ts=raw?Number(raw):this.lastActivityMs;return ts===0?!0:Date.now()-ts>18e5}};export{SessionManager};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interfere/react",
3
- "version": "10.0.1-canary.1",
3
+ "version": "11.0.0-canary.3",
4
4
  "license": "MIT",
5
5
  "description": "Client-side React SDK for Interfere. Error tracking, session replay, and analytics.",
6
6
  "keywords": [
@@ -17,7 +17,7 @@
17
17
  "author": "Interfere <support@interfere.com> (https://interfere.com)",
18
18
  "repository": {
19
19
  "type": "git",
20
- "url": "git+https://github.com/interfere-inc/interfere.git",
20
+ "url": "git+https://interfere.ghe.com/interfere/interfere.git",
21
21
  "directory": "src/packages/public/react"
22
22
  },
23
23
  "files": [
@@ -82,42 +82,37 @@
82
82
  "scripts": {
83
83
  "build": "tsdown",
84
84
  "test": "vitest run",
85
- "typecheck": "tsgo --noEmit --incremental",
85
+ "typecheck": "tsgo --noEmit",
86
86
  "typegen": "bun run scripts/build-sw.ts"
87
87
  },
88
88
  "dependencies": {
89
89
  "@fingerprintjs/fingerprintjs": "^5.2.0",
90
90
  "@interfere/constants": "^9.0.2-canary.0",
91
- "@interfere/types": "^9.0.3-canary.0",
91
+ "@interfere/types": "^9.0.3-canary.2",
92
92
  "@opentelemetry/api": "^1.9.1",
93
- "@opentelemetry/api-logs": "^0.218.0",
93
+ "@opentelemetry/api-logs": "^0.219.0",
94
94
  "@opentelemetry/context-zone": "^2.7.1",
95
95
  "@opentelemetry/core": "^2.7.0",
96
- "@opentelemetry/exporter-logs-otlp-http": "^0.218.0",
97
- "@opentelemetry/exporter-metrics-otlp-http": "^0.218.0",
98
- "@opentelemetry/exporter-trace-otlp-http": "^0.218.0",
99
- "@opentelemetry/instrumentation": "^0.218.0",
100
- "@opentelemetry/instrumentation-browser-navigation": "^0.11.0",
101
- "@opentelemetry/instrumentation-document-load": "^0.63.0",
102
- "@opentelemetry/instrumentation-fetch": "^0.218.0",
103
- "@opentelemetry/instrumentation-long-task": "^0.62.0",
104
- "@opentelemetry/instrumentation-user-interaction": "^0.62.0",
105
- "@opentelemetry/instrumentation-xml-http-request": "^0.218.0",
106
- "@opentelemetry/opentelemetry-browser-detector": "^0.218.0",
107
- "@opentelemetry/otlp-transformer": "^0.218.0",
96
+ "@opentelemetry/instrumentation": "^0.219.0",
97
+ "@opentelemetry/instrumentation-browser-navigation": "^0.12.0",
98
+ "@opentelemetry/instrumentation-document-load": "^0.64.0",
99
+ "@opentelemetry/instrumentation-fetch": "^0.219.0",
100
+ "@opentelemetry/instrumentation-long-task": "^0.63.0",
101
+ "@opentelemetry/instrumentation-user-interaction": "^0.63.0",
102
+ "@opentelemetry/instrumentation-xml-http-request": "^0.219.0",
103
+ "@opentelemetry/opentelemetry-browser-detector": "^0.219.0",
104
+ "@opentelemetry/otlp-transformer": "^0.219.0",
108
105
  "@opentelemetry/resources": "^2.7.0",
109
- "@opentelemetry/sdk-logs": "^0.218.0",
106
+ "@opentelemetry/sdk-logs": "^0.219.0",
110
107
  "@opentelemetry/sdk-metrics": "^2.7.0",
111
108
  "@opentelemetry/sdk-trace-base": "^2.7.0",
112
109
  "@opentelemetry/sdk-trace-web": "^2.7.1",
113
- "@opentelemetry/web-common": "^0.218.0",
110
+ "@opentelemetry/web-common": "^0.219.0",
114
111
  "@ua-parser-js/pro-enterprise": "^2.0.6",
115
- "rrweb": "2.0.0-alpha.4",
112
+ "rrweb": "2.0.1",
116
113
  "uuid": "^14.0.0",
117
- "web-vitals": "^5.2.0",
118
- "workbox-background-sync": "^7.4.0",
119
- "workbox-routing": "^7.4.0",
120
- "workbox-strategies": "^7.4.0"
114
+ "web-vitals": "^5.3.0",
115
+ "workbox-background-sync": "^7.4.0"
121
116
  },
122
117
  "peerDependencies": {
123
118
  "react": "^19.2.6",
@@ -134,20 +129,20 @@
134
129
  "devDependencies": {
135
130
  "@interfere/test-utils": "^9.0.0",
136
131
  "@interfere/typescript-config": "^9.0.0",
137
- "@rrweb/types": "2.0.0-alpha.20",
138
- "@types/node": "^24.12.0",
139
- "@types/react": "19.2.15",
132
+ "@types/node": "^25.9.3",
133
+ "@types/react": "19.2.17",
140
134
  "@types/react-dom": "19.2.3",
141
- "@typescript/native-preview": "7.0.0-dev.20260522.1",
142
- "@vitest/browser": "4.1.7",
143
- "@vitest/browser-playwright": "4.1.7",
144
- "@vitest/coverage-v8": "^4.1.6",
135
+ "@typescript/native-preview": "7.0.0-dev.20260617.2",
136
+ "@vitest/browser": "4.1.9",
137
+ "@vitest/browser-playwright": "4.1.9",
138
+ "@vitest/coverage-v8": "4.1.9",
139
+ "fflate": "^0.8.3",
145
140
  "playwright": "^1.60.0",
146
141
  "react": "^19.2.6",
147
142
  "react-dom": "^19.2.6",
148
- "rolldown": "^1.0.0-rc.17",
149
- "tsdown": "^0.22.0",
150
- "vitest": "^4.1.6",
143
+ "rolldown": "^1.1.1",
144
+ "tsdown": "^0.22.2",
145
+ "vitest": "4.1.9",
151
146
  "vitest-browser-react": "2.2.0"
152
147
  }
153
148
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"api.d.mts","names":[],"sources":["../src/api.ts"],"mappings":";;;;;AAuCA;;;;;;;;iBAAgB,IAAA,GAAA,CACd,IAAA,UACA,EAAA,GAAK,IAAA,EAAM,IAAA,KAAS,CAAA,EACpB,OAAA,GAAU,WAAA,GACT,CAAA;;;;;;;;;iBAuCa,OAAA,CAAQ,KAAc"}
package/dist/api.mjs.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"api.mjs","names":[],"sources":["../src/api.ts"],"sourcesContent":["import {\n MECHANISM_TYPE,\n toError,\n toException,\n} from \"@interfere/types/sdk/errors\";\n\nimport {\n type Span,\n type SpanOptions,\n SpanStatusCode,\n trace,\n} from \"@opentelemetry/api\";\n\nimport { activeKernel } from \"./internal/kernel-registry.js\";\n\nconst TRACER_NAME = \"@interfere/react\";\n\nfunction tracer() {\n return trace.getTracer(TRACER_NAME);\n}\n\nfunction isPromise<T>(value: unknown): value is Promise<T> {\n return (\n !!value &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n typeof (value as { then?: unknown }).then === \"function\"\n );\n}\n\n/**\n * Wraps `fn` in an OTel span. The span becomes the active context for the\n * duration of the call (sync) or the awaited promise (async). Errors thrown\n * by `fn` are recorded on the span before re-throwing — caller still owns\n * the error.\n *\n * Works pre-init: `trace.getTracer()` returns a no-op tracer that still\n * runs `fn` and propagates its return value, so call sites don't need\n * defensive checks.\n */\nexport function span<T>(\n name: string,\n fn: (span: Span) => T,\n options?: SpanOptions\n): T {\n return tracer().startActiveSpan(name, options ?? {}, (s): T => {\n try {\n const result = fn(s);\n if (isPromise<T>(result)) {\n return result.then(\n (value) => {\n s.end();\n return value;\n },\n (err: unknown) => {\n const e = toError(err);\n s.recordException(e);\n s.setStatus({ code: SpanStatusCode.ERROR, message: e.message });\n s.end();\n throw err;\n }\n ) as T;\n }\n s.end();\n return result;\n } catch (err) {\n const e = toError(err);\n s.recordException(e);\n s.setStatus({ code: SpanStatusCode.ERROR, message: e.message });\n s.end();\n throw err;\n }\n });\n}\n\n/**\n * Captures an error. Records on the active OTel span (when present) AND on\n * the legacy envelope path. Accepts any thrown value; non-Error inputs that\n * carry structure (objects without a recoverable nested Error) are preserved\n * via `toException` so the original shape survives to the wire.\n *\n * Safe to call before init — drops silently if no kernel is registered.\n */\nexport function capture(error: unknown): void {\n activeKernel()?.recordException(toException(error), {\n mechanism: { type: MECHANISM_TYPE.manual.capture, handled: true },\n });\n}\n"],"mappings":"0LAiBA,SAAS,QAAS,CAChB,OAAO,MAAM,UAAU,kBAAW,CACpC,CAEA,SAAS,UAAa,MAAqC,CACzD,MACE,CAAC,CAAC,QACD,OAAO,OAAU,UAAY,OAAO,OAAU,aAC/C,OAAQ,MAA6B,MAAS,UAElD,CAYA,SAAgB,KACd,KACA,GACA,QACG,CACH,OAAO,OAAO,EAAE,gBAAgB,KAAM,SAAW,CAAC,EAAI,GAAS,CAC7D,GAAI,CACF,IAAM,OAAS,GAAG,CAAC,EAiBnB,OAhBI,UAAa,MAAM,EACd,OAAO,KACX,QACC,EAAE,IAAI,EACC,OAER,KAAiB,CAChB,IAAM,EAAI,QAAQ,GAAG,EAIrB,MAHA,EAAE,gBAAgB,CAAC,EACnB,EAAE,UAAU,CAAE,KAAM,eAAe,MAAO,QAAS,EAAE,OAAQ,CAAC,EAC9D,EAAE,IAAI,EACA,GACR,CACF,GAEF,EAAE,IAAI,EACC,OACT,OAAS,IAAK,CACZ,IAAM,EAAI,QAAQ,GAAG,EAIrB,MAHA,EAAE,gBAAgB,CAAC,EACnB,EAAE,UAAU,CAAE,KAAM,eAAe,MAAO,QAAS,EAAE,OAAQ,CAAC,EAC9D,EAAE,IAAI,EACA,GACR,CACF,CAAC,CACH,CAUA,SAAgB,QAAQ,MAAsB,CAC5C,aAAa,GAAG,gBAAgB,YAAY,KAAK,EAAG,CAClD,UAAW,CAAE,KAAM,eAAe,OAAO,QAAS,QAAS,EAAK,CAClE,CAAC,CACH"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"error-boundary.d.mts","names":[],"sources":["../src/error-boundary.tsx"],"mappings":";;;;UAciB,kBAAA;EACf,QAAA,EAAU,SAAA;EACV,QAAA,GAAW,SAAA,KAAc,KAAA,EAAO,KAAA,EAAO,KAAA,iBAAsB,SAAA;EAC7D,OAAA,IAAW,KAAA,EAAO,KAAA,EAAO,IAAA,EAAM,SAAA;AAAA;AAAA,UAGvB,kBAAA;EACR,KAAA,EAAO,KAAK;AAAA;;;;;;;;;cAWD,aAAA,SAAsB,SAAA,CACjC,kBAAA,EACA,kBAAA;EAAA,OAEgB,WAAA,kBAAW,OAAA,CAFT,qBAAA;EAGV,OAAA,EAAS,WAAA,QAAmB,gBAAA;EAE3B,KAAA,EAAO,kBAAA;EAAA,OAET,wBAAA,CAAyB,KAAA,EAAO,KAAA,GAAQ,kBAAA;EAItC,iBAAA,CACP,KAAA,EAAO,KAAA;IAAU,MAAA;EAAA,GACjB,IAAA,EAAM,SAAA;EAAA,iBAeS,KAAA;EAIR,MAAA,CAAA,GAAM,SAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"error-boundary.mjs","names":[],"sources":["../src/error-boundary.tsx"],"sourcesContent":["\"use client\";\n\nimport { MECHANISM_TYPE } from \"@interfere/types/sdk/errors\";\n\nimport {\n Component,\n type ContextType,\n type ErrorInfo,\n type ReactNode,\n} from \"react\";\n\nimport { captureReactError } from \"./internal/capture.js\";\nimport { InterfereContext } from \"./internal/react-context.js\";\n\nexport interface ErrorBoundaryProps {\n children: ReactNode;\n fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);\n onError?: (error: Error, info: ErrorInfo) => void;\n}\n\ninterface ErrorBoundaryState {\n error: Error | null;\n}\n\n/**\n * Catches render-phase React errors, reports them to the SDK, and renders a\n * fallback. Reads the kernel from `InterfereContext`; mounting outside the\n * provider is supported — capture is silently skipped, fallback + `onError`\n * still fire.\n *\n * Class component required — React has no hook-based error boundary API.\n */\nexport class ErrorBoundary extends Component<\n ErrorBoundaryProps,\n ErrorBoundaryState\n> {\n static override contextType = InterfereContext;\n declare context: ContextType<typeof InterfereContext>;\n\n override state: ErrorBoundaryState = { error: null };\n\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n return { error };\n }\n\n override componentDidCatch(\n error: Error & { digest?: string },\n info: ErrorInfo\n ) {\n captureReactError(\n this.context?.kernel ?? null,\n error,\n info.componentStack,\n {\n type: MECHANISM_TYPE.react.errorBoundary,\n handled: true,\n }\n );\n\n this.props.onError?.(error, info);\n }\n\n private readonly reset = () => {\n this.setState({ error: null });\n };\n\n override render() {\n const { error } = this.state;\n\n if (error) {\n const { fallback } = this.props;\n if (typeof fallback === \"function\") {\n return fallback(error, this.reset);\n }\n return fallback ?? null;\n }\n\n return this.props.children;\n }\n}\n"],"mappings":"mNAgCA,IAAa,cAAb,cAAmC,SAGjC,CACA,OAAgB,YAAc,iBAG9B,MAAqC,CAAE,MAAO,IAAK,EAEnD,OAAO,yBAAyB,MAAkC,CAChE,MAAO,CAAE,KAAM,CACjB,CAEA,kBACE,MACA,KACA,CACA,kBACE,KAAK,SAAS,QAAU,KACxB,MACA,KAAK,eACL,CACE,KAAM,eAAe,MAAM,cAC3B,QAAS,EACX,CACF,EAEA,KAAK,MAAM,UAAU,MAAO,IAAI,CAClC,CAEA,UAA+B,CAC7B,KAAK,SAAS,CAAE,MAAO,IAAK,CAAC,CAC/B,EAEA,QAAkB,CAChB,GAAM,CAAE,OAAU,KAAK,MAEvB,GAAI,MAAO,CACT,GAAM,CAAE,UAAa,KAAK,MAI1B,OAHI,OAAO,UAAa,WACf,SAAS,MAAO,KAAK,KAAK,EAE5B,UAAY,IACrB,CAEA,OAAO,KAAK,MAAM,QACpB,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"browser-context.d.mts","names":[],"sources":["../../src/internal/browser-context.ts"],"mappings":";;;iBA0GsB,cAAA,CAAA,GAAkB,OAAO,CAAC,cAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"browser-context.mjs","names":[],"sources":["../../src/internal/browser-context.ts"],"sourcesContent":["import type {\n BrowserContext,\n BrowserMetadata,\n DeviceMetadata,\n} from \"@interfere/types/sdk/plugins/context/browser\";\n\nimport { UAParser } from \"@ua-parser-js/pro-enterprise\";\nimport { InApps } from \"@ua-parser-js/pro-enterprise/extensions\";\nimport { isFrozenUA } from \"@ua-parser-js/pro-enterprise/helpers\";\n\ninterface NavigatorUABrand {\n brand: string;\n version: string;\n}\n\ninterface NavigatorUAData {\n brands?: NavigatorUABrand[];\n getHighEntropyValues?: (hints: string[]) => Promise<HighEntropyValues>;\n}\n\ninterface HighEntropyValues {\n bitness?: string;\n fullVersionList?: NavigatorUABrand[];\n platformVersion?: string;\n}\n\nconst NOT_A_BRAND_REGEX = /not.a.brand/i;\nconst HIGH_ENTROPY_HINTS = [\"bitness\", \"platformVersion\", \"fullVersionList\"];\n\nasync function getDeviceMetadata(): Promise<DeviceMetadata | null> {\n if (typeof navigator === \"undefined\") {\n return null;\n }\n\n const uaData = (navigator as Navigator & { userAgentData?: NavigatorUAData })\n .userAgentData;\n\n // `withClientHints()` resolves with UA-only data on non-Chromium and never\n // rejects. We separately request the raw high-entropy values so we can\n // surface `bitness`, the unrounded `platformVersion`, and the full\n // browser version — fields the parser folds into existing slots and\n // therefore loses precision on. `getHighEntropyValues` can reject with\n // `NotAllowedError` (Permissions Policy / Privacy Budget); swallow it.\n // The `InApps` extension is merged into the default regex map (see\n // `extend(defaultRegexes, extensions)` in ua-parser-js), so we still get\n // normal browser/OS detection plus tagging for in-app browsers (Slack,\n // Teams, Discord, VS Code, etc.) — those land as `browser.type: \"inapp\"`.\n const [parsed, hints] = await Promise.all([\n UAParser(navigator.userAgent, InApps).withClientHints(),\n uaData?.getHighEntropyValues\n ? uaData\n .getHighEntropyValues(HIGH_ENTROPY_HINTS)\n .catch((): HighEntropyValues => ({}))\n : Promise.resolve<HighEntropyValues>({}),\n ]);\n\n if (!parsed) {\n return null;\n }\n\n const fullVersion = hints.fullVersionList?.find(\n (b) => !NOT_A_BRAND_REGEX.test(b.brand)\n )?.version;\n\n return {\n ...parsed,\n browser: {\n ...parsed.browser,\n ...(fullVersion ? { fullVersion } : {}),\n },\n cpu: {\n ...parsed.cpu,\n ...(hints.bitness ? { bitness: hints.bitness } : {}),\n },\n os: {\n ...parsed.os,\n ...(hints.platformVersion ? { versionFull: hints.platformVersion } : {}),\n },\n frozenUa: isFrozenUA(navigator.userAgent),\n };\n}\n\nfunction getBrowserMetadata(): BrowserMetadata | null {\n if (\n [typeof navigator, typeof screen, typeof globalThis].some(\n (x) => x === \"undefined\"\n )\n ) {\n return null;\n }\n\n const { language } = navigator;\n\n return {\n language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n display: {\n screen: {\n height: screen.availHeight,\n width: screen.availWidth,\n orientation: screen.orientation?.type,\n },\n },\n };\n}\n\nexport async function collectContext(): Promise<BrowserContext> {\n return {\n runtime: \"browser\",\n browser: getBrowserMetadata(),\n device: await getDeviceMetadata(),\n };\n}\n"],"mappings":"4KA0BA,MAAM,kBAAoB,eACpB,mBAAqB,CAAC,UAAW,kBAAmB,iBAAiB,EAE3E,eAAe,mBAAoD,CACjE,GAAI,OAAO,UAAc,IACvB,OAAO,KAGT,IAAM,OAAU,UACb,cAYG,CAAC,OAAQ,OAAS,MAAM,QAAQ,IAAI,CACxC,SAAS,UAAU,UAAW,MAAM,EAAE,gBAAgB,EACtD,QAAQ,qBACJ,OACG,qBAAqB,kBAAkB,EACvC,WAAgC,CAAC,EAAE,EACtC,QAAQ,QAA2B,CAAC,CAAC,CAC3C,CAAC,EAED,GAAI,CAAC,OACH,OAAO,KAGT,IAAM,YAAc,MAAM,iBAAiB,KACxC,GAAM,CAAC,kBAAkB,KAAK,EAAE,KAAK,CACxC,GAAG,QAEH,MAAO,CACL,GAAG,OACH,QAAS,CACP,GAAG,OAAO,QACV,GAAI,YAAc,CAAE,WAAY,EAAI,CAAC,CACvC,EACA,IAAK,CACH,GAAG,OAAO,IACV,GAAI,MAAM,QAAU,CAAE,QAAS,MAAM,OAAQ,EAAI,CAAC,CACpD,EACA,GAAI,CACF,GAAG,OAAO,GACV,GAAI,MAAM,gBAAkB,CAAE,YAAa,MAAM,eAAgB,EAAI,CAAC,CACxE,EACA,SAAU,WAAW,UAAU,SAAS,CAC1C,CACF,CAEA,SAAS,oBAA6C,CACpD,GACE,CAAC,OAAO,UAAW,OAAO,OAAQ,OAAO,UAAU,EAAE,KAClD,GAAM,IAAM,WACf,EAEA,OAAO,KAGT,GAAM,CAAE,UAAa,UAErB,MAAO,CACL,SACA,SAAU,KAAK,eAAe,EAAE,gBAAgB,EAAE,SAClD,QAAS,CACP,OAAQ,CACN,OAAQ,OAAO,YACf,MAAO,OAAO,WACd,YAAa,OAAO,aAAa,IACnC,CACF,CACF,CACF,CAEA,eAAsB,gBAA0C,CAC9D,MAAO,CACL,QAAS,UACT,QAAS,mBAAmB,EAC5B,OAAQ,MAAM,kBAAkB,CAClC,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"capture-boundary.d.mts","names":[],"sources":["../../src/internal/capture-boundary.tsx"],"mappings":";;;;UASU,oBAAA;EACR,QAAA,EAAU,SAAS;AAAA;AAAA,UAGX,oBAAA;EACR,KAAA,EAAO,KAAK;AAAA;AAJO;AAAA;;;;AAIP;AA0Bd;;;;;;;;;;;;;;;;;AA9BqB,cA8BR,eAAA,SAAwB,SAAA,CACnC,oBAAA,EACA,oBAAA;EAAA,OAEgB,WAAA,kBAAW,OAAA,CAFP,qBAAA;EAGZ,OAAA,EAAS,WAAA,QAAmB,gBAAA;EAE3B,KAAA,EAAO,oBAAA;EAAA,OAET,wBAAA,CAAyB,KAAA,EAAO,KAAA,GAAQ,oBAAA;EAItC,MAAA,CAAA,GAAU,SAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"capture-boundary.mjs","names":[],"sources":["../../src/internal/capture-boundary.tsx"],"sourcesContent":["\"use client\";\n\nimport { MECHANISM_TYPE } from \"@interfere/types/sdk/errors\";\n\nimport { Component, type ContextType, type ReactNode } from \"react\";\n\nimport { captureReactError } from \"./capture.js\";\nimport { InterfereContext } from \"./react-context.js\";\n\ninterface CaptureBoundaryProps {\n children: ReactNode;\n}\n\ninterface CaptureBoundaryState {\n error: Error | null;\n}\n\n/**\n * Internal boundary used by `<InterfereProvider>` to capture render-phase\n * React errors without changing the app's UX.\n *\n * Unlike the public `ErrorBoundary`, this boundary always re-throws the\n * captured error in its `render()` so upstream boundaries — the customer's\n * own `ErrorBoundary`, Next.js's `error.tsx` / `global-error.tsx`, or React's\n * default unmount — keep control of what the user sees. The net effect:\n * zero visual change, full capture coverage for any render-phase error in\n * the subtree.\n *\n * Capture happens inside `getDerivedStateFromError` rather than\n * `componentDidCatch` because `componentDidCatch` does not fire on a boundary\n * that re-throws in render — React considers such a boundary to have failed\n * and skips its commit-phase lifecycle. The trade-off: we don't get\n * `errorInfo.componentStack` in this capture. Callers who want the component\n * tree should use the public `ErrorBoundary` (which renders a fallback and\n * therefore receives `componentDidCatch`), or pass\n * {@link reactErrorHandler} to `createRoot()`.\n *\n * `mechanism.handled` is `false` because from Interfere's perspective we\n * captured the error but did not render a fallback — propagation continues.\n */\nexport class CaptureBoundary extends Component<\n CaptureBoundaryProps,\n CaptureBoundaryState\n> {\n static override contextType = InterfereContext;\n declare context: ContextType<typeof InterfereContext>;\n\n override state: CaptureBoundaryState = { error: null };\n\n static getDerivedStateFromError(error: Error): CaptureBoundaryState {\n return { error };\n }\n\n override render(): ReactNode {\n if (this.state.error) {\n // Capture before re-throwing. `componentDidCatch` never fires when\n // render throws (React treats the boundary itself as failed), so the\n // capture has to happen here. `captureReactError` dedupes on the\n // error instance, so the multiple-render case in concurrent React\n // doesn't cause duplicate captures.\n captureReactError(this.context?.kernel ?? null, this.state.error, null, {\n type: MECHANISM_TYPE.react.captureBoundary,\n handled: false,\n });\n throw this.state.error;\n }\n return this.props.children;\n }\n}\n"],"mappings":"iMAwCA,IAAa,gBAAb,cAAqC,SAGnC,CACA,OAAgB,YAAc,iBAG9B,MAAuC,CAAE,MAAO,IAAK,EAErD,OAAO,yBAAyB,MAAoC,CAClE,MAAO,CAAE,KAAM,CACjB,CAEA,QAA6B,CAC3B,GAAI,KAAK,MAAM,MAUb,MAJA,kBAAkB,KAAK,SAAS,QAAU,KAAM,KAAK,MAAM,MAAO,KAAM,CACtE,KAAM,eAAe,MAAM,gBAC3B,QAAS,EACX,CAAC,EACK,KAAK,MAAM,MAEnB,OAAO,KAAK,MAAM,QACpB,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"capture.d.mts","names":[],"sources":["../../src/internal/capture.ts"],"mappings":";;;;;;AAoBA;;;;;;;;;;;;;iBAAgB,iBAAA,CACd,MAAA,EAAQ,MAAA,SACR,KAAA,EAAO,KAAA;EAAU,MAAA;AAAA,GACjB,cAAA,6BACA,SAAA,EAAW,cAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"capture.mjs","names":[],"sources":["../../src/internal/capture.ts"],"sourcesContent":["import { parseReactComponentStack } from \"@interfere/types/sdk/errors\";\nimport type { ErrorMechanism } from \"@interfere/types/sdk/plugins/payload/errors\";\n\nimport type { Kernel } from \"./kernel.js\";\n\n/**\n * Routes a React error through `kernel.recordException()`, attaching the\n * component stack reported by React as additional frames. Dedup of the\n * same `Error` instance caught by both the boundary and `window.onerror`\n * happens inside `recordException`.\n *\n * `error.digest` is React's identifier for an RSC server-side throw that\n * was sanitised and rebuilt on the client — promote it so enrichment can\n * fingerprint the redacted client capture into the same problem as the\n * unredacted server capture.\n *\n * Accepts a nullable kernel — error boundaries can mount outside the\n * `<InterfereProvider>` tree, in which case we silently skip capture and\n * let the host render its fallback / call its `onError` callback.\n */\nexport function captureReactError(\n kernel: Kernel | null,\n error: Error & { digest?: string },\n componentStack: string | null | undefined,\n mechanism: ErrorMechanism\n): void {\n if (!kernel) {\n return;\n }\n kernel.recordException(error, {\n mechanism,\n ...(componentStack\n ? { appendFrames: parseReactComponentStack(componentStack) }\n : {}),\n ...(error.digest ? { errorDigest: error.digest } : {}),\n });\n}\n"],"mappings":"kEAoBA,SAAgB,kBACd,OACA,MACA,eACA,UACM,CACD,QAGL,OAAO,gBAAgB,MAAO,CAC5B,UACA,GAAI,eACA,CAAE,aAAc,yBAAyB,cAAc,CAAE,EACzD,CAAC,EACL,GAAI,MAAM,OAAS,CAAE,YAAa,MAAM,MAAO,EAAI,CAAC,CACtD,CAAC,CACH"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.d.mts","names":[],"sources":["../../src/internal/config.ts"],"mappings":";UAUiB,YAAA;EACf,OAAA,EAAS,OAAO;EAChB,GAAA;AAAA;AAAA,UAGe,WAAA;EACf,OAAA,EAAS,OAAO;AAAA;AAAA,iBA0CF,cAAA,CACd,GAAA,UACA,GAAA,UACA,KAAA;AAAA,iBAWc,qBAAA,CAAsB,GAAA,UAAa,IAAY;AAAA,iBAO/C,cAAA,CAAA;EAhEC;;;;AACC;EAqEhB,gBAAA;EACA,MAAA,EAAQ,YAAA;;;;;;EAMR,MAAA,EAAQ,WAAA;EACR,OAAA,EAAS,YAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.mjs","names":[],"sources":["../../src/internal/config.ts"],"sourcesContent":["import { API_PATHS, API_URL } from \"@interfere/constants/api\";\nimport { resolveRoutePrefix } from \"@interfere/constants/route-prefix\";\n\nimport { getGlobal } from \"../util/global.js\";\n\nconst PUBLIC_KEY_QUERY = \"pk\";\nconst PROXY_MODE_GLOBAL = \"__INTERFERE_PROXY_MODE__\";\nconst TRAILING_SLASH_RE = /\\/$/;\nconst RELATIVE_URL_BASE = \"https://interfere.local\";\n\nexport interface IngestTarget {\n headers: Headers;\n url: string;\n}\n\nexport interface AuthHeaders {\n headers: Headers;\n}\n\nfunction resolvePublicKey(): string | undefined {\n // Vite plugin injects the key onto globalThis at build time\n const injected = getGlobal<string>(\"__INTERFERE_PUBLIC_KEY__\");\n if (injected) {\n return injected;\n }\n\n // Node / webpack / Next.js: read from process.env\n if (typeof process !== \"undefined\") {\n return process.env[\"INTERFERE_PUBLIC_KEY\"] ?? undefined;\n }\n\n return;\n}\n\nfunction resolveApiUrlOverride(): string | undefined {\n const injected = getGlobal<string>(\"__INTERFERE_API_URL__\");\n if (injected) {\n return injected;\n }\n\n return;\n}\n\nfunction isForceEnableActive(): boolean {\n return !!getGlobal<boolean>(\"__INTERFERE_FORCE_ENABLE__\");\n}\n\nfunction isProxyModeActive(): boolean {\n return !!getGlobal<boolean>(PROXY_MODE_GLOBAL);\n}\n\nfunction serializeUrl(input: string, url: URL): string {\n if (input.startsWith(\"/\")) {\n return `${url.pathname}${url.search}`;\n }\n return url.toString();\n}\n\nexport function withQueryParam(\n url: string,\n key: string,\n value: string\n): string {\n const next = new URL(url, RELATIVE_URL_BASE);\n next.searchParams.set(key, value);\n return serializeUrl(url, next);\n}\n\nfunction appendPublicKey(url: string, publicKey: string | undefined): string {\n return publicKey ? withQueryParam(url, PUBLIC_KEY_QUERY, publicKey) : url;\n}\n\nexport function appendPathBeforeQuery(url: string, path: string): string {\n const next = new URL(url, RELATIVE_URL_BASE);\n const suffix = path.startsWith(\"/\") ? path : `/${path}`;\n next.pathname = `${next.pathname.replace(TRAILING_SLASH_RE, \"\")}${suffix}`;\n return serializeUrl(url, next);\n}\n\nexport function resolveTargets(): {\n /**\n * Base URL for OTLP exports — the trace/metric exporters append the\n * sink path themselves. Avoids the kernel having to regex-strip\n * `/v2/sink` off the configured URL to derive a base.\n */\n collectorBaseUrl: string;\n config: IngestTarget;\n /**\n * Identity headers shared across every collector-bound request\n * (OTLP exporters, replay upload, session sync). One source of\n * truth for auth + force-enable.\n */\n ingest: AuthHeaders;\n session: IngestTarget;\n} {\n const publicKey = resolvePublicKey();\n const headers = new Headers({ \"content-type\": \"application/json\" });\n // Single chokepoint: every collector request (OTLP exporters, replay\n // upload, session, config) reuses these headers, so the force-enable\n // flag travels everywhere a customer event might. Production\n // collectors drop on this header (ENG-1356).\n if (isForceEnableActive()) {\n headers.set(\"x-interfere-force-enable\", \"1\");\n }\n\n const baseUrl =\n publicKey && !isProxyModeActive()\n ? (resolveApiUrlOverride() ?? API_URL)\n : resolveRoutePrefix();\n const authenticatedBaseUrl = appendPublicKey(baseUrl, publicKey);\n\n return {\n collectorBaseUrl: authenticatedBaseUrl,\n config: {\n url: appendPublicKey(`${baseUrl}${API_PATHS.CONFIG}`, publicKey),\n headers,\n },\n ingest: { headers },\n session: {\n url: appendPublicKey(`${baseUrl}${API_PATHS.SESSION}`, publicKey),\n headers,\n },\n };\n}\n"],"mappings":"oKAKA,MAEM,kBAAoB,MACpB,kBAAoB,0BAW1B,SAAS,kBAAuC,CAE9C,IAAM,SAAW,UAAkB,0BAA0B,EAC7D,GAAI,SACF,OAAO,SAIT,GAAI,OAAO,QAAY,IACrB,OAAO,QAAQ,IAAI,sBAA2B,IAAA,EAIlD,CAEA,SAAS,uBAA4C,CACnD,IAAM,SAAW,UAAkB,uBAAuB,EAC1D,GAAI,SACF,OAAO,QAIX,CAEA,SAAS,qBAA+B,CACtC,MAAO,CAAC,CAAC,UAAmB,4BAA4B,CAC1D,CAEA,SAAS,mBAA6B,CACpC,MAAO,CAAC,CAAC,UAAmB,0BAAiB,CAC/C,CAEA,SAAS,aAAa,MAAe,IAAkB,CAIrD,OAHI,MAAM,WAAW,GAAG,EACf,GAAG,IAAI,WAAW,IAAI,SAExB,IAAI,SAAS,CACtB,CAEA,SAAgB,eACd,IACA,IACA,MACQ,CACR,IAAM,KAAO,IAAI,IAAI,IAAK,iBAAiB,EAE3C,OADA,KAAK,aAAa,IAAI,IAAK,KAAK,EACzB,aAAa,IAAK,IAAI,CAC/B,CAEA,SAAS,gBAAgB,IAAa,UAAuC,CAC3E,OAAO,UAAY,eAAe,IAAK,KAAkB,SAAS,EAAI,GACxE,CAEA,SAAgB,sBAAsB,IAAa,KAAsB,CACvE,IAAM,KAAO,IAAI,IAAI,IAAK,iBAAiB,EACrC,OAAS,KAAK,WAAW,GAAG,EAAI,KAAO,IAAI,OAEjD,MADA,MAAK,SAAW,GAAG,KAAK,SAAS,QAAQ,kBAAmB,EAAE,IAAI,SAC3D,aAAa,IAAK,IAAI,CAC/B,CAEA,SAAgB,gBAed,CACA,IAAM,UAAY,iBAAiB,EAC7B,QAAU,IAAI,QAAQ,CAAE,eAAgB,kBAAmB,CAAC,EAK9D,oBAAoB,GACtB,QAAQ,IAAI,2BAA4B,GAAG,EAG7C,IAAM,QACJ,WAAa,CAAC,kBAAkB,EAC3B,sBAAsB,GAAK,QAC5B,mBAAmB,EAGzB,MAAO,CACL,iBAH2B,gBAAgB,QAAS,SAGf,EACrC,OAAQ,CACN,IAAK,gBAAgB,GAAG,UAAU,UAAU,SAAU,SAAS,EAC/D,OACF,EACA,OAAQ,CAAE,OAAQ,EAClB,QAAS,CACP,IAAK,gBAAgB,GAAG,UAAU,UAAU,UAAW,SAAS,EAChE,OACF,CACF,CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"consent.d.mts","names":[],"sources":["../../src/internal/consent.ts"],"mappings":";;;;cASa,eAAA;EAAA,SAGoB,SAAA;EAAA,SAAA,MAAA;AAAA;AAAA,iBAcjB,wBAAA,CAAyB,GAAA,EAAK,SAAA,GAAY,eAAe;AAAA,iBAIzD,gBAAA,CACd,QAAA,EAAU,eAAA,EACV,YAAA,EAAc,YAAY;AAAA,iBASZ,kBAAA,CACd,IAAA,EAAM,SAAA,EACN,YAAA,EAAc,YAAY;AAAA,iBAKZ,qBAAA,CAAsB,OAAA,GAAU,YAAA,GAAe,YAAY;AAAA,iBAI3D,iBAAA,CACd,OAAA,EAAS,YAAA,SACT,IAAA,EAAM,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"consent.mjs","names":[],"sources":["../../src/internal/consent.ts"],"sourcesContent":["import type { EventType } from \"@interfere/types/sdk/envelope\";\nimport {\n type ConsentCategory,\n type ConsentState,\n type GateableCategory,\n PLUGIN_MANIFEST,\n type PluginKey,\n} from \"@interfere/types/sdk/plugins/manifest\";\n\nexport const DEFAULT_CONSENT = {\n analytics: true,\n replay: true,\n} as const satisfies ConsentState;\n\nconst PLUGIN_CONSENT_BY_KEY = Object.fromEntries(\n PLUGIN_MANIFEST.map((plugin) => [plugin.name, plugin.consentCategory])\n) as Record<PluginKey, ConsentCategory>;\n\nconst EVENT_CONSENT_BY_TYPE = Object.fromEntries(\n PLUGIN_MANIFEST.flatMap((plugin) =>\n plugin.events.map((event) => [event.name, plugin.consentCategory] as const)\n )\n) as Record<EventType, ConsentCategory>;\n\nconst GATEABLE_CATEGORIES = Object.keys(DEFAULT_CONSENT) as GateableCategory[];\n\nexport function getPluginConsentCategory(key: PluginKey): ConsentCategory {\n return PLUGIN_CONSENT_BY_KEY[key];\n}\n\nexport function isConsentAllowed(\n category: ConsentCategory,\n consentState: ConsentState | null\n): boolean {\n return (\n category === \"necessary\" ||\n consentState === null ||\n consentState[category] === true\n );\n}\n\nexport function shouldCaptureEvent(\n type: EventType,\n consentState: ConsentState | null\n): boolean {\n return isConsentAllowed(EVENT_CONSENT_BY_TYPE[type], consentState);\n}\n\nexport function resolveGrantedConsent(consent?: ConsentState): ConsentState {\n return consent ?? { ...DEFAULT_CONSENT };\n}\n\nexport function hasConsentChanged(\n current: ConsentState | null,\n next: ConsentState | null\n): boolean {\n for (const category of GATEABLE_CATEGORIES) {\n if (current?.[category] !== next?.[category]) {\n return true;\n }\n }\n return false;\n}\n"],"mappings":"mEASA,MAAa,gBAAkB,CAC7B,UAAW,GACX,OAAQ,EACV,EAEM,sBAAwB,OAAO,YACnC,gBAAgB,IAAK,QAAW,CAAC,OAAO,KAAM,OAAO,eAAe,CAAC,CACvE,EAEM,sBAAwB,OAAO,YACnC,gBAAgB,QAAS,QACvB,OAAO,OAAO,IAAK,OAAU,CAAC,MAAM,KAAM,OAAO,eAAe,CAAU,CAC5E,CACF,EAEM,oBAAsB,OAAO,KAAK,eAAe,EAEvD,SAAgB,yBAAyB,IAAiC,CACxE,OAAO,sBAAsB,IAC/B,CAEA,SAAgB,iBACd,SACA,aACS,CACT,OACE,WAAa,aACb,eAAiB,MACjB,aAAa,YAAc,EAE/B,CAEA,SAAgB,mBACd,KACA,aACS,CACT,OAAO,iBAAiB,sBAAsB,MAAO,YAAY,CACnE,CAEA,SAAgB,sBAAsB,QAAsC,CAC1E,OAAO,SAAW,CAAE,GAAG,eAAgB,CACzC,CAEA,SAAgB,kBACd,QACA,KACS,CACT,IAAK,IAAM,YAAY,oBACrB,GAAI,UAAU,YAAc,OAAO,UACjC,MAAO,GAGX,MAAO,EACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"console-patch.d.mts","names":[],"sources":["../../src/internal/console-patch.ts"],"mappings":";;AAUA;;;;AAMU;AAEV;;;;cARa,cAAA;AAAA,KAQD,YAAA,WAAuB,cAAc;AAAA,KAErC,cAAA,IAAkB,KAAA,EAAO,YAAY,EAAE,IAAA;AAAA,iBAiDnC,aAAA,CAAc,OAAuB,EAAd,cAAc;;iBAYrC,0BAAA,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"console-patch.mjs","names":[],"sources":["../../src/internal/console-patch.ts"],"sourcesContent":["/**\n * Single point of `console.{debug,log,info,warn,error}` patching. The\n * errors plugin and the logs plugin both want to observe console calls;\n * before this consolidation, each patched independently and the\n * resulting wrap-order coupling made teardown leaky.\n *\n * Now: a single set of patched methods is installed on first\n * subscription, fans out to every subscriber, and is uninstalled on the\n * last unsubscription. Handlers never wrap each other; they observe.\n */\nexport const CONSOLE_LEVELS = [\n \"debug\",\n \"log\",\n \"info\",\n \"warn\",\n \"error\",\n] as const;\n\nexport type ConsoleLevel = (typeof CONSOLE_LEVELS)[number];\n\nexport type ConsoleHandler = (level: ConsoleLevel, args: unknown[]) => void;\n\nconst handlers = new Set<ConsoleHandler>();\nconst originals: Partial<Record<ConsoleLevel, (...args: unknown[]) => void>> =\n {};\nlet installed = false;\n\nfunction install(): void {\n if (installed) {\n return;\n }\n installed = true;\n const target = globalThis.console as unknown as Record<\n ConsoleLevel,\n (...args: unknown[]) => void\n >;\n for (const level of CONSOLE_LEVELS) {\n const orig = target[level];\n if (typeof orig !== \"function\") {\n continue;\n }\n originals[level] = orig;\n target[level] = (...args: unknown[]) => {\n orig.apply(globalThis.console, args);\n for (const handler of handlers) {\n handler(level, args);\n }\n };\n }\n}\n\nfunction uninstall(): void {\n if (!installed) {\n return;\n }\n installed = false;\n const target = globalThis.console as unknown as Record<\n ConsoleLevel,\n (...args: unknown[]) => void\n >;\n for (const level of CONSOLE_LEVELS) {\n const orig = originals[level];\n if (orig) {\n target[level] = orig;\n delete originals[level];\n }\n }\n}\n\nexport function onConsoleCall(handler: ConsoleHandler): () => void {\n install();\n handlers.add(handler);\n return () => {\n handlers.delete(handler);\n if (handlers.size === 0) {\n uninstall();\n }\n };\n}\n\n/** Test hook — drops every handler and restores originals. */\nexport function _resetConsolePatchForTests(): void {\n handlers.clear();\n uninstall();\n}\n"],"mappings":"AAUA,MAAa,eAAiB,CAC5B,QACA,MACA,OACA,OACA,OACF,EAMM,SAAW,IAAI,IACf,UACJ,CAAC,EACH,IAAI,UAAY,GAEhB,SAAS,SAAgB,CACvB,GAAI,UACF,OAEF,UAAY,GACZ,IAAM,OAAS,WAAW,QAI1B,IAAK,IAAM,SAAS,eAAgB,CAClC,IAAM,KAAO,OAAO,OAChB,OAAO,MAAS,aAGpB,UAAU,OAAS,KACnB,OAAO,QAAU,GAAG,OAAoB,CACtC,KAAK,MAAM,WAAW,QAAS,IAAI,EACnC,IAAK,IAAM,WAAW,SACpB,QAAQ,MAAO,IAAI,CAEvB,EACF,CACF,CAEA,SAAS,WAAkB,CACzB,GAAI,CAAC,UACH,OAEF,UAAY,GACZ,IAAM,OAAS,WAAW,QAI1B,IAAK,IAAM,SAAS,eAAgB,CAClC,IAAM,KAAO,UAAU,OACnB,OACF,OAAO,OAAS,KAChB,OAAO,UAAU,OAErB,CACF,CAEA,SAAgB,cAAc,QAAqC,CAGjE,OAFA,QAAQ,EACR,SAAS,IAAI,OAAO,MACP,CACX,SAAS,OAAO,OAAO,EACnB,SAAS,OAAS,GACpB,UAAU,CAEd,CACF,CAGA,SAAgB,4BAAmC,CACjD,SAAS,MAAM,EACf,UAAU,CACZ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"actionable.d.mts","names":[],"sources":["../../../src/internal/dom/actionable.ts"],"mappings":";;AAkCA;;;;AAAwC;AAWxC;;;;;iBAXgB,YAAA,CAAa,EAAW,EAAP,OAAO;AAAA,iBAWxB,iBAAA,CAAkB,MAAA,EAAQ,WAAA,UAAqB,OAAO;AAAA,UAcrD,oBAAA;EACf,SAAA;EACA,IAAA;EACA,EAAA;EACA,IAAA;EACA,IAAA;EACA,GAAA;EACA,IAAA;EAAA,CACC,GAAA;AAAA;AAAA,iBAGa,kBAAA,CAAmB,EAAA,EAAI,OAAA,GAAU,oBAAoB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"actionable.mjs","names":[],"sources":["../../../src/internal/dom/actionable.ts"],"sourcesContent":["/**\n * Single source of truth for \"what counts as a clickable element\" across\n * the SDK. Used by both the `pages` plugin (which captures `ui_event`\n * envelopes) and the OTel `UserInteractionInstrumentation` enrichment\n * hook (which gates span creation + stamps target attrs). Before this\n * was extracted, the two sites diverged and produced inconsistent\n * notions of \"actionable.\"\n *\n * `data-track-click` is the customer escape hatch — any element opted\n * in via that attribute is treated as actionable regardless of tag/role.\n */\n\nconst ACTIONABLE_TAGS = new Set([\n \"a\",\n \"button\",\n \"input\",\n \"select\",\n \"textarea\",\n \"summary\",\n \"label\",\n]);\n\nconst ACTIONABLE_ROLES = new Set([\n \"button\",\n \"link\",\n \"menuitem\",\n \"tab\",\n \"checkbox\",\n \"radio\",\n \"switch\",\n]);\n\nconst TEXT_TRUNCATE = 120;\n\nexport function isActionable(el: Element): boolean {\n if (ACTIONABLE_TAGS.has(el.tagName.toLowerCase())) {\n return true;\n }\n const role = el.getAttribute(\"role\");\n if (role && ACTIONABLE_ROLES.has(role)) {\n return true;\n }\n return el.hasAttribute(\"data-track-click\");\n}\n\nexport function closestActionable(target: EventTarget | null): Element | null {\n if (!(target instanceof Element)) {\n return null;\n }\n let el: Element | null = target;\n while (el) {\n if (isActionable(el)) {\n return el;\n }\n el = el.parentElement;\n }\n return null;\n}\n\nexport interface ActionableDescriptor {\n ariaLabel?: string | undefined;\n href?: string | undefined;\n id?: string | undefined;\n name?: string | undefined;\n role?: string | undefined;\n tag: string;\n text?: string | undefined;\n [key: string]: unknown;\n}\n\nexport function describeActionable(el: Element): ActionableDescriptor {\n const desc: ActionableDescriptor = {\n tag: el.tagName.toLowerCase(),\n };\n if (el.id) {\n desc.id = el.id;\n }\n const role = el.getAttribute(\"role\");\n if (role) {\n desc.role = role;\n }\n const name = el.getAttribute(\"name\");\n if (name) {\n desc.name = name;\n }\n const ariaLabel = el.getAttribute(\"aria-label\");\n if (ariaLabel) {\n desc.ariaLabel = ariaLabel;\n }\n if (el instanceof HTMLAnchorElement) {\n desc.href = el.href;\n }\n const text = el.textContent?.trim().slice(0, TEXT_TRUNCATE);\n if (text) {\n desc.text = text;\n }\n return desc;\n}\n"],"mappings":"AAYA,MAAM,gBAAkB,IAAI,IAAI,CAC9B,IACA,SACA,QACA,SACA,WACA,UACA,OACF,CAAC,EAEK,iBAAmB,IAAI,IAAI,CAC/B,SACA,OACA,WACA,MACA,WACA,QACA,QACF,CAAC,EAID,SAAgB,aAAa,GAAsB,CACjD,GAAI,gBAAgB,IAAI,GAAG,QAAQ,YAAY,CAAC,EAC9C,MAAO,GAET,IAAM,KAAO,GAAG,aAAa,MAAM,EAInC,OAHI,MAAQ,iBAAiB,IAAI,IAAI,EAC5B,GAEF,GAAG,aAAa,kBAAkB,CAC3C,CAEA,SAAgB,kBAAkB,OAA4C,CAC5E,GAAI,EAAE,kBAAkB,SACtB,OAAO,KAET,IAAI,GAAqB,OACzB,KAAO,IAAI,CACT,GAAI,aAAa,EAAE,EACjB,OAAO,GAET,GAAK,GAAG,aACV,CACA,OAAO,IACT,CAaA,SAAgB,mBAAmB,GAAmC,CACpE,IAAM,KAA6B,CACjC,IAAK,GAAG,QAAQ,YAAY,CAC9B,EACI,GAAG,KACL,KAAK,GAAK,GAAG,IAEf,IAAM,KAAO,GAAG,aAAa,MAAM,EAC/B,OACF,KAAK,KAAO,MAEd,IAAM,KAAO,GAAG,aAAa,MAAM,EAC/B,OACF,KAAK,KAAO,MAEd,IAAM,UAAY,GAAG,aAAa,YAAY,EAC1C,YACF,KAAK,UAAY,WAEf,cAAc,oBAChB,KAAK,KAAO,GAAG,MAEjB,IAAM,KAAO,GAAG,aAAa,KAAK,EAAE,MAAM,EAAG,GAAa,EAI1D,OAHI,OACF,KAAK,KAAO,MAEP,IACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"kernel-registry.d.mts","names":[],"sources":["../../src/internal/kernel-registry.ts"],"mappings":";;;iBAsBgB,cAAA,CAAe,MAAc,EAAN,MAAM;AAAA,iBAS7B,gBAAA,CAAiB,MAAc,EAAN,MAAM;AAAA,iBAM/B,YAAA,CAAA,GAAgB,MAAM"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"kernel-registry.mjs","names":[],"sources":["../../src/internal/kernel-registry.ts"],"sourcesContent":["import { createLogger } from \"../util/log.js\";\nimport type { Kernel } from \"./kernel.js\";\n\nconst log = createLogger(\"kernel-registry\");\n\n/**\n * Module-scoped active kernel reference. The framework wrapper\n * (`@interfere/next`, `@interfere/vite`) registers its kernel here on\n * `init()` so the public `api.ts` helpers can find it without each customer\n * call site threading the kernel through.\n *\n * Single-kernel by design: each app bundle has its own module instance, so\n * the common case (one app, one init) gets a clean global. The warn on\n * overwrite catches HMR / accidental double-init / module-federated MFEs\n * that share `@interfere/react` across surfaces — those setups need to\n * reach for `kernel.recordException()` directly rather than the registry.\n *\n * @internal Wrappers only. Importing this from app code will silently\n * de-fang the public api helpers.\n */\nlet active: Kernel | null = null;\n\nexport function registerKernel(kernel: Kernel): void {\n if (active && active !== kernel) {\n log.warn(\n \"init() called twice with different kernels; later one wins. The public capture()/span() helpers route only to the latest kernel — multi-surface apps should call kernel.recordException() directly.\"\n );\n }\n active = kernel;\n}\n\nexport function unregisterKernel(kernel: Kernel): void {\n if (active === kernel) {\n active = null;\n }\n}\n\nexport function activeKernel(): Kernel | null {\n return active;\n}\n"],"mappings":"0CAGA,MAAM,IAAM,aAAa,iBAAiB,EAiB1C,IAAI,OAAwB,KAE5B,SAAgB,eAAe,OAAsB,CAC/C,QAAU,SAAW,QACvB,IAAI,KACF,qMACF,EAEF,OAAS,MACX,CAEA,SAAgB,iBAAiB,OAAsB,CACjD,SAAW,SACb,OAAS,KAEb,CAEA,SAAgB,cAA8B,CAC5C,OAAO,MACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"kernel.d.mts","names":[],"sources":["../../src/internal/kernel.ts"],"mappings":";;;;;;;;;;;;;;;;;iBAyCgB,aAAA,CAAc,eAA0B;AAAA,UAyDvC,aAAA;EACf,OAAA,GAAU,YAAA;EA1DI;;;;AAAwC;AAyDxD;;EASE,OAAA;EARU;EAUV,KAAA,UAAe,UAAA,CAAW,KAAA;EAC1B,OAAA,GAAU,eAAA;EAgBU;;;;;;;EARpB,WAAA;EAT0B;;;;;;;EAiB1B,OAAA,aAAoB,cAAA;AAAA;;;;;;;UASL,cAAA;EAsBf;;;;AAMS;AAUX;EA/BE,UAAA,aAAuB,MAAA;;;;;;;EAOvB,oBAAA,aAAiC,MAAM;EAwBM;;;;;;;EAhB7C,YAAA,IAAgB,QAAA;EAuCA;AAAA;AAGlB;;;EApCE,SAAA;AAAA;;;;;;AAsCwB;AAG1B;UA/BiB,qBAAA,SAA8B,aAAA;;;;;;;;EAQ7C,sCAAA,GAAyC,kBAAA;EAyBzC;;;;;AAAoC;AAGtC;EApBE,gCAAA,GAAmC,YAAA;;;AAsB1B;AAGX;EApBE,iCAAA,GAAoC,aAAA;;EAEpC,gBAAA;AAAA;AAAA,UAGe,aAAA;EACf,GAAA,IAAO,YAAA;EACP,GAAA,CAAI,KAAA,GAAQ,YAAY;AAAA;AAAA,UAGT,cAAA;EACf,GAAA,IAAO,cAAA;EACP,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA;AAAA;AAAA,UAGd,YAAA;EACf,WAAA;EACA,SAAS;AAAA;AAAA,UAGM,aAAA;EACf,KAAA,IAAS,SAAS;EAClB,WAAA;AAAA;AAAA,UAGe,mBAAA;EAuBN;;;AAAyB;AAcpC;;EAdW,SAhBA,YAAA,YAAwB,aAAA;EA+Bf;;;;;;;EAAA,SAvBT,WAAA;EA0HD;;;;;;EAAA,SAnHC,cAAA,YAA0B,aAAA;EAAA,SAC1B,SAAA,EAAW,cAAA;AAAA;;;;;;;;;;;;cAcT,MAAA,YAAkB,aAAA;EAAA,SACpB,OAAA,EAAS,aAAA;EAAA,SACT,QAAA,EAAU,cAAA;EAAA,SACV,MAAA,EAAQ,YAAA;EAAA,SACR,OAAA,EAAS,aAAA;EAAA,iBAED,OAAA;EAAA,iBAGA,IAAA;EAAA,QAKT,OAAA;EAAA,QACA,IAAA;EAAA,QACA,WAAA;EAAA,QACA,qBAAA;cAEI,OAAA,EAAS,cAAA;EA+ErB;EAtCA,aAAA,CAAc,OAAA,EAAS,aAAA;EAAA,QAIf,cAAA;EAmCN;EAzBF,YAAA,CAAA;EA0BE;;;;;;;;;;;;;;;;;;;;EAFF,eAAA,CACE,KAAA,EAAO,KAAA,GAAQ,iBAAA,EACf,IAAA,EAAM,mBAAA;EA6HA;;;;AACoB;AAS9B;;EAlFE,SAAA,CAAU,KAAA;IACR,YAAA;IACA,cAAA;IACA,IAAA;IACA,UAAA,GAAa,MAAA;EAAA;EA+ER;;;;;AACQ;AA4PjB;EAxTQ,KAAA,CAAA,GAAS,OAAA;;;AAwTqB;AAyBtC;;;;EApUE,UAAA,CAAW,MAAA,EAAQ,kBAAA,EAAoB,OAAA;;;;;EASvC,WAAA,CAAY,KAAA,EAAO,YAAA;EASb,OAAA,CAAA,GAAW,OAAA;AAAA;AAAA,UAiBT,iBAAA;EACR,IAAA,GAAO,qBAAqB;AAAA;;;;;;;iBASR,YAAA,CACpB,KAAA,GAAO,iBAAA,GACN,OAAA,CAAQ,MAAA;AAAA,iBA4PK,sBAAA,CAAA;;;;;;;;;;;;iBAyBA,0BAAA,CAAA,GAA8B,WAAW"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"kernel.mjs","names":[],"sources":["../../src/internal/kernel.ts"],"sourcesContent":["import type { IngestedFrame } from \"@interfere/types/data/frame\";\nimport type { SessionId } from \"@interfere/types/data/session\";\nimport {\n type ReleaseSlug,\n releaseSlugSchema,\n} from \"@interfere/types/releases/slug\";\nimport {\n isNonErrorException,\n type NonErrorException,\n shouldDropBrowserExtensionNoise,\n shouldDropUnresolvableStack,\n toExceptions,\n} from \"@interfere/types/sdk/errors\";\nimport type { IdentifyParams } from \"@interfere/types/sdk/identify\";\nimport type { ConsentState } from \"@interfere/types/sdk/plugins/manifest\";\nimport type { ErrorMechanism } from \"@interfere/types/sdk/plugins/payload/errors\";\nimport type { RemoteConfig } from \"@interfere/types/sdk/remote-config\";\n\nimport { trace } from \"@opentelemetry/api\";\nimport type { LogRecordProcessor } from \"@opentelemetry/sdk-logs\";\nimport type { MetricReader } from \"@opentelemetry/sdk-metrics\";\nimport type { SpanProcessor } from \"@opentelemetry/sdk-trace-base\";\n\nimport type { PluginOverrides } from \"../plugins/lib/loader.js\";\nimport type { PluginContext } from \"../plugins/lib/types.js\";\nimport { SessionTracker } from \"../tracking/api.js\";\nimport { getGlobal } from \"../util/global.js\";\nimport { createLogger } from \"../util/log.js\";\nimport { safeStringify } from \"../util/stringify.js\";\nimport type { AuthHeaders, IngestTarget } from \"./config.js\";\nimport { resolveTargets } from \"./config.js\";\nimport type { OtelProviderHandle } from \"./otel/index.js\";\nimport { onPageHidden } from \"./page-lifecycle.js\";\nimport { PluginRuntime } from \"./plugin-runtime.js\";\nimport { registerServiceWorker, type SwMessage } from \"./sw.js\";\nimport { PRODUCER_VERSION } from \"./version.js\";\n\nconst log = createLogger(\"kernel\");\n\nconst TRAILING_SLASH_RE = /\\/$/;\n\nexport function buildSdkStack(wrapperVersions?: string[]): string[] {\n return [...(wrapperVersions ?? []), PRODUCER_VERSION];\n}\n\nfunction buildExceptionEventAttrs(\n value: Error | NonErrorException,\n opts: RecordExceptionOpts\n): Record<string, string> {\n const attrs: Record<string, string> = {\n \"interfere.exception.mechanism\": opts.mechanism.type,\n \"interfere.exception.handled\": String(opts.mechanism.handled),\n };\n if (opts.errorDigest) {\n attrs[\"interfere.error.digest\"] = opts.errorDigest;\n }\n if (isNonErrorException(value)) {\n attrs[\"exception.type\"] = value.type;\n attrs[\"exception.message\"] = value.value;\n attrs[\"interfere.exception.kind\"] = \"non-error\";\n // OTel attrs are scalars; serialize the structured payload so the\n // OTLP mapper can reconstruct it server-side.\n attrs[\"interfere.exception.serialized\"] = safeStringify(value.serialized);\n return attrs;\n }\n attrs[\"exception.type\"] = value.name;\n attrs[\"exception.message\"] = value.message;\n attrs[\"interfere.exception.kind\"] = \"error\";\n if (value.stack) {\n attrs[\"exception.stacktrace\"] = value.stack;\n }\n // Walk Error.cause and ship every entry on a single attr. Each entry\n // carries `{type, value, mechanism?, kind, stack}` — the SDK does not\n // parse stacks; the collector parses `stack` server-side.\n attrs[\"interfere.exception.chain\"] = safeStringify(\n toExceptions(value, opts.mechanism)\n );\n // Supplementary frames the SDK has runtime context to assemble that\n // the server can't synthesise from the raw stack:\n // - `appendFrames`: parsed React component-stack frames (regex\n // parser stays in the SDK — React-specific format, tiny).\n // - `fallbackFrames`: source/line/col tuple from `window.onerror`\n // when the Error's `.stack` is degenerate (CORS-masked, etc.).\n // The collector mapper merges these into the `frames` column after\n // parsing the raw JS stack.\n if (opts.appendFrames && opts.appendFrames.length > 0) {\n attrs[\"interfere.exception.appended_frames\"] = safeStringify(\n opts.appendFrames\n );\n }\n if (opts.fallbackFrames && opts.fallbackFrames.length > 0) {\n attrs[\"interfere.exception.fallback_frames\"] = safeStringify(\n opts.fallbackFrames\n );\n }\n return attrs;\n}\n\nexport interface KernelOptions {\n consent?: ConsentState;\n /**\n * Override the automatic dev-mode guard. When `undefined`, the SDK\n * auto-detects: it disables itself if `process.env[\"NODE_ENV\"]` is not\n * `\"production\"` (Node / webpack / Next.js). In environments where\n * `process` does not exist (Vite, CRA, plain browser) the SDK\n * defaults to **enabled** — pass `false` to disable explicitly.\n */\n enabled?: boolean;\n /** Override `globalThis.fetch` for tests / non-browser hosts. */\n fetch?: typeof globalThis.fetch;\n plugins?: PluginOverrides;\n /**\n * Override the OTel `service.name` resource attribute. Defaults to\n * `\"interfere-sdk\"`. Customers running multiple frontends in one\n * monitoring backend (e.g. `\"@interfere/homepage\"` vs\n * `\"@interfere/app\"`) set this so spans/metrics/logs slice\n * cleanly by surface.\n */\n serviceName?: string;\n /**\n * `false` skips the OTel module entirely — the bundler code-splits\n * `internal/otel/*` out of this path so the error-only bundle never\n * imports the OTel SDK. `true` (default) loads it with default\n * instrumentation config. An object loads it with the supplied\n * config.\n */\n tracing?: boolean | TracingOptions;\n}\n\n/**\n * Per-instrumentation tuning. Every field is optional; defaults match\n * what `interfere/homepage` and `interfere/dashboard` use today via\n * the internal `observability/browser/rum.ts`. New customers don't\n * need to touch any of these — sensible defaults ship out of the box.\n */\nexport interface TracingOptions {\n /**\n * Additional URL patterns to skip when creating fetch / XHR spans.\n * The SDK's own collector endpoint is always ignored; customer-side\n * third-party SDKs (Clerk, Sentry, GTM, etc.) are also skipped by\n * default — supplement with anything specific to your app.\n */\n ignoreUrls?: (string | RegExp)[];\n /**\n * URL patterns that should receive W3C `traceparent` + `baggage`\n * headers on outgoing fetch / XHR. Same-origin requests always\n * propagate; this allowlist enables cross-origin propagation\n * (separate API origin, internal service mesh, etc.).\n */\n propagateContextUrls?: (string | RegExp)[];\n /**\n * Resolve a URL pathname to a low-cardinality route template\n * (e.g. `/blog/[slug]`). Without this, dynamic routes produce one\n * unique span / metric label per visited slug — a cardinality\n * hazard. Used by fetch, document-load resource enrichment,\n * user-interaction, long-task, and web-vitals.\n */\n resolveRoute?: (pathname: string) => string | undefined;\n /**\n * `false` disables Core Web Vitals capture (LCP/FCP/TTFB/INP/CLS).\n * Defaults to enabled; customers who run their own vitals reporting\n * can opt out.\n */\n webVitals?: boolean;\n}\n\n/**\n * @internal\n * Framework wrappers (`@interfere/next`, `@interfere/vite`) pass extra\n * fields through `createKernel` that customers never set themselves.\n * Kept off the customer-facing `KernelOptions` to make the public surface\n * clean.\n */\nexport interface KernelInternalOptions extends KernelOptions {\n /**\n * Extra log-record processors fanned into the LoggerProvider's\n * processor list. Used by `@interfere/observability` for internal-only\n * dual-write to BetterStack from `interfere/homepage` +\n * `interfere/dashboard`. **Not part of the customer surface** — the\n * SDK doesn't expose a way to fan-out to other observability vendors.\n */\n _internalAdditionalLogRecordProcessors?: LogRecordProcessor[];\n /**\n * Extra metric readers fanned into the MeterProvider's reader list.\n * See `_internalAdditionalLogRecordProcessors`. Used to keep web\n * vitals histograms landing on the BetterStack-fronting OTel\n * collector after the rum.ts → SDK migration moved the primary path\n * to `/v2/sink` (Tinybird-only).\n */\n _internalAdditionalMetricReaders?: MetricReader[];\n /**\n * Extra span processors fanned into the WebTracerProvider's processor\n * list. See `_internalAdditionalLogRecordProcessors`.\n */\n _internalAdditionalSpanProcessors?: SpanProcessor[];\n /** Wrapper SDK versions (e.g. `@interfere/next@10.0.0`). */\n _wrapperVersions?: string[];\n}\n\nexport interface KernelConsent {\n get(): ConsentState | null;\n set(value?: ConsentState): void;\n}\n\nexport interface KernelIdentity {\n get(): IdentifyParams | null;\n set(params: IdentifyParams): Promise<void>;\n}\n\nexport interface KernelDevice {\n getDeviceId(): string | null;\n getFpHash(): string | null;\n}\n\nexport interface KernelSession {\n getId(): SessionId | null;\n getWindowId(): string | null;\n}\n\nexport interface RecordExceptionOpts {\n /**\n * Frames always appended to the first exception's stack. Used by\n * `<ErrorBoundary>` to inject React's component-stack frames as\n * supplementary context — these add on top of the real JS frames, they\n * don't replace them.\n */\n readonly appendFrames?: readonly IngestedFrame[];\n /**\n * `error.digest` from React when an RSC server-side throw is\n * sanitised and rebuilt as a synthetic Error on the client. The only\n * identifier that pairs the redacted client capture with the\n * unredacted server capture — enrichment mixes this into the\n * fingerprint so both events group as one problem.\n */\n readonly errorDigest?: string;\n /**\n * Frames adopted only when the first exception's parsed stack is empty.\n * Used by the `window.onerror` handler when the browser hands us\n * source/line/col but the `Error` itself has a degenerate stack — never\n * glued on top of a real stack.\n */\n readonly fallbackFrames?: readonly IngestedFrame[];\n readonly mechanism: ErrorMechanism;\n}\n\n/**\n * The replacement for the old `Client` god-object. Pure construction graph\n * via `createKernel`; no module-level mutable state, no global lookups.\n * Framework wrappers (`@interfere/next`, `@interfere/vite`) own the\n * singleton lifecycle.\n *\n * Implements `PluginContext` directly: plugins are wired with the kernel\n * itself rather than a closure adapter. The kernel⇄runtime cycle is\n * resolved by constructing the kernel first and binding the runtime via\n * `attachRuntime` immediately after.\n */\nexport class Kernel implements PluginContext {\n readonly consent: KernelConsent;\n readonly identity: KernelIdentity;\n readonly device: KernelDevice;\n readonly session: KernelSession;\n\n private readonly tracker: SessionTracker;\n // Single dedup authority for `recordException`. WeakSet so GC'd errors\n // don't pin memory. Upstream callers do not pre-filter.\n private readonly seen = new WeakSet<Error>();\n // Lateinit. `createKernel` constructs the kernel first, then the runtime\n // (passing the kernel as `PluginContext`), then calls `attachRuntime`.\n // Anything kernel-side that touches `runtime` (consent / dispose) runs\n // after the bind.\n private runtime: PluginRuntime | null = null;\n private otel: OtelProviderHandle | null = null;\n private otelDispose: (() => void) | null = null;\n private unsubscribePageHidden: (() => void) | null = null;\n\n constructor(tracker: SessionTracker) {\n this.tracker = tracker;\n\n this.consent = {\n get: () => this.requireRuntime().getConsent(),\n set: (value) => {\n const runtime = this.requireRuntime();\n if (value) {\n runtime.setConsent(value);\n } else {\n runtime.resetConsent();\n }\n },\n };\n\n this.identity = {\n get: () => this.tracker.getIdentity(),\n set: (params) => this.tracker.identify(params),\n };\n\n this.device = {\n getDeviceId: () => this.tracker.getDeviceId(),\n getFpHash: () => this.tracker.getFpHash(),\n };\n\n this.session = {\n getId: () => this.tracker.sessionId(),\n getWindowId: () => this.tracker.windowId(),\n };\n\n // Force-flush the OTel exporters when the tab is about to be hidden.\n // Tail spans + web vitals (CLS/INP fire on visibility hidden) are\n // otherwise lost when the user navigates away. Subscription removed\n // in `dispose()`.\n this.unsubscribePageHidden = onPageHidden(() => {\n // Fire-and-forget — we can't block the unload path.\n this.flush().catch(() => undefined);\n });\n }\n\n /** @internal Bound by `createKernel` immediately after construction. */\n attachRuntime(runtime: PluginRuntime): void {\n this.runtime = runtime;\n }\n\n private requireRuntime(): PluginRuntime {\n if (!this.runtime) {\n throw new Error(\n \"Kernel runtime not attached. createKernel must call attachRuntime before any consent/dispose path runs.\"\n );\n }\n return this.runtime;\n }\n\n /** PluginContext shim: plugins expect a non-null string. */\n getSessionId(): string {\n return this.tracker.sessionId() ?? \"\";\n }\n\n /**\n * Records an exception event on the active OTel span. No envelope\n * path — the SDK is OTel-only. Single dedup boundary: drops repeats\n * of the same `Error` instance, plus browser-extension noise and\n * unresolvable stacks. Upstream callers (the errors plugin's\n * `window.onerror` / `console.error` / `unhandledrejection`, the\n * React error boundary) just call this — they don't pre-filter.\n *\n * Does NOT mark the active span as failed. A console.error inside a\n * customer-owned `span()` block would otherwise taint the customer's\n * span even when the customer caught the error explicitly. The\n * customer's `span()` helper sets status itself when it observes a\n * throw.\n *\n * `appendFrames` are always appended (component-stack supplements);\n * `fallbackFrames` are only adopted when the parsed stack is empty\n * (`window.onerror` source/line/col when the Error has no usable\n * stack). Both ride on the serialised `interfere.exception.chain`\n * attribute when the chain is non-trivial.\n */\n recordException(\n value: Error | NonErrorException,\n opts: RecordExceptionOpts\n ): void {\n // Dedup applies only to real Errors. NonErrorException is a fresh\n // object on every capture site (`toException(event.reason)` allocates\n // it inline) so the WeakSet would never see a repeat anyway.\n if (!isNonErrorException(value)) {\n if (this.seen.has(value)) {\n return;\n }\n this.seen.add(value);\n }\n\n // Noise filters operate on the raw stack strings — Error variants\n // only. Non-Error rejections carry no stack to inspect, but they\n // ship structured payloads the agent can reason over directly, so\n // they're never dropped here.\n if (!isNonErrorException(value)) {\n const stacks: string[] = [];\n let current: Error | undefined = value;\n for (let depth = 0; current && depth < 5; depth += 1) {\n if (current.stack) {\n stacks.push(current.stack);\n }\n current = current.cause instanceof Error ? current.cause : undefined;\n }\n if (\n shouldDropBrowserExtensionNoise(stacks) ||\n shouldDropUnresolvableStack(stacks)\n ) {\n return;\n }\n }\n\n // Stamp our mechanism taxonomy onto the OTel exception event so the\n // collector mapper can recover handled / unhandled and the\n // originating mechanism (browser.onerror / unhandledrejection /\n // manual.api.capture / etc.). OTel's standard `recordException`\n // only carries type/message/stack; mechanism is our enrichment, so\n // we emit the event ourselves via `addEvent` with the full attr\n // set.\n const span = trace.getActiveSpan();\n if (span) {\n span.addEvent(\"exception\", buildExceptionEventAttrs(value, opts));\n }\n }\n\n /**\n * Emits an OTel `LogRecord` via the kernel's logger provider. Used by\n * `plugins/logs.ts` to capture string-only `console.*` calls (the\n * errors plugin still owns Error-bearing console calls — class\n * boundary). No-ops when OTel is not wired (`tracing: false`) since\n * there's no logger provider to emit through.\n */\n recordLog(input: {\n severityText: string;\n severityNumber: number;\n body: string;\n attributes?: Record<string, string>;\n }): void {\n if (!this.otel) {\n return;\n }\n this.otel.loggerProvider.getLogger(\"@interfere/react\").emit({\n severityText: input.severityText,\n severityNumber: input.severityNumber,\n body: input.body,\n ...(input.attributes ? { attributes: input.attributes } : {}),\n });\n }\n\n /**\n * Force-flushes the OTel exporters. Returns once they've settled\n * (success or failure) so callers driving unload — page-lifecycle\n * handlers, integration test teardown — can actually await\n * completion. The OTel SDK swallows export errors internally; we\n * surface the resolution either way.\n */\n async flush(): Promise<void> {\n if (this.otel) {\n await this.otel.flush();\n }\n }\n\n /**\n * @internal Wired by `createKernel` after the OTel module has been\n * lazy-loaded. Held on the kernel so `flush()` and `dispose()` can fan\n * out to the providers. The tracer/meter providers stay private —\n * customers don't get raw OTel access; the public surface is the\n * `span()` / `capture()` helpers in `api.ts`.\n */\n attachOtel(handle: OtelProviderHandle, dispose: () => void): void {\n this.otel = handle;\n this.otelDispose = dispose;\n }\n\n /**\n * Provider-driven consent sync. Pass `undefined` to clear consent (treats\n * the prop as \"unmanaged\"); pass a state to apply it.\n */\n syncConsent(value: ConsentState | undefined): void {\n const runtime = this.requireRuntime();\n if (value) {\n runtime.setConsent(value);\n return;\n }\n runtime.resetConsent();\n }\n\n async dispose(): Promise<void> {\n this.unsubscribePageHidden?.();\n this.unsubscribePageHidden = null;\n if (this.runtime) {\n await this.runtime.dispose();\n }\n this.tracker.dispose();\n\n this.otelDispose?.();\n this.otelDispose = null;\n if (this.otel) {\n await this.otel.shutdown();\n this.otel = null;\n }\n }\n}\n\ninterface CreateKernelInput {\n opts?: KernelInternalOptions;\n}\n\n/**\n * Pure construction graph. Awaits the remote-config fetch before resolving,\n * so the runtime sees a fully-applied config before any capture is attempted\n * by the caller. Removes the capture-before-config race the old `Client`\n * had.\n */\nexport async function createKernel(\n input: CreateKernelInput = {}\n): Promise<Kernel> {\n const { opts = {} } = input;\n const fetcher = opts.fetch ?? globalThis.fetch.bind(globalThis);\n const targets = resolveTargets();\n\n log.info(\"collector: %s\", targets.collectorBaseUrl);\n\n // Misconfigured prod (NODE_ENV missing/wrong + force-enable on) would\n // happily ship the header to a prod collector and have every event\n // silently dropped. The collector returns a structured `dropped` reason\n // so dev tools can surface it, but the warning here is the first line\n // of defense — fires at most once per kernel construction.\n if (getGlobal<boolean>(\"__INTERFERE_FORCE_ENABLE__\")) {\n log.warn(\n \"FORCE_ENABLE is active. Events will be dropped by production collectors. This should only be set during local development.\"\n );\n }\n\n const tracker = new SessionTracker({ target: targets.session, fetcher });\n\n // Single write site for the SDK-stack global. The OTLP exporter\n // headers and the OTel resource attribute both attribute events to\n // the producer chain (`@interfere/next@10.0.0`,\n // `@interfere/react@10.0.0`, …). Wrapper passes `_wrapperVersions`;\n // the kernel publishes the resulting stack here.\n if (opts._wrapperVersions?.length) {\n (globalThis as Record<string, unknown>)[\"__INTERFERE_SDK_STACK__\"] =\n opts._wrapperVersions;\n }\n\n // Resolve the kernel⇄runtime cycle by constructing the kernel first\n // (without runtime), then the runtime with the kernel as PluginContext,\n // then binding. No closure adapter — plugins call the kernel directly.\n const kernel = new Kernel(tracker);\n const runtime = new PluginRuntime(kernel, opts.plugins, opts.consent);\n kernel.attachRuntime(runtime);\n\n tracker.start();\n runtime.start();\n\n if (opts.tracing !== false) {\n const tracingOpts =\n typeof opts.tracing === \"object\" ? opts.tracing : undefined;\n await wireOtel(kernel, targets, opts._wrapperVersions, {\n ...(tracingOpts ?? {}),\n ...(opts.serviceName ? { serviceName: opts.serviceName } : {}),\n ...(opts._internalAdditionalSpanProcessors\n ? {\n additionalSpanProcessors: opts._internalAdditionalSpanProcessors,\n }\n : {}),\n ...(opts._internalAdditionalLogRecordProcessors\n ? {\n additionalLogRecordProcessors:\n opts._internalAdditionalLogRecordProcessors,\n }\n : {}),\n ...(opts._internalAdditionalMetricReaders\n ? {\n additionalMetricReaders: opts._internalAdditionalMetricReaders,\n }\n : {}),\n });\n }\n\n await fetchRemoteConfig(targets.config, fetcher, runtime);\n\n // Service-worker registration is best-effort and never blocks\n // kernel resolution. The SW intercepts `/api/interfere/*` POSTs\n // from the page and queues failures in IndexedDB so OTel batches\n // survive page-hide / offline / >30s collector blips. See\n // `internal/sw.ts`.\n registerServiceWorker({\n onMessage: (msg) => recordSwMessage(kernel, msg),\n }).catch(() => {\n // registerServiceWorker is best-effort and swallows its own\n // failures; this catch is just to satisfy biome's no-floating-\n // promise rule without using `void`.\n });\n\n return kernel;\n}\n\nfunction recordSwMessage(\n kernel: { recordLog: Kernel[\"recordLog\"] },\n msg: SwMessage\n): void {\n // SW activity rides as INFO LogRecords so dashboards can rank\n // queued vs replayed vs dropped without a new metric surface. The\n // SW runs in its own context — we only see what gets posted back\n // when a page is open, which is fine: \"did the SW catch anything\"\n // is the question dashboards need to answer.\n kernel.recordLog({\n severityText: \"info\",\n severityNumber: 9,\n body: msg.type,\n attributes: {\n \"url.full\": msg.url,\n ...(msg.reason ? { \"interfere.sw.reason\": msg.reason } : {}),\n },\n });\n}\n\n/**\n * Lazy-imports the OTel module so `init({ tracing: false })` keeps the\n * error-only bundle path light — the dynamic `import()` is what tells the\n * bundler to code-split. Inherits the kernel's resolved auth headers so\n * OTLP exports use the same collector URL and headers the rest of the SDK\n * resolved; no separate auth knob.\n *\n * Module-load failures are NOT caught — a bundled dynamic import only\n * fails on OOM or module-federation split-brain, both real problems we\n * want to surface, not silently flip into \"tracing disabled.\"\n */\ninterface ResolvedTracingOptions extends TracingOptions {\n additionalLogRecordProcessors?: LogRecordProcessor[];\n additionalMetricReaders?: MetricReader[];\n additionalSpanProcessors?: SpanProcessor[];\n serviceName?: string;\n}\n\nasync function wireOtel(\n kernel: Kernel,\n targets: {\n collectorBaseUrl: string;\n config: IngestTarget;\n ingest: AuthHeaders;\n session: IngestTarget;\n },\n wrapperVersions: string[] | undefined,\n tracing: ResolvedTracingOptions\n): Promise<void> {\n const {\n buildOtelProvider,\n captureWebVitals,\n readPropagationFromDocument,\n registerBundledInstrumentations,\n } = await import(\"./otel/index.js\");\n\n const collectorUrl = targets.collectorBaseUrl.replace(TRAILING_SLASH_RE, \"\");\n const releaseSlug = readReleaseSlugFromGlobals();\n\n const handle = buildOtelProvider({\n collectorUrl,\n authHeaders: targets.ingest.headers,\n sdkStack: buildSdkStack(wrapperVersions),\n // Session id is read per-span by the session span processor, so a\n // mid-page session rotation is reflected on subsequent spans\n // without rebuilding the provider.\n getSessionId: () => kernel.session.getId(),\n // `device.id` is stable for the page lifetime — `DeviceManager.init()`\n // ran during `createKernel` before this point, so the id is in\n // localStorage / cookie / memory by the time the provider boots.\n // Stamped as a resource attribute so every span / log / metric\n // carries it without per-record re-stamping; the collector projects\n // it into `spans.deviceId`. Without this the column is permanently\n // null in production (which is exactly what we observed in\n // 2026-05-12 telemetry — 0/50,632 spans had a device id).\n deviceId: kernel.device.getDeviceId(),\n releaseSlug,\n ...(tracing.serviceName ? { serviceName: tracing.serviceName } : {}),\n ...(tracing.additionalSpanProcessors\n ? { additionalSpanProcessors: tracing.additionalSpanProcessors }\n : {}),\n ...(tracing.additionalLogRecordProcessors\n ? {\n additionalLogRecordProcessors: tracing.additionalLogRecordProcessors,\n }\n : {}),\n ...(tracing.additionalMetricReaders\n ? { additionalMetricReaders: tracing.additionalMetricReaders }\n : {}),\n });\n\n // Phase 4: stitch the client trace onto whatever the SSR renderer was\n // already in. SSR injects a `traceparent` meta tag; we extract it into a\n // Context and seed the page-scope context manager with it. Spans created\n // outside any zone (DocumentLoad, Fetch listeners attached by OTel\n // instrumentations, app code in `useEffect`) fall back to that context\n // and become children of the server-side parent. No meta tag → page\n // scope stays at ROOT_CONTEXT; spans default to fresh roots.\n const extracted = readPropagationFromDocument();\n if (extracted) {\n handle.contextManager.setPageScope(extracted);\n }\n\n const dispose = registerBundledInstrumentations({\n tracerProvider: handle.tracerProvider,\n // Skip auto-instrumenting every fetch the SDK itself makes.\n // Without this we trace OTLP sink POSTs (infinite loop) and the\n // SDK's own config / session-sync requests — and worse, those\n // self-fetches' transient failures (DOMException timeouts on\n // flaky networks, ECONNRESET on the homepage's outbound\n // collector calls) get recorded as `exception` events and end up\n // in `events_pending`, burning enrichment cycles on\n // pipeline-internal noise that has nothing to do with the\n // customer's app. Measured 2026-05-12: 100% of `events_pending`\n // exceptions were children of `fetch GET …/v2/config` / `…/v2/sink`\n // spans.\n //\n // Exact-string ignore on the resolved target URLs (set at SDK\n // boot from `resolveTargets`) so customer proxies on\n // `/api/interfere/v2/*` are caught too. The OTLP sink lives at\n // `${collectorBaseUrl}/v2/sink` (`COLLECTOR_SINK_PATH` in the\n // exporter); config and session land on full URLs already.\n ignoreUrls: [\n `${collectorUrl}/v2/sink`,\n targets.config.url,\n targets.session.url,\n ],\n ...(tracing.propagateContextUrls\n ? { propagateContextUrls: tracing.propagateContextUrls }\n : {}),\n ...(tracing.resolveRoute ? { resolveRoute: tracing.resolveRoute } : {}),\n });\n\n if (tracing.webVitals !== false) {\n captureWebVitals({\n meter: handle.meterProvider.getMeter(\"@interfere/react/web-vitals\"),\n flush: () => handle.flush(),\n ...(tracing.resolveRoute ? { resolveRoute: tracing.resolveRoute } : {}),\n });\n }\n\n kernel.attachOtel(handle, dispose);\n}\n\nasync function fetchRemoteConfig(\n configTarget: IngestTarget,\n fetcher: typeof globalThis.fetch,\n runtime: PluginRuntime\n): Promise<void> {\n try {\n const headers = Object.fromEntries(configTarget.headers.entries());\n const res = await fetcher(configTarget.url, {\n method: \"GET\",\n headers,\n signal: AbortSignal.timeout(10_000),\n });\n if (!res.ok) {\n return;\n }\n const config = (await res.json()) as RemoteConfig;\n if (config?.plugins) {\n runtime.applyRemoteConfig(config.plugins);\n log.debug(\"applied remote config\");\n }\n } catch {\n log.warn(\"remote config fetch failed, using local defaults\");\n }\n}\n\nexport function isEnabledByEnvironment(): boolean {\n if (typeof process === \"undefined\" || !process.env) {\n return true;\n }\n const nodeEnv = process.env[\"NODE_ENV\"];\n if (nodeEnv === \"production\" || nodeEnv === undefined) {\n return true;\n }\n return !!(\n getGlobal<boolean>(\"__INTERFERE_FORCE_ENABLE__\") ||\n process.env[\"INTERFERE_FORCE_ENABLE\"]\n );\n}\n\n/**\n * Build-time-derived release slug. The build step\n * (`@interfere/next/withInterfere`, `@interfere/vite/plugin`) takes the\n * leading 16 hex chars of the commit SHA (`rel_<16hex>`) and stamps it into\n * the bundle as `__INTERFERE_RELEASE_SLUG__`. The collector derives the same\n * value from the create-release request, so the bundle's runtime slug is\n * guaranteed to match the row without a round-trip.\n *\n * Returns `null` when no slug was injected (dev builds, customers who\n * haven't wired up the plugin).\n */\nexport function readReleaseSlugFromGlobals(): ReleaseSlug | null {\n const value = getGlobal<unknown>(\"__INTERFERE_RELEASE_SLUG__\");\n\n return typeof value === \"string\" ? releaseSlugSchema.parse(value) : null;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"exporter.d.mts","names":[],"sources":["../../../src/internal/otel/exporter.ts"],"mappings":";;;;;;UA0EU,cAAA;EAAA;;;;EAKR,WAAA,EAAa,OAAO;EAAP;EAEb,YAAA;AAAA;AAAY;AAAA,iBAIE,YAAA,CAAa,YAAoB;;;;AAAA;AAUjD;;iBAAgB,cAAA,CAAe,KAAqB,EAAd,cAAc;;AAAA;AAMnD;;;;;;;;;;;;;;;;;cAoGK,mBAAA,YAA+B,YAAA;EAAA,iBAClB,GAAA;cAEL,GAAA;EAIZ,MAAA,CACE,KAAA,EAAO,YAAA,IACP,cAAA,GAAiB,MAAA,EAAQ,YAAA;EAc3B,QAAA,CAAA,GAAY,OAAA;EAIZ,UAAA,CAAA,GAAc,OAAA;AAAA;;AAAO;AAAA;;cASjB,iBAAA;EAAA,iBACa,GAAA;cAEL,GAAA;EAIZ,MAAA,CACE,IAAA,EAAM,iBAAA,IACN,cAAA,GAAiB,MAAA,EAAQ,YAAA;EAc3B,QAAA,CAAA,GAAY,OAAA;EAIZ,UAAA,CAAA,GAAc,OAAA;AAAA;;;;;;;;;;;;cAgBV,oBAAA,YAAgC,kBAAA;EAAA,iBACnB,GAAA;cAEL,GAAA;EAIZ,MAAA,CACE,OAAA,EAAS,eAAA,EACT,cAAA,GAAiB,MAAA,EAAQ,YAAA;EAU3B,4BAAA,CAAA,GAAgC,sBAAA;EAIhC,UAAA,CAAA,GAAc,OAAA;EAId,QAAA,CAAA,GAAY,OAAA;AAAA;AAAA,iBAKE,yBAAA,CACd,KAAA,EAAO,cAAA,GACN,mBAAmB;AAAA,iBAIN,0BAAA,CACd,KAAA,EAAO,cAAA,GACN,oBAAoB;AAAA,iBAIP,uBAAA,CACd,KAAA,EAAO,cAAA,GACN,iBAAiB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"exporter.mjs","names":[],"sources":["../../../src/internal/otel/exporter.ts"],"sourcesContent":["import { diag } from \"@opentelemetry/api\";\nimport type { ExportResult } from \"@opentelemetry/core\";\nimport { ExportResultCode } from \"@opentelemetry/core\";\n// Deep imports against the package build output — the barrel\n// (`@opentelemetry/otlp-transformer`) re-exports the JSON *and*\n// Protobuf serializers, and the protobuf side reaches `protobufjs`\n// which transitively hits `worker_threads.MessageChannel` (via\n// `import-in-the-middle`). Vite externalizes `worker_threads` for\n// the browser test environment with a runtime-throwing stub; the\n// kernel's `await import(\"./otel/index.js\")` chunk inherits any file\n// reachable from the package root through Vite's dep optimizer, so\n// importing from the barrel breaks `provider.test.tsx` with\n// \"Failed to fetch dynamically imported module\" on the chunk URL.\n//\n// The `*/json/trace.js` (etc.) modules import only `@opentelemetry/\n// api` and the package's own JSON-only utility files — no protobuf\n// in the transitive graph. We also pin the file extension (`.js`)\n// instead of letting the directory's `index.js` resolution kick in\n// because resolvers (bun on Linux vs macOS, vite vs raw esbuild)\n// disagree about whether a missing extension on a directory-shaped\n// specifier should resolve to `<dir>/index.js` or fail. The explicit\n// `.js` is the only spelling all of them agree on.\n//\n// `@opentelemetry/otlp-transformer` doesn't ship an `\"exports\"` field\n// in its `package.json`, so these deep paths are the supported escape\n// hatch (the OTel HTTP exporters take the same approach internally —\n// see `@opentelemetry/exporter-trace-otlp-http/build/esm/platform/\n// browser/OTLPTraceExporter.js`). Pinned via the workspace catalog so\n// a minor version bump can't quietly relocate the build directory.\nimport { JsonLogsSerializer } from \"@opentelemetry/otlp-transformer/build/esm/logs/json/logs.js\";\nimport { JsonMetricsSerializer } from \"@opentelemetry/otlp-transformer/build/esm/metrics/json/metrics.js\";\nimport { JsonTraceSerializer } from \"@opentelemetry/otlp-transformer/build/esm/trace/json/trace.js\";\nimport type { ReadableLogRecord } from \"@opentelemetry/sdk-logs\";\nimport type {\n PushMetricExporter,\n ResourceMetrics,\n} from \"@opentelemetry/sdk-metrics\";\nimport { AggregationTemporality } from \"@opentelemetry/sdk-metrics\";\nimport type { ReadableSpan, SpanExporter } from \"@opentelemetry/sdk-trace-base\";\n\nimport { appendPathBeforeQuery, withQueryParam } from \"../config.js\";\nimport { PRODUCER_VERSION } from \"../version.js\";\n\n/**\n * Opaque collector ingest path. Single endpoint for every OTLP signal —\n * server-side dispatches by which `resource*` block the request carries.\n *\n * Semantically nondescript so common adblock filter lists (EasyPrivacy,\n * uBlock) don't fingerprint it the way they do `traces` / `metrics` /\n * `logs` / `telemetry`. Customer-domain proxy mode is still the\n * recommended path for browser SDKs; this is the second line of defence\n * for pk-authenticated browser clients.\n */\nconst COLLECTOR_SINK_PATH = \"/v2/sink\";\n\n/**\n * Identity gate values the collector accepts via URL query when their\n * matching headers can't be set.\n *\n * `navigator.sendBeacon` (the only browser transport that survives\n * `visibilitychange→hidden`) only attaches `Content-Type` via the\n * `Blob` — no other request headers. We rely on that exclusively for\n * the browser SDK's primary path, so the identity gates the collector\n * enforces have to ride the URL instead.\n *\n * Public-key query exposure note: public keys ship in browser bundles\n * and are visible in DevTools by design, so URL exposure is no worse\n * than the existing header path. API keys deliberately have **no**\n * query fallback — see `surfaceAkAuth` for the matching server-side\n * rationale.\n */\nconst PRODUCER_VERSION_QUERY = \"_pv\";\n\nconst JSON_CONTENT_TYPE = \"application/json\";\ninterface ExporterCommon {\n /**\n * Kept for wrapper compatibility. Public-key identity is baked into\n * `collectorUrl` as a `pk` query parameter before exporters are built.\n */\n authHeaders: Headers;\n /** Base collector URL — the opaque sink path is appended. */\n collectorUrl: string;\n}\n\n/** Exported for direct unit testing. */\nexport function buildSinkUrl(collectorUrl: string): string {\n return appendPathBeforeQuery(collectorUrl, COLLECTOR_SINK_PATH);\n}\n\n/**\n * Beacon-flavoured sink URL — encodes the producer-version (always)\n * while preserving the `pk` query already baked into `collectorUrl`.\n *\n * Exported for direct unit testing.\n */\nexport function buildBeaconUrl(input: ExporterCommon): string {\n return withQueryParam(\n buildSinkUrl(input.collectorUrl),\n PRODUCER_VERSION_QUERY,\n PRODUCER_VERSION\n );\n}\n\n/**\n * Returns true iff the runtime can dispatch a `navigator.sendBeacon`\n * call. Browser-only; server contexts (SSR, prerender, Vitest's `node`\n * environment) trip the typeof guards.\n */\nfunction canSendBeacon(): boolean {\n return (\n typeof navigator !== \"undefined\" &&\n typeof navigator.sendBeacon === \"function\"\n );\n}\n\n/**\n * Dispatches an OTLP payload via `navigator.sendBeacon` and always\n * reports `SUCCESS` to the BSP. `sendBeacon` is fire-and-forget by\n * spec — a `true` return only means \"the user agent accepted the\n * payload into its delivery queue\", not \"the server received it\".\n *\n * Why never report `FAILED`:\n * - `BatchSpanProcessor._flushOneBatch` rejects its promise on\n * `code !== SUCCESS`, and that rejection propagates up through\n * `forceFlush()` / `shutdown()`. In production, that surfaces\n * during Vercel function teardown (and other awaited unload\n * paths) as an unhandled rejection. In tests, it surfaces as\n * `provider.test.tsx > passes through useSession` — vitest's\n * `afterEach(close)` awaits `kernel.dispose() → BSP.shutdown\n * → _flushAll`, the rejection bubbles, and the test fails.\n * - `sendBeacon`'s `false` return signals a *local* drop (per-call\n * ~64KiB ceiling, per-page queue full, disallowed scheme). No\n * network request was attempted, so the service-worker backstop\n * in `internal/sw.ts` can't intercept it either. Retrying inside\n * the exporter would just hit the same browser limit again.\n *\n * The data is gone, but the export *completed* in the only sense\n * that matters to the BSP. A `diag.debug` keeps visibility for any\n * OTel diag listener wired into the kernel.\n */\nfunction reportBeacon(\n url: string,\n payload: Uint8Array | undefined,\n signal: \"trace\" | \"log\" | \"metric\",\n resultCallback: (result: ExportResult) => void\n): void {\n if (!payload || payload.byteLength === 0) {\n diag.debug(`[interfere/beacon] ${signal} serializer returned no bytes`);\n resultCallback({ code: ExportResultCode.SUCCESS });\n return;\n }\n if (!canSendBeacon()) {\n diag.debug(\n `[interfere/beacon] navigator.sendBeacon unavailable; dropping ${signal} batch (${payload.byteLength}B)`\n );\n resultCallback({ code: ExportResultCode.SUCCESS });\n return;\n }\n // `Blob` is the only sendBeacon argument shape that lets us pin\n // `Content-Type: application/json` on the request — using the raw\n // `Uint8Array` would default to `application/octet-stream`, which\n // the collector's content-type allowlist rejects with 415.\n //\n // The cast is necessary because `JsonTraceSerializer.serializeRequest`\n // returns `Uint8Array<ArrayBufferLike>`, but `Blob`'s constructor\n // narrows `BlobPart` to `Uint8Array<ArrayBuffer>` (excluding\n // `SharedArrayBuffer`). The serializer never returns shared-buffer-\n // backed bytes (it allocates a fresh `Uint8Array(byteLength)`\n // internally) so the runtime check the type system is asking for is\n // already true; a structural cast preserves that without forcing an\n // unnecessary buffer copy.\n const blob = new Blob([payload as Uint8Array<ArrayBuffer>], {\n type: JSON_CONTENT_TYPE,\n });\n if (!navigator.sendBeacon(url, blob)) {\n diag.debug(\n `[interfere/beacon] navigator.sendBeacon refused ${signal} payload (oversized or queue full); dropping ${payload.byteLength}B`\n );\n }\n resultCallback({ code: ExportResultCode.SUCCESS });\n}\n\n/**\n * Browser-side OTLP trace exporter.\n *\n * Dispatches every export via `navigator.sendBeacon` — which is the\n * only browser transport that reliably commits a request the page is\n * also tearing down (`visibilitychange→hidden`, hard navigation). The\n * `keepalive: true` fetch path the OTLP HTTP exporter ships with\n * works for small payloads but falls back to ordinary fetch (which\n * the renderer aborts on unload) once the cumulative 64KiB / 9-\n * concurrent budget is exhausted. Production data on 2026-05-11\n * attributed ~15% browser-fetch span loss to that fallback; the\n * beacon path closes that hole by design.\n *\n * Identity rides the URL because beacons can't carry custom headers.\n *\n * No retry on failure. The service worker backstop captures 5xx /\n * network failures separately by intercepting the same beacon POST\n * and queueing into IndexedDB for replay (`internal/sw.ts`).\n */\nclass BeaconTraceExporter implements SpanExporter {\n private readonly url: string;\n\n constructor(url: string) {\n this.url = url;\n }\n\n export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void\n ): void {\n if (spans.length === 0) {\n resultCallback({ code: ExportResultCode.SUCCESS });\n return;\n }\n reportBeacon(\n this.url,\n JsonTraceSerializer.serializeRequest(spans),\n \"trace\",\n resultCallback\n );\n }\n\n shutdown(): Promise<void> {\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n\n/**\n * Browser-side OTLP log exporter — same beacon transport, log\n * payload. See `BeaconTraceExporter` for the design rationale.\n */\nclass BeaconLogExporter {\n private readonly url: string;\n\n constructor(url: string) {\n this.url = url;\n }\n\n export(\n logs: ReadableLogRecord[],\n resultCallback: (result: ExportResult) => void\n ): void {\n if (logs.length === 0) {\n resultCallback({ code: ExportResultCode.SUCCESS });\n return;\n }\n reportBeacon(\n this.url,\n JsonLogsSerializer.serializeRequest(logs),\n \"log\",\n resultCallback\n );\n }\n\n shutdown(): Promise<void> {\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n\n/**\n * Browser-side OTLP metric exporter — beacon transport, metric\n * payload, `DELTA` temporality.\n *\n * `DELTA` matches what the OTLP HTTP exporter the kit's previous\n * `OTLPMetricExporter` was configured with (see the\n * `temporalityPreference: AggregationTemporalityPreference.DELTA`\n * arg `provider.ts` used to pass). Returning the same temporality\n * for every instrument type keeps the wire shape downstream\n * consumers receive unchanged across the migration.\n */\nclass BeaconMetricExporter implements PushMetricExporter {\n private readonly url: string;\n\n constructor(url: string) {\n this.url = url;\n }\n\n export(\n metrics: ResourceMetrics,\n resultCallback: (result: ExportResult) => void\n ): void {\n reportBeacon(\n this.url,\n JsonMetricsSerializer.serializeRequest(metrics),\n \"metric\",\n resultCallback\n );\n }\n\n selectAggregationTemporality(): AggregationTemporality {\n return AggregationTemporality.DELTA;\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n\n shutdown(): Promise<void> {\n return Promise.resolve();\n }\n}\n\nexport function createBeaconTraceExporter(\n input: ExporterCommon\n): BeaconTraceExporter {\n return new BeaconTraceExporter(buildBeaconUrl(input));\n}\n\nexport function createBeaconMetricExporter(\n input: ExporterCommon\n): BeaconMetricExporter {\n return new BeaconMetricExporter(buildBeaconUrl(input));\n}\n\nexport function createBeaconLogExporter(\n input: ExporterCommon\n): BeaconLogExporter {\n return new BeaconLogExporter(buildBeaconUrl(input));\n}\n"],"mappings":"miBAqFA,SAAgB,aAAa,aAA8B,CACzD,OAAO,sBAAsB,aAAc,UAAmB,CAChE,CAQA,SAAgB,eAAe,MAA+B,CAC5D,OAAO,eACL,aAAa,MAAM,YAAY,EAC/B,MACA,gBACF,CACF,CAOA,SAAS,eAAyB,CAChC,OACE,OAAO,UAAc,KACrB,OAAO,UAAU,YAAe,UAEpC,CA2BA,SAAS,aACP,IACA,QACA,OACA,eACM,CACN,GAAI,CAAC,SAAW,QAAQ,aAAe,EAAG,CACxC,KAAK,MAAM,sBAAsB,OAAO,8BAA8B,EACtE,eAAe,CAAE,KAAM,iBAAiB,OAAQ,CAAC,EACjD,MACF,CACA,GAAI,CAAC,cAAc,EAAG,CACpB,KAAK,MACH,iEAAiE,OAAO,UAAU,QAAQ,WAAW,GACvG,EACA,eAAe,CAAE,KAAM,iBAAiB,OAAQ,CAAC,EACjD,MACF,CAcA,IAAM,KAAO,IAAI,KAAK,CAAC,OAAkC,EAAG,CAC1D,KAAM,kBACR,CAAC,EACI,UAAU,WAAW,IAAK,IAAI,GACjC,KAAK,MACH,mDAAmD,OAAO,+CAA+C,QAAQ,WAAW,EAC9H,EAEF,eAAe,CAAE,KAAM,iBAAiB,OAAQ,CAAC,CACnD,CAqBA,IAAM,oBAAN,KAAkD,CAChD,IAEA,YAAY,IAAa,CACvB,KAAK,IAAM,GACb,CAEA,OACE,MACA,eACM,CACN,GAAI,MAAM,SAAW,EAAG,CACtB,eAAe,CAAE,KAAM,iBAAiB,OAAQ,CAAC,EACjD,MACF,CACA,aACE,KAAK,IACL,oBAAoB,iBAAiB,KAAK,EAC1C,QACA,cACF,CACF,CAEA,UAA0B,CACxB,OAAO,QAAQ,QAAQ,CACzB,CAEA,YAA4B,CAC1B,OAAO,QAAQ,QAAQ,CACzB,CACF,EAMM,kBAAN,KAAwB,CACtB,IAEA,YAAY,IAAa,CACvB,KAAK,IAAM,GACb,CAEA,OACE,KACA,eACM,CACN,GAAI,KAAK,SAAW,EAAG,CACrB,eAAe,CAAE,KAAM,iBAAiB,OAAQ,CAAC,EACjD,MACF,CACA,aACE,KAAK,IACL,mBAAmB,iBAAiB,IAAI,EACxC,MACA,cACF,CACF,CAEA,UAA0B,CACxB,OAAO,QAAQ,QAAQ,CACzB,CAEA,YAA4B,CAC1B,OAAO,QAAQ,QAAQ,CACzB,CACF,EAaM,qBAAN,KAAyD,CACvD,IAEA,YAAY,IAAa,CACvB,KAAK,IAAM,GACb,CAEA,OACE,QACA,eACM,CACN,aACE,KAAK,IACL,sBAAsB,iBAAiB,OAAO,EAC9C,SACA,cACF,CACF,CAEA,8BAAuD,CACrD,OAAO,uBAAuB,KAChC,CAEA,YAA4B,CAC1B,OAAO,QAAQ,QAAQ,CACzB,CAEA,UAA0B,CACxB,OAAO,QAAQ,QAAQ,CACzB,CACF,EAEA,SAAgB,0BACd,MACqB,CACrB,OAAO,IAAI,oBAAoB,eAAe,KAAK,CAAC,CACtD,CAEA,SAAgB,2BACd,MACsB,CACtB,OAAO,IAAI,qBAAqB,eAAe,KAAK,CAAC,CACvD,CAEA,SAAgB,wBACd,MACmB,CACnB,OAAO,IAAI,kBAAkB,eAAe,KAAK,CAAC,CACpD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"instrumentations.d.mts","names":[],"sources":["../../../src/internal/otel/instrumentations.ts"],"mappings":";;;UAgHiB,qBAAA;;AAAjB;;;;;EAOE,UAAA,YAAsB,MAAA;EAiBW;;;;;EAXjC,oBAAA,aAAiC,MAAA;EAMjC;;;;;EAAA,YAAA,IAAgB,QAAA;EAkBF;;;;EAbd,cAAA,EAAgB,iBAAA;AAAA;;;;;;;;;;;iBAaF,+BAAA,CACd,KAA4B,EAArB,qBAAqB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"instrumentations.mjs","names":[],"sources":["../../../src/internal/otel/instrumentations.ts"],"sourcesContent":["import type { Span } from \"@opentelemetry/api\";\nimport { registerInstrumentations } from \"@opentelemetry/instrumentation\";\nimport {\n BrowserNavigationInstrumentation,\n defaultSanitizeUrl,\n} from \"@opentelemetry/instrumentation-browser-navigation\";\nimport { DocumentLoadInstrumentation } from \"@opentelemetry/instrumentation-document-load\";\nimport { FetchInstrumentation } from \"@opentelemetry/instrumentation-fetch\";\nimport {\n LongTaskInstrumentation,\n type ObserverCallbackInformation,\n} from \"@opentelemetry/instrumentation-long-task\";\nimport { UserInteractionInstrumentation } from \"@opentelemetry/instrumentation-user-interaction\";\nimport { XMLHttpRequestInstrumentation } from \"@opentelemetry/instrumentation-xml-http-request\";\nimport type { WebTracerProvider } from \"@opentelemetry/sdk-trace-web\";\n\nimport { describeActionable, isActionable } from \"../dom/actionable.js\";\n\nconst ATTR_HTTP_ROUTE = \"http.route\" as const;\nconst ATTR_URL_FULL = \"url.full\" as const;\nconst ATTR_URL_PATH = \"url.path\" as const;\n\n/**\n * Mirrors the `semconvStabilityOptIn` constant from the internal\n * `observability/browser/rum.ts`. Pins the HTTP attribute set to the\n * stable-namespace conventions (`url.path`, `server.address`, …) so\n * dashboards that filter on those keys aren't broken by minor\n * upgrades of the instrumentation packages.\n */\nconst SEMCONV_STABILITY_OPT_IN =\n \"http,database,messaging,gen_ai_latest_experimental\";\n\n/**\n * Hosts/paths whose CLIENT fetch spans we never want to emit: they\n * always become 1-span orphan traces (no traceparent propagation to\n * 3rd-party origins) and clutter live tail. Conservative, customer-\n * generic list — common analytics / ads / auth / replay vendors that\n * many apps use. Customer-specific noise (feature-flag CDNs, our own\n * BetterStack, etc.) belongs in the customer's own `ignoreUrls` config,\n * not hardcoded into a public SDK.\n */\nconst THIRD_PARTY_IGNORE: RegExp[] = [\n /(?:^|\\/\\/)clerk\\./,\n /\\.clerk\\./,\n /\\.sentry\\./,\n /\\.intercom\\./,\n /\\.posthog\\./,\n /\\.googletagmanager\\.com/,\n /\\.google-analytics\\.com/,\n /\\.googleapis\\.com/,\n /\\.doubleclick\\.net/,\n /\\.facebook\\.com/,\n /\\.fbcdn\\.net/,\n /\\.analytics\\.google\\.com/,\n /px\\.ads\\.linkedin\\.com/,\n /[?&]_rsc=/,\n];\n\nconst NEVER_MATCH = /^$/;\nconst ESCAPE_RE = /[.*+?^${}()|[\\]\\\\]/g;\n\nfunction sameOriginPattern(): RegExp {\n if (typeof window === \"undefined\") {\n return NEVER_MATCH;\n }\n const { protocol, host } = window.location;\n return new RegExp(`^${protocol}//${host.replace(ESCAPE_RE, \"\\\\$&\")}`);\n}\n\nfunction stampInteractionAttrs(\n eventType: string,\n element: HTMLElement,\n span: Span\n): void {\n const desc = describeActionable(element);\n span.setAttribute(\"ui.event_type\", eventType);\n span.setAttribute(\"ui.target.tag\", desc.tag);\n if (desc.id) {\n span.setAttribute(\"ui.target.id\", desc.id);\n }\n if (desc.name) {\n span.setAttribute(\"ui.target.name\", desc.name);\n }\n if (desc.role) {\n span.setAttribute(\"ui.target.role\", desc.role);\n }\n if (desc.ariaLabel) {\n span.setAttribute(\"ui.target.aria_label\", desc.ariaLabel);\n }\n // Truncated visible label — useful for triage without exfiltrating\n // full DOM contents. Only meaningful on the direct target.\n if (desc.text) {\n span.setAttribute(\"ui.target.text\", desc.text);\n }\n}\n\nfunction stampUrlAttrs(\n span: Span,\n resolveRoute: ((pathname: string) => string | undefined) | undefined\n): void {\n if (typeof window === \"undefined\") {\n return;\n }\n const pathname = window.location.pathname;\n const route = resolveRoute?.(pathname);\n span.setAttribute(ATTR_URL_PATH, route ?? pathname);\n span.setAttribute(ATTR_URL_FULL, window.location.href);\n if (route) {\n span.setAttribute(ATTR_HTTP_ROUTE, route);\n }\n}\n\nexport interface InstrumentationsInput {\n /**\n * URLs the SDK exempts from `fetch` and `XHR` instrumentation —\n * typically our own ingest/OTLP endpoints, so the SDK doesn't trace\n * its own export requests in an infinite loop. Combined with the\n * `THIRD_PARTY_IGNORE` defaults and the customer's `ignoreUrls`.\n */\n ignoreUrls: (string | RegExp)[];\n /**\n * Customer-supplied URL patterns the fetch + XHR instrumentations\n * inject `traceparent` + `baggage` headers on. Same-origin requests\n * always propagate.\n */\n propagateContextUrls?: (string | RegExp)[];\n /**\n * Pathname → low-cardinality route template (e.g. `/blog/[slug]`).\n * Drives span renaming + `url.path` / `http.route` attrs across\n * fetch, document-load, user-interaction, and long-task.\n */\n resolveRoute?: (pathname: string) => string | undefined;\n /**\n * The kernel's private provider — instrumentations register against\n * it, not the global one, so customer OTel setups stay untouched.\n */\n tracerProvider: WebTracerProvider;\n}\n\n/**\n * Registers every browser auto-instrumentation against the kernel's\n * private provider. Every instrumentation is enriched with the same\n * route-template-aware URL attributes so dashboards can slice fetch /\n * interaction / long-task / resource-load by `http.route` without\n * cardinality blow-up from raw pathnames.\n *\n * Returns a disposer that unregisters everything; called by\n * `kernel.dispose()`.\n */\nexport function registerBundledInstrumentations(\n input: InstrumentationsInput\n): () => void {\n const { tracerProvider, ignoreUrls, propagateContextUrls = [] } = input;\n const resolveRoute = input.resolveRoute;\n const origin = sameOriginPattern();\n const fetchIgnore = [...ignoreUrls, ...THIRD_PARTY_IGNORE];\n\n return registerInstrumentations({\n tracerProvider,\n instrumentations: [\n new FetchInstrumentation({\n propagateTraceHeaderCorsUrls: [origin, ...propagateContextUrls],\n ignoreUrls: fetchIgnore,\n semconvStabilityOptIn: SEMCONV_STABILITY_OPT_IN,\n measureRequestSize: true,\n applyCustomAttributesOnSpan: (\n span: Span,\n request: Request | RequestInit\n ) => {\n const url =\n request instanceof Request ? request.url : request?.toString();\n if (typeof url !== \"string\") {\n return;\n }\n try {\n const parsed = new URL(url);\n span.setAttribute(ATTR_URL_PATH, parsed.pathname);\n const isTracked =\n origin.test(url) ||\n propagateContextUrls.some((re) =>\n re instanceof RegExp ? re.test(url) : url.includes(re)\n );\n if (isTracked) {\n const method =\n (request instanceof Request ? request.method : undefined) ??\n \"GET\";\n const route = resolveRoute?.(parsed.pathname);\n if (route) {\n span.setAttribute(ATTR_HTTP_ROUTE, route);\n }\n span.updateName(`${method} ${route ?? parsed.pathname}`);\n }\n } catch {\n return;\n }\n },\n }),\n new XMLHttpRequestInstrumentation({\n propagateTraceHeaderCorsUrls: [origin, ...propagateContextUrls],\n ignoreUrls: fetchIgnore,\n semconvStabilityOptIn: SEMCONV_STABILITY_OPT_IN,\n }),\n new DocumentLoadInstrumentation({\n semconvStabilityOptIn: SEMCONV_STABILITY_OPT_IN,\n // Default `resourceFetch` span name is the literal string\n // `\"resourceFetch\"` for every entry — useless when a single\n // page load emits 30 of them. Replace with the pathname (or\n // origin+path for cross-origin) so the waterfall is scannable.\n applyCustomAttributesOnSpan: {\n resourceFetch: (span, entry) => {\n const name = entry?.name;\n if (typeof name !== \"string\") {\n return;\n }\n try {\n const url = new URL(name);\n const isCrossOrigin =\n typeof window !== \"undefined\" &&\n url.origin !== window.location.origin;\n span.updateName(\n isCrossOrigin\n ? `resource: ${url.host}${url.pathname}`\n : `resource: ${url.pathname}`\n );\n span.setAttribute(ATTR_URL_FULL, name);\n span.setAttribute(ATTR_URL_PATH, url.pathname);\n span.setAttribute(\"server.address\", url.host);\n } catch {\n // Malformed/relative URL — leave the default name and\n // let the entry's own attributes carry whatever info\n // the SDK already stamped.\n }\n },\n },\n }),\n // `BrowserNavigationInstrumentation` emits a `browser.navigation`\n // log record per hard navigation (page load) and soft navigation\n // (history.pushState, hash change, back/forward). The default\n // sanitizer redacts credentials in the URL and common sensitive\n // query params (`token`, `api_key`, `secret`, …) before emit.\n new BrowserNavigationInstrumentation({\n sanitizeUrl: defaultSanitizeUrl,\n }),\n new UserInteractionInstrumentation({\n eventNames: [\"click\", \"submit\"],\n // Despite the name, `shouldPreventSpanCreation` is also the\n // hook for *enriching* spans (return falsy → create the span;\n // the callback may set attributes on the way through). We use\n // it as both: stamp a stable target descriptor for\n // dashboarding, then suppress spans on elements that aren't\n // actionable (scroll containers, body clicks, etc.).\n shouldPreventSpanCreation: (eventType, element, span) => {\n if (eventType === \"click\" && !isActionable(element)) {\n return true;\n }\n stampInteractionAttrs(eventType, element, span);\n stampUrlAttrs(span, resolveRoute);\n return false;\n },\n }),\n // Long-task spans default to a generic `longtask` name with\n // attribution embedded as attributes — but no page context.\n // Empty `url.path` makes it impossible to triage which routes\n // are jank-heavy. Stamp it in the observer callback so\n // dashboards can slice by route.\n new LongTaskInstrumentation({\n observerCallback: (span, info: ObserverCallbackInformation) => {\n stampUrlAttrs(span, resolveRoute);\n // PerformanceLongTaskTiming attribution[0].containerType is\n // the surface that hosted the offending task. Most are\n // `\"window\"` (main frame); the few that aren't (`\"iframe\"`,\n // `\"embed\"`, `\"object\"`) are the actionable ones — surface\n // them as a top-level attr instead of leaving them buried\n // in `longtask.attribution.container_type`.\n const containerType =\n info.longtaskEntry.attribution[0]?.containerType;\n if (containerType) {\n span.setAttribute(\"longtask.container_type\", containerType);\n }\n },\n }),\n ],\n });\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"page-scope-context-manager.d.mts","names":[],"sources":["../../../src/internal/otel/page-scope-context-manager.ts"],"mappings":";;;;;;AAyBA;;;;;;;;;;;;;;;;AAO4B;;;cAPf,uBAAA,SAAgC,kBAAA;EAAA,QACnC,SAAA;EAER,YAAA,CAAa,GAAA,EAAK,OAAA;EAIT,MAAA,CAAA,GAAU,OAAA;AAAA"}
@@ -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":";;;;;AAuCA;;;;AAAsD;;;;;;;;;iBAAtC,2BAAA,CAAA,GAA+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// Local instance — extraction must not depend on whether the customer (or\n// our own bootstrap) set a global propagator.\nconst W3C_PROPAGATOR = new W3CTraceContextPropagator();\n\nfunction readMeta(name: string): string | null {\n if (typeof document === \"undefined\") {\n return null;\n }\n const el = document.querySelector(`meta[name=\"${name}\"]`);\n return el?.getAttribute(\"content\") ?? null;\n}\n\n/**\n * Reads the W3C `traceparent` (and optional `tracestate`) meta tag from the\n * document head and extracts an OTel `Context`. SSR renderers (Next, Vite,\n * …) inject the server-side request span's context so the client SDK can\n * stitch its spans onto the same trace.\n *\n * Returns `null` when:\n * - called server-side (no `document`)\n * - no `traceparent` meta tag is present\n * - the extracted context has an invalid trace id\n * - the `sampled` flag is unset — the browser doesn't honor the bit when\n * creating child spans, so an unsampled parent would orphan every\n * browser span under a trace with no recorded server segments. Drop\n * instead so spans fall back to a fresh root.\n */\nexport function readPropagationFromDocument(): Context | null {\n const traceparent = readMeta(TRACEPARENT_META);\n if (!traceparent) {\n return null;\n }\n\n const carrier: Record<string, string> = { traceparent };\n const tracestate = readMeta(TRACESTATE_META);\n if (tracestate) {\n carrier[TRACESTATE_META] = tracestate;\n }\n\n const extracted = W3C_PROPAGATOR.extract(\n ROOT_CONTEXT,\n carrier,\n defaultTextMapGetter\n );\n const spanCtx = trace.getSpanContext(extracted);\n // octet of flag bits per the W3C trace-context spec; bit 0 is the sampled\n // bit. Bitwise AND is the canonical extraction.\n if (\n !spanCtx ||\n spanCtx.traceId === INVALID_TRACE_ID ||\n // biome-ignore lint/suspicious/noBitwiseOperators: traceFlags is a single\n (spanCtx.traceFlags & TRACE_FLAGS_SAMPLED) === 0\n ) {\n return null;\n }\n return extracted;\n}\n"],"mappings":"mIAQA,MACM,gBAAkB,aAKlB,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,EAW9C,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":";;;;;;;;;UA0GiB,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"}