@nathanclaire/edge-telemetry-sdk 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +831 -186
- package/dist/appLifecycle-CECgRVu_.cjs +2 -0
- package/dist/appLifecycle-CECgRVu_.cjs.map +1 -0
- package/dist/appLifecycle-DQErzJIR.js +22 -0
- package/dist/appLifecycle-DQErzJIR.js.map +1 -0
- package/dist/batch-CwoO7noL.js +56 -0
- package/dist/batch-CwoO7noL.js.map +1 -0
- package/dist/batch-DNu8Xr9T.cjs +2 -0
- package/dist/batch-DNu8Xr9T.cjs.map +1 -0
- package/dist/crashCapture-BOJ8nSAZ.cjs +4 -0
- package/dist/crashCapture-BOJ8nSAZ.cjs.map +1 -0
- package/dist/crashCapture-BW8WuyaU.js +70 -0
- package/dist/crashCapture-BW8WuyaU.js.map +1 -0
- package/dist/crashHandler.web-8F_k2veo.js +29 -0
- package/dist/crashHandler.web-8F_k2veo.js.map +1 -0
- package/dist/crashHandler.web-CWmF56mJ.cjs +2 -0
- package/dist/crashHandler.web-CWmF56mJ.cjs.map +1 -0
- package/dist/crashHandlerNative.native-C2kSikDt.cjs +2 -0
- package/dist/crashHandlerNative.native-C2kSikDt.cjs.map +1 -0
- package/dist/crashHandlerNative.native-FupMnr1U.js +39 -0
- package/dist/crashHandlerNative.native-FupMnr1U.js.map +1 -0
- package/dist/deviceInfo.native-BJ-3Jj1T.js +53 -0
- package/dist/deviceInfo.native-BJ-3Jj1T.js.map +1 -0
- package/dist/deviceInfo.native-BoeTZcD6.cjs +2 -0
- package/dist/deviceInfo.native-BoeTZcD6.cjs.map +1 -0
- package/dist/deviceInfo.web-3L3IDQQA.cjs +2 -0
- package/dist/deviceInfo.web-3L3IDQQA.cjs.map +1 -0
- package/dist/deviceInfo.web-BFB1-pcA.js +41 -0
- package/dist/deviceInfo.web-BFB1-pcA.js.map +1 -0
- package/dist/frameTracker-CHG6sIx9.cjs +2 -0
- package/dist/frameTracker-CHG6sIx9.cjs.map +1 -0
- package/dist/frameTracker-DdB8W_kC.js +54 -0
- package/dist/frameTracker-DdB8W_kC.js.map +1 -0
- package/dist/httpAttributes-BrrtM8zh.js +70 -0
- package/dist/httpAttributes-BrrtM8zh.js.map +1 -0
- package/dist/httpAttributes-D0m6UID7.cjs +2 -0
- package/dist/httpAttributes-D0m6UID7.cjs.map +1 -0
- package/dist/index-BtLlV1HA.js +57 -0
- package/dist/index-BtLlV1HA.js.map +1 -0
- package/dist/{deviceInfo.native-D2k0LDhI.js → index-C2XQ-EYw.js} +362 -411
- package/dist/index-C2XQ-EYw.js.map +1 -0
- package/dist/index-CXsVZY35.cjs +2 -0
- package/dist/index-CXsVZY35.cjs.map +1 -0
- package/dist/index-DLquF88M.cjs +2 -0
- package/dist/index-DLquF88M.cjs.map +1 -0
- package/dist/index.native.cjs +1 -1
- package/dist/index.native.cjs.map +1 -1
- package/dist/index.native.js +94 -59
- package/dist/index.native.js.map +1 -1
- package/dist/index.web.cjs +1 -1
- package/dist/index.web.cjs.map +1 -1
- package/dist/index.web.js +88 -33
- package/dist/index.web.js.map +1 -1
- package/dist/interactionWeb.web-Bl_cZlgh.cjs +2 -0
- package/dist/interactionWeb.web-Bl_cZlgh.cjs.map +1 -0
- package/dist/interactionWeb.web-CJAviVQ7.js +109 -0
- package/dist/interactionWeb.web-CJAviVQ7.js.map +1 -0
- package/dist/interceptFetchWeb.web-T88kO-bE.cjs +2 -0
- package/dist/interceptFetchWeb.web-T88kO-bE.cjs.map +1 -0
- package/dist/interceptFetchWeb.web-UXC2Lb6X.js +61 -0
- package/dist/interceptFetchWeb.web-UXC2Lb6X.js.map +1 -0
- package/dist/interceptHttpNative.native-BVpFuoNB.cjs +2 -0
- package/dist/interceptHttpNative.native-BVpFuoNB.cjs.map +1 -0
- package/dist/interceptHttpNative.native-rX-lPNST.js +18 -0
- package/dist/interceptHttpNative.native-rX-lPNST.js.map +1 -0
- package/dist/loadingTime-Bhomd9qq.cjs +2 -0
- package/dist/loadingTime-Bhomd9qq.cjs.map +1 -0
- package/dist/loadingTime-ZkYvFijA.js +70 -0
- package/dist/loadingTime-ZkYvFijA.js.map +1 -0
- package/dist/memoryNative.native-C42h9SjK.js +54 -0
- package/dist/memoryNative.native-C42h9SjK.js.map +1 -0
- package/dist/memoryNative.native-CA-7w9w1.cjs +2 -0
- package/dist/memoryNative.native-CA-7w9w1.cjs.map +1 -0
- package/dist/memoryStore-BIGZmmfD.cjs +2 -0
- package/dist/memoryStore-BIGZmmfD.cjs.map +1 -0
- package/dist/memoryStore-F5I-GQKM.js +193 -0
- package/dist/memoryStore-F5I-GQKM.js.map +1 -0
- package/dist/nativeSender-CfQaRuDK.cjs +2 -0
- package/dist/nativeSender-CfQaRuDK.cjs.map +1 -0
- package/dist/nativeSender-pgVSIv-X.js +59 -0
- package/dist/nativeSender-pgVSIv-X.js.map +1 -0
- package/dist/navigationRef-BzYbs8cm.js +28 -0
- package/dist/navigationRef-BzYbs8cm.js.map +1 -0
- package/dist/navigationRef-C-_qhb43.cjs +2 -0
- package/dist/navigationRef-C-_qhb43.cjs.map +1 -0
- package/dist/{navigationTracker-Cts68dgG.js → navigationTracker-BmywA_Bg.js} +4 -4
- package/dist/navigationTracker-BmywA_Bg.js.map +1 -0
- package/dist/navigationTracker-HkwVyywz.cjs +2 -0
- package/dist/navigationTracker-HkwVyywz.cjs.map +1 -0
- package/dist/navigationWeb.web-CW_z7Ca4.cjs +2 -0
- package/dist/navigationWeb.web-CW_z7Ca4.cjs.map +1 -0
- package/dist/navigationWeb.web-CcwgKuIZ.js +55 -0
- package/dist/navigationWeb.web-CcwgKuIZ.js.map +1 -0
- package/dist/{networkInfo.native-BK5fpAOL.js → networkInfo.native-CGrx9cnH.js} +15 -15
- package/dist/{networkInfo.native-BK5fpAOL.js.map → networkInfo.native-CGrx9cnH.js.map} +1 -1
- package/dist/{networkInfo.native-cxuw93Uy.cjs → networkInfo.native-YVySdv3S.cjs} +2 -2
- package/dist/{networkInfo.native-cxuw93Uy.cjs.map → networkInfo.native-YVySdv3S.cjs.map} +1 -1
- package/dist/{networkInfo.web-twcNn_hc.js → networkInfo.web-B2YlLNsI.js} +3 -3
- package/dist/networkInfo.web-B2YlLNsI.js.map +1 -0
- package/dist/networkInfo.web-PltJTHsy.cjs +2 -0
- package/dist/networkInfo.web-PltJTHsy.cjs.map +1 -0
- package/dist/runtimeReady.native-Bh7EcYmK.js +14 -0
- package/dist/runtimeReady.native-Bh7EcYmK.js.map +1 -0
- package/dist/runtimeReady.native-jUmuE1lV.cjs +2 -0
- package/dist/runtimeReady.native-jUmuE1lV.cjs.map +1 -0
- package/dist/runtimeReady.web-C8fdIbVR.js +25 -0
- package/dist/runtimeReady.web-C8fdIbVR.js.map +1 -0
- package/dist/runtimeReady.web-DHW682RN.cjs +2 -0
- package/dist/runtimeReady.web-DHW682RN.cjs.map +1 -0
- package/dist/seedRuntimeReady-BKwkBEAF.cjs +2 -0
- package/dist/seedRuntimeReady-BKwkBEAF.cjs.map +1 -0
- package/dist/seedRuntimeReady-Cz8nOKoo.js +10 -0
- package/dist/seedRuntimeReady-Cz8nOKoo.js.map +1 -0
- package/dist/src/adapters/appLifecycle.d.ts +8 -1
- package/dist/src/adapters/batch.d.ts +2 -0
- package/dist/src/adapters/crashCapture.d.ts +38 -9
- package/dist/src/adapters/failedEvents.d.ts +43 -0
- package/dist/src/adapters/frameAggregate.d.ts +8 -3
- package/dist/src/adapters/frameTracker.d.ts +30 -0
- package/dist/src/adapters/httpAttributes.d.ts +18 -0
- package/dist/src/adapters/loadingTime.d.ts +67 -0
- package/dist/src/adapters/native/interceptHttpNative.native.d.ts +18 -0
- package/dist/src/adapters/native/memoryNative.native.d.ts +15 -11
- package/dist/src/adapters/native/runtimeReady.native.d.ts +1 -0
- package/dist/src/adapters/native/store.native.d.ts +10 -0
- package/dist/src/adapters/nativeSender.d.ts +2 -2
- package/dist/src/adapters/navigationRef.d.ts +21 -0
- package/dist/src/adapters/navigationTracker.d.ts +1 -1
- package/dist/src/adapters/screenTiming.d.ts +8 -2
- package/dist/src/adapters/seedRuntimeReady.d.ts +8 -0
- package/dist/src/adapters/traceHeader.d.ts +33 -0
- package/dist/src/adapters/traceManager.d.ts +131 -0
- package/dist/src/adapters/uiInteraction.d.ts +111 -0
- package/dist/src/adapters/viewManager.d.ts +161 -0
- package/dist/src/adapters/viewport.d.ts +15 -0
- package/dist/src/adapters/web/interactionWeb.web.d.ts +36 -0
- package/dist/src/adapters/web/interceptFetchWeb.web.d.ts +3 -11
- package/dist/src/adapters/web/navigationWeb.web.d.ts +10 -0
- package/dist/src/adapters/web/runtimeReady.web.d.ts +5 -0
- package/dist/src/adapters/web/store.web.d.ts +12 -0
- package/dist/src/adapters/web/webVitals.web.d.ts +49 -0
- package/dist/src/adapters/webSender.d.ts +2 -2
- package/dist/src/adapters/webVitals.d.ts +25 -0
- package/dist/src/adapters/xhrIntercept.d.ts +53 -0
- package/dist/src/core/beforeSend.d.ts +20 -0
- package/dist/src/core/debug.d.ts +7 -0
- package/dist/src/core/memoryStore.d.ts +23 -0
- package/dist/src/core/store.d.ts +41 -0
- package/dist/src/core/telemetry.d.ts +305 -35
- package/dist/src/core/userProfile.d.ts +12 -0
- package/dist/src/core/utils/json.d.ts +7 -0
- package/dist/src/core/utils/uuid.d.ts +10 -1
- package/dist/src/createTelemetry.native.d.ts +7 -0
- package/dist/src/createTelemetry.web.d.ts +7 -0
- package/dist/src/index.base.d.ts +17 -1
- package/dist/src/index.native.d.ts +46 -9
- package/dist/src/index.web.d.ts +33 -2
- package/dist/store.native-BYBJcy6o.js +32 -0
- package/dist/store.native-BYBJcy6o.js.map +1 -0
- package/dist/store.native-TPqXngmF.cjs +2 -0
- package/dist/store.native-TPqXngmF.cjs.map +1 -0
- package/dist/store.web-CvqqN-_Z.cjs +2 -0
- package/dist/store.web-CvqqN-_Z.cjs.map +1 -0
- package/dist/store.web-DJAlN0nc.js +31 -0
- package/dist/store.web-DJAlN0nc.js.map +1 -0
- package/dist/telemetry-ByDJIz_9.js +1280 -0
- package/dist/telemetry-ByDJIz_9.js.map +1 -0
- package/dist/telemetry-CrU7i1Jc.cjs +2 -0
- package/dist/telemetry-CrU7i1Jc.cjs.map +1 -0
- package/dist/uiInteraction-CdzvEI1p.cjs +2 -0
- package/dist/uiInteraction-CdzvEI1p.cjs.map +1 -0
- package/dist/uiInteraction-DAqxxz0q.js +104 -0
- package/dist/uiInteraction-DAqxxz0q.js.map +1 -0
- package/dist/viewport-Dd85GUNO.cjs +2 -0
- package/dist/viewport-Dd85GUNO.cjs.map +1 -0
- package/dist/viewport-Dzmt5QwK.js +15 -0
- package/dist/viewport-Dzmt5QwK.js.map +1 -0
- package/dist/web-vitals.attribution-CVCPiX-h.cjs +2 -0
- package/dist/web-vitals.attribution-CVCPiX-h.cjs.map +1 -0
- package/dist/web-vitals.attribution-xw9N5MKQ.js +488 -0
- package/dist/web-vitals.attribution-xw9N5MKQ.js.map +1 -0
- package/dist/webSender-BIwslfpT.js +63 -0
- package/dist/webSender-BIwslfpT.js.map +1 -0
- package/dist/webSender-CVtE4sOF.cjs +2 -0
- package/dist/webSender-CVtE4sOF.cjs.map +1 -0
- package/dist/webVitals.web-CNIpsKlA.js +85 -0
- package/dist/webVitals.web-CNIpsKlA.js.map +1 -0
- package/dist/webVitals.web-DpWJ-iYp.cjs +2 -0
- package/dist/webVitals.web-DpWJ-iYp.cjs.map +1 -0
- package/dist/xhrIntercept-BADex6Cq.cjs +2 -0
- package/dist/xhrIntercept-BADex6Cq.cjs.map +1 -0
- package/dist/xhrIntercept-BNiA41Mj.js +53 -0
- package/dist/xhrIntercept-BNiA41Mj.js.map +1 -0
- package/docs/migration-v4.md +190 -0
- package/package.json +12 -4
- package/dist/appLifecycle-Cynw0kAv.js +0 -14
- package/dist/appLifecycle-Cynw0kAv.js.map +0 -1
- package/dist/appLifecycle-DvJIt7ym.cjs +0 -2
- package/dist/appLifecycle-DvJIt7ym.cjs.map +0 -1
- package/dist/batch-CLBFgYPC.cjs +0 -2
- package/dist/batch-CLBFgYPC.cjs.map +0 -1
- package/dist/batch-DLG_gtOm.js +0 -12
- package/dist/batch-DLG_gtOm.js.map +0 -1
- package/dist/crashCapture-BYTM4FLC.cjs +0 -2
- package/dist/crashCapture-BYTM4FLC.cjs.map +0 -1
- package/dist/crashCapture-D_rkkBVa.js +0 -31
- package/dist/crashCapture-D_rkkBVa.js.map +0 -1
- package/dist/crashHandler.web-BSxDpFTo.cjs +0 -2
- package/dist/crashHandler.web-BSxDpFTo.cjs.map +0 -1
- package/dist/crashHandler.web-D59bmhg9.js +0 -32
- package/dist/crashHandler.web-D59bmhg9.js.map +0 -1
- package/dist/crashHandlerNative.native-DeAFze4E.cjs +0 -2
- package/dist/crashHandlerNative.native-DeAFze4E.cjs.map +0 -1
- package/dist/crashHandlerNative.native-d7qJkzA6.js +0 -41
- package/dist/crashHandlerNative.native-d7qJkzA6.js.map +0 -1
- package/dist/debug-DsvQ9y3W.js +0 -68
- package/dist/debug-DsvQ9y3W.js.map +0 -1
- package/dist/debug-rnOOxFpN.cjs +0 -2
- package/dist/debug-rnOOxFpN.cjs.map +0 -1
- package/dist/deviceInfo.native-7hbRYixR.cjs +0 -2
- package/dist/deviceInfo.native-7hbRYixR.cjs.map +0 -1
- package/dist/deviceInfo.native-D2k0LDhI.js.map +0 -1
- package/dist/deviceInfo.web-ByNAlEJh.cjs +0 -2
- package/dist/deviceInfo.web-ByNAlEJh.cjs.map +0 -1
- package/dist/deviceInfo.web-DzQyEku6.js +0 -40
- package/dist/deviceInfo.web-DzQyEku6.js.map +0 -1
- package/dist/frameAggregate-C_HoVFcK.cjs +0 -2
- package/dist/frameAggregate-C_HoVFcK.cjs.map +0 -1
- package/dist/frameAggregate-Cwh1juw1.js +0 -17
- package/dist/frameAggregate-Cwh1juw1.js.map +0 -1
- package/dist/frameDropsNative.native-CP7zrXDf.js +0 -23
- package/dist/frameDropsNative.native-CP7zrXDf.js.map +0 -1
- package/dist/frameDropsNative.native-DKqEDqip.cjs +0 -2
- package/dist/frameDropsNative.native-DKqEDqip.cjs.map +0 -1
- package/dist/frameDropsWeb.web-C9Lt3Fuj.js +0 -22
- package/dist/frameDropsWeb.web-C9Lt3Fuj.js.map +0 -1
- package/dist/frameDropsWeb.web-azpwQCUz.cjs +0 -2
- package/dist/frameDropsWeb.web-azpwQCUz.cjs.map +0 -1
- package/dist/httpAttributes-BV50UKQU.js +0 -19
- package/dist/httpAttributes-BV50UKQU.js.map +0 -1
- package/dist/httpAttributes-xXOD9trQ.cjs +0 -2
- package/dist/httpAttributes-xXOD9trQ.cjs.map +0 -1
- package/dist/interaction-CH8yoY5-.js +0 -23
- package/dist/interaction-CH8yoY5-.js.map +0 -1
- package/dist/interaction-DcNFBW7a.cjs +0 -2
- package/dist/interaction-DcNFBW7a.cjs.map +0 -1
- package/dist/interceptFetchNative.native-VlhtMG-U.js +0 -49
- package/dist/interceptFetchNative.native-VlhtMG-U.js.map +0 -1
- package/dist/interceptFetchNative.native-jEAldH14.cjs +0 -2
- package/dist/interceptFetchNative.native-jEAldH14.cjs.map +0 -1
- package/dist/interceptFetchWeb.web-DFzZliik.cjs +0 -2
- package/dist/interceptFetchWeb.web-DFzZliik.cjs.map +0 -1
- package/dist/interceptFetchWeb.web-DfsMfoRP.js +0 -77
- package/dist/interceptFetchWeb.web-DfsMfoRP.js.map +0 -1
- package/dist/memoryNative.native-BWLh2S9L.js +0 -48
- package/dist/memoryNative.native-BWLh2S9L.js.map +0 -1
- package/dist/memoryNative.native-DCXSOd4Q.cjs +0 -2
- package/dist/memoryNative.native-DCXSOd4Q.cjs.map +0 -1
- package/dist/memoryWeb.web-B3YuV-cB.js +0 -48
- package/dist/memoryWeb.web-B3YuV-cB.js.map +0 -1
- package/dist/memoryWeb.web-CheXAcJn.cjs +0 -2
- package/dist/memoryWeb.web-CheXAcJn.cjs.map +0 -1
- package/dist/nativeSender-BE_mzw_S.js +0 -70
- package/dist/nativeSender-BE_mzw_S.js.map +0 -1
- package/dist/nativeSender-DyAUGY7c.cjs +0 -2
- package/dist/nativeSender-DyAUGY7c.cjs.map +0 -1
- package/dist/navigationNative.native-3tO5JxSd.cjs +0 -2
- package/dist/navigationNative.native-3tO5JxSd.cjs.map +0 -1
- package/dist/navigationNative.native-BUCGPQX2.js +0 -25
- package/dist/navigationNative.native-BUCGPQX2.js.map +0 -1
- package/dist/navigationTracker-B2GMwyKk.cjs +0 -2
- package/dist/navigationTracker-B2GMwyKk.cjs.map +0 -1
- package/dist/navigationTracker-Cts68dgG.js.map +0 -1
- package/dist/navigationWeb.web-B0necr5j.cjs +0 -2
- package/dist/navigationWeb.web-B0necr5j.cjs.map +0 -1
- package/dist/navigationWeb.web-CORzE_Id.js +0 -46
- package/dist/navigationWeb.web-CORzE_Id.js.map +0 -1
- package/dist/networkInfo.web-DJmw1sNK.cjs +0 -2
- package/dist/networkInfo.web-DJmw1sNK.cjs.map +0 -1
- package/dist/networkInfo.web-twcNn_hc.js.map +0 -1
- package/dist/src/adapters/interaction.d.ts +0 -17
- package/dist/src/adapters/native/frameDropsNative.native.d.ts +0 -16
- package/dist/src/adapters/native/interceptFetchNative.native.d.ts +0 -23
- package/dist/src/adapters/native/navigationNative.native.d.ts +0 -12
- package/dist/src/adapters/web/frameDropsWeb.web.d.ts +0 -16
- package/dist/src/adapters/web/memoryWeb.web.d.ts +0 -26
- package/dist/telemetry-Co4MYdwa.cjs +0 -2
- package/dist/telemetry-Co4MYdwa.cjs.map +0 -1
- package/dist/telemetry-KI0x4gzC.js +0 -389
- package/dist/telemetry-KI0x4gzC.js.map +0 -1
- package/dist/v4-BysszJq8.js +0 -31
- package/dist/v4-BysszJq8.js.map +0 -1
- package/dist/v4-CqdK_b8Y.cjs +0 -2
- package/dist/v4-CqdK_b8Y.cjs.map +0 -1
- package/dist/webSender-B27tmT1w.js +0 -42
- package/dist/webSender-B27tmT1w.js.map +0 -1
- package/dist/webSender-BWi4x1df.cjs +0 -2
- package/dist/webSender-BWi4x1df.cjs.map +0 -1
package/dist/index.web.js
CHANGED
|
@@ -1,80 +1,135 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { s as m, n as k, T as p, d as a } from "./memoryStore-F5I-GQKM.js";
|
|
2
|
+
import { m as S } from "./memoryStore-F5I-GQKM.js";
|
|
3
|
+
function y(n) {
|
|
3
4
|
if (!n || !n.startsWith("edge_"))
|
|
4
5
|
throw new Error("createTelemetry: apiKey is required and must start with 'edge_'");
|
|
5
6
|
}
|
|
6
|
-
function
|
|
7
|
-
return
|
|
7
|
+
function T(n) {
|
|
8
|
+
return y(n?.apiKey), m(n?.debug ?? !1), k(n?.traceHostAllowlist), new W(n);
|
|
8
9
|
}
|
|
9
|
-
class
|
|
10
|
+
class W extends p {
|
|
10
11
|
constructor(t) {
|
|
11
|
-
|
|
12
|
-
const { Telemetry: e } = await import("./telemetry-
|
|
13
|
-
|
|
14
|
-
sender: m,
|
|
12
|
+
m(t?.debug ?? !1), super(), a.log("🌍 Running Web Telemetry"), this.instancePromise = (async () => {
|
|
13
|
+
const { Telemetry: e } = await import("./telemetry-ByDJIz_9.js"), { webSender: r } = await import("./webSender-BIwslfpT.js"), { webStore: s } = await import("./store.web-DJAlN0nc.js"), { DeviceInfoTrackerWeb: i } = await import("./deviceInfo.web-BFB1-pcA.js"), { NetworkInfoTrackerWeb: c } = await import("./networkInfo.web-B2YlLNsI.js"), d = new i(), h = new c(), l = t?.store ?? s(), u = t?.sender ?? r(t?.endpoint, t?.apiKey, l), o = new e({
|
|
14
|
+
sender: u,
|
|
15
15
|
batchSize: t?.batchSize,
|
|
16
16
|
flushIntervalMs: t?.flushIntervalMs,
|
|
17
17
|
endpoint: t?.endpoint,
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
// device.id is suffixed `_web`; session.id is not — §3.3 gives session.id a
|
|
19
|
+
// `_web` suffix only in v4. generateSessionId() holds that rule.
|
|
20
|
+
platform: "web",
|
|
21
|
+
// §6.2: web's launch root reports true navigation start, which is *better*
|
|
22
|
+
// than Android's fork-time-free approximation — and is why native's and
|
|
23
|
+
// web's launch envelopes must not be compared as the same interval.
|
|
24
|
+
traceLaunchStart: typeof performance < "u" ? performance.timeOrigin : void 0,
|
|
25
|
+
deviceInfoHandler: d,
|
|
26
|
+
networkInfoHandler: h,
|
|
27
|
+
store: l,
|
|
28
|
+
beforeSend: t?.beforeSend,
|
|
29
|
+
sessionSampleRate: t?.sessionSampleRate,
|
|
30
|
+
traceHostAllowlist: t?.traceHostAllowlist,
|
|
31
|
+
buildId: t?.buildId,
|
|
32
|
+
// §4.11: web has never emitted `screen.duration`, and a now-shared
|
|
33
|
+
// attachNavigation must not be what starts it. `navigation` is untouched by
|
|
34
|
+
// this flag — web's own history+popstate path still emits it (see the known
|
|
35
|
+
// gap in CLAUDE.md); the flag only stops the ref path adding a second feed.
|
|
36
|
+
deprecatedScreenFeeds: !1
|
|
37
|
+
}), { runtimeReadyAt: f } = await import("./runtimeReady.web-C8fdIbVR.js").catch((w) => (a.warn("Web runtime-ready module unavailable:", w), { runtimeReadyAt: async () => {
|
|
38
|
+
} })), { seedRuntimeReady: b } = await import("./seedRuntimeReady-Cz8nOKoo.js");
|
|
39
|
+
return b(o.views, f, "Web"), await o.resumeOrStartSession().catch((w) => a.warn("Web session resume failed:", w)), o;
|
|
40
|
+
})(), this.trackErrors({ captureConsole: t?.captureConsole }).catch((e) => {
|
|
24
41
|
a.warn("Web trackErrors failed:", e);
|
|
25
42
|
}), this.trackFrameDrops().catch((e) => {
|
|
26
43
|
a.log("Web trackFrameDrops errors", e);
|
|
27
44
|
}), this.trackNetworkRequests().catch((e) => {
|
|
28
45
|
a.log("Web trackNetworkRequests errors", e);
|
|
29
|
-
}), this.trackMemoryUsage().catch((e) => {
|
|
30
|
-
a.log("Web trackMemoryUsage errors", e);
|
|
31
46
|
}), this.autoTrackNavigation().catch((e) => {
|
|
32
47
|
a.log("Web autoTrackNavigation errors", e);
|
|
48
|
+
}), this.trackInteractions().catch((e) => {
|
|
49
|
+
a.log("Web trackInteractions errors", e);
|
|
50
|
+
}), this.trackWebVitals().catch((e) => {
|
|
51
|
+
a.log("Web trackWebVitals errors", e);
|
|
33
52
|
}), this.attachAppLifecycle().catch((e) => {
|
|
34
53
|
a.log("Web attachAppLifecycle errors", e);
|
|
54
|
+
}), this.attachBfcacheRestore().catch((e) => {
|
|
55
|
+
a.log("Web attachBfcacheRestore errors", e);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A bfcache restore resumes a frozen JS context, so the in-memory session survives on its
|
|
60
|
+
* own — but the freeze can have outlasted the idle window, and a sibling tab may have
|
|
61
|
+
* rotated the shared localStorage record while this one was suspended. Re-running the
|
|
62
|
+
* init decision against the Store is what reconciles both (#92, §4.2).
|
|
63
|
+
*/
|
|
64
|
+
async attachBfcacheRestore() {
|
|
65
|
+
if (typeof window > "u") return;
|
|
66
|
+
const t = await this.instancePromise;
|
|
67
|
+
window.addEventListener("pageshow", (e) => {
|
|
68
|
+
e.persisted && t.resumeOrStartSession().catch((r) => a.warn("Web bfcache session resume failed:", r));
|
|
35
69
|
});
|
|
36
70
|
}
|
|
37
71
|
// app_lifecycle on tab visibility change — web equivalent of native AppState (#30)
|
|
38
72
|
async attachAppLifecycle() {
|
|
39
73
|
if (typeof document > "u") return;
|
|
40
|
-
const t = await this.instancePromise, { AppLifecycleEmitter: e } = await import("./appLifecycle-
|
|
41
|
-
|
|
74
|
+
const t = await this.instancePromise, { AppLifecycleEmitter: e } = await import("./appLifecycle-DQErzJIR.js"), r = new e(t), s = () => {
|
|
75
|
+
const i = document.visibilityState === "visible";
|
|
76
|
+
return r.onState(i).then(() => i ? void 0 : t.flush()).catch((c) => a.warn("Web app lifecycle failed:", c));
|
|
77
|
+
};
|
|
78
|
+
s(), document.addEventListener("visibilitychange", s);
|
|
42
79
|
}
|
|
43
80
|
// Web uses the web crash handler (window.onerror/onunhandledrejection), not the native one.
|
|
44
81
|
async trackErrors(t) {
|
|
45
|
-
const { CrashHandler: e } = await import("./crashHandler.web-
|
|
82
|
+
const { CrashHandler: e } = await import("./crashHandler.web-8F_k2veo.js"), r = await this.instancePromise, s = new e(r);
|
|
46
83
|
return r.trackErrors(s, t);
|
|
47
84
|
}
|
|
48
|
-
async startSessionOnInit() {
|
|
49
|
-
await (await this.instancePromise).startSession();
|
|
50
|
-
}
|
|
51
85
|
async trackFrameDrops() {
|
|
52
|
-
const {
|
|
86
|
+
const { FrameDropTracker: t } = await import("./frameTracker-DdB8W_kC.js"), e = await this.instancePromise, r = new t(e);
|
|
53
87
|
return e.trackFrameDrops(r);
|
|
54
88
|
}
|
|
55
89
|
async trackNetworkRequests() {
|
|
56
|
-
const { NetworkTrackerWeb: t } = await import("./interceptFetchWeb.web-
|
|
90
|
+
const { NetworkTrackerWeb: t } = await import("./interceptFetchWeb.web-UXC2Lb6X.js"), e = await this.instancePromise, r = new t(e);
|
|
57
91
|
return e.trackNetworkRequests(r);
|
|
58
92
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
93
|
+
// §5.2/#105: web emits no `memory_usage` at all, and exposes no `trackMemoryUsage`.
|
|
94
|
+
// Why, in CLAUDE.md's `memory_usage` section.
|
|
95
|
+
/**
|
|
96
|
+
* §4.6/#102 — one capture-phase `click` listener on `document`. Auto-started, like every
|
|
97
|
+
* other capture: `ui.interaction` is what §6.2's interaction root hangs off, so a
|
|
98
|
+
* consumer forgetting to call it would leave every tap-driven request unattributed.
|
|
99
|
+
*/
|
|
100
|
+
async trackInteractions() {
|
|
101
|
+
const { InteractionTrackerWeb: t } = await import("./interactionWeb.web-CJAviVQ7.js"), e = await this.instancePromise;
|
|
102
|
+
(e.webInteractions ??= new t(e)).start();
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* §5.3/#106 — the five Core Web Vitals on the metric path, web only. Auto-started and
|
|
106
|
+
* deliberately not public API: there is nothing for a consumer to configure, and a vital
|
|
107
|
+
* that only fires if someone remembered to call a method is a vital nobody has.
|
|
108
|
+
*
|
|
109
|
+
* `web-vitals/attribution` is bundled, so this is the only import of it in the tree and
|
|
110
|
+
* `index.native` never reaches it. A failed chunk load is caught by the caller and costs
|
|
111
|
+
* the vitals, nothing else.
|
|
112
|
+
*/
|
|
113
|
+
async trackWebVitals() {
|
|
114
|
+
const { WebVitalsTracker: t } = await import("./webVitals.web-CNIpsKlA.js"), e = await this.instancePromise;
|
|
115
|
+
await (e.webVitals ??= new t(e)).start();
|
|
62
116
|
}
|
|
63
117
|
async autoTrackNavigation() {
|
|
64
|
-
const { NavigationTrackerWeb: t } = await import("./navigationWeb.web-
|
|
118
|
+
const { NavigationTrackerWeb: t } = await import("./navigationWeb.web-CcwgKuIZ.js"), e = await this.instancePromise, r = new t(e);
|
|
65
119
|
return e.start(r);
|
|
66
120
|
}
|
|
67
121
|
async getDeviceInfo() {
|
|
68
|
-
const { DeviceInfoTrackerWeb: t } = await import("./deviceInfo.web-
|
|
122
|
+
const { DeviceInfoTrackerWeb: t } = await import("./deviceInfo.web-BFB1-pcA.js"), e = await this.instancePromise, r = new t();
|
|
69
123
|
return e.getDeviceInfo(r);
|
|
70
124
|
}
|
|
71
125
|
async getNetworkInfo() {
|
|
72
|
-
const { NetworkInfoTrackerWeb: t } = await import("./networkInfo.web-
|
|
126
|
+
const { NetworkInfoTrackerWeb: t } = await import("./networkInfo.web-B2YlLNsI.js"), e = await this.instancePromise, r = new t();
|
|
73
127
|
return e.getNetworkInfo(r);
|
|
74
128
|
}
|
|
75
129
|
}
|
|
76
130
|
export {
|
|
77
|
-
|
|
78
|
-
|
|
131
|
+
W as TelemetryWeb,
|
|
132
|
+
T as createTelemetry,
|
|
133
|
+
S as memoryStore
|
|
79
134
|
};
|
|
80
135
|
//# sourceMappingURL=index.web.js.map
|
package/dist/index.web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.web.js","sources":["../src/createTelemetry.web.ts","../src/index.web.ts"],"sourcesContent":["import { TelemetryWeb } from \"./index.web\";\n\nexport type TelemetryOpts = {\n apiKey: string; // required; must start with \"edge_\"\n sender?: any;\n batchSize?: number;\n flushIntervalMs?: number;\n endpoint?: string;\n captureConsole?: boolean; // funnel console.error/warn into app.crash (default on, opt-out)\n debug?: boolean; // SDK-internal diagnostics; off by default (#23)\n};\n\nexport function assertApiKey(apiKey?: string) {\n if (!apiKey || !apiKey.startsWith(\"edge_\")) {\n throw new Error(\"createTelemetry: apiKey is required and must start with 'edge_'\");\n }\n}\n\n/**\n * Cross-platform factory for telemetry. The platform is chosen by the bundler\n * via the .web / .native file split — no runtime navigator sniffing.\n */\nexport function createTelemetry(opts: TelemetryOpts) {\n assertApiKey(opts?.apiKey);\n return new TelemetryWeb(opts);\n}\n","// src/index.web.ts\nimport { TelemetryBase } from \"./index.base\";\nimport { debug, setDebug } from \"./core/debug\";\n\nexport { createTelemetry, type TelemetryOpts } from \"./createTelemetry.web\";\n\nexport class TelemetryWeb extends TelemetryBase {\n constructor(opts?: {\n apiKey?: string;\n sender?: any;\n batchSize?: number;\n flushIntervalMs?: number;\n endpoint?: string;\n captureConsole?: boolean;\n debug?: boolean;\n }) {\n setDebug(opts?.debug ?? false); // gate SDK console noise before anything logs (#23)\n super();\n\n debug.log(\"🌍 Running Web Telemetry\");\n\n this.instancePromise = (async () => {\n const { Telemetry } = await import(\"./core/telemetry\");\n const { webSender } = await import(\"./adapters/webSender\");\n\n const { DeviceInfoTrackerWeb } = await import(\"./adapters/web/deviceInfo.web\");\n const { NetworkInfoTrackerWeb } = await import(\"./adapters/web/networkInfo.web\");\n const deviceInfoTrackerWeb = new DeviceInfoTrackerWeb();\n const networkInfoTrackerWeb = new NetworkInfoTrackerWeb();\n\n const sender = opts?.sender ?? webSender(opts?.endpoint, opts?.apiKey);\n\n const telemetry = new Telemetry({\n sender,\n batchSize: opts?.batchSize,\n flushIntervalMs: opts?.flushIntervalMs,\n endpoint: opts?.endpoint,\n // no id suffix on web: the contract suffix is ios|android only.\n // device.platform=\"web\" still rides as an attribute from the adapter.\n deviceInfoHandler: deviceInfoTrackerWeb,\n networkInfoHandler: networkInfoTrackerWeb,\n });\n\n return telemetry;\n })();\n\n // session.started on init (#29). Background/foreground via AppState is native-only.\n this.startSessionOnInit().catch(err => debug.warn(\"Web startSession failed:\", err));\n\n this.trackErrors({ captureConsole: opts?.captureConsole }).catch(err => {\n debug.warn(\"Web trackErrors failed:\", err);\n });\n\n this.trackFrameDrops().catch(err => {\n debug.log(\"Web trackFrameDrops errors\", err);\n });\n this.trackNetworkRequests().catch(err => {\n debug.log(\"Web trackNetworkRequests errors\", err);\n });\n this.trackMemoryUsage().catch(err => {\n debug.log(\"Web trackMemoryUsage errors\", err);\n });\n this.autoTrackNavigation().catch(err => {\n debug.log(\"Web autoTrackNavigation errors\", err);\n });\n this.attachAppLifecycle().catch(err => {\n debug.log(\"Web attachAppLifecycle errors\", err);\n });\n }\n\n // app_lifecycle on tab visibility change — web equivalent of native AppState (#30)\n async attachAppLifecycle() {\n if (typeof document === \"undefined\") return;\n const inst = await this.instancePromise;\n const { AppLifecycleEmitter } = await import(\"./adapters/appLifecycle\");\n const emitter = new AppLifecycleEmitter(inst);\n emitter.onState(document.visibilityState === \"visible\"); // seed current state\n document.addEventListener(\"visibilitychange\", () =>\n emitter.onState(document.visibilityState === \"visible\"));\n }\n\n // Web uses the web crash handler (window.onerror/onunhandledrejection), not the native one.\n async trackErrors(options?: { captureConsole?: boolean }) {\n const { CrashHandler } = await import(\"./adapters/web/crashHandler.web\");\n const inst = await this.instancePromise;\n const crashHandler = new CrashHandler(inst);\n return inst.trackErrors(crashHandler, options);\n }\n\n private async startSessionOnInit() {\n const inst = await this.instancePromise;\n await inst.startSession();\n }\n\n async trackFrameDrops() {\n const { FrameDropTrackerWeb } = await import(\"./adapters/web/frameDropsWeb.web\");\n const inst = await this.instancePromise;\n const frameDropTracker = new FrameDropTrackerWeb(inst);\n return inst.trackFrameDrops(frameDropTracker);\n }\n\n async trackNetworkRequests() {\n const { NetworkTrackerWeb } = await import(\"./adapters/web/interceptFetchWeb.web\");\n const inst = await this.instancePromise;\n const networkTracker = new NetworkTrackerWeb(inst);\n return inst.trackNetworkRequests(networkTracker);\n }\n\n async trackMemoryUsage() {\n const { TelemetryMemoryUsageWeb } = await import(\"./adapters/web/memoryWeb.web\");\n const inst = await this.instancePromise;\n const memoryTracker = new TelemetryMemoryUsageWeb(inst);\n return inst.trackMemoryUsage(memoryTracker);\n }\n\n async autoTrackNavigation() {\n const { NavigationTrackerWeb } = await import(\"./adapters/web/navigationWeb.web\");\n const inst = await this.instancePromise;\n const navigationTracker = new NavigationTrackerWeb(inst);\n return inst.start(navigationTracker);\n }\n\n async getDeviceInfo() {\n const { DeviceInfoTrackerWeb } = await import(\"./adapters/web/deviceInfo.web\");\n const inst = await this.instancePromise;\n const deviceInfoTrackerWeb = new DeviceInfoTrackerWeb();\n return inst.getDeviceInfo(deviceInfoTrackerWeb);\n }\n\n async getNetworkInfo() {\n const { NetworkInfoTrackerWeb } = await import(\"./adapters/web/networkInfo.web\");\n const inst = await this.instancePromise;\n const networkInfoTrackerWeb = new NetworkInfoTrackerWeb();\n return inst.getNetworkInfo(networkInfoTrackerWeb);\n }\n}\n"],"names":["assertApiKey","apiKey","createTelemetry","opts","TelemetryWeb","TelemetryBase","setDebug","debug","Telemetry","webSender","DeviceInfoTrackerWeb","NetworkInfoTrackerWeb","deviceInfoTrackerWeb","networkInfoTrackerWeb","sender","err","inst","AppLifecycleEmitter","emitter","options","CrashHandler","crashHandler","FrameDropTrackerWeb","frameDropTracker","NetworkTrackerWeb","networkTracker","TelemetryMemoryUsageWeb","memoryTracker","NavigationTrackerWeb","navigationTracker"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.web.js","sources":["../src/createTelemetry.web.ts","../src/index.web.ts"],"sourcesContent":["import { TelemetryWeb } from \"./index.web\";\nimport type { SyncStore } from \"./core/store\";\nimport type { BeforeSend } from \"./core/beforeSend\";\nimport { normalizeAllowlist } from \"./adapters/traceHeader\";\nimport { setDebug } from \"./core/debug\";\n\nexport type TelemetryOpts = {\n apiKey: string; // required credential; must start with \"edge_\" (API key or JWT, #90)\n sender?: any;\n batchSize?: number;\n flushIntervalMs?: number;\n endpoint?: string;\n // console.error -> app.error, console.warn -> a breadcrumb. Default OFF (§4.7, #100).\n captureConsole?: boolean;\n debug?: boolean; // SDK-internal diagnostics; off by default (#23)\n store?: SyncStore; // persisted-state port (#89); MUST be sync — see core/store.ts\n // Constructor-only (§3.6) — there is deliberately no runtime setter for either.\n beforeSend?: BeforeSend; // sync scrubbing hook, run at enqueue over events and metrics\n sessionSampleRate?: number; // 0.0-1.0, sticky per session, re-rolled at rotation; default 1\n // §6.4/#99. Bare hosts, exact match, **ports ignored** (a deliberate mismatch with\n // `http.host`, which keeps the port — do not join them). Empty by default: v4 is dark on\n // upgrade. Listing a host asserts that host's CORS allows `traceparent`; a malformed\n // entry throws in dev and is dropped in production.\n traceHostAllowlist?: string[];\n // §4.8 — the symbolication resolve key. A git SHA or CI run number; consumer-supplied\n // (the SDK has zero OTA awareness), **omitted when unset, never `\"\"`**, and never\n // derived from app.version + app.build_number under any circumstances.\n buildId?: string;\n};\n\n// Deliberately loose: `apiKey` is the *credential*, and under AUTH_MODE=jwt it is\n// `edge_<jwt>` — a shape the collector's >=3-`_`-part API-key check would reject. Tightening\n// to that check here would hard-reject every JWT and make the segmented deployment\n// unreachable, which is the exact bug #90 exists to close. Prefix + non-empty remainder is\n// all we assert; the real catch is the 401 warn (contract §11.1).\nexport function assertApiKey(apiKey?: string) {\n if (!apiKey || !apiKey.startsWith(\"edge_\")) {\n throw new Error(\"createTelemetry: apiKey is required and must start with 'edge_'\");\n }\n}\n\n/**\n * Cross-platform factory for telemetry. The platform is chosen by the bundler\n * via the .web / .native file split — no runtime navigator sniffing.\n */\nexport function createTelemetry(opts: TelemetryOpts) {\n assertApiKey(opts?.apiKey);\n // §6.4's dev throw has to happen *here*, synchronously: the core Telemetry is built\n // inside `instancePromise`, which deliberately never rethrows, so a throw down there\n // would be a silently rejected promise instead of the loud config error dev asks for.\n // In production it drops the bad entry and reports through debug() — which is why the\n // gate has to be open first: the entry ctor's own setDebug() runs *after* this line, so\n // without this the production report would be a guaranteed no-op even with debug: true.\n // It runs again in TraceManager, where it is idempotent.\n setDebug(opts?.debug ?? false);\n normalizeAllowlist(opts?.traceHostAllowlist);\n return new TelemetryWeb(opts);\n}\n","// src/index.web.ts\nimport { TelemetryBase } from \"./index.base\";\nimport { debug, setDebug } from \"./core/debug\";\nimport type { SyncStore } from \"./core/store\";\nimport type { BeforeSend } from \"./core/beforeSend\";\n\nexport { createTelemetry, type TelemetryOpts } from \"./createTelemetry.web\";\n\n// The Store port (#89) — public so a consumer can inject their own persistence,\n// and so the in-memory fake is available outside the test tree.\nexport type { Store, SyncStore, AsyncStore, StoreRead, StoreWrite } from \"./core/store\";\nexport { memoryStore, type MemoryStoreOpts } from \"./core/memoryStore\";\n\n// The scrubbing hook (#93) — public so a consumer can type their beforeSend.\nexport type { BeforeSend } from \"./core/beforeSend\";\n\nexport class TelemetryWeb extends TelemetryBase {\n constructor(opts?: {\n apiKey?: string;\n sender?: any;\n batchSize?: number;\n flushIntervalMs?: number;\n endpoint?: string;\n captureConsole?: boolean;\n debug?: boolean;\n store?: SyncStore; // sync only: the web build's guarantee depends on it\n // Constructor-only (§3.6). No runtime setter: registering later leaves a window\n // where session.started and the earliest requests have already been enqueued.\n beforeSend?: BeforeSend;\n sessionSampleRate?: number;\n // §6.4/#99 — bare hosts, exact match, ports ignored, empty by default.\n traceHostAllowlist?: string[];\n // §4.8 — symbolication resolve key. Omitted when unset, never `\"\"`, never derived.\n buildId?: string;\n }) {\n setDebug(opts?.debug ?? false); // gate SDK console noise before anything logs (#23)\n super();\n\n debug.log(\"🌍 Running Web Telemetry\");\n\n this.instancePromise = (async () => {\n const { Telemetry } = await import(\"./core/telemetry\");\n const { webSender } = await import(\"./adapters/webSender\");\n const { webStore } = await import(\"./adapters/web/store.web\");\n\n const { DeviceInfoTrackerWeb } = await import(\"./adapters/web/deviceInfo.web\");\n const { NetworkInfoTrackerWeb } = await import(\"./adapters/web/networkInfo.web\");\n const deviceInfoTrackerWeb = new DeviceInfoTrackerWeb();\n const networkInfoTrackerWeb = new NetworkInfoTrackerWeb();\n\n // One store, shared by the offline queue and core (#89) — so a consumer that\n // injects a store governs both, instead of the sender quietly keeping its own.\n const store = opts?.store ?? webStore();\n const sender = opts?.sender ?? webSender(opts?.endpoint, opts?.apiKey, store);\n\n const telemetry = new Telemetry({\n sender,\n batchSize: opts?.batchSize,\n flushIntervalMs: opts?.flushIntervalMs,\n endpoint: opts?.endpoint,\n // device.id is suffixed `_web`; session.id is not — §3.3 gives session.id a\n // `_web` suffix only in v4. generateSessionId() holds that rule.\n platform: \"web\",\n // §6.2: web's launch root reports true navigation start, which is *better*\n // than Android's fork-time-free approximation — and is why native's and\n // web's launch envelopes must not be compared as the same interval.\n traceLaunchStart: typeof performance !== \"undefined\" ? performance.timeOrigin : undefined,\n deviceInfoHandler: deviceInfoTrackerWeb,\n networkInfoHandler: networkInfoTrackerWeb,\n store,\n beforeSend: opts?.beforeSend,\n sessionSampleRate: opts?.sessionSampleRate,\n traceHostAllowlist: opts?.traceHostAllowlist,\n buildId: opts?.buildId,\n // §4.11: web has never emitted `screen.duration`, and a now-shared\n // attachNavigation must not be what starts it. `navigation` is untouched by\n // this flag — web's own history+popstate path still emits it (see the known\n // gap in CLAUDE.md); the flag only stops the ref path adding a second feed.\n deprecatedScreenFeeds: false,\n });\n\n // Resume or start the session before the instance is visible (#92). On web the\n // Store is localStorage, so a hard reload, a bfcache restore and a second tab\n // all resume the same session.\n // Never rethrown into instancePromise: finalizeSession() flushes, flush() rethrows on a\n // dead collector, and a rejected instancePromise would brick every public method for the\n // life of the process. Hydration has already run; only the emission is lost.\n // §4.5.2's `initial_load` seed: the launch view stays busy until the platform's\n // runtime-ready marker. The *module* is awaited with everything else, so the seed\n // is armed before `instancePromise` resolves; the *marker* is not, because on web\n // it arrives with the load event and blocking init on it would delay every host\n // call behind the page's own images.\n // The module is awaited so the seed is armed before `instancePromise` resolves,\n // but never rethrown into it: a failed chunk load must not brick every public\n // method for the life of the process. A missing reader just releases the gate.\n const { runtimeReadyAt } = await import(\"./adapters/web/runtimeReady.web\")\n .catch((err: unknown) => {\n debug.warn(\"Web runtime-ready module unavailable:\", err);\n return { runtimeReadyAt: async () => undefined };\n });\n const { seedRuntimeReady } = await import(\"./adapters/seedRuntimeReady\");\n seedRuntimeReady(telemetry.views, runtimeReadyAt, \"Web\");\n\n await telemetry.resumeOrStartSession()\n .catch(err => debug.warn(\"Web session resume failed:\", err));\n\n return telemetry;\n })();\n\n this.trackErrors({ captureConsole: opts?.captureConsole }).catch(err => {\n debug.warn(\"Web trackErrors failed:\", err);\n });\n\n this.trackFrameDrops().catch(err => {\n debug.log(\"Web trackFrameDrops errors\", err);\n });\n this.trackNetworkRequests().catch(err => {\n debug.log(\"Web trackNetworkRequests errors\", err);\n });\n this.autoTrackNavigation().catch(err => {\n debug.log(\"Web autoTrackNavigation errors\", err);\n });\n this.trackInteractions().catch(err => {\n debug.log(\"Web trackInteractions errors\", err);\n });\n this.trackWebVitals().catch(err => {\n debug.log(\"Web trackWebVitals errors\", err);\n });\n this.attachAppLifecycle().catch(err => {\n debug.log(\"Web attachAppLifecycle errors\", err);\n });\n this.attachBfcacheRestore().catch(err => {\n debug.log(\"Web attachBfcacheRestore errors\", err);\n });\n }\n\n /**\n * A bfcache restore resumes a frozen JS context, so the in-memory session survives on its\n * own — but the freeze can have outlasted the idle window, and a sibling tab may have\n * rotated the shared localStorage record while this one was suspended. Re-running the\n * init decision against the Store is what reconciles both (#92, §4.2).\n */\n private async attachBfcacheRestore() {\n if (typeof window === \"undefined\") return;\n const inst = await this.instancePromise;\n window.addEventListener(\"pageshow\", (e: PageTransitionEvent) => {\n if (!e.persisted) return; // an ordinary load already ran this at init\n inst.resumeOrStartSession()\n .catch((err: any) => debug.warn(\"Web bfcache session resume failed:\", err));\n });\n }\n\n // app_lifecycle on tab visibility change — web equivalent of native AppState (#30)\n async attachAppLifecycle() {\n if (typeof document === \"undefined\") return;\n const inst = await this.instancePromise;\n const { AppLifecycleEmitter } = await import(\"./adapters/appLifecycle\");\n const emitter = new AppLifecycleEmitter(inst);\n const onState = () => {\n const isActive = document.visibilityState === \"visible\";\n // Awaited before the flush, not fired alongside it — the same ordering native has\n // (§4.5). Backgrounding is a view boundary, and the rows it emits are exactly the\n // rows this flush exists to rescue: the `view` row, and §5.3's drained CLS and INP,\n // whose whole reason for being held is that the library's page-hide report loses\n // them on a closed tab. Holding them and then not flushing would move that defect\n // rather than close it. Web's sender uses `fetch({keepalive:true})`, which is what\n // lets a hidden tab's batch outlive the document.\n return emitter.onState(isActive)\n .then(() => (isActive ? undefined : inst.flush()))\n .catch((e: any) => debug.warn(\"Web app lifecycle failed:\", e));\n };\n onState(); // seed current state\n document.addEventListener(\"visibilitychange\", onState);\n }\n\n // Web uses the web crash handler (window.onerror/onunhandledrejection), not the native one.\n async trackErrors(options?: { captureConsole?: boolean }) {\n const { CrashHandler } = await import(\"./adapters/web/crashHandler.web\");\n const inst = await this.instancePromise;\n const crashHandler = new CrashHandler(inst);\n return inst.trackErrors(crashHandler, options);\n }\n\n async trackFrameDrops() {\n const { FrameDropTracker } = await import(\"./adapters/frameTracker\");\n const inst = await this.instancePromise;\n const frameDropTracker = new FrameDropTracker(inst);\n return inst.trackFrameDrops(frameDropTracker);\n }\n\n async trackNetworkRequests() {\n const { NetworkTrackerWeb } = await import(\"./adapters/web/interceptFetchWeb.web\");\n const inst = await this.instancePromise;\n const networkTracker = new NetworkTrackerWeb(inst);\n return inst.trackNetworkRequests(networkTracker);\n }\n\n // §5.2/#105: web emits no `memory_usage` at all, and exposes no `trackMemoryUsage`.\n // Why, in CLAUDE.md's `memory_usage` section.\n\n /**\n * §4.6/#102 — one capture-phase `click` listener on `document`. Auto-started, like every\n * other capture: `ui.interaction` is what §6.2's interaction root hangs off, so a\n * consumer forgetting to call it would leave every tap-driven request unattributed.\n */\n async trackInteractions() {\n const { InteractionTrackerWeb } = await import(\"./adapters/web/interactionWeb.web\");\n const inst = await this.instancePromise;\n // One tracker per core instance, like the XHR patch: `start()`'s own guard is\n // per-tracker, so constructing a fresh one on a second call would add a second\n // capture-phase listener and double every `ui.interaction` row.\n (inst.webInteractions ??= new InteractionTrackerWeb(inst)).start();\n }\n\n /**\n * §5.3/#106 — the five Core Web Vitals on the metric path, web only. Auto-started and\n * deliberately not public API: there is nothing for a consumer to configure, and a vital\n * that only fires if someone remembered to call a method is a vital nobody has.\n *\n * `web-vitals/attribution` is bundled, so this is the only import of it in the tree and\n * `index.native` never reaches it. A failed chunk load is caught by the caller and costs\n * the vitals, nothing else.\n */\n private async trackWebVitals() {\n const { WebVitalsTracker } = await import(\"./adapters/web/webVitals.web\");\n const inst = await this.instancePromise;\n // One tracker per core instance: `start()`'s guard is per-tracker, so a fresh one on a\n // second call would double-subscribe and double every vital row.\n await (inst.webVitals ??= new WebVitalsTracker(inst)).start();\n }\n\n async autoTrackNavigation() {\n const { NavigationTrackerWeb } = await import(\"./adapters/web/navigationWeb.web\");\n const inst = await this.instancePromise;\n const navigationTracker = new NavigationTrackerWeb(inst);\n return inst.start(navigationTracker);\n }\n\n async getDeviceInfo() {\n const { DeviceInfoTrackerWeb } = await import(\"./adapters/web/deviceInfo.web\");\n const inst = await this.instancePromise;\n const deviceInfoTrackerWeb = new DeviceInfoTrackerWeb();\n return inst.getDeviceInfo(deviceInfoTrackerWeb);\n }\n\n async getNetworkInfo() {\n const { NetworkInfoTrackerWeb } = await import(\"./adapters/web/networkInfo.web\");\n const inst = await this.instancePromise;\n const networkInfoTrackerWeb = new NetworkInfoTrackerWeb();\n return inst.getNetworkInfo(networkInfoTrackerWeb);\n }\n}\n"],"names":["assertApiKey","apiKey","createTelemetry","opts","setDebug","normalizeAllowlist","TelemetryWeb","TelemetryBase","debug","Telemetry","webSender","webStore","DeviceInfoTrackerWeb","NetworkInfoTrackerWeb","deviceInfoTrackerWeb","networkInfoTrackerWeb","store","sender","telemetry","runtimeReadyAt","err","seedRuntimeReady","inst","AppLifecycleEmitter","emitter","onState","isActive","e","options","CrashHandler","crashHandler","FrameDropTracker","frameDropTracker","NetworkTrackerWeb","networkTracker","InteractionTrackerWeb","WebVitalsTracker","NavigationTrackerWeb","navigationTracker"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("./memoryStore-BIGZmmfD.cjs"),h=require("./loadingTime-Bhomd9qq.cjs"),s=require("./uiInteraction-CdzvEI1p.cjs"),l=h.QUIET_WINDOW_MS;class m{constructor(e){this.telemetry=e,this.started=!1,this.rage=new s.RageTracker(s.RAGE_WINDOW_MS),this.aliveTick=0,this.pending=new Set}start(){if(!this.started){if(typeof document>"u"||!document?.addEventListener){d.debug.log("Web interactions: no document — ui.interaction is not captured");return}this.started=!0,document.addEventListener("click",e=>this.onClick(e),!0),typeof window<"u"&&window.addEventListener?.("pagehide",()=>this.drainPending()),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.drainPending()}),this.telemetry.views.onActivity(()=>{this.aliveTick++})}}onClick(e){const i=Date.now(),t=v(e),r=s.resolveUiName(t,{pointerCursor:p(t[0])}),o=this.telemetry.snapshot(i),u=this.telemetry.trace.interactionSpan(i),c=e,a={...u,...s.uiAttributes({type:"click",target:r.target,nameSource:r.nameSource,tag:r.tag,x:c.clientX,y:c.clientY,rage:this.rage.record(r.node,i)})};r.actionable&&!s.isDeadClickExempt(t[0])&&this.canObserve()?this.watchForDeath(a,o):this.emit(a,o)}watchForDeath(e,i){const t={attrs:e,snapshot:i,tick:this.aliveTick,timer:setTimeout(()=>{this.pending.delete(t),this.pending.size===0&&this.disconnectObserver(),this.emit({...e,"ui.dead":this.aliveTick===t.tick},i)},l)};this.pending.add(t),this.connectObserver()}drainPending(){for(const e of this.pending)clearTimeout(e.timer),this.emit(e.attrs,e.snapshot);this.pending.clear(),this.disconnectObserver()}emit(e,i){Promise.resolve(this.telemetry.log("ui.interaction",e,i)).catch(t=>d.debug.warn("Web ui.interaction failed:",t))}canObserve(){return typeof MutationObserver<"u"}connectObserver(){const e=typeof document>"u"?void 0:document.documentElement;this.observer||!this.canObserve()||!e||(this.observer=new MutationObserver(()=>{this.aliveTick++}),this.observer.observe(e,{subtree:!0,childList:!0,attributes:!0,characterData:!0}))}disconnectObserver(){this.observer?.disconnect(),this.observer=void 0}}function v(n){const e=typeof n.composedPath=="function"?n.composedPath():void 0;if(Array.isArray(e)&&e.length>0)return e;const i=[];let t=n.target;for(;t;)i.push(t),t=t.parentElement??t.parentNode;return i}function p(n){if(!n||typeof getComputedStyle>"u")return!1;try{return getComputedStyle(n)?.cursor==="pointer"}catch{return!1}}exports.InteractionTrackerWeb=m;
|
|
2
|
+
//# sourceMappingURL=interactionWeb.web-Bl_cZlgh.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactionWeb.web-Bl_cZlgh.cjs","sources":["../src/adapters/web/interactionWeb.web.ts"],"sourcesContent":["// `ui.interaction` on web (§4.6, #102) — one capture-phase `click` listener on `document`,\n// the role-gated name ladder from `../uiInteraction`, and the two frustration signals.\n//\n// **Every click emits, actionable or not.** Suppressing the role-less ones would destroy\n// dead-click detection at the source, and it is what makes `unnamed` vs `surface` readable\n// as \"an instrumentation gap\" vs \"someone tapped whitespace\".\n\nimport type { LogSnapshot, Telemetry } from \"../../core/telemetry\";\nimport { debug } from \"../../core/debug\";\nimport { QUIET_WINDOW_MS } from \"../loadingTime\";\nimport {\n RAGE_WINDOW_MS,\n RageTracker,\n isDeadClickExempt,\n resolveUiName,\n uiAttributes,\n type UiElement,\n} from \"../uiInteraction\";\n\n/**\n * ⚠ The dead-click window **is** §4.5.2's quiet window — one named constant, not a second\n * literal `1000`. If one moves, both move. (`RAGE_WINDOW_MS` is deliberately *not* bound to\n * it: §4.6 states the same number for rage, but tuning network settle must not retune rage.)\n */\nconst DEAD_CLICK_WINDOW_MS = QUIET_WINDOW_MS;\n\n/** One click whose dead-click window is still open. */\ntype Pending = {\n attrs: Record<string, any>;\n snapshot: LogSnapshot;\n /** The aliveness reading at mint; unchanged when the window closes means dead. */\n tick: number;\n timer: ReturnType<typeof setTimeout>;\n};\n\nexport class InteractionTrackerWeb {\n private started = false;\n private readonly rage = new RageTracker(RAGE_WINDOW_MS);\n\n /**\n * Monotonic aliveness counter. A DOM mutation, a request start or a view mint bumps it;\n * a click that sees the same value a window later saw nothing happen. A counter rather\n * than a timestamp so the comparison needs no clock and cannot be fooled by a clock that\n * moved backwards.\n */\n private aliveTick = 0;\n private readonly pending = new Set<Pending>();\n private observer?: MutationObserver;\n\n constructor(private telemetry: Telemetry) {}\n\n start(): void {\n if (this.started) return;\n if (typeof document === \"undefined\" || !document?.addEventListener) {\n debug.log(\"Web interactions: no document — ui.interaction is not captured\");\n return;\n }\n this.started = true;\n // Capture phase: the mint has to happen before the host's own handler navigates,\n // because the snapshot it takes is the whole point of §4.6's mint/emit split.\n document.addEventListener(\"click\", (e) => this.onClick(e), true);\n\n // A same-tab `<a href>` or a form submit unloads the document while its dead-click\n // window is still open, and an unemitted row also orphans the interaction root its\n // children already joined. Both edges drain what is pending — `pagehide` covers the\n // unload and the bfcache freeze, `visibilitychange` covers a tab switch and is the\n // edge mobile Safari actually fires.\n if (typeof window !== \"undefined\") {\n window.addEventListener?.(\"pagehide\", () => this.drainPending());\n }\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") this.drainPending();\n });\n\n this.telemetry.views.onActivity(() => { this.aliveTick++; });\n }\n\n private onClick(e: Event): void {\n const at = Date.now();\n const path = pathOf(e);\n const resolution = resolveUiName(path, { pointerCursor: hasPointerCursor(path[0]) });\n\n // Snapshotted here, synchronously, while the click's own view is still current.\n const snapshot = this.telemetry.snapshot(at);\n // §6.2: **every** click mints an interaction root, live carrier or not.\n const span = this.telemetry.trace.interactionSpan(at);\n const mouse = e as MouseEvent;\n\n const attrs: Record<string, any> = {\n ...span,\n // The key block is shared with native's `trackTap` so the two cannot drift; only\n // the values are web's. `clientX/Y` is absent on a synthetic or keyboard-driven\n // click, and §4.6 types both never-null, so the builder floors them at 0.\n ...uiAttributes({\n type: \"click\",\n target: resolution.target,\n nameSource: resolution.nameSource,\n tag: resolution.tag,\n x: mouse.clientX,\n y: mouse.clientY,\n rage: this.rage.record(resolution.node, at),\n }),\n };\n\n // `ui.dead` is judged on **actionable** clicks only, minus text entry and\n // download/`_blank` anchors — and is **omitted when not evaluated**. That absence is\n // load-bearing: the signal must under-report rather than falsely accuse.\n if (resolution.actionable && !isDeadClickExempt(path[0]) && this.canObserve()) {\n this.watchForDeath(attrs, snapshot);\n } else {\n this.emit(attrs, snapshot);\n }\n }\n\n /** Arm one dead-click window and emit when it closes. */\n private watchForDeath(attrs: Record<string, any>, snapshot: LogSnapshot): void {\n const entry: Pending = {\n attrs,\n snapshot,\n tick: this.aliveTick,\n timer: setTimeout(() => {\n this.pending.delete(entry);\n if (this.pending.size === 0) this.disconnectObserver();\n this.emit({ ...attrs, \"ui.dead\": this.aliveTick === entry.tick }, snapshot);\n }, DEAD_CLICK_WINDOW_MS),\n };\n this.pending.add(entry);\n this.connectObserver();\n }\n\n /**\n * Emit every open window **now**, with `ui.dead` **omitted** — the window never closed,\n * so it was not evaluated, which is exactly what its absence means (§4.6). Inventing a\n * verdict from a partial window is the false accusation the key is built to avoid.\n */\n private drainPending(): void {\n for (const entry of this.pending) {\n clearTimeout(entry.timer);\n this.emit(entry.attrs, entry.snapshot);\n }\n this.pending.clear();\n this.disconnectObserver();\n }\n\n private emit(attrs: Record<string, any>, snapshot: LogSnapshot): void {\n void Promise.resolve(this.telemetry.log(\"ui.interaction\", attrs, snapshot))\n .catch((err: unknown) => debug.warn(\"Web ui.interaction failed:\", err));\n }\n\n private canObserve(): boolean {\n return typeof MutationObserver !== \"undefined\";\n }\n\n /**\n * Observed only while a window is open. A permanently-connected whole-document observer\n * is a real cost on a busy React tree, and there is nothing to answer between clicks.\n * `attributes: true` because §4.6 counts an attribute-only change as alive — a CSS class\n * flip is a response.\n */\n private connectObserver(): void {\n const root = typeof document === \"undefined\" ? undefined : document.documentElement;\n if (this.observer || !this.canObserve() || !root) return;\n this.observer = new MutationObserver(() => { this.aliveTick++; });\n this.observer.observe(root, {\n subtree: true,\n childList: true,\n attributes: true,\n characterData: true,\n });\n }\n\n private disconnectObserver(): void {\n this.observer?.disconnect();\n this.observer = undefined;\n }\n}\n\n/**\n * `composedPath()` innermost-first, so a click inside a shadow root still finds its host's\n * role. Falls back to the parent chain where the event has no `composedPath` (older\n * browsers, and every synthetic event a test hands in).\n */\nfunction pathOf(e: Event): UiElement[] {\n const composed = typeof e.composedPath === \"function\" ? e.composedPath() : undefined;\n if (Array.isArray(composed) && composed.length > 0) return composed as unknown as UiElement[];\n const path: UiElement[] = [];\n let node = e.target as (Node & UiElement) | null;\n while (node) {\n path.push(node);\n node = ((node as any).parentElement ?? node.parentNode) as (Node & UiElement) | null;\n }\n return path;\n}\n\n/**\n * The one signal separating `unnamed` from `surface` on a role-less element. Wrapped\n * because `getComputedStyle` throws on a detached node in some engines, and a click must\n * never be lost to a style read.\n */\nfunction hasPointerCursor(el: UiElement | undefined): boolean {\n if (!el || typeof getComputedStyle === \"undefined\") return false;\n try {\n return getComputedStyle(el as unknown as Element)?.cursor === \"pointer\";\n } catch {\n return false;\n }\n}\n"],"names":["DEAD_CLICK_WINDOW_MS","QUIET_WINDOW_MS","InteractionTrackerWeb","telemetry","RageTracker","RAGE_WINDOW_MS","debug","at","path","pathOf","resolution","resolveUiName","hasPointerCursor","snapshot","span","mouse","attrs","uiAttributes","isDeadClickExempt","entry","err","root","e","composed","node","el"],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { d as c } from "./memoryStore-F5I-GQKM.js";
|
|
2
|
+
import { Q as h } from "./loadingTime-ZkYvFijA.js";
|
|
3
|
+
import { R as u, a as l, r as m, u as p, i as v } from "./uiInteraction-DAqxxz0q.js";
|
|
4
|
+
const f = h;
|
|
5
|
+
class w {
|
|
6
|
+
constructor(e) {
|
|
7
|
+
this.telemetry = e, this.started = !1, this.rage = new u(l), this.aliveTick = 0, this.pending = /* @__PURE__ */ new Set();
|
|
8
|
+
}
|
|
9
|
+
start() {
|
|
10
|
+
if (!this.started) {
|
|
11
|
+
if (typeof document > "u" || !document?.addEventListener) {
|
|
12
|
+
c.log("Web interactions: no document — ui.interaction is not captured");
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
this.started = !0, document.addEventListener("click", (e) => this.onClick(e), !0), typeof window < "u" && window.addEventListener?.("pagehide", () => this.drainPending()), document.addEventListener("visibilitychange", () => {
|
|
16
|
+
document.visibilityState === "hidden" && this.drainPending();
|
|
17
|
+
}), this.telemetry.views.onActivity(() => {
|
|
18
|
+
this.aliveTick++;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
onClick(e) {
|
|
23
|
+
const i = Date.now(), t = b(e), r = m(t, { pointerCursor: g(t[0]) }), s = this.telemetry.snapshot(i), d = this.telemetry.trace.interactionSpan(i), o = e, a = {
|
|
24
|
+
...d,
|
|
25
|
+
// The key block is shared with native's `trackTap` so the two cannot drift; only
|
|
26
|
+
// the values are web's. `clientX/Y` is absent on a synthetic or keyboard-driven
|
|
27
|
+
// click, and §4.6 types both never-null, so the builder floors them at 0.
|
|
28
|
+
...p({
|
|
29
|
+
type: "click",
|
|
30
|
+
target: r.target,
|
|
31
|
+
nameSource: r.nameSource,
|
|
32
|
+
tag: r.tag,
|
|
33
|
+
x: o.clientX,
|
|
34
|
+
y: o.clientY,
|
|
35
|
+
rage: this.rage.record(r.node, i)
|
|
36
|
+
})
|
|
37
|
+
};
|
|
38
|
+
r.actionable && !v(t[0]) && this.canObserve() ? this.watchForDeath(a, s) : this.emit(a, s);
|
|
39
|
+
}
|
|
40
|
+
/** Arm one dead-click window and emit when it closes. */
|
|
41
|
+
watchForDeath(e, i) {
|
|
42
|
+
const t = {
|
|
43
|
+
attrs: e,
|
|
44
|
+
snapshot: i,
|
|
45
|
+
tick: this.aliveTick,
|
|
46
|
+
timer: setTimeout(() => {
|
|
47
|
+
this.pending.delete(t), this.pending.size === 0 && this.disconnectObserver(), this.emit({ ...e, "ui.dead": this.aliveTick === t.tick }, i);
|
|
48
|
+
}, f)
|
|
49
|
+
};
|
|
50
|
+
this.pending.add(t), this.connectObserver();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Emit every open window **now**, with `ui.dead` **omitted** — the window never closed,
|
|
54
|
+
* so it was not evaluated, which is exactly what its absence means (§4.6). Inventing a
|
|
55
|
+
* verdict from a partial window is the false accusation the key is built to avoid.
|
|
56
|
+
*/
|
|
57
|
+
drainPending() {
|
|
58
|
+
for (const e of this.pending)
|
|
59
|
+
clearTimeout(e.timer), this.emit(e.attrs, e.snapshot);
|
|
60
|
+
this.pending.clear(), this.disconnectObserver();
|
|
61
|
+
}
|
|
62
|
+
emit(e, i) {
|
|
63
|
+
Promise.resolve(this.telemetry.log("ui.interaction", e, i)).catch((t) => c.warn("Web ui.interaction failed:", t));
|
|
64
|
+
}
|
|
65
|
+
canObserve() {
|
|
66
|
+
return typeof MutationObserver < "u";
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Observed only while a window is open. A permanently-connected whole-document observer
|
|
70
|
+
* is a real cost on a busy React tree, and there is nothing to answer between clicks.
|
|
71
|
+
* `attributes: true` because §4.6 counts an attribute-only change as alive — a CSS class
|
|
72
|
+
* flip is a response.
|
|
73
|
+
*/
|
|
74
|
+
connectObserver() {
|
|
75
|
+
const e = typeof document > "u" ? void 0 : document.documentElement;
|
|
76
|
+
this.observer || !this.canObserve() || !e || (this.observer = new MutationObserver(() => {
|
|
77
|
+
this.aliveTick++;
|
|
78
|
+
}), this.observer.observe(e, {
|
|
79
|
+
subtree: !0,
|
|
80
|
+
childList: !0,
|
|
81
|
+
attributes: !0,
|
|
82
|
+
characterData: !0
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
disconnectObserver() {
|
|
86
|
+
this.observer?.disconnect(), this.observer = void 0;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function b(n) {
|
|
90
|
+
const e = typeof n.composedPath == "function" ? n.composedPath() : void 0;
|
|
91
|
+
if (Array.isArray(e) && e.length > 0) return e;
|
|
92
|
+
const i = [];
|
|
93
|
+
let t = n.target;
|
|
94
|
+
for (; t; )
|
|
95
|
+
i.push(t), t = t.parentElement ?? t.parentNode;
|
|
96
|
+
return i;
|
|
97
|
+
}
|
|
98
|
+
function g(n) {
|
|
99
|
+
if (!n || typeof getComputedStyle > "u") return !1;
|
|
100
|
+
try {
|
|
101
|
+
return getComputedStyle(n)?.cursor === "pointer";
|
|
102
|
+
} catch {
|
|
103
|
+
return !1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
w as InteractionTrackerWeb
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=interactionWeb.web-CJAviVQ7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactionWeb.web-CJAviVQ7.js","sources":["../src/adapters/web/interactionWeb.web.ts"],"sourcesContent":["// `ui.interaction` on web (§4.6, #102) — one capture-phase `click` listener on `document`,\n// the role-gated name ladder from `../uiInteraction`, and the two frustration signals.\n//\n// **Every click emits, actionable or not.** Suppressing the role-less ones would destroy\n// dead-click detection at the source, and it is what makes `unnamed` vs `surface` readable\n// as \"an instrumentation gap\" vs \"someone tapped whitespace\".\n\nimport type { LogSnapshot, Telemetry } from \"../../core/telemetry\";\nimport { debug } from \"../../core/debug\";\nimport { QUIET_WINDOW_MS } from \"../loadingTime\";\nimport {\n RAGE_WINDOW_MS,\n RageTracker,\n isDeadClickExempt,\n resolveUiName,\n uiAttributes,\n type UiElement,\n} from \"../uiInteraction\";\n\n/**\n * ⚠ The dead-click window **is** §4.5.2's quiet window — one named constant, not a second\n * literal `1000`. If one moves, both move. (`RAGE_WINDOW_MS` is deliberately *not* bound to\n * it: §4.6 states the same number for rage, but tuning network settle must not retune rage.)\n */\nconst DEAD_CLICK_WINDOW_MS = QUIET_WINDOW_MS;\n\n/** One click whose dead-click window is still open. */\ntype Pending = {\n attrs: Record<string, any>;\n snapshot: LogSnapshot;\n /** The aliveness reading at mint; unchanged when the window closes means dead. */\n tick: number;\n timer: ReturnType<typeof setTimeout>;\n};\n\nexport class InteractionTrackerWeb {\n private started = false;\n private readonly rage = new RageTracker(RAGE_WINDOW_MS);\n\n /**\n * Monotonic aliveness counter. A DOM mutation, a request start or a view mint bumps it;\n * a click that sees the same value a window later saw nothing happen. A counter rather\n * than a timestamp so the comparison needs no clock and cannot be fooled by a clock that\n * moved backwards.\n */\n private aliveTick = 0;\n private readonly pending = new Set<Pending>();\n private observer?: MutationObserver;\n\n constructor(private telemetry: Telemetry) {}\n\n start(): void {\n if (this.started) return;\n if (typeof document === \"undefined\" || !document?.addEventListener) {\n debug.log(\"Web interactions: no document — ui.interaction is not captured\");\n return;\n }\n this.started = true;\n // Capture phase: the mint has to happen before the host's own handler navigates,\n // because the snapshot it takes is the whole point of §4.6's mint/emit split.\n document.addEventListener(\"click\", (e) => this.onClick(e), true);\n\n // A same-tab `<a href>` or a form submit unloads the document while its dead-click\n // window is still open, and an unemitted row also orphans the interaction root its\n // children already joined. Both edges drain what is pending — `pagehide` covers the\n // unload and the bfcache freeze, `visibilitychange` covers a tab switch and is the\n // edge mobile Safari actually fires.\n if (typeof window !== \"undefined\") {\n window.addEventListener?.(\"pagehide\", () => this.drainPending());\n }\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") this.drainPending();\n });\n\n this.telemetry.views.onActivity(() => { this.aliveTick++; });\n }\n\n private onClick(e: Event): void {\n const at = Date.now();\n const path = pathOf(e);\n const resolution = resolveUiName(path, { pointerCursor: hasPointerCursor(path[0]) });\n\n // Snapshotted here, synchronously, while the click's own view is still current.\n const snapshot = this.telemetry.snapshot(at);\n // §6.2: **every** click mints an interaction root, live carrier or not.\n const span = this.telemetry.trace.interactionSpan(at);\n const mouse = e as MouseEvent;\n\n const attrs: Record<string, any> = {\n ...span,\n // The key block is shared with native's `trackTap` so the two cannot drift; only\n // the values are web's. `clientX/Y` is absent on a synthetic or keyboard-driven\n // click, and §4.6 types both never-null, so the builder floors them at 0.\n ...uiAttributes({\n type: \"click\",\n target: resolution.target,\n nameSource: resolution.nameSource,\n tag: resolution.tag,\n x: mouse.clientX,\n y: mouse.clientY,\n rage: this.rage.record(resolution.node, at),\n }),\n };\n\n // `ui.dead` is judged on **actionable** clicks only, minus text entry and\n // download/`_blank` anchors — and is **omitted when not evaluated**. That absence is\n // load-bearing: the signal must under-report rather than falsely accuse.\n if (resolution.actionable && !isDeadClickExempt(path[0]) && this.canObserve()) {\n this.watchForDeath(attrs, snapshot);\n } else {\n this.emit(attrs, snapshot);\n }\n }\n\n /** Arm one dead-click window and emit when it closes. */\n private watchForDeath(attrs: Record<string, any>, snapshot: LogSnapshot): void {\n const entry: Pending = {\n attrs,\n snapshot,\n tick: this.aliveTick,\n timer: setTimeout(() => {\n this.pending.delete(entry);\n if (this.pending.size === 0) this.disconnectObserver();\n this.emit({ ...attrs, \"ui.dead\": this.aliveTick === entry.tick }, snapshot);\n }, DEAD_CLICK_WINDOW_MS),\n };\n this.pending.add(entry);\n this.connectObserver();\n }\n\n /**\n * Emit every open window **now**, with `ui.dead` **omitted** — the window never closed,\n * so it was not evaluated, which is exactly what its absence means (§4.6). Inventing a\n * verdict from a partial window is the false accusation the key is built to avoid.\n */\n private drainPending(): void {\n for (const entry of this.pending) {\n clearTimeout(entry.timer);\n this.emit(entry.attrs, entry.snapshot);\n }\n this.pending.clear();\n this.disconnectObserver();\n }\n\n private emit(attrs: Record<string, any>, snapshot: LogSnapshot): void {\n void Promise.resolve(this.telemetry.log(\"ui.interaction\", attrs, snapshot))\n .catch((err: unknown) => debug.warn(\"Web ui.interaction failed:\", err));\n }\n\n private canObserve(): boolean {\n return typeof MutationObserver !== \"undefined\";\n }\n\n /**\n * Observed only while a window is open. A permanently-connected whole-document observer\n * is a real cost on a busy React tree, and there is nothing to answer between clicks.\n * `attributes: true` because §4.6 counts an attribute-only change as alive — a CSS class\n * flip is a response.\n */\n private connectObserver(): void {\n const root = typeof document === \"undefined\" ? undefined : document.documentElement;\n if (this.observer || !this.canObserve() || !root) return;\n this.observer = new MutationObserver(() => { this.aliveTick++; });\n this.observer.observe(root, {\n subtree: true,\n childList: true,\n attributes: true,\n characterData: true,\n });\n }\n\n private disconnectObserver(): void {\n this.observer?.disconnect();\n this.observer = undefined;\n }\n}\n\n/**\n * `composedPath()` innermost-first, so a click inside a shadow root still finds its host's\n * role. Falls back to the parent chain where the event has no `composedPath` (older\n * browsers, and every synthetic event a test hands in).\n */\nfunction pathOf(e: Event): UiElement[] {\n const composed = typeof e.composedPath === \"function\" ? e.composedPath() : undefined;\n if (Array.isArray(composed) && composed.length > 0) return composed as unknown as UiElement[];\n const path: UiElement[] = [];\n let node = e.target as (Node & UiElement) | null;\n while (node) {\n path.push(node);\n node = ((node as any).parentElement ?? node.parentNode) as (Node & UiElement) | null;\n }\n return path;\n}\n\n/**\n * The one signal separating `unnamed` from `surface` on a role-less element. Wrapped\n * because `getComputedStyle` throws on a detached node in some engines, and a click must\n * never be lost to a style read.\n */\nfunction hasPointerCursor(el: UiElement | undefined): boolean {\n if (!el || typeof getComputedStyle === \"undefined\") return false;\n try {\n return getComputedStyle(el as unknown as Element)?.cursor === \"pointer\";\n } catch {\n return false;\n }\n}\n"],"names":["DEAD_CLICK_WINDOW_MS","QUIET_WINDOW_MS","InteractionTrackerWeb","telemetry","RageTracker","RAGE_WINDOW_MS","debug","at","path","pathOf","resolution","resolveUiName","hasPointerCursor","snapshot","span","mouse","attrs","uiAttributes","isDeadClickExempt","entry","err","root","e","composed","node","el"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("./httpAttributes-D0m6UID7.cjs"),T=require("./xhrIntercept-BADex6Cq.cjs"),a=require("./memoryStore-BIGZmmfD.cjs");function b(u,s){const e=u;return s||typeof e?.referrer!="string"?{}:{referrer:e.referrer,referrerPolicy:e.referrerPolicy}}class R{constructor(s){this.telemetry=s}start(){return new Promise(s=>{const e=this.telemetry,m=window.fetch;window.fetch=async(t,r)=>{const d=Date.now(),l=typeof t=="string"?t:t.toString(),n=i.isCollectorUrl(l,e.getEndpoint?.()),p=n?void 0:e.views.requestStarted(d),w=n?void 0:e.snapshot(),f=r?.headers!==void 0?r.headers:t?.headers,h=n?void 0:e.trace.requestTrace(d,{url:l,sampled:e.isSampled(),consumerTraceparent:a.readHeader(f,a.TRACEPARENT),noCors:(r?.mode??t?.mode)==="no-cors"}),g=h?.finish,q=h?.header?{...b(t,r),...r,headers:a.withHeader(f,a.TRACEPARENT,h.header)}:r;let c=null,y=null;try{return c=await m(t,q),c}catch(o){throw y=o,o}finally{const o=Date.now();p?.(o),n||e.log("http.request",{...i.buildHttpAttributes({url:l,method:r?.method??"GET",statusCode:c?.status??0,durationMs:o-d,error:y,requestBody:r?.body,responseSize:i.contentLengthSize(c?.headers.get("content-length"))}),...g?.(o)},w)}},typeof XMLHttpRequest<"u"&&T.patchXHR(e,XMLHttpRequest),s()})}}exports.NetworkTrackerWeb=R;
|
|
2
|
+
//# sourceMappingURL=interceptFetchWeb.web-T88kO-bE.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptFetchWeb.web-T88kO-bE.cjs","sources":["../src/adapters/web/interceptFetchWeb.web.ts"],"sourcesContent":["// interceptHttp.web.ts\nimport { Telemetry } from \"../../core/telemetry\";\nimport { buildHttpAttributes, contentLengthSize, isCollectorUrl } from \"../httpAttributes\";\nimport { patchXHR } from \"../xhrIntercept\";\nimport { TRACEPARENT, readHeader, withHeader } from \"../traceHeader\";\n\n/**\n * Per the fetch spec a **non-empty `init` resets a `Request`'s `referrer` and\n * `referrerPolicy`**. When the consumer passed no init at all, handing fetch one just to carry\n * our header would silently change their request — and §6.4 is explicit that the SDK does\n * nothing else at runtime. So carry both across. A consumer who already passed a non-empty\n * init has triggered the same reset themselves, and their values win over these.\n */\nfunction carriedReferrer(input: unknown, init: RequestInit | undefined): RequestInit {\n const req = input as { referrer?: unknown; referrerPolicy?: unknown };\n if (init || typeof req?.referrer !== \"string\") return {};\n return { referrer: req.referrer, referrerPolicy: req.referrerPolicy as ReferrerPolicy };\n}\n\n/**\n * NetworkTrackerWeb intercepts HTTP made through `window.fetch` and `XMLHttpRequest`.\n *\n * Both are patched here, unlike native: browser `fetch` is a native implementation, not\n * XHR-backed, so the two are genuinely separate channels and neither double-counts (§4.4).\n */\nexport class NetworkTrackerWeb {\n private telemetry: Telemetry;\n\n /**\n * Create a NetworkTrackerWeb instance.\n * @param telemetry - An instance of Telemetry used for logging network events.\n */\n constructor(telemetry: Telemetry) {\n this.telemetry = telemetry;\n }\n\n /**\n * Starts intercepting HTTP requests made through `fetch` and `XMLHttpRequest`.\n *\n * @returns A Promise that resolves immediately once interception is setup.\n */\n public start(): Promise<void> {\n return new Promise((resolve) => {\n // Capture telemetry instance to use inside patched functions\n const telemetry = this.telemetry;\n\n // --- Patch window.fetch ---\n const originalFetch = window.fetch;\n window.fetch = async (\n input: RequestInfo | URL,\n init?: RequestInit\n ): Promise<Response> => {\n const start = Date.now();\n const url = typeof input === \"string\" ? input : input.toString();\n const isCollector = isCollectorUrl(url, telemetry.getEndpoint?.());\n // Bound to the view live at *send*, not at completion (§4.5.2). The collector's\n // own POST is excluded from settle for the same reason it is excluded from\n // `http.request`: the SDK must not hold a view open with its own traffic.\n const settled = isCollector ? undefined : telemetry.views.requestStarted(start);\n // §3.1 — `session.id`, `session.start_time` and `view.id` frozen at send.\n // No `at`: the row keeps the completion timestamp §4.4 gives it.\n const snap = isCollector ? undefined : telemetry.snapshot();\n // §6.3's Tier 1 plus §6.5's outcome ladder, captured at send for the same\n // reason: the root live *now* is the parent, and a root minted here is this\n // request's own (§6.2). `init.headers` *replaces* a Request's own headers\n // per the fetch spec, so the presence check mirrors that precedence rather\n // than merging the two — read-scope means one name, one presence check.\n const reqHeaders = init?.headers !== undefined\n ? init.headers\n : (input as any)?.headers;\n const trace = isCollector ? undefined : telemetry.trace.requestTrace(start, {\n url,\n sampled: telemetry.isSampled(),\n consumerTraceparent: readHeader(reqHeaders, TRACEPARENT),\n // fetch-only, and the sharper statement than \"web-only\": on a `no-cors`\n // request the headers guard drops the consumer's traceparent too.\n noCors: (init?.mode ?? (input as any)?.mode) === \"no-cors\",\n });\n const span = trace?.finish;\n // A **copy**. The consumer's Request/Headers object is never touched, so\n // never-strip holds by construction and their retry of the same Request\n // carries no SDK header — each attempt gets its own fresh `span.id`.\n const fetchInit = trace?.header\n ? { ...carriedReferrer(input, init), ...init, headers: withHeader(reqHeaders, TRACEPARENT, trace.header) }\n : init;\n let response: Response | null = null;\n let error: any = null;\n\n try {\n response = await originalFetch(input, fetchInit as RequestInit);\n return response;\n } catch (err) {\n error = err;\n throw err;\n } finally {\n const end = Date.now();\n settled?.(end);\n\n // Invariant: an http.request never describes the SDK's own collector POST.\n if (!isCollector) {\n telemetry.log(\"http.request\", {\n ...buildHttpAttributes({\n url,\n method: init?.method ?? \"GET\", // fetch's own default, a real observation\n statusCode: response?.status ?? 0,\n durationMs: end - start,\n error,\n requestBody: init?.body,\n responseSize: contentLengthSize(response?.headers.get(\"content-length\")),\n }),\n ...span?.(end),\n }, snap);\n }\n }\n };\n\n // --- Patch XMLHttpRequest (absent in some RN-Web runtimes) ---\n if (typeof XMLHttpRequest !== \"undefined\") patchXHR(telemetry, XMLHttpRequest);\n\n // Resolve immediately since interception setup is synchronous\n resolve();\n });\n }\n}\n"],"names":["carriedReferrer","input","init","req","NetworkTrackerWeb","telemetry","resolve","originalFetch","start","url","isCollector","isCollectorUrl","settled","snap","reqHeaders","trace","readHeader","TRACEPARENT","span","fetchInit","withHeader","response","error","err","end","buildHttpAttributes","contentLengthSize","patchXHR"],"mappings":"kNAaA,SAASA,EAAgBC,EAAgBC,EAA4C,CACjF,MAAMC,EAAMF,EACZ,OAAIC,GAAQ,OAAOC,GAAK,UAAa,SAAiB,CAAA,EAC/C,CAAE,SAAUA,EAAI,SAAU,eAAgBA,EAAI,cAAA,CACzD,CAQO,MAAMC,CAAkB,CAO3B,YAAYC,EAAsB,CAC9B,KAAK,UAAYA,CACrB,CAOO,OAAuB,CAC1B,OAAO,IAAI,QAASC,GAAY,CAE5B,MAAMD,EAAY,KAAK,UAGjBE,EAAgB,OAAO,MAC7B,OAAO,MAAQ,MACXN,EACAC,IACoB,CACpB,MAAMM,EAAQ,KAAK,IAAA,EACbC,EAAM,OAAOR,GAAU,SAAWA,EAAQA,EAAM,SAAA,EAChDS,EAAcC,EAAAA,eAAeF,EAAKJ,EAAU,eAAe,EAI3DO,EAAUF,EAAc,OAAYL,EAAU,MAAM,eAAeG,CAAK,EAGxEK,EAAOH,EAAc,OAAYL,EAAU,SAAA,EAM3CS,EAAaZ,GAAM,UAAY,OAC/BA,EAAK,QACJD,GAAe,QAChBc,EAAQL,EAAc,OAAYL,EAAU,MAAM,aAAaG,EAAO,CACxE,IAAAC,EACA,QAASJ,EAAU,UAAA,EACnB,oBAAqBW,EAAAA,WAAWF,EAAYG,aAAW,EAGvD,QAASf,GAAM,MAASD,GAAe,QAAU,SAAA,CACpD,EACKiB,EAAOH,GAAO,OAIdI,EAAYJ,GAAO,OACnB,CAAE,GAAGf,EAAgBC,EAAOC,CAAI,EAAG,GAAGA,EAAM,QAASkB,EAAAA,WAAWN,EAAYG,EAAAA,YAAaF,EAAM,MAAM,GACrGb,EACN,IAAImB,EAA4B,KAC5BC,EAAa,KAEjB,GAAI,CACA,OAAAD,EAAW,MAAMd,EAAcN,EAAOkB,CAAwB,EACvDE,CACX,OAASE,EAAK,CACV,MAAAD,EAAQC,EACFA,CACV,QAAA,CACI,MAAMC,EAAM,KAAK,IAAA,EACjBZ,IAAUY,CAAG,EAGRd,GACDL,EAAU,IAAI,eAAgB,CAC1B,GAAGoB,sBAAoB,CACnB,IAAAhB,EACA,OAAQP,GAAM,QAAU,MACxB,WAAYmB,GAAU,QAAU,EAChC,WAAYG,EAAMhB,EAClB,MAAAc,EACA,YAAapB,GAAM,KACnB,aAAcwB,EAAAA,kBAAkBL,GAAU,QAAQ,IAAI,gBAAgB,CAAC,CAAA,CAC1E,EACD,GAAGH,IAAOM,CAAG,CAAA,EACdX,CAAI,CAEf,CACJ,EAGI,OAAO,eAAmB,KAAac,EAAAA,SAAStB,EAAW,cAAc,EAG7EC,EAAA,CACJ,CAAC,CACL,CACJ"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { i as q, b as H, c as R } from "./httpAttributes-BrrtM8zh.js";
|
|
2
|
+
import { p as T } from "./xhrIntercept-BNiA41Mj.js";
|
|
3
|
+
import { r as b, w as v, a as m } from "./memoryStore-F5I-GQKM.js";
|
|
4
|
+
function C(h, o) {
|
|
5
|
+
const e = h;
|
|
6
|
+
return o || typeof e?.referrer != "string" ? {} : { referrer: e.referrer, referrerPolicy: e.referrerPolicy };
|
|
7
|
+
}
|
|
8
|
+
class A {
|
|
9
|
+
/**
|
|
10
|
+
* Create a NetworkTrackerWeb instance.
|
|
11
|
+
* @param telemetry - An instance of Telemetry used for logging network events.
|
|
12
|
+
*/
|
|
13
|
+
constructor(o) {
|
|
14
|
+
this.telemetry = o;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Starts intercepting HTTP requests made through `fetch` and `XMLHttpRequest`.
|
|
18
|
+
*
|
|
19
|
+
* @returns A Promise that resolves immediately once interception is setup.
|
|
20
|
+
*/
|
|
21
|
+
start() {
|
|
22
|
+
return new Promise((o) => {
|
|
23
|
+
const e = this.telemetry, p = window.fetch;
|
|
24
|
+
window.fetch = async (t, r) => {
|
|
25
|
+
const c = Date.now(), d = typeof t == "string" ? t : t.toString(), a = q(d, e.getEndpoint?.()), u = a ? void 0 : e.views.requestStarted(c), w = a ? void 0 : e.snapshot(), f = r?.headers !== void 0 ? r.headers : t?.headers, l = a ? void 0 : e.trace.requestTrace(c, {
|
|
26
|
+
url: d,
|
|
27
|
+
sampled: e.isSampled(),
|
|
28
|
+
consumerTraceparent: b(f, m),
|
|
29
|
+
// fetch-only, and the sharper statement than "web-only": on a `no-cors`
|
|
30
|
+
// request the headers guard drops the consumer's traceparent too.
|
|
31
|
+
noCors: (r?.mode ?? t?.mode) === "no-cors"
|
|
32
|
+
}), y = l?.finish, g = l?.header ? { ...C(t, r), ...r, headers: v(f, m, l.header) } : r;
|
|
33
|
+
let n = null, i = null;
|
|
34
|
+
try {
|
|
35
|
+
return n = await p(t, g), n;
|
|
36
|
+
} catch (s) {
|
|
37
|
+
throw i = s, s;
|
|
38
|
+
} finally {
|
|
39
|
+
const s = Date.now();
|
|
40
|
+
u?.(s), a || e.log("http.request", {
|
|
41
|
+
...H({
|
|
42
|
+
url: d,
|
|
43
|
+
method: r?.method ?? "GET",
|
|
44
|
+
// fetch's own default, a real observation
|
|
45
|
+
statusCode: n?.status ?? 0,
|
|
46
|
+
durationMs: s - c,
|
|
47
|
+
error: i,
|
|
48
|
+
requestBody: r?.body,
|
|
49
|
+
responseSize: R(n?.headers.get("content-length"))
|
|
50
|
+
}),
|
|
51
|
+
...y?.(s)
|
|
52
|
+
}, w);
|
|
53
|
+
}
|
|
54
|
+
}, typeof XMLHttpRequest < "u" && T(e, XMLHttpRequest), o();
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
A as NetworkTrackerWeb
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=interceptFetchWeb.web-UXC2Lb6X.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptFetchWeb.web-UXC2Lb6X.js","sources":["../src/adapters/web/interceptFetchWeb.web.ts"],"sourcesContent":["// interceptHttp.web.ts\nimport { Telemetry } from \"../../core/telemetry\";\nimport { buildHttpAttributes, contentLengthSize, isCollectorUrl } from \"../httpAttributes\";\nimport { patchXHR } from \"../xhrIntercept\";\nimport { TRACEPARENT, readHeader, withHeader } from \"../traceHeader\";\n\n/**\n * Per the fetch spec a **non-empty `init` resets a `Request`'s `referrer` and\n * `referrerPolicy`**. When the consumer passed no init at all, handing fetch one just to carry\n * our header would silently change their request — and §6.4 is explicit that the SDK does\n * nothing else at runtime. So carry both across. A consumer who already passed a non-empty\n * init has triggered the same reset themselves, and their values win over these.\n */\nfunction carriedReferrer(input: unknown, init: RequestInit | undefined): RequestInit {\n const req = input as { referrer?: unknown; referrerPolicy?: unknown };\n if (init || typeof req?.referrer !== \"string\") return {};\n return { referrer: req.referrer, referrerPolicy: req.referrerPolicy as ReferrerPolicy };\n}\n\n/**\n * NetworkTrackerWeb intercepts HTTP made through `window.fetch` and `XMLHttpRequest`.\n *\n * Both are patched here, unlike native: browser `fetch` is a native implementation, not\n * XHR-backed, so the two are genuinely separate channels and neither double-counts (§4.4).\n */\nexport class NetworkTrackerWeb {\n private telemetry: Telemetry;\n\n /**\n * Create a NetworkTrackerWeb instance.\n * @param telemetry - An instance of Telemetry used for logging network events.\n */\n constructor(telemetry: Telemetry) {\n this.telemetry = telemetry;\n }\n\n /**\n * Starts intercepting HTTP requests made through `fetch` and `XMLHttpRequest`.\n *\n * @returns A Promise that resolves immediately once interception is setup.\n */\n public start(): Promise<void> {\n return new Promise((resolve) => {\n // Capture telemetry instance to use inside patched functions\n const telemetry = this.telemetry;\n\n // --- Patch window.fetch ---\n const originalFetch = window.fetch;\n window.fetch = async (\n input: RequestInfo | URL,\n init?: RequestInit\n ): Promise<Response> => {\n const start = Date.now();\n const url = typeof input === \"string\" ? input : input.toString();\n const isCollector = isCollectorUrl(url, telemetry.getEndpoint?.());\n // Bound to the view live at *send*, not at completion (§4.5.2). The collector's\n // own POST is excluded from settle for the same reason it is excluded from\n // `http.request`: the SDK must not hold a view open with its own traffic.\n const settled = isCollector ? undefined : telemetry.views.requestStarted(start);\n // §3.1 — `session.id`, `session.start_time` and `view.id` frozen at send.\n // No `at`: the row keeps the completion timestamp §4.4 gives it.\n const snap = isCollector ? undefined : telemetry.snapshot();\n // §6.3's Tier 1 plus §6.5's outcome ladder, captured at send for the same\n // reason: the root live *now* is the parent, and a root minted here is this\n // request's own (§6.2). `init.headers` *replaces* a Request's own headers\n // per the fetch spec, so the presence check mirrors that precedence rather\n // than merging the two — read-scope means one name, one presence check.\n const reqHeaders = init?.headers !== undefined\n ? init.headers\n : (input as any)?.headers;\n const trace = isCollector ? undefined : telemetry.trace.requestTrace(start, {\n url,\n sampled: telemetry.isSampled(),\n consumerTraceparent: readHeader(reqHeaders, TRACEPARENT),\n // fetch-only, and the sharper statement than \"web-only\": on a `no-cors`\n // request the headers guard drops the consumer's traceparent too.\n noCors: (init?.mode ?? (input as any)?.mode) === \"no-cors\",\n });\n const span = trace?.finish;\n // A **copy**. The consumer's Request/Headers object is never touched, so\n // never-strip holds by construction and their retry of the same Request\n // carries no SDK header — each attempt gets its own fresh `span.id`.\n const fetchInit = trace?.header\n ? { ...carriedReferrer(input, init), ...init, headers: withHeader(reqHeaders, TRACEPARENT, trace.header) }\n : init;\n let response: Response | null = null;\n let error: any = null;\n\n try {\n response = await originalFetch(input, fetchInit as RequestInit);\n return response;\n } catch (err) {\n error = err;\n throw err;\n } finally {\n const end = Date.now();\n settled?.(end);\n\n // Invariant: an http.request never describes the SDK's own collector POST.\n if (!isCollector) {\n telemetry.log(\"http.request\", {\n ...buildHttpAttributes({\n url,\n method: init?.method ?? \"GET\", // fetch's own default, a real observation\n statusCode: response?.status ?? 0,\n durationMs: end - start,\n error,\n requestBody: init?.body,\n responseSize: contentLengthSize(response?.headers.get(\"content-length\")),\n }),\n ...span?.(end),\n }, snap);\n }\n }\n };\n\n // --- Patch XMLHttpRequest (absent in some RN-Web runtimes) ---\n if (typeof XMLHttpRequest !== \"undefined\") patchXHR(telemetry, XMLHttpRequest);\n\n // Resolve immediately since interception setup is synchronous\n resolve();\n });\n }\n}\n"],"names":["carriedReferrer","input","init","req","NetworkTrackerWeb","telemetry","resolve","originalFetch","start","url","isCollector","isCollectorUrl","settled","snap","reqHeaders","trace","readHeader","TRACEPARENT","span","fetchInit","withHeader","response","error","err","end","buildHttpAttributes","contentLengthSize","patchXHR"],"mappings":";;;AAaA,SAASA,EAAgBC,GAAgBC,GAA4C;AACjF,QAAMC,IAAMF;AACZ,SAAIC,KAAQ,OAAOC,GAAK,YAAa,WAAiB,CAAA,IAC/C,EAAE,UAAUA,EAAI,UAAU,gBAAgBA,EAAI,eAAA;AACzD;AAQO,MAAMC,EAAkB;AAAA;AAAA;AAAA;AAAA;AAAA,EAO3B,YAAYC,GAAsB;AAC9B,SAAK,YAAYA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,QAAuB;AAC1B,WAAO,IAAI,QAAQ,CAACC,MAAY;AAE5B,YAAMD,IAAY,KAAK,WAGjBE,IAAgB,OAAO;AAC7B,aAAO,QAAQ,OACXN,GACAC,MACoB;AACpB,cAAMM,IAAQ,KAAK,IAAA,GACbC,IAAM,OAAOR,KAAU,WAAWA,IAAQA,EAAM,SAAA,GAChDS,IAAcC,EAAeF,GAAKJ,EAAU,eAAe,GAI3DO,IAAUF,IAAc,SAAYL,EAAU,MAAM,eAAeG,CAAK,GAGxEK,IAAOH,IAAc,SAAYL,EAAU,SAAA,GAM3CS,IAAaZ,GAAM,YAAY,SAC/BA,EAAK,UACJD,GAAe,SAChBc,IAAQL,IAAc,SAAYL,EAAU,MAAM,aAAaG,GAAO;AAAA,UACxE,KAAAC;AAAA,UACA,SAASJ,EAAU,UAAA;AAAA,UACnB,qBAAqBW,EAAWF,GAAYG,CAAW;AAAA;AAAA;AAAA,UAGvD,SAASf,GAAM,QAASD,GAAe,UAAU;AAAA,QAAA,CACpD,GACKiB,IAAOH,GAAO,QAIdI,IAAYJ,GAAO,SACnB,EAAE,GAAGf,EAAgBC,GAAOC,CAAI,GAAG,GAAGA,GAAM,SAASkB,EAAWN,GAAYG,GAAaF,EAAM,MAAM,MACrGb;AACN,YAAImB,IAA4B,MAC5BC,IAAa;AAEjB,YAAI;AACA,iBAAAD,IAAW,MAAMd,EAAcN,GAAOkB,CAAwB,GACvDE;AAAA,QACX,SAASE,GAAK;AACV,gBAAAD,IAAQC,GACFA;AAAA,QACV,UAAA;AACI,gBAAMC,IAAM,KAAK,IAAA;AACjB,UAAAZ,IAAUY,CAAG,GAGRd,KACDL,EAAU,IAAI,gBAAgB;AAAA,YAC1B,GAAGoB,EAAoB;AAAA,cACnB,KAAAhB;AAAA,cACA,QAAQP,GAAM,UAAU;AAAA;AAAA,cACxB,YAAYmB,GAAU,UAAU;AAAA,cAChC,YAAYG,IAAMhB;AAAA,cAClB,OAAAc;AAAA,cACA,aAAapB,GAAM;AAAA,cACnB,cAAcwB,EAAkBL,GAAU,QAAQ,IAAI,gBAAgB,CAAC;AAAA,YAAA,CAC1E;AAAA,YACD,GAAGH,IAAOM,CAAG;AAAA,UAAA,GACdX,CAAI;AAAA,QAEf;AAAA,MACJ,GAGI,OAAO,iBAAmB,OAAac,EAAStB,GAAW,cAAc,GAG7EC,EAAA;AAAA,IACJ,CAAC;AAAA,EACL;AACJ;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./xhrIntercept-BADex6Cq.cjs");class r{constructor(t){this.telemetry=t}start(){const t=globalThis.XMLHttpRequest;return t&&(this.unpatch=e.patchXHR(this.telemetry,t)),Promise.resolve()}stop(){this.unpatch?.(),this.unpatch=void 0}}exports.NetworkTrackerNative=r;
|
|
2
|
+
//# sourceMappingURL=interceptHttpNative.native-BVpFuoNB.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptHttpNative.native-BVpFuoNB.cjs","sources":["../src/adapters/native/interceptHttpNative.native.ts"],"sourcesContent":["import { Telemetry } from \"../../core/telemetry\";\nimport { patchXHR } from \"../xhrIntercept\";\n\n/**\n * Captures all JS-originated HTTP on React Native by patching `XMLHttpRequest` — and nothing\n * else. RN's `global.fetch` is `XMLHttpRequest` underneath (contract §4.4), so patching both\n * would emit **two** `http.request` events per `fetch()` call; the one chokepoint catches\n * `fetch` *and* axios, which is why an axios app's HTTP dashboard was empty before #95.\n *\n * Outside the boundary and uncountable: rn-fetch-blob, RN Firebase, native Apollo links,\n * expo-file-system, `Image` loading and `WebSocket` — none of them touch JS HTTP.\n */\nexport class NetworkTrackerNative {\n private unpatch?: () => void;\n\n constructor(private telemetry: Telemetry) { }\n\n public start(): Promise<void> {\n const xhr = (globalThis as any).XMLHttpRequest;\n if (xhr) this.unpatch = patchXHR(this.telemetry, xhr);\n return Promise.resolve();\n }\n\n /** Restores the original XHR prototype, removing the interception. */\n public stop(): void {\n this.unpatch?.();\n this.unpatch = undefined;\n }\n}\n"],"names":["NetworkTrackerNative","telemetry","xhr","patchXHR"],"mappings":"+HAYO,MAAMA,CAAqB,CAG9B,YAAoBC,EAAsB,CAAtB,KAAA,UAAAA,CAAwB,CAErC,OAAuB,CAC1B,MAAMC,EAAO,WAAmB,eAChC,OAAIA,IAAK,KAAK,QAAUC,EAAAA,SAAS,KAAK,UAAWD,CAAG,GAC7C,QAAQ,QAAA,CACnB,CAGO,MAAa,CAChB,KAAK,UAAA,EACL,KAAK,QAAU,MACnB,CACJ"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { p as e } from "./xhrIntercept-BNiA41Mj.js";
|
|
2
|
+
class o {
|
|
3
|
+
constructor(t) {
|
|
4
|
+
this.telemetry = t;
|
|
5
|
+
}
|
|
6
|
+
start() {
|
|
7
|
+
const t = globalThis.XMLHttpRequest;
|
|
8
|
+
return t && (this.unpatch = e(this.telemetry, t)), Promise.resolve();
|
|
9
|
+
}
|
|
10
|
+
/** Restores the original XHR prototype, removing the interception. */
|
|
11
|
+
stop() {
|
|
12
|
+
this.unpatch?.(), this.unpatch = void 0;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
o as NetworkTrackerNative
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=interceptHttpNative.native-rX-lPNST.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptHttpNative.native-rX-lPNST.js","sources":["../src/adapters/native/interceptHttpNative.native.ts"],"sourcesContent":["import { Telemetry } from \"../../core/telemetry\";\nimport { patchXHR } from \"../xhrIntercept\";\n\n/**\n * Captures all JS-originated HTTP on React Native by patching `XMLHttpRequest` — and nothing\n * else. RN's `global.fetch` is `XMLHttpRequest` underneath (contract §4.4), so patching both\n * would emit **two** `http.request` events per `fetch()` call; the one chokepoint catches\n * `fetch` *and* axios, which is why an axios app's HTTP dashboard was empty before #95.\n *\n * Outside the boundary and uncountable: rn-fetch-blob, RN Firebase, native Apollo links,\n * expo-file-system, `Image` loading and `WebSocket` — none of them touch JS HTTP.\n */\nexport class NetworkTrackerNative {\n private unpatch?: () => void;\n\n constructor(private telemetry: Telemetry) { }\n\n public start(): Promise<void> {\n const xhr = (globalThis as any).XMLHttpRequest;\n if (xhr) this.unpatch = patchXHR(this.telemetry, xhr);\n return Promise.resolve();\n }\n\n /** Restores the original XHR prototype, removing the interception. */\n public stop(): void {\n this.unpatch?.();\n this.unpatch = undefined;\n }\n}\n"],"names":["NetworkTrackerNative","telemetry","xhr","patchXHR"],"mappings":";AAYO,MAAMA,EAAqB;AAAA,EAG9B,YAAoBC,GAAsB;AAAtB,SAAA,YAAAA;AAAA,EAAwB;AAAA,EAErC,QAAuB;AAC1B,UAAMC,IAAO,WAAmB;AAChC,WAAIA,MAAK,KAAK,UAAUC,EAAS,KAAK,WAAWD,CAAG,IAC7C,QAAQ,QAAA;AAAA,EACnB;AAAA;AAAA,EAGO,OAAa;AAChB,SAAK,UAAA,GACL,KAAK,UAAU;AAAA,EACnB;AACJ;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";class n{constructor(e,t){this.viewStart=e,this.started=0,this.inFlight=0,this.awaitingRuntimeReady=t?.awaitRuntimeReady??!1}get requestCount(){return this.started}seedRuntimeReady(e){this.awaitingRuntimeReady&&(this.awaitingRuntimeReady=!1,this.runtimeReadyAt=e)}requestStarted(e=Date.now()){if(this.started++,this.freezeIfQuiet(e),this.settledAt!==void 0)return()=>{};this.inFlight++;let t=!1;return(i=Date.now())=>{t||(t=!0,this.inFlight--,(this.lastEnd===void 0||i>this.lastEnd)&&(this.lastEnd=i))}}resolve(e=Date.now()){if(this.started===0)return{loadingTime:null,outcome:"no_activity"};this.freezeIfQuiet(e);const t=this.settledAt??this.quietSince();if(t===void 0)return{loadingTime:null,outcome:e-this.viewStart>=3e4?"capped":"abandoned"};const s=Math.max(t,this.runtimeReadyAt??0)-this.viewStart;return s>=3e4?{loadingTime:null,outcome:"capped"}:{loadingTime:Math.max(0,s),outcome:"settled"}}freezeIfQuiet(e){if(this.settledAt!==void 0)return;const t=this.quietSince();t!==void 0&&e-t>=1e3&&(this.settledAt=t)}quietSince(){if(!this.awaitingRuntimeReady&&!(this.inFlight>0))return this.lastEnd}}exports.LOADING_TIME_CAP_MS=3e4;exports.NetworkSettle=n;exports.QUIET_WINDOW_MS=1e3;
|
|
2
|
+
//# sourceMappingURL=loadingTime-Bhomd9qq.cjs.map
|