@hexclave/js 1.0.40 → 1.0.42
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/dist/admin-app-impl-Bth-U45X.d.ts +1095 -0
- package/dist/admin-app-impl-Bth-U45X.d.ts.map +1 -0
- package/dist/clickmap/clickmap-core.d.ts +1 -2
- package/dist/clickmap/clickmap-core.d.ts.map +1 -1
- package/dist/clickmap/clickmap-core.js +2 -4
- package/dist/clickmap/clickmap-core.js.map +1 -1
- package/dist/clickmap/clickmap-styles.js +3 -6
- package/dist/clickmap/clickmap-styles.js.map +1 -1
- package/dist/clickmap/index.d.ts +1 -2
- package/dist/clickmap/index.d.ts.map +1 -1
- package/dist/clickmap/index.js +2 -4
- package/dist/clickmap/index.js.map +1 -1
- package/dist/client-app-D8-Lm4RT.d.ts +1235 -0
- package/dist/client-app-D8-Lm4RT.d.ts.map +1 -0
- package/dist/common-BDyEKSM6.d.ts +174 -0
- package/dist/common-BDyEKSM6.d.ts.map +1 -0
- package/dist/common-CRu2FsO9.d.ts +65 -0
- package/dist/common-CRu2FsO9.d.ts.map +1 -0
- package/dist/config.js +17 -19
- package/dist/cookie-DTOQvCYn.d.ts +42 -0
- package/dist/cookie-DTOQvCYn.d.ts.map +1 -0
- package/dist/dev-tool/dev-tool-core.d.ts +1 -2
- package/dist/dev-tool/dev-tool-core.d.ts.map +1 -1
- package/dist/dev-tool/dev-tool-core.js +2 -4
- package/dist/dev-tool/dev-tool-core.js.map +1 -1
- package/dist/dev-tool/dev-tool-styles.js +3 -6
- package/dist/dev-tool/dev-tool-styles.js.map +1 -1
- package/dist/dev-tool/dev-tool-trigger-position.d.ts.map +1 -1
- package/dist/dev-tool/dev-tool-trigger-position.js +6 -7
- package/dist/dev-tool/dev-tool-trigger-position.js.map +1 -1
- package/dist/dev-tool/dev-tool-trigger-position.test.js +1 -3
- package/dist/dev-tool/dev-tool-trigger-position.test.js.map +1 -1
- package/dist/dev-tool/index.d.ts +1 -2
- package/dist/dev-tool/index.d.ts.map +1 -1
- package/dist/dev-tool/index.js +2 -4
- package/dist/dev-tool/index.js.map +1 -1
- package/dist/esm/clickmap/clickmap-core.d.ts +1 -1
- package/dist/esm/clickmap/clickmap-core.d.ts.map +1 -1
- package/dist/esm/clickmap/clickmap-core.js +1 -2
- package/dist/esm/clickmap/clickmap-core.js.map +1 -1
- package/dist/esm/clickmap/clickmap-styles.js +1 -2
- package/dist/esm/clickmap/clickmap-styles.js.map +1 -1
- package/dist/esm/clickmap/index.d.ts +1 -1
- package/dist/esm/clickmap/index.d.ts.map +1 -1
- package/dist/esm/clickmap/index.js +1 -2
- package/dist/esm/clickmap/index.js.map +1 -1
- package/dist/esm/config.js +1 -2
- package/dist/esm/dev-tool/dev-tool-core.d.ts +1 -1
- package/dist/esm/dev-tool/dev-tool-core.d.ts.map +1 -1
- package/dist/esm/dev-tool/dev-tool-core.js +1 -2
- package/dist/esm/dev-tool/dev-tool-core.js.map +1 -1
- package/dist/esm/dev-tool/dev-tool-styles.js +1 -2
- package/dist/esm/dev-tool/dev-tool-styles.js.map +1 -1
- package/dist/esm/dev-tool/dev-tool-trigger-position.d.ts.map +1 -1
- package/dist/esm/dev-tool/dev-tool-trigger-position.js +5 -5
- package/dist/esm/dev-tool/dev-tool-trigger-position.js.map +1 -1
- package/dist/esm/dev-tool/dev-tool-trigger-position.test.js +2 -3
- package/dist/esm/dev-tool/dev-tool-trigger-position.test.js.map +1 -1
- package/dist/esm/dev-tool/index.d.ts +1 -1
- package/dist/esm/dev-tool/index.d.ts.map +1 -1
- package/dist/esm/dev-tool/index.js +1 -2
- package/dist/esm/dev-tool/index.js.map +1 -1
- package/dist/esm/generated/env.js +1 -1
- package/dist/esm/generated/env.js.map +1 -1
- package/dist/esm/global.d.ts.map +1 -1
- package/dist/esm/in-page-ui/base-styles.d.ts.map +1 -1
- package/dist/esm/in-page-ui/base-styles.js +1 -1
- package/dist/esm/in-page-ui/base-styles.js.map +1 -1
- package/dist/esm/in-page-ui/dom.d.ts.map +1 -1
- package/dist/esm/in-page-ui/dom.js +1 -1
- package/dist/esm/in-page-ui/dom.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -4
- package/dist/esm/integrations/convex/component/convex.config.d.ts +1 -3
- package/dist/esm/integrations/convex/component/convex.config.d.ts.map +1 -1
- package/dist/esm/integrations/convex/component/convex.config.js +1 -2
- package/dist/esm/integrations/convex/component/convex.config.js.map +1 -1
- package/dist/esm/integrations/convex.js +1 -2
- package/dist/esm/integrations/convex.js.map +1 -1
- package/dist/esm/lib/auth.js +1 -2
- package/dist/esm/lib/auth.js.map +1 -1
- package/dist/esm/lib/auth.test.js +2 -3
- package/dist/esm/lib/auth.test.js.map +1 -1
- package/dist/esm/lib/cookie.d.ts.map +1 -1
- package/dist/esm/lib/cookie.js +4 -22
- package/dist/esm/lib/cookie.js.map +1 -1
- package/dist/esm/lib/hexclave-app/api-keys/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/api-keys/index.js +1 -2
- package/dist/esm/lib/hexclave-app/api-keys/index.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +34 -42
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.js +1 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +2 -3
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +26 -28
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +1 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.oauth-prefetch.test.js +2 -3
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.oauth-prefetch.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js +7 -8
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.js +1 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.test.js +2 -3
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/index.d.ts +3 -3
- package/dist/esm/lib/hexclave-app/apps/implementations/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/index.js +1 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/index.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-page-urls.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-page-urls.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-page-urls.js +1 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/redirect-page-urls.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +13 -13
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-refresh-subscription.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-refresh-subscription.js +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-refresh-subscription.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-refresh-subscription.test.js +2 -3
- package/dist/esm/lib/hexclave-app/apps/implementations/session-refresh-subscription.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.js +13 -7
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.test.js +134 -3
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/index.d.ts +3 -3
- package/dist/esm/lib/hexclave-app/apps/index.js +1 -2
- package/dist/esm/lib/hexclave-app/apps/interfaces/admin-app.d.ts +8 -8
- package/dist/esm/lib/hexclave-app/apps/interfaces/admin-app.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/admin-app.js +1 -2
- package/dist/esm/lib/hexclave-app/apps/interfaces/admin-app.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts +6 -6
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.js +1 -2
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/server-app.d.ts +7 -7
- package/dist/esm/lib/hexclave-app/apps/interfaces/server-app.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/server-app.js +1 -2
- package/dist/esm/lib/hexclave-app/apps/interfaces/server-app.js.map +1 -1
- package/dist/esm/lib/hexclave-app/common.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/common.js +1 -1
- package/dist/esm/lib/hexclave-app/common.js.map +1 -1
- package/dist/esm/lib/hexclave-app/connected-accounts/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/connected-accounts/index.js +1 -1
- package/dist/esm/lib/hexclave-app/contact-channels/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/contact-channels/index.js +1 -1
- package/dist/esm/lib/hexclave-app/contact-channels/index.js.map +1 -1
- package/dist/esm/lib/hexclave-app/customers/index.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/customers/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/customers/index.js +1 -1
- package/dist/esm/lib/hexclave-app/data-vault/index.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/data-vault/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/data-vault/index.js +1 -1
- package/dist/esm/lib/hexclave-app/email/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/email/index.js +1 -1
- package/dist/esm/lib/hexclave-app/email-templates/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/email-templates/index.js +1 -1
- package/dist/esm/lib/hexclave-app/email-templates/index.js.map +1 -1
- package/dist/esm/lib/hexclave-app/index.d.ts +14 -14
- package/dist/esm/lib/hexclave-app/index.js +1 -2
- package/dist/esm/lib/hexclave-app/internal-api-keys/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/internal-api-keys/index.js +1 -1
- package/dist/esm/lib/hexclave-app/internal-api-keys/index.js.map +1 -1
- package/dist/esm/lib/hexclave-app/notification-categories/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/notification-categories/index.js +1 -1
- package/dist/esm/lib/hexclave-app/permissions/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/permissions/index.js +1 -1
- package/dist/esm/lib/hexclave-app/permissions/index.js.map +1 -1
- package/dist/esm/lib/hexclave-app/plan-usage/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/plan-usage/index.js +1 -1
- package/dist/esm/lib/hexclave-app/project-configs/index.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/project-configs/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/project-configs/index.js +1 -1
- package/dist/esm/lib/hexclave-app/projects/index.d.ts +2 -2
- package/dist/esm/lib/hexclave-app/projects/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/projects/index.js +5 -1
- package/dist/esm/lib/hexclave-app/projects/index.js.map +1 -1
- package/dist/esm/lib/hexclave-app/session-replays/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/session-replays/index.js +1 -1
- package/dist/esm/lib/hexclave-app/teams/index.d.ts +4 -4
- package/dist/esm/lib/hexclave-app/teams/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/teams/index.js +1 -1
- package/dist/esm/lib/hexclave-app/teams/index.js.map +1 -1
- package/dist/esm/lib/hexclave-app/url-targets.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/url-targets.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/url-targets.js +2 -3
- package/dist/esm/lib/hexclave-app/url-targets.js.map +1 -1
- package/dist/esm/lib/hexclave-app/url-targets.test.js +2 -3
- package/dist/esm/lib/hexclave-app/url-targets.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/users/index.d.ts +10 -10
- package/dist/esm/lib/hexclave-app/users/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/users/index.js +1 -2
- package/dist/esm/lib/hexclave-app/users/index.js.map +1 -1
- package/dist/esm/pushed-config-error-overlay/index.d.ts +1 -1
- package/dist/esm/pushed-config-error-overlay/index.d.ts.map +1 -1
- package/dist/esm/pushed-config-error-overlay/index.js +1 -2
- package/dist/esm/pushed-config-error-overlay/index.js.map +1 -1
- package/dist/esm/pushed-config-error-overlay/index.test.js +2 -3
- package/dist/esm/pushed-config-error-overlay/index.test.js.map +1 -1
- package/dist/esm/utils/url.d.ts.map +1 -1
- package/dist/esm/utils/url.js +1 -2
- package/dist/esm/utils/url.js.map +1 -1
- package/dist/generated/env.js +2 -3
- package/dist/generated/env.js.map +1 -1
- package/dist/global.d.ts.map +1 -1
- package/dist/in-page-ui/base-styles.d.ts.map +1 -1
- package/dist/in-page-ui/base-styles.js +2 -3
- package/dist/in-page-ui/base-styles.js.map +1 -1
- package/dist/in-page-ui/dom.d.ts.map +1 -1
- package/dist/in-page-ui/dom.js +2 -3
- package/dist/in-page-ui/dom.js.map +1 -1
- package/dist/index-BKZ67WMa.d.ts +1 -0
- package/dist/index-BVVgDSdq.d.ts +1 -0
- package/dist/index-Bo5DpPFH.d.ts +17 -0
- package/dist/index-Bo5DpPFH.d.ts.map +1 -0
- package/dist/index-CBDR4cNg.d.ts +206 -0
- package/dist/index-CBDR4cNg.d.ts.map +1 -0
- package/dist/index-ClBTM-St.d.ts +40 -0
- package/dist/index-ClBTM-St.d.ts.map +1 -0
- package/dist/index-D7Vdg30O.d.ts +27 -0
- package/dist/index-D7Vdg30O.d.ts.map +1 -0
- package/dist/index-DLPccxVU.d.ts +14 -0
- package/dist/index-DLPccxVU.d.ts.map +1 -0
- package/dist/index-DN04TjLM.d.ts +43 -0
- package/dist/index-DN04TjLM.d.ts.map +1 -0
- package/dist/index-DSzMf-rS.d.ts +43 -0
- package/dist/index-DSzMf-rS.d.ts.map +1 -0
- package/dist/index-Dq4xsW7_.d.ts +106 -0
- package/dist/index-Dq4xsW7_.d.ts.map +1 -0
- package/dist/index-O3hJsHSl.d.ts +11 -0
- package/dist/index-O3hJsHSl.d.ts.map +1 -0
- package/dist/index-TUtJOdMm.d.ts +42 -0
- package/dist/index-TUtJOdMm.d.ts.map +1 -0
- package/dist/index-ZiEXZJBF.d.ts +129 -0
- package/dist/index-ZiEXZJBF.d.ts.map +1 -0
- package/dist/index-fOYcs4NH.d.ts +53 -0
- package/dist/{lib/hexclave-app/api-keys/index.d.ts.map → index-fOYcs4NH.d.ts.map} +1 -1
- package/dist/index-hYjsGKNc.d.ts +66 -0
- package/dist/index-hYjsGKNc.d.ts.map +1 -0
- package/dist/index.d.ts +11 -16
- package/dist/index.js +23 -23
- package/dist/integrations/convex/component/convex.config.d.ts +1 -1
- package/dist/integrations/convex/component/convex.config.d.ts.map +1 -1
- package/dist/integrations/convex/component/convex.config.js +2 -5
- package/dist/integrations/convex/component/convex.config.js.map +1 -1
- package/dist/integrations/convex.js +2 -4
- package/dist/integrations/convex.js.map +1 -1
- package/dist/lib/auth.js +2 -4
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/auth.test.js +1 -3
- package/dist/lib/auth.test.js.map +1 -1
- package/dist/lib/cookie.d.ts +2 -42
- package/dist/lib/cookie.js +7 -25
- package/dist/lib/cookie.js.map +1 -1
- package/dist/lib/hexclave-app/api-keys/index.d.ts +2 -53
- package/dist/lib/hexclave-app/api-keys/index.js +2 -4
- package/dist/lib/hexclave-app/api-keys/index.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +2 -376
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.js +2 -4
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +1 -3
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +2 -554
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +4 -5
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.oauth-prefetch.test.js +1 -3
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.oauth-prefetch.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/common.d.ts +2 -65
- package/dist/lib/hexclave-app/apps/implementations/common.js +7 -9
- package/dist/lib/hexclave-app/apps/implementations/common.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.js +2 -4
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.test.js +1 -3
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/index.d.ts +1 -3
- package/dist/lib/hexclave-app/apps/implementations/index.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/index.js +2 -4
- package/dist/lib/hexclave-app/apps/implementations/index.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/redirect-page-urls.d.ts +2 -35
- package/dist/lib/hexclave-app/apps/implementations/redirect-page-urls.js +2 -4
- package/dist/lib/hexclave-app/apps/implementations/redirect-page-urls.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +2 -217
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js +2 -4
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/session-refresh-subscription.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/session-refresh-subscription.js +2 -3
- package/dist/lib/hexclave-app/apps/implementations/session-refresh-subscription.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/session-refresh-subscription.test.js +1 -3
- package/dist/lib/hexclave-app/apps/implementations/session-refresh-subscription.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/session-replay.d.ts +2 -122
- package/dist/lib/hexclave-app/apps/implementations/session-replay.js +14 -9
- package/dist/lib/hexclave-app/apps/implementations/session-replay.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/session-replay.test.js +135 -3
- package/dist/lib/hexclave-app/apps/implementations/session-replay.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/index.d.ts +1 -3
- package/dist/lib/hexclave-app/apps/index.js +32 -34
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts +3 -255
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.js +3 -6
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.js.map +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts +2 -219
- package/dist/lib/hexclave-app/apps/interfaces/client-app.js +3 -6
- package/dist/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts +2 -133
- package/dist/lib/hexclave-app/apps/interfaces/server-app.js +3 -6
- package/dist/lib/hexclave-app/apps/interfaces/server-app.js.map +1 -1
- package/dist/lib/hexclave-app/common.d.ts +2 -174
- package/dist/lib/hexclave-app/common.js +2 -3
- package/dist/lib/hexclave-app/common.js.map +1 -1
- package/dist/lib/hexclave-app/connected-accounts/index.d.ts +2 -40
- package/dist/lib/hexclave-app/contact-channels/index.d.ts +2 -43
- package/dist/lib/hexclave-app/contact-channels/index.js +2 -3
- package/dist/lib/hexclave-app/contact-channels/index.js.map +1 -1
- package/dist/lib/hexclave-app/customers/index.d.ts +2 -129
- package/dist/lib/hexclave-app/data-vault/index.d.ts +2 -14
- package/dist/lib/hexclave-app/email/index.d.ts +2 -206
- package/dist/lib/hexclave-app/email-templates/index.d.ts +2 -17
- package/dist/lib/hexclave-app/email-templates/index.js +2 -3
- package/dist/lib/hexclave-app/email-templates/index.js.map +1 -1
- package/dist/lib/hexclave-app/index.d.ts +10 -15
- package/dist/lib/hexclave-app/index.js +49 -51
- package/dist/lib/hexclave-app/internal-api-keys/index.d.ts +2 -42
- package/dist/lib/hexclave-app/internal-api-keys/index.js +2 -3
- package/dist/lib/hexclave-app/internal-api-keys/index.js.map +1 -1
- package/dist/lib/hexclave-app/notification-categories/index.d.ts +2 -11
- package/dist/lib/hexclave-app/permissions/index.d.ts +2 -43
- package/dist/lib/hexclave-app/permissions/index.js +2 -3
- package/dist/lib/hexclave-app/permissions/index.js.map +1 -1
- package/dist/lib/hexclave-app/plan-usage/index.d.ts +2 -27
- package/dist/lib/hexclave-app/project-configs/index.d.ts +2 -106
- package/dist/lib/hexclave-app/projects/index.d.ts +2 -152
- package/dist/lib/hexclave-app/projects/index.js +6 -3
- package/dist/lib/hexclave-app/projects/index.js.map +1 -1
- package/dist/lib/hexclave-app/session-replays/index.d.ts +2 -66
- package/dist/lib/hexclave-app/teams/index.d.ts +2 -164
- package/dist/lib/hexclave-app/teams/index.js +2 -3
- package/dist/lib/hexclave-app/teams/index.js.map +1 -1
- package/dist/lib/hexclave-app/url-targets.d.ts +2 -39
- package/dist/lib/hexclave-app/url-targets.js +3 -5
- package/dist/lib/hexclave-app/url-targets.js.map +1 -1
- package/dist/lib/hexclave-app/url-targets.test.js +1 -3
- package/dist/lib/hexclave-app/url-targets.test.js.map +1 -1
- package/dist/lib/hexclave-app/users/index.d.ts +2 -357
- package/dist/lib/hexclave-app/users/index.js +2 -4
- package/dist/lib/hexclave-app/users/index.js.map +1 -1
- package/dist/pushed-config-error-overlay/index.d.ts +1 -2
- package/dist/pushed-config-error-overlay/index.d.ts.map +1 -1
- package/dist/pushed-config-error-overlay/index.js +2 -4
- package/dist/pushed-config-error-overlay/index.js.map +1 -1
- package/dist/pushed-config-error-overlay/index.test.js +1 -3
- package/dist/pushed-config-error-overlay/index.test.js.map +1 -1
- package/dist/redirect-page-urls-elbPx2J1.d.ts +35 -0
- package/dist/redirect-page-urls-elbPx2J1.d.ts.map +1 -0
- package/dist/{chunk-BE-pF4vm.js → rolldown-runtime-D6vf50IK.js} +12 -18
- package/dist/session-replay-8_3WCrsZ.d.ts +122 -0
- package/dist/session-replay-8_3WCrsZ.d.ts.map +1 -0
- package/dist/{storage-CKsyME2q.d.ts → storage-CA867GtX.d.ts} +1 -1
- package/dist/storage-CA867GtX.d.ts.map +1 -0
- package/dist/url-targets-BJgkwPGW.d.ts +39 -0
- package/dist/url-targets-BJgkwPGW.d.ts.map +1 -0
- package/dist/utils/url.d.ts.map +1 -1
- package/dist/utils/url.js +2 -4
- package/dist/utils/url.js.map +1 -1
- package/package.json +4 -4
- package/src/lib/hexclave-app/apps/implementations/session-replay.test.ts +160 -1
- package/src/lib/hexclave-app/apps/implementations/session-replay.ts +38 -12
- package/dist/lib/cookie.d.ts.map +0 -1
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +0 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +0 -1
- package/dist/lib/hexclave-app/apps/implementations/common.d.ts.map +0 -1
- package/dist/lib/hexclave-app/apps/implementations/redirect-page-urls.d.ts.map +0 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts.map +0 -1
- package/dist/lib/hexclave-app/apps/implementations/session-replay.d.ts.map +0 -1
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts.map +0 -1
- package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts.map +0 -1
- package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts.map +0 -1
- package/dist/lib/hexclave-app/common.d.ts.map +0 -1
- package/dist/lib/hexclave-app/connected-accounts/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/contact-channels/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/customers/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/data-vault/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/email/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/email-templates/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/internal-api-keys/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/notification-categories/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/permissions/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/plan-usage/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/project-configs/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/projects/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/session-replays/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/teams/index.d.ts.map +0 -1
- package/dist/lib/hexclave-app/url-targets.d.ts.map +0 -1
- package/dist/lib/hexclave-app/users/index.d.ts.map +0 -1
- package/dist/storage-CKsyME2q.d.ts.map +0 -1
|
@@ -1,122 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
//#region src/lib/hexclave-app/apps/implementations/session-replay.d.ts
|
|
4
|
-
type AnalyticsReplayOptions = {
|
|
5
|
-
/**
|
|
6
|
-
* Whether session replays are enabled.
|
|
7
|
-
*
|
|
8
|
-
* @default true
|
|
9
|
-
*/
|
|
10
|
-
enabled?: boolean;
|
|
11
|
-
/**
|
|
12
|
-
* Whether to mask the content of all `<input>` elements.
|
|
13
|
-
*
|
|
14
|
-
* @default true
|
|
15
|
-
*/
|
|
16
|
-
maskAllInputs?: boolean;
|
|
17
|
-
/**
|
|
18
|
-
* A CSS class name or RegExp. Elements with a matching class will be blocked
|
|
19
|
-
* (replaced with a placeholder in the recording).
|
|
20
|
-
*
|
|
21
|
-
* @default undefined
|
|
22
|
-
*/
|
|
23
|
-
blockClass?: string | RegExp;
|
|
24
|
-
/**
|
|
25
|
-
* A CSS selector string. Elements matching this selector will be blocked
|
|
26
|
-
* (replaced with a placeholder in the recording).
|
|
27
|
-
*
|
|
28
|
-
* @default undefined
|
|
29
|
-
*/
|
|
30
|
-
blockSelector?: string;
|
|
31
|
-
};
|
|
32
|
-
type AnalyticsOptions = {
|
|
33
|
-
/**
|
|
34
|
-
* Whether SDK-managed analytics capture is enabled.
|
|
35
|
-
*
|
|
36
|
-
* @default true
|
|
37
|
-
*/
|
|
38
|
-
enabled?: boolean;
|
|
39
|
-
/**
|
|
40
|
-
* Options for session replay recording. Replays are enabled by default;
|
|
41
|
-
* set `enabled: false` to opt out.
|
|
42
|
-
*/
|
|
43
|
-
replays?: AnalyticsReplayOptions;
|
|
44
|
-
};
|
|
45
|
-
declare function getSessionReplayOptions(analyticsOptions: AnalyticsOptions | undefined): AnalyticsReplayOptions;
|
|
46
|
-
/**
|
|
47
|
-
* Converts AnalyticsOptions to a JSON-safe representation.
|
|
48
|
-
* RegExp blockClass values are serialized as `{ __regexp, __flags }` objects.
|
|
49
|
-
* The return type is AnalyticsOptions to keep StackClientAppJson simple;
|
|
50
|
-
* the actual runtime value is JSON-safe.
|
|
51
|
-
*/
|
|
52
|
-
declare function analyticsOptionsToJson(options: AnalyticsOptions | undefined): AnalyticsOptions | undefined;
|
|
53
|
-
/**
|
|
54
|
-
* Reconstructs AnalyticsOptions from a JSON-deserialized value.
|
|
55
|
-
* Converts `{ __regexp, __flags }` objects back to RegExp instances.
|
|
56
|
-
*/
|
|
57
|
-
declare function analyticsOptionsFromJson(json: AnalyticsOptions | undefined): AnalyticsOptions | undefined;
|
|
58
|
-
type StoredSession = {
|
|
59
|
-
session_id: string;
|
|
60
|
-
created_at_ms: number;
|
|
61
|
-
last_activity_ms: number;
|
|
62
|
-
};
|
|
63
|
-
declare function safeParseStoredSession(raw: string | null): StoredSession | null;
|
|
64
|
-
declare function makeStorageKey(projectId: string): string;
|
|
65
|
-
declare function makeLegacyStorageKey(projectId: string): string;
|
|
66
|
-
declare function generateUuid(): `${string}-${string}-${string}-${string}-${string}`;
|
|
67
|
-
declare function getOrRotateSession(options: {
|
|
68
|
-
key: string;
|
|
69
|
-
legacyKey?: string;
|
|
70
|
-
nowMs: number;
|
|
71
|
-
}): StoredSession;
|
|
72
|
-
type SessionRecorderDeps = {
|
|
73
|
-
projectId: string;
|
|
74
|
-
sendBatch: (body: string, options: {
|
|
75
|
-
keepalive: boolean;
|
|
76
|
-
}) => Promise<Result<Response, Error>>;
|
|
77
|
-
};
|
|
78
|
-
declare function isAnalyticsNotEnabledError(error: unknown): boolean;
|
|
79
|
-
/**
|
|
80
|
-
* Whether the error looks like a network failure caused by an ad blocker or
|
|
81
|
-
* similar extension blocking analytics requests. These are expected in
|
|
82
|
-
* production and should be silently ignored rather than logged as warnings.
|
|
83
|
-
*/
|
|
84
|
-
declare function isAdBlockerNetworkError(error: unknown): boolean;
|
|
85
|
-
declare class SessionRecorder {
|
|
86
|
-
private _started;
|
|
87
|
-
private _cancelled;
|
|
88
|
-
private _disabled;
|
|
89
|
-
private _stopRecording;
|
|
90
|
-
private _detachListeners;
|
|
91
|
-
private _flushTimer;
|
|
92
|
-
private _events;
|
|
93
|
-
private _eventSizes;
|
|
94
|
-
private _approxBytes;
|
|
95
|
-
private _lastPersistActivity;
|
|
96
|
-
private _recording;
|
|
97
|
-
private _rrwebModule;
|
|
98
|
-
private _lastBrowserSessionId;
|
|
99
|
-
private _takingSnapshot;
|
|
100
|
-
private _flushInProgress;
|
|
101
|
-
private readonly _sessionReplaySegmentId;
|
|
102
|
-
private readonly _storageKey;
|
|
103
|
-
private readonly _legacyStorageKey;
|
|
104
|
-
private readonly _deps;
|
|
105
|
-
private readonly _replayOptions;
|
|
106
|
-
constructor(deps: SessionRecorderDeps, replayOptions: AnalyticsReplayOptions);
|
|
107
|
-
/**
|
|
108
|
-
* Starts recording. Idempotent — calling multiple times is safe.
|
|
109
|
-
*/
|
|
110
|
-
start(): void;
|
|
111
|
-
stop(): void;
|
|
112
|
-
clearBuffer(): void;
|
|
113
|
-
private _persistActivity;
|
|
114
|
-
private _flush;
|
|
115
|
-
private _disable;
|
|
116
|
-
private _startRecording;
|
|
117
|
-
private _stopCurrentRecording;
|
|
118
|
-
private _tick;
|
|
119
|
-
}
|
|
120
|
-
//#endregion
|
|
121
|
-
export { AnalyticsOptions, AnalyticsReplayOptions, SessionRecorder, SessionRecorderDeps, StoredSession, analyticsOptionsFromJson, analyticsOptionsToJson, generateUuid, getOrRotateSession, getSessionReplayOptions, isAdBlockerNetworkError, isAnalyticsNotEnabledError, makeLegacyStorageKey, makeStorageKey, safeParseStoredSession };
|
|
122
|
-
//# sourceMappingURL=session-replay.d.ts.map
|
|
1
|
+
import { a as StoredSession, c as generateUuid, d as isAdBlockerNetworkError, f as isAnalyticsNotEnabledError, h as safeParseStoredSession, i as SessionRecorderDeps, l as getOrRotateSession, m as makeStorageKey, n as AnalyticsReplayOptions, o as analyticsOptionsFromJson, p as makeLegacyStorageKey, r as SessionRecorder, s as analyticsOptionsToJson, t as AnalyticsOptions, u as getSessionReplayOptions } from "../../../../session-replay-8_3WCrsZ.js";
|
|
2
|
+
export { AnalyticsOptions, AnalyticsReplayOptions, SessionRecorder, SessionRecorderDeps, StoredSession, analyticsOptionsFromJson, analyticsOptionsToJson, generateUuid, getOrRotateSession, getSessionReplayOptions, isAdBlockerNetworkError, isAnalyticsNotEnabledError, makeLegacyStorageKey, makeStorageKey, safeParseStoredSession };
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value:
|
|
2
|
-
const require_chunk = require('../../../../chunk-BE-pF4vm.js');
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
2
|
let _hexclave_shared_dist_utils_promises = require("@hexclave/shared/dist/utils/promises");
|
|
4
3
|
let _hexclave_shared_dist_utils_errors = require("@hexclave/shared/dist/utils/errors");
|
|
5
4
|
let _hexclave_shared_dist_utils_results = require("@hexclave/shared/dist/utils/results");
|
|
6
5
|
let _hexclave_shared_dist_utils_env = require("@hexclave/shared/dist/utils/env");
|
|
7
6
|
let _hexclave_shared_dist_known_errors = require("@hexclave/shared/dist/known-errors");
|
|
8
|
-
|
|
9
7
|
//#region src/lib/hexclave-app/apps/implementations/session-replay.ts
|
|
10
8
|
function getSessionReplayOptions(analyticsOptions) {
|
|
11
9
|
return {
|
|
@@ -59,7 +57,8 @@ const IDLE_TTL_MS = 180 * 1e3;
|
|
|
59
57
|
const FLUSH_INTERVAL_MS = 5e3;
|
|
60
58
|
const MAX_EVENTS_PER_BATCH = 200;
|
|
61
59
|
const MAX_APPROX_BYTES_PER_BATCH = 512e3;
|
|
62
|
-
const
|
|
60
|
+
const MAX_BATCH_UNCOMPRESSED_BYTES = 9e5;
|
|
61
|
+
const MAX_SINGLE_EVENT_BYTES = 8 * 1024 * 1024 - 1024;
|
|
63
62
|
const textEncoder = new TextEncoder();
|
|
64
63
|
function safeParseStoredSession(raw) {
|
|
65
64
|
if (!raw) return null;
|
|
@@ -183,13 +182,14 @@ var SessionRecorder = class {
|
|
|
183
182
|
this._events = [];
|
|
184
183
|
this._eventSizes = [];
|
|
185
184
|
this._approxBytes = 0;
|
|
185
|
+
const maxSingleEventBytes = options.keepalive ? MAX_BATCH_UNCOMPRESSED_BYTES : MAX_SINGLE_EVENT_BYTES;
|
|
186
186
|
this._flushInProgress = true;
|
|
187
187
|
try {
|
|
188
188
|
let offset = 0;
|
|
189
189
|
while (offset < allEvents.length) {
|
|
190
190
|
const firstSize = allSizes[offset] ?? (0, _hexclave_shared_dist_utils_errors.throwErr)("_eventSizes out of sync with _events — this should never happen");
|
|
191
|
-
if (firstSize >
|
|
192
|
-
(0, _hexclave_shared_dist_utils_errors.captureWarning)("SessionRecorder.flush", /* @__PURE__ */ new Error(`Dropping oversized session replay event (${firstSize} bytes > ${
|
|
191
|
+
if (firstSize > maxSingleEventBytes) {
|
|
192
|
+
(0, _hexclave_shared_dist_utils_errors.captureWarning)("SessionRecorder.flush", /* @__PURE__ */ new Error(`Dropping oversized session replay event (${firstSize} bytes > ${maxSingleEventBytes} byte limit); it cannot be sent without exceeding the server's body limit.`));
|
|
193
193
|
offset += 1;
|
|
194
194
|
continue;
|
|
195
195
|
}
|
|
@@ -197,7 +197,7 @@ var SessionRecorder = class {
|
|
|
197
197
|
let batchEnd = offset;
|
|
198
198
|
for (let i = offset; i < allEvents.length; i++) {
|
|
199
199
|
const nextSize = allSizes[i] ?? (0, _hexclave_shared_dist_utils_errors.throwErr)("_eventSizes out of sync with _events — this should never happen");
|
|
200
|
-
if (batchBytes + nextSize >
|
|
200
|
+
if (batchBytes + nextSize > MAX_BATCH_UNCOMPRESSED_BYTES && batchEnd > offset) break;
|
|
201
201
|
batchBytes += nextSize;
|
|
202
202
|
batchEnd = i + 1;
|
|
203
203
|
}
|
|
@@ -223,7 +223,12 @@ var SessionRecorder = class {
|
|
|
223
223
|
return;
|
|
224
224
|
}
|
|
225
225
|
if (!res.data.ok) {
|
|
226
|
-
|
|
226
|
+
const droppedCount = batchEvents.length + (allEvents.length - offset);
|
|
227
|
+
if (res.data.status === 413) {
|
|
228
|
+
(0, _hexclave_shared_dist_utils_errors.captureWarning)("SessionRecorder.flush", /* @__PURE__ */ new Error(`Session replay batch rejected with 413 (payload too large) despite client-side size caps; dropping ${droppedCount} buffered event(s). A poorly-compressible event likely exceeded the server's body limit after gzip.`));
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
(0, _hexclave_shared_dist_utils_errors.captureWarning)("SessionRecorder.flush", /* @__PURE__ */ new Error(`SessionRecorder flush failed (dropping ${droppedCount} buffered event(s)): ${res.data.status} ${await res.data.text()}`));
|
|
227
232
|
return;
|
|
228
233
|
}
|
|
229
234
|
}
|
|
@@ -305,7 +310,6 @@ var SessionRecorder = class {
|
|
|
305
310
|
if (this._events.length > 0) (0, _hexclave_shared_dist_utils_promises.runAsynchronously)(() => this._flush({ keepalive: false }));
|
|
306
311
|
}
|
|
307
312
|
};
|
|
308
|
-
|
|
309
313
|
//#endregion
|
|
310
314
|
exports.SessionRecorder = SessionRecorder;
|
|
311
315
|
exports.analyticsOptionsFromJson = analyticsOptionsFromJson;
|
|
@@ -318,4 +322,5 @@ exports.isAnalyticsNotEnabledError = isAnalyticsNotEnabledError;
|
|
|
318
322
|
exports.makeLegacyStorageKey = makeLegacyStorageKey;
|
|
319
323
|
exports.makeStorageKey = makeStorageKey;
|
|
320
324
|
exports.safeParseStoredSession = safeParseStoredSession;
|
|
325
|
+
|
|
321
326
|
//# sourceMappingURL=session-replay.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-replay.js","names":["KnownErrors","Result"],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/session-replay.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { KnownErrors } from \"@hexclave/shared/dist/known-errors\";\nimport { isBrowserLike } from \"@hexclave/shared/dist/utils/env\";\nimport { captureWarning, throwErr } from \"@hexclave/shared/dist/utils/errors\";\nimport { runAsynchronously } from \"@hexclave/shared/dist/utils/promises\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\n\nexport type AnalyticsReplayOptions = {\n /**\n * Whether session replays are enabled.\n *\n * @default true\n */\n enabled?: boolean,\n /**\n * Whether to mask the content of all `<input>` elements.\n *\n * @default true\n */\n maskAllInputs?: boolean,\n /**\n * A CSS class name or RegExp. Elements with a matching class will be blocked\n * (replaced with a placeholder in the recording).\n *\n * @default undefined\n */\n blockClass?: string | RegExp,\n /**\n * A CSS selector string. Elements matching this selector will be blocked\n * (replaced with a placeholder in the recording).\n *\n * @default undefined\n */\n blockSelector?: string,\n};\n\nexport type AnalyticsOptions = {\n /**\n * Whether SDK-managed analytics capture is enabled.\n *\n * @default true\n */\n enabled?: boolean,\n /**\n * Options for session replay recording. Replays are enabled by default;\n * set `enabled: false` to opt out.\n */\n replays?: AnalyticsReplayOptions,\n};\n\nexport function getSessionReplayOptions(analyticsOptions: AnalyticsOptions | undefined): AnalyticsReplayOptions {\n return {\n ...analyticsOptions?.replays,\n enabled: analyticsOptions?.replays?.enabled ?? true,\n };\n}\n\n/**\n * Converts AnalyticsOptions to a JSON-safe representation.\n * RegExp blockClass values are serialized as `{ __regexp, __flags }` objects.\n * The return type is AnalyticsOptions to keep StackClientAppJson simple;\n * the actual runtime value is JSON-safe.\n */\nexport function analyticsOptionsToJson(options: AnalyticsOptions | undefined): AnalyticsOptions | undefined {\n if (!options?.replays?.blockClass) return options;\n const { blockClass, ...rest } = options.replays;\n if (!(blockClass instanceof RegExp)) return options;\n return {\n ...options,\n replays: {\n ...rest,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n blockClass: { __regexp: blockClass.source, __flags: blockClass.flags } as any,\n },\n };\n}\n\n/**\n * Reconstructs AnalyticsOptions from a JSON-deserialized value.\n * Converts `{ __regexp, __flags }` objects back to RegExp instances.\n */\nexport function analyticsOptionsFromJson(json: AnalyticsOptions | undefined): AnalyticsOptions | undefined {\n if (!json?.replays?.blockClass) return json;\n const { blockClass, ...rest } = json.replays;\n if (typeof blockClass === 'object' && '__regexp' in blockClass) {\n const bc = blockClass as unknown as { __regexp: string, __flags: string };\n return {\n ...json,\n replays: {\n ...rest,\n blockClass: new RegExp(bc.__regexp, bc.__flags),\n },\n };\n }\n return json;\n}\n\n// ---------- Recording internals ----------\n\n// Hexclave rebrand: canonical localStorage prefix (colon delimiters preserved).\nconst LOCAL_STORAGE_PREFIX = \"hexclave:session-replay:v1\";\n// Hexclave rebrand: legacy prefix — dual-read only, so a recording session active\n// across an SDK upgrade is not orphaned. Never written.\nconst LEGACY_LOCAL_STORAGE_PREFIX = \"stack:session-replay:v1\";\nconst IDLE_TTL_MS = 3 * 60 * 1000;\n\nconst FLUSH_INTERVAL_MS = 5_000;\nconst MAX_EVENTS_PER_BATCH = 200;\nconst MAX_APPROX_BYTES_PER_BATCH = 512_000;\n// The server rejects payloads > 1MB. Stay well under to account for JSON\n// envelope overhead (browser_session_id, timestamps, wrapper keys, etc.).\nconst MAX_FLUSH_PAYLOAD_BYTES = 900_000;\n\n// Reused across the emit hot path to avoid per-event allocation.\nconst textEncoder = new TextEncoder();\n\nexport type StoredSession = {\n session_id: string,\n created_at_ms: number,\n last_activity_ms: number,\n};\n\nexport function safeParseStoredSession(raw: string | null): StoredSession | null {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw);\n if (typeof parsed !== \"object\" || parsed === null) return null;\n if (typeof parsed.session_id !== \"string\") return null;\n if (typeof parsed.created_at_ms !== \"number\") return null;\n if (typeof parsed.last_activity_ms !== \"number\") return null;\n return parsed as StoredSession;\n } catch {\n return null;\n }\n}\n\nexport function makeStorageKey(projectId: string) {\n return `${LOCAL_STORAGE_PREFIX}:${projectId}`;\n}\n\n// Hexclave rebrand: legacy key, dual-read only (never written).\nexport function makeLegacyStorageKey(projectId: string) {\n return `${LEGACY_LOCAL_STORAGE_PREFIX}:${projectId}`;\n}\n\nexport function generateUuid() {\n return crypto.randomUUID();\n}\n\nexport function getOrRotateSession(options: { key: string, legacyKey?: string, nowMs: number }): StoredSession {\n // Hexclave rebrand: prefer the new key; fall back to the legacy key so a\n // recording session active across an SDK upgrade is not orphaned.\n const existing = safeParseStoredSession(localStorage.getItem(options.key))\n ?? (options.legacyKey ? safeParseStoredSession(localStorage.getItem(options.legacyKey)) : null);\n if (existing && options.nowMs - existing.last_activity_ms <= IDLE_TTL_MS) {\n return existing;\n }\n const next: StoredSession = {\n session_id: generateUuid(),\n created_at_ms: options.nowMs,\n last_activity_ms: options.nowMs,\n };\n localStorage.setItem(options.key, JSON.stringify(next));\n return next;\n}\n\nexport type SessionRecorderDeps = {\n projectId: string,\n sendBatch: (body: string, options: { keepalive: boolean }) => Promise<Result<Response, Error>>,\n};\n\nexport function isAnalyticsNotEnabledError(error: unknown): boolean {\n return KnownErrors.AnalyticsNotEnabled.isInstance(error);\n}\n\n/**\n * Whether the error looks like a network failure caused by an ad blocker or\n * similar extension blocking analytics requests. These are expected in\n * production and should be silently ignored rather than logged as warnings.\n */\nexport function isAdBlockerNetworkError(error: unknown): boolean {\n if (error instanceof Error) {\n return error.message.includes(\"Failed to fetch\")\n || error.message.includes(\"NetworkError\")\n || error.message.includes(\"Load failed\")\n || error.message.includes(\"network connection\");\n }\n return false;\n}\n\nexport class SessionRecorder {\n private _started = false;\n private _cancelled = false;\n private _disabled = false;\n private _stopRecording: (() => void) | null = null;\n private _detachListeners: (() => void) | null = null;\n private _flushTimer: ReturnType<typeof setInterval> | null = null;\n private _events: unknown[] = [];\n private _eventSizes: number[] = [];\n private _approxBytes = 0;\n private _lastPersistActivity = 0;\n private _recording = false;\n private _rrwebModule: typeof import(\"rrweb\") | null = null;\n private _lastBrowserSessionId: string | null = null;\n private _takingSnapshot = false;\n private _flushInProgress = false;\n private readonly _sessionReplaySegmentId: string;\n private readonly _storageKey: string;\n // Hexclave rebrand: legacy key used for dual-read fallback only.\n private readonly _legacyStorageKey: string;\n private readonly _deps: SessionRecorderDeps;\n private readonly _replayOptions: AnalyticsReplayOptions;\n\n constructor(deps: SessionRecorderDeps, replayOptions: AnalyticsReplayOptions) {\n this._deps = deps;\n this._replayOptions = replayOptions;\n this._sessionReplaySegmentId = generateUuid();\n this._storageKey = makeStorageKey(deps.projectId);\n this._legacyStorageKey = makeLegacyStorageKey(deps.projectId);\n }\n\n /**\n * Starts recording. Idempotent — calling multiple times is safe.\n */\n start() {\n if (this._started) return;\n if (!isBrowserLike()) return;\n this._started = true;\n\n // Kick off rrweb recording\n runAsynchronously(() => this._startRecording(), { noErrorLogging: true });\n\n // Periodic flush\n this._flushTimer = setInterval(() => this._tick(), FLUSH_INTERVAL_MS);\n }\n\n stop() {\n this._cancelled = true;\n if (this._flushTimer !== null) {\n clearInterval(this._flushTimer);\n this._flushTimer = null;\n }\n // Flush remaining events before cleanup\n runAsynchronously(() => this._flush({ keepalive: true }));\n this._stopCurrentRecording();\n }\n\n clearBuffer() {\n this._events = [];\n this._eventSizes = [];\n this._approxBytes = 0;\n }\n\n private _persistActivity(nowMs: number): StoredSession {\n const stored = getOrRotateSession({ key: this._storageKey, legacyKey: this._legacyStorageKey, nowMs });\n if (nowMs - this._lastPersistActivity < 5_000) return stored;\n this._lastPersistActivity = nowMs;\n const updated: StoredSession = { ...stored, last_activity_ms: nowMs };\n localStorage.setItem(this._storageKey, JSON.stringify(updated));\n return stored;\n }\n\n private async _flush(options: { keepalive: boolean }) {\n if (this._disabled) return;\n if (this._events.length === 0) return;\n // Prevent concurrent in-flight HTTP requests. When a flush is already\n // in-flight, a second batch could race on the server (both call\n // findRecentSessionReplay before either upsert commits) and create\n // duplicate SessionReplay records. Events stay in _events and will be\n // picked up by the next tick or batch-size check.\n if (this._flushInProgress) return;\n\n const nowMs = Date.now();\n const stored = getOrRotateSession({ key: this._storageKey, legacyKey: this._legacyStorageKey, nowMs });\n\n // Capture all buffered events upfront (before any await) so that\n // stop() / _stopCurrentRecording() clearing this._events cannot race\n // with the async send loop below and silently discard overflow batches.\n const allEvents = this._events;\n const allSizes = this._eventSizes;\n this._events = [];\n this._eventSizes = [];\n this._approxBytes = 0;\n\n this._flushInProgress = true;\n try {\n let offset = 0;\n while (offset < allEvents.length) {\n // Build a batch that fits under the server's payload limit.\n // When _flushInProgress blocked earlier flushes, events can accumulate\n // well past MAX_APPROX_BYTES_PER_BATCH; sending them all at once would\n // exceed the server's 1MB body limit (413).\n // A single event over the limit can't be sent (rrweb events aren't splittable); drop it and move on.\n const firstSize = allSizes[offset] ?? throwErr(\"_eventSizes out of sync with _events — this should never happen\");\n if (firstSize > MAX_FLUSH_PAYLOAD_BYTES) {\n captureWarning(\n \"SessionRecorder.flush\",\n new Error(`Dropping oversized session replay event (${firstSize} bytes > ${MAX_FLUSH_PAYLOAD_BYTES} byte limit); it cannot be sent without a 413.`),\n );\n offset += 1;\n continue;\n }\n\n let batchBytes = 0;\n let batchEnd = offset;\n for (let i = offset; i < allEvents.length; i++) {\n const nextSize = allSizes[i] ?? throwErr(\"_eventSizes out of sync with _events — this should never happen\");\n if (batchBytes + nextSize > MAX_FLUSH_PAYLOAD_BYTES && batchEnd > offset) break;\n batchBytes += nextSize;\n batchEnd = i + 1;\n }\n\n const batchEvents = allEvents.slice(offset, batchEnd);\n offset = batchEnd;\n\n const batchId = generateUuid();\n const payload = {\n browser_session_id: stored.session_id,\n session_replay_segment_id: this._sessionReplaySegmentId,\n batch_id: batchId,\n started_at_ms: stored.created_at_ms,\n sent_at_ms: nowMs,\n events: batchEvents,\n };\n\n const res = await this._deps.sendBatch(\n JSON.stringify(payload),\n { keepalive: options.keepalive },\n );\n\n if (res.status === \"error\") {\n if (isAnalyticsNotEnabledError(res.error)) {\n this._disable();\n return;\n }\n // Ad blockers commonly block analytics endpoints, causing network\n // errors. These are expected and should not pollute the console.\n if (isAdBlockerNetworkError(res.error)) {\n return;\n }\n captureWarning(\"SessionRecorder.flush\", res.error);\n return;\n }\n\n if (!res.data.ok) {\n captureWarning(\"SessionRecorder.flush\", new Error(`SessionRecorder flush failed: ${res.data.status} ${await res.data.text()}`));\n return;\n }\n }\n } finally {\n this._flushInProgress = false;\n }\n }\n\n private _disable() {\n this._disabled = true;\n this.clearBuffer();\n if (this._flushTimer !== null) {\n clearInterval(this._flushTimer);\n this._flushTimer = null;\n }\n this._stopCurrentRecording();\n }\n\n private async _startRecording() {\n if (this._recording || this._cancelled) return;\n\n if (!this._rrwebModule) {\n const rrwebImport = await Result.fromPromise(import(\"rrweb\"));\n if (rrwebImport.status === \"error\") {\n console.warn(\"SessionRecorder: rrweb import failed. Is rrweb installed?\", rrwebImport.error);\n return;\n }\n this._rrwebModule = rrwebImport.data;\n }\n\n // cancelled may change during the await above\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this._cancelled) return;\n\n this._stopRecording = this._rrwebModule.record({\n emit: (event) => {\n const nowMs = Date.now();\n const stored = this._persistActivity(nowMs);\n\n // Detect session rotation: after 3+ minutes idle, getOrRotateSession\n // creates a new session ID. We need to inject a FullSnapshot so the\n // new server-side SessionReplay record is playable.\n if (this._lastBrowserSessionId === null) {\n this._lastBrowserSessionId = stored.session_id;\n } else if (stored.session_id !== this._lastBrowserSessionId && !this._takingSnapshot) {\n this._lastBrowserSessionId = stored.session_id;\n // Inject a FullSnapshot for the new session (calls emit synchronously)\n this._takingSnapshot = true;\n try {\n this._rrwebModule!.record.takeFullSnapshot();\n } finally {\n this._takingSnapshot = false;\n }\n }\n\n // Measure UTF-8 byte length to match the server's byte limit (.length counts UTF-16 units, undercounting multibyte content).\n const eventSize = textEncoder.encode(JSON.stringify(event)).byteLength;\n this._events.push(event);\n this._eventSizes.push(eventSize);\n this._approxBytes += eventSize;\n if (this._events.length >= MAX_EVENTS_PER_BATCH || this._approxBytes >= MAX_APPROX_BYTES_PER_BATCH) {\n runAsynchronously(() => this._flush({ keepalive: false }));\n }\n },\n maskAllInputs: this._replayOptions.maskAllInputs ?? true,\n ...(this._replayOptions.blockClass !== undefined ? { blockClass: this._replayOptions.blockClass } : {}),\n ...(this._replayOptions.blockSelector !== undefined ? { blockSelector: this._replayOptions.blockSelector } : {}),\n }) ?? null;\n\n this._recording = true;\n\n const onPageHide = () => {\n runAsynchronously(() => this._flush({ keepalive: true }));\n };\n window.addEventListener(\"pagehide\", onPageHide);\n document.addEventListener(\"visibilitychange\", onPageHide);\n this._detachListeners = () => {\n window.removeEventListener(\"pagehide\", onPageHide);\n document.removeEventListener(\"visibilitychange\", onPageHide);\n };\n }\n\n private _stopCurrentRecording() {\n if (this._detachListeners) {\n this._detachListeners();\n this._detachListeners = null;\n }\n if (this._stopRecording) {\n this._stopRecording();\n this._stopRecording = null;\n }\n this._events = [];\n this._eventSizes = [];\n this._approxBytes = 0;\n this._recording = false;\n }\n\n private _tick() {\n if (this._cancelled) return;\n if (this._events.length > 0) {\n runAsynchronously(() => this._flush({ keepalive: false }));\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session-replay.js","names":["KnownErrors","Result"],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/session-replay.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { KnownErrors } from \"@hexclave/shared/dist/known-errors\";\nimport { isBrowserLike } from \"@hexclave/shared/dist/utils/env\";\nimport { captureWarning, throwErr } from \"@hexclave/shared/dist/utils/errors\";\nimport { runAsynchronously } from \"@hexclave/shared/dist/utils/promises\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\n\nexport type AnalyticsReplayOptions = {\n /**\n * Whether session replays are enabled.\n *\n * @default true\n */\n enabled?: boolean,\n /**\n * Whether to mask the content of all `<input>` elements.\n *\n * @default true\n */\n maskAllInputs?: boolean,\n /**\n * A CSS class name or RegExp. Elements with a matching class will be blocked\n * (replaced with a placeholder in the recording).\n *\n * @default undefined\n */\n blockClass?: string | RegExp,\n /**\n * A CSS selector string. Elements matching this selector will be blocked\n * (replaced with a placeholder in the recording).\n *\n * @default undefined\n */\n blockSelector?: string,\n};\n\nexport type AnalyticsOptions = {\n /**\n * Whether SDK-managed analytics capture is enabled.\n *\n * @default true\n */\n enabled?: boolean,\n /**\n * Options for session replay recording. Replays are enabled by default;\n * set `enabled: false` to opt out.\n */\n replays?: AnalyticsReplayOptions,\n};\n\nexport function getSessionReplayOptions(analyticsOptions: AnalyticsOptions | undefined): AnalyticsReplayOptions {\n return {\n ...analyticsOptions?.replays,\n enabled: analyticsOptions?.replays?.enabled ?? true,\n };\n}\n\n/**\n * Converts AnalyticsOptions to a JSON-safe representation.\n * RegExp blockClass values are serialized as `{ __regexp, __flags }` objects.\n * The return type is AnalyticsOptions to keep StackClientAppJson simple;\n * the actual runtime value is JSON-safe.\n */\nexport function analyticsOptionsToJson(options: AnalyticsOptions | undefined): AnalyticsOptions | undefined {\n if (!options?.replays?.blockClass) return options;\n const { blockClass, ...rest } = options.replays;\n if (!(blockClass instanceof RegExp)) return options;\n return {\n ...options,\n replays: {\n ...rest,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n blockClass: { __regexp: blockClass.source, __flags: blockClass.flags } as any,\n },\n };\n}\n\n/**\n * Reconstructs AnalyticsOptions from a JSON-deserialized value.\n * Converts `{ __regexp, __flags }` objects back to RegExp instances.\n */\nexport function analyticsOptionsFromJson(json: AnalyticsOptions | undefined): AnalyticsOptions | undefined {\n if (!json?.replays?.blockClass) return json;\n const { blockClass, ...rest } = json.replays;\n if (typeof blockClass === 'object' && '__regexp' in blockClass) {\n const bc = blockClass as unknown as { __regexp: string, __flags: string };\n return {\n ...json,\n replays: {\n ...rest,\n blockClass: new RegExp(bc.__regexp, bc.__flags),\n },\n };\n }\n return json;\n}\n\n// ---------- Recording internals ----------\n\n// Hexclave rebrand: canonical localStorage prefix (colon delimiters preserved).\nconst LOCAL_STORAGE_PREFIX = \"hexclave:session-replay:v1\";\n// Hexclave rebrand: legacy prefix — dual-read only, so a recording session active\n// across an SDK upgrade is not orphaned. Never written.\nconst LEGACY_LOCAL_STORAGE_PREFIX = \"stack:session-replay:v1\";\nconst IDLE_TTL_MS = 3 * 60 * 1000;\n\nconst FLUSH_INTERVAL_MS = 5_000;\nconst MAX_EVENTS_PER_BATCH = 200;\nconst MAX_APPROX_BYTES_PER_BATCH = 512_000;\n// Uncompressed per-batch target; the transport gzips before sending.\nconst MAX_BATCH_UNCOMPRESSED_BYTES = 900_000;\n// The server gunzips the whole batch (envelope + events) and rejects anything\n// whose decompressed size exceeds MAX_DECOMPRESSED_BYTES (8 MiB) in the backend\n// route. Since a single oversized event is sent alone, reserve headroom for the\n// JSON envelope (session/batch ids, timestamps, wrapper keys) so an event that\n// passes this check can't render a batch that trips the server's cap by a few\n// hundred bytes. Keep the server's MAX_DECOMPRESSED_BYTES >= this + the margin.\nconst BATCH_ENVELOPE_OVERHEAD_BYTES = 1024;\nconst MAX_SINGLE_EVENT_BYTES = 8 * 1024 * 1024 - BATCH_ENVELOPE_OVERHEAD_BYTES;\n\n// Reused across the emit hot path to avoid per-event allocation.\nconst textEncoder = new TextEncoder();\n\nexport type StoredSession = {\n session_id: string,\n created_at_ms: number,\n last_activity_ms: number,\n};\n\nexport function safeParseStoredSession(raw: string | null): StoredSession | null {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw);\n if (typeof parsed !== \"object\" || parsed === null) return null;\n if (typeof parsed.session_id !== \"string\") return null;\n if (typeof parsed.created_at_ms !== \"number\") return null;\n if (typeof parsed.last_activity_ms !== \"number\") return null;\n return parsed as StoredSession;\n } catch {\n return null;\n }\n}\n\nexport function makeStorageKey(projectId: string) {\n return `${LOCAL_STORAGE_PREFIX}:${projectId}`;\n}\n\n// Hexclave rebrand: legacy key, dual-read only (never written).\nexport function makeLegacyStorageKey(projectId: string) {\n return `${LEGACY_LOCAL_STORAGE_PREFIX}:${projectId}`;\n}\n\nexport function generateUuid() {\n return crypto.randomUUID();\n}\n\nexport function getOrRotateSession(options: { key: string, legacyKey?: string, nowMs: number }): StoredSession {\n // Hexclave rebrand: prefer the new key; fall back to the legacy key so a\n // recording session active across an SDK upgrade is not orphaned.\n const existing = safeParseStoredSession(localStorage.getItem(options.key))\n ?? (options.legacyKey ? safeParseStoredSession(localStorage.getItem(options.legacyKey)) : null);\n if (existing && options.nowMs - existing.last_activity_ms <= IDLE_TTL_MS) {\n return existing;\n }\n const next: StoredSession = {\n session_id: generateUuid(),\n created_at_ms: options.nowMs,\n last_activity_ms: options.nowMs,\n };\n localStorage.setItem(options.key, JSON.stringify(next));\n return next;\n}\n\nexport type SessionRecorderDeps = {\n projectId: string,\n sendBatch: (body: string, options: { keepalive: boolean }) => Promise<Result<Response, Error>>,\n};\n\nexport function isAnalyticsNotEnabledError(error: unknown): boolean {\n return KnownErrors.AnalyticsNotEnabled.isInstance(error);\n}\n\n/**\n * Whether the error looks like a network failure caused by an ad blocker or\n * similar extension blocking analytics requests. These are expected in\n * production and should be silently ignored rather than logged as warnings.\n */\nexport function isAdBlockerNetworkError(error: unknown): boolean {\n if (error instanceof Error) {\n return error.message.includes(\"Failed to fetch\")\n || error.message.includes(\"NetworkError\")\n || error.message.includes(\"Load failed\")\n || error.message.includes(\"network connection\");\n }\n return false;\n}\n\nexport class SessionRecorder {\n private _started = false;\n private _cancelled = false;\n private _disabled = false;\n private _stopRecording: (() => void) | null = null;\n private _detachListeners: (() => void) | null = null;\n private _flushTimer: ReturnType<typeof setInterval> | null = null;\n private _events: unknown[] = [];\n private _eventSizes: number[] = [];\n private _approxBytes = 0;\n private _lastPersistActivity = 0;\n private _recording = false;\n private _rrwebModule: typeof import(\"rrweb\") | null = null;\n private _lastBrowserSessionId: string | null = null;\n private _takingSnapshot = false;\n private _flushInProgress = false;\n private readonly _sessionReplaySegmentId: string;\n private readonly _storageKey: string;\n // Hexclave rebrand: legacy key used for dual-read fallback only.\n private readonly _legacyStorageKey: string;\n private readonly _deps: SessionRecorderDeps;\n private readonly _replayOptions: AnalyticsReplayOptions;\n\n constructor(deps: SessionRecorderDeps, replayOptions: AnalyticsReplayOptions) {\n this._deps = deps;\n this._replayOptions = replayOptions;\n this._sessionReplaySegmentId = generateUuid();\n this._storageKey = makeStorageKey(deps.projectId);\n this._legacyStorageKey = makeLegacyStorageKey(deps.projectId);\n }\n\n /**\n * Starts recording. Idempotent — calling multiple times is safe.\n */\n start() {\n if (this._started) return;\n if (!isBrowserLike()) return;\n this._started = true;\n\n // Kick off rrweb recording\n runAsynchronously(() => this._startRecording(), { noErrorLogging: true });\n\n // Periodic flush\n this._flushTimer = setInterval(() => this._tick(), FLUSH_INTERVAL_MS);\n }\n\n stop() {\n this._cancelled = true;\n if (this._flushTimer !== null) {\n clearInterval(this._flushTimer);\n this._flushTimer = null;\n }\n // Flush remaining events before cleanup\n runAsynchronously(() => this._flush({ keepalive: true }));\n this._stopCurrentRecording();\n }\n\n clearBuffer() {\n this._events = [];\n this._eventSizes = [];\n this._approxBytes = 0;\n }\n\n private _persistActivity(nowMs: number): StoredSession {\n const stored = getOrRotateSession({ key: this._storageKey, legacyKey: this._legacyStorageKey, nowMs });\n if (nowMs - this._lastPersistActivity < 5_000) return stored;\n this._lastPersistActivity = nowMs;\n const updated: StoredSession = { ...stored, last_activity_ms: nowMs };\n localStorage.setItem(this._storageKey, JSON.stringify(updated));\n return stored;\n }\n\n private async _flush(options: { keepalive: boolean }) {\n if (this._disabled) return;\n if (this._events.length === 0) return;\n // Prevent concurrent in-flight HTTP requests. When a flush is already\n // in-flight, a second batch could race on the server (both call\n // findRecentSessionReplay before either upsert commits) and create\n // duplicate SessionReplay records. Events stay in _events and will be\n // picked up by the next tick or batch-size check.\n if (this._flushInProgress) return;\n\n const nowMs = Date.now();\n const stored = getOrRotateSession({ key: this._storageKey, legacyKey: this._legacyStorageKey, nowMs });\n\n // Capture all buffered events upfront (before any await) so that\n // stop() / _stopCurrentRecording() clearing this._events cannot race\n // with the async send loop below and silently discard overflow batches.\n const allEvents = this._events;\n const allSizes = this._eventSizes;\n this._events = [];\n this._eventSizes = [];\n this._approxBytes = 0;\n\n // Non-keepalive flushes gzip before sending, so a single event up to the\n // server's decompressed budget can be sent alone. Keepalive flushes\n // (pagehide/visibilitychange/stop) skip async gzip to dispatch before page\n // tear-down, so they're bound by the server's raw ~1MB body limit; cap their\n // single-event size at the uncompressed batch target so an oversized event\n // is dropped rather than 413-ing the flush and losing the events behind it.\n const maxSingleEventBytes = options.keepalive ? MAX_BATCH_UNCOMPRESSED_BYTES : MAX_SINGLE_EVENT_BYTES;\n\n this._flushInProgress = true;\n try {\n let offset = 0;\n while (offset < allEvents.length) {\n // A single event over the limit can't be sent (rrweb events aren't\n // splittable); drop it and move on to the rest of the buffer.\n const firstSize = allSizes[offset] ?? throwErr(\"_eventSizes out of sync with _events — this should never happen\");\n if (firstSize > maxSingleEventBytes) {\n captureWarning(\n \"SessionRecorder.flush\",\n new Error(`Dropping oversized session replay event (${firstSize} bytes > ${maxSingleEventBytes} byte limit); it cannot be sent without exceeding the server's body limit.`),\n );\n offset += 1;\n continue;\n }\n\n let batchBytes = 0;\n let batchEnd = offset;\n for (let i = offset; i < allEvents.length; i++) {\n const nextSize = allSizes[i] ?? throwErr(\"_eventSizes out of sync with _events — this should never happen\");\n if (batchBytes + nextSize > MAX_BATCH_UNCOMPRESSED_BYTES && batchEnd > offset) break;\n batchBytes += nextSize;\n batchEnd = i + 1;\n }\n\n const batchEvents = allEvents.slice(offset, batchEnd);\n offset = batchEnd;\n\n const batchId = generateUuid();\n const payload = {\n browser_session_id: stored.session_id,\n session_replay_segment_id: this._sessionReplaySegmentId,\n batch_id: batchId,\n started_at_ms: stored.created_at_ms,\n sent_at_ms: nowMs,\n events: batchEvents,\n };\n\n const res = await this._deps.sendBatch(\n JSON.stringify(payload),\n { keepalive: options.keepalive },\n );\n\n if (res.status === \"error\") {\n if (isAnalyticsNotEnabledError(res.error)) {\n this._disable();\n return;\n }\n // Ad blockers commonly block analytics endpoints, causing network\n // errors. These are expected and should not pollute the console.\n if (isAdBlockerNetworkError(res.error)) {\n return;\n }\n captureWarning(\"SessionRecorder.flush\", res.error);\n return;\n }\n\n if (!res.data.ok) {\n // On any non-2xx we stop the loop, so this batch and every event still\n // buffered behind it are dropped (not retried). Count them for the log.\n const droppedCount = batchEvents.length + (allEvents.length - offset);\n if (res.data.status === 413) {\n // The payload exceeded the server's body limit despite the client-side\n // size caps — most likely a single poorly-compressible event (e.g. an\n // embedded image/canvas) that gzipped above the wire limit. Distinct\n // from other failures because the caps are supposed to prevent it.\n captureWarning(\n \"SessionRecorder.flush\",\n new Error(`Session replay batch rejected with 413 (payload too large) despite client-side size caps; dropping ${droppedCount} buffered event(s). A poorly-compressible event likely exceeded the server's body limit after gzip.`),\n );\n return;\n }\n captureWarning(\"SessionRecorder.flush\", new Error(`SessionRecorder flush failed (dropping ${droppedCount} buffered event(s)): ${res.data.status} ${await res.data.text()}`));\n return;\n }\n }\n } finally {\n this._flushInProgress = false;\n }\n }\n\n private _disable() {\n this._disabled = true;\n this.clearBuffer();\n if (this._flushTimer !== null) {\n clearInterval(this._flushTimer);\n this._flushTimer = null;\n }\n this._stopCurrentRecording();\n }\n\n private async _startRecording() {\n if (this._recording || this._cancelled) return;\n\n if (!this._rrwebModule) {\n const rrwebImport = await Result.fromPromise(import(\"rrweb\"));\n if (rrwebImport.status === \"error\") {\n console.warn(\"SessionRecorder: rrweb import failed. Is rrweb installed?\", rrwebImport.error);\n return;\n }\n this._rrwebModule = rrwebImport.data;\n }\n\n // cancelled may change during the await above\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this._cancelled) return;\n\n this._stopRecording = this._rrwebModule.record({\n emit: (event) => {\n const nowMs = Date.now();\n const stored = this._persistActivity(nowMs);\n\n // Detect session rotation: after 3+ minutes idle, getOrRotateSession\n // creates a new session ID. We need to inject a FullSnapshot so the\n // new server-side SessionReplay record is playable.\n if (this._lastBrowserSessionId === null) {\n this._lastBrowserSessionId = stored.session_id;\n } else if (stored.session_id !== this._lastBrowserSessionId && !this._takingSnapshot) {\n this._lastBrowserSessionId = stored.session_id;\n // Inject a FullSnapshot for the new session (calls emit synchronously)\n this._takingSnapshot = true;\n try {\n this._rrwebModule!.record.takeFullSnapshot();\n } finally {\n this._takingSnapshot = false;\n }\n }\n\n // Measure UTF-8 byte length to match the server's byte limit (.length counts UTF-16 units, undercounting multibyte content).\n const eventSize = textEncoder.encode(JSON.stringify(event)).byteLength;\n this._events.push(event);\n this._eventSizes.push(eventSize);\n this._approxBytes += eventSize;\n if (this._events.length >= MAX_EVENTS_PER_BATCH || this._approxBytes >= MAX_APPROX_BYTES_PER_BATCH) {\n runAsynchronously(() => this._flush({ keepalive: false }));\n }\n },\n maskAllInputs: this._replayOptions.maskAllInputs ?? true,\n ...(this._replayOptions.blockClass !== undefined ? { blockClass: this._replayOptions.blockClass } : {}),\n ...(this._replayOptions.blockSelector !== undefined ? { blockSelector: this._replayOptions.blockSelector } : {}),\n }) ?? null;\n\n this._recording = true;\n\n const onPageHide = () => {\n runAsynchronously(() => this._flush({ keepalive: true }));\n };\n window.addEventListener(\"pagehide\", onPageHide);\n document.addEventListener(\"visibilitychange\", onPageHide);\n this._detachListeners = () => {\n window.removeEventListener(\"pagehide\", onPageHide);\n document.removeEventListener(\"visibilitychange\", onPageHide);\n };\n }\n\n private _stopCurrentRecording() {\n if (this._detachListeners) {\n this._detachListeners();\n this._detachListeners = null;\n }\n if (this._stopRecording) {\n this._stopRecording();\n this._stopRecording = null;\n }\n this._events = [];\n this._eventSizes = [];\n this._approxBytes = 0;\n this._recording = false;\n }\n\n private _tick() {\n if (this._cancelled) return;\n if (this._events.length > 0) {\n runAsynchronously(() => this._flush({ keepalive: false }));\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
const
|
|
1
|
+
const require_rolldown_runtime = require("../../../../rolldown-runtime-D6vf50IK.js");
|
|
2
|
+
let _hexclave_shared_dist_utils_errors = require("@hexclave/shared/dist/utils/errors");
|
|
3
|
+
_hexclave_shared_dist_utils_errors = require_rolldown_runtime.__toESM(_hexclave_shared_dist_utils_errors);
|
|
2
4
|
let vitest = require("vitest");
|
|
3
5
|
let _hexclave_shared_dist_utils_results = require("@hexclave/shared/dist/utils/results");
|
|
4
6
|
let __session_replay_js = require("./session-replay.js");
|
|
5
7
|
let _hexclave_shared_dist_known_errors = require("@hexclave/shared/dist/known-errors");
|
|
6
|
-
|
|
7
8
|
//#region src/lib/hexclave-app/apps/implementations/session-replay.test.ts
|
|
8
9
|
// @vitest-environment jsdom
|
|
9
10
|
(0, vitest.describe)("session replay options", () => {
|
|
@@ -166,6 +167,137 @@ let _hexclave_shared_dist_known_errors = require("@hexclave/shared/dist/known-er
|
|
|
166
167
|
vitest.vi.useRealTimers();
|
|
167
168
|
}
|
|
168
169
|
});
|
|
170
|
+
(0, vitest.it)("drops a single event that exceeds the server's decompressed budget", async () => {
|
|
171
|
+
vitest.vi.useFakeTimers();
|
|
172
|
+
const storageKey = `hexclave:session-replay:v1:test-project`;
|
|
173
|
+
localStorage.setItem(storageKey, JSON.stringify({
|
|
174
|
+
session_id: "test-session",
|
|
175
|
+
created_at_ms: Date.now(),
|
|
176
|
+
last_activity_ms: Date.now()
|
|
177
|
+
}));
|
|
178
|
+
const sentBodies = [];
|
|
179
|
+
const recorder = new __session_replay_js.SessionRecorder({
|
|
180
|
+
projectId: "test-project",
|
|
181
|
+
sendBatch: async (body) => {
|
|
182
|
+
sentBodies.push(body);
|
|
183
|
+
return _hexclave_shared_dist_utils_results.Result.ok(new Response("ok", { status: 200 }));
|
|
184
|
+
}
|
|
185
|
+
}, {});
|
|
186
|
+
try {
|
|
187
|
+
const hugeEvent = {
|
|
188
|
+
type: 2,
|
|
189
|
+
timestamp: Date.now(),
|
|
190
|
+
data: "z".repeat(9e6)
|
|
191
|
+
};
|
|
192
|
+
const smallEvent = {
|
|
193
|
+
type: 3,
|
|
194
|
+
timestamp: Date.now(),
|
|
195
|
+
data: "ok"
|
|
196
|
+
};
|
|
197
|
+
const sizeOf = (e) => JSON.stringify(e).length;
|
|
198
|
+
recorder._events = [hugeEvent, smallEvent];
|
|
199
|
+
recorder._eventSizes = [sizeOf(hugeEvent), sizeOf(smallEvent)];
|
|
200
|
+
recorder._approxBytes = sizeOf(hugeEvent) + sizeOf(smallEvent);
|
|
201
|
+
recorder._tick();
|
|
202
|
+
await vitest.vi.advanceTimersByTimeAsync(0);
|
|
203
|
+
(0, vitest.expect)(sentBodies).toHaveLength(1);
|
|
204
|
+
const batch = JSON.parse(sentBodies[0]);
|
|
205
|
+
(0, vitest.expect)(batch.events).toHaveLength(1);
|
|
206
|
+
(0, vitest.expect)(batch.events[0].type).toBe(3);
|
|
207
|
+
} finally {
|
|
208
|
+
recorder.stop();
|
|
209
|
+
localStorage.removeItem(storageKey);
|
|
210
|
+
vitest.vi.useRealTimers();
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
(0, vitest.it)("on a keepalive flush, drops an event over the uncompressed batch target (it can't be gzipped before page tear-down)", async () => {
|
|
214
|
+
vitest.vi.useFakeTimers();
|
|
215
|
+
const storageKey = `hexclave:session-replay:v1:test-project`;
|
|
216
|
+
localStorage.setItem(storageKey, JSON.stringify({
|
|
217
|
+
session_id: "test-session",
|
|
218
|
+
created_at_ms: Date.now(),
|
|
219
|
+
last_activity_ms: Date.now()
|
|
220
|
+
}));
|
|
221
|
+
const sentBodies = [];
|
|
222
|
+
const recorder = new __session_replay_js.SessionRecorder({
|
|
223
|
+
projectId: "test-project",
|
|
224
|
+
sendBatch: async (body) => {
|
|
225
|
+
sentBodies.push(body);
|
|
226
|
+
return _hexclave_shared_dist_utils_results.Result.ok(new Response("ok", { status: 200 }));
|
|
227
|
+
}
|
|
228
|
+
}, {});
|
|
229
|
+
try {
|
|
230
|
+
const midEvent = {
|
|
231
|
+
type: 2,
|
|
232
|
+
timestamp: Date.now(),
|
|
233
|
+
data: "z".repeat(2e6)
|
|
234
|
+
};
|
|
235
|
+
const smallEvent = {
|
|
236
|
+
type: 3,
|
|
237
|
+
timestamp: Date.now(),
|
|
238
|
+
data: "ok"
|
|
239
|
+
};
|
|
240
|
+
const sizeOf = (e) => new TextEncoder().encode(JSON.stringify(e)).byteLength;
|
|
241
|
+
recorder._events = [midEvent, smallEvent];
|
|
242
|
+
recorder._eventSizes = [sizeOf(midEvent), sizeOf(smallEvent)];
|
|
243
|
+
recorder._approxBytes = sizeOf(midEvent) + sizeOf(smallEvent);
|
|
244
|
+
await recorder._flush({ keepalive: true });
|
|
245
|
+
(0, vitest.expect)(sentBodies).toHaveLength(1);
|
|
246
|
+
const batch = JSON.parse(sentBodies[0]);
|
|
247
|
+
(0, vitest.expect)(batch.events).toHaveLength(1);
|
|
248
|
+
(0, vitest.expect)(batch.events[0].type).toBe(3);
|
|
249
|
+
} finally {
|
|
250
|
+
recorder.stop();
|
|
251
|
+
localStorage.removeItem(storageKey);
|
|
252
|
+
vitest.vi.useRealTimers();
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
(0, vitest.it)("logs a distinct 413 warning and drops the buffered events when the server rejects a batch as too large", async () => {
|
|
256
|
+
vitest.vi.useFakeTimers();
|
|
257
|
+
const captureWarningSpy = vitest.vi.spyOn(_hexclave_shared_dist_utils_errors, "captureWarning").mockImplementation(() => {});
|
|
258
|
+
const storageKey = `hexclave:session-replay:v1:test-project`;
|
|
259
|
+
localStorage.setItem(storageKey, JSON.stringify({
|
|
260
|
+
session_id: "test-session",
|
|
261
|
+
created_at_ms: Date.now(),
|
|
262
|
+
last_activity_ms: Date.now()
|
|
263
|
+
}));
|
|
264
|
+
let calls = 0;
|
|
265
|
+
const recorder = new __session_replay_js.SessionRecorder({
|
|
266
|
+
projectId: "test-project",
|
|
267
|
+
sendBatch: async () => {
|
|
268
|
+
calls += 1;
|
|
269
|
+
return _hexclave_shared_dist_utils_results.Result.ok(new Response("payload too large", { status: 413 }));
|
|
270
|
+
}
|
|
271
|
+
}, {});
|
|
272
|
+
try {
|
|
273
|
+
const eventA = {
|
|
274
|
+
type: 3,
|
|
275
|
+
timestamp: Date.now(),
|
|
276
|
+
data: "a"
|
|
277
|
+
};
|
|
278
|
+
const eventB = {
|
|
279
|
+
type: 3,
|
|
280
|
+
timestamp: Date.now(),
|
|
281
|
+
data: "b"
|
|
282
|
+
};
|
|
283
|
+
const sizeOf = (e) => new TextEncoder().encode(JSON.stringify(e)).byteLength;
|
|
284
|
+
recorder._events = [eventA, eventB];
|
|
285
|
+
recorder._eventSizes = [sizeOf(eventA), sizeOf(eventB)];
|
|
286
|
+
recorder._approxBytes = sizeOf(eventA) + sizeOf(eventB);
|
|
287
|
+
await recorder._flush({ keepalive: false });
|
|
288
|
+
(0, vitest.expect)(calls).toBe(1);
|
|
289
|
+
(0, vitest.expect)(captureWarningSpy).toHaveBeenCalledTimes(1);
|
|
290
|
+
const warned = captureWarningSpy.mock.calls[0]?.[1];
|
|
291
|
+
(0, vitest.expect)(warned).toBeInstanceOf(Error);
|
|
292
|
+
(0, vitest.expect)(warned.message).toContain("413");
|
|
293
|
+
(0, vitest.expect)(warned.message).toContain("2 buffered event");
|
|
294
|
+
} finally {
|
|
295
|
+
recorder.stop();
|
|
296
|
+
localStorage.removeItem(storageKey);
|
|
297
|
+
captureWarningSpy.mockRestore();
|
|
298
|
+
vitest.vi.useRealTimers();
|
|
299
|
+
}
|
|
300
|
+
});
|
|
169
301
|
(0, vitest.it)("silently disables when client interface returns ANALYTICS_NOT_ENABLED as an error", async () => {
|
|
170
302
|
vitest.vi.useFakeTimers();
|
|
171
303
|
const storageKey = `hexclave:session-replay:v1:test-project`;
|
|
@@ -213,6 +345,6 @@ let _hexclave_shared_dist_known_errors = require("@hexclave/shared/dist/known-er
|
|
|
213
345
|
}
|
|
214
346
|
});
|
|
215
347
|
});
|
|
216
|
-
|
|
217
348
|
//#endregion
|
|
349
|
+
|
|
218
350
|
//# sourceMappingURL=session-replay.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-replay.test.js","names":["SessionRecorder","Result","vi","KnownErrors"],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/session-replay.test.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n// @vitest-environment jsdom\n\nimport { KnownErrors } from \"@hexclave/shared/dist/known-errors\";\nimport { describe, expect, it, vi } from \"vitest\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { analyticsOptionsFromJson, analyticsOptionsToJson, getSessionReplayOptions, SessionRecorder } from \"./session-replay\";\n\ndescribe(\"session replay options\", () => {\n it(\"enables replays by default\", () => {\n expect(getSessionReplayOptions(undefined).enabled).toBe(true);\n expect(getSessionReplayOptions({}).enabled).toBe(true);\n expect(getSessionReplayOptions({ replays: {} }).enabled).toBe(true);\n });\n\n it(\"preserves explicit replay opt-out\", () => {\n expect(getSessionReplayOptions({ replays: { enabled: false } }).enabled).toBe(false);\n });\n});\n\ndescribe(\"analytics option JSON conversion\", () => {\n it(\"preserves top-level analytics options when serializing replay block classes\", () => {\n const json = analyticsOptionsToJson({\n enabled: false,\n replays: {\n enabled: true,\n blockClass: /stack-sensitive/u,\n },\n });\n\n expect(json?.enabled).toBe(false);\n expect(json?.replays?.enabled).toBe(true);\n });\n\n it(\"preserves top-level analytics options when deserializing replay block classes\", () => {\n const roundTripped = analyticsOptionsFromJson(analyticsOptionsToJson({\n enabled: false,\n replays: {\n blockClass: /stack-sensitive/u,\n },\n }));\n\n expect(roundTripped?.enabled).toBe(false);\n expect(roundTripped?.replays?.blockClass).toEqual(/stack-sensitive/u);\n });\n});\n\ndescribe(\"SessionRecorder flush\", () => {\n it(\"silently ignores network errors caused by ad blockers\", async () => {\n vi.useFakeTimers();\n\n const storageKey = `hexclave:session-replay:v1:test-project`;\n localStorage.setItem(storageKey, JSON.stringify({\n session_id: \"test-session\",\n created_at_ms: Date.now(),\n last_activity_ms: Date.now(),\n }));\n\n const sentBodies: string[] = [];\n const recorder = new SessionRecorder(\n {\n projectId: \"test-project\",\n sendBatch: async (body) => {\n sentBodies.push(body);\n return Result.error(new TypeError(\"Failed to fetch\"));\n },\n },\n {},\n );\n\n const warnSpy = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n try {\n const event1 = { type: 2, timestamp: Date.now(), data: {} };\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [event1];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [JSON.stringify(event1).length];\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n\n expect(sentBodies).toHaveLength(1);\n expect(warnSpy).not.toHaveBeenCalled();\n\n // Unlike ANALYTICS_NOT_ENABLED, ad blocker errors do NOT disable the\n // recorder — subsequent flushes continue attempting delivery.\n const event2 = { type: 3, timestamp: Date.now(), data: {} };\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [event2];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [JSON.stringify(event2).length];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n expect(sentBodies).toHaveLength(2);\n expect(warnSpy).not.toHaveBeenCalled();\n } finally {\n recorder.stop();\n warnSpy.mockRestore();\n localStorage.removeItem(storageKey);\n vi.useRealTimers();\n }\n });\n\n it(\"splits large batches into multiple requests to stay under server 1MB limit\", async () => {\n vi.useFakeTimers();\n\n const storageKey = `hexclave:session-replay:v1:test-project`;\n localStorage.setItem(storageKey, JSON.stringify({\n session_id: \"test-session\",\n created_at_ms: Date.now(),\n last_activity_ms: Date.now(),\n }));\n\n const sentBodies: string[] = [];\n const recorder = new SessionRecorder(\n {\n projectId: \"test-project\",\n sendBatch: async (body) => {\n sentBodies.push(body);\n return Result.ok(new Response(\"ok\", { status: 200 }));\n },\n },\n {},\n );\n\n try {\n // Create events that together exceed 900KB (the per-batch cap).\n // Each event is ~500KB, so two events (~1MB) must be split into two batches.\n const largeData = \"x\".repeat(500_000);\n const event1 = { type: 2, timestamp: Date.now(), data: largeData };\n const event2 = { type: 3, timestamp: Date.now(), data: largeData };\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [event1, event2];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [JSON.stringify(event1).length, JSON.stringify(event2).length];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._approxBytes = JSON.stringify(event1).length + JSON.stringify(event2).length;\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n\n // Should have sent two separate batches\n expect(sentBodies).toHaveLength(2);\n\n // Each batch should contain exactly one event\n const batch1 = JSON.parse(sentBodies[0]);\n const batch2 = JSON.parse(sentBodies[1]);\n expect(batch1.events).toHaveLength(1);\n expect(batch2.events).toHaveLength(1);\n\n // They should have different batch IDs\n expect(batch1.batch_id).not.toBe(batch2.batch_id);\n } finally {\n recorder.stop();\n localStorage.removeItem(storageKey);\n vi.useRealTimers();\n }\n });\n\n it(\"sends a single oversized event alone without dropping it\", async () => {\n vi.useFakeTimers();\n\n const storageKey = `hexclave:session-replay:v1:test-project`;\n localStorage.setItem(storageKey, JSON.stringify({\n session_id: \"test-session\",\n created_at_ms: Date.now(),\n last_activity_ms: Date.now(),\n }));\n\n const sentBodies: string[] = [];\n const recorder = new SessionRecorder(\n {\n projectId: \"test-project\",\n sendBatch: async (body) => {\n sentBodies.push(body);\n return Result.ok(new Response(\"ok\", { status: 200 }));\n },\n },\n {},\n );\n\n try {\n // A single event larger than 900KB — should still be sent (not dropped)\n const hugeData = \"y\".repeat(1_000_000);\n const hugeEvent = { type: 2, timestamp: Date.now(), data: hugeData };\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [hugeEvent];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [JSON.stringify(hugeEvent).length];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._approxBytes = JSON.stringify(hugeEvent).length;\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n\n // Should still send the event (the server may reject it, but we don't drop it client-side)\n expect(sentBodies).toHaveLength(1);\n const batch = JSON.parse(sentBodies[0]);\n expect(batch.events).toHaveLength(1);\n } finally {\n recorder.stop();\n localStorage.removeItem(storageKey);\n vi.useRealTimers();\n }\n });\n\n it(\"silently disables when client interface returns ANALYTICS_NOT_ENABLED as an error\", async () => {\n vi.useFakeTimers();\n\n const storageKey = `hexclave:session-replay:v1:test-project`;\n localStorage.setItem(storageKey, JSON.stringify({\n session_id: \"test-session\",\n created_at_ms: Date.now(),\n last_activity_ms: Date.now(),\n }));\n\n const sentBodies: string[] = [];\n const recorder = new SessionRecorder(\n {\n projectId: \"test-project\",\n sendBatch: async (body) => {\n sentBodies.push(body);\n return Result.error(new KnownErrors.AnalyticsNotEnabled());\n },\n },\n {},\n );\n\n const warnSpy = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n try {\n const event1 = { type: 2, timestamp: Date.now(), data: {} };\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [event1];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [JSON.stringify(event1).length];\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n\n expect(sentBodies).toHaveLength(1);\n expect(warnSpy).not.toHaveBeenCalled();\n\n const event2 = { type: 3, timestamp: Date.now(), data: {} };\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [event2];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [JSON.stringify(event2).length];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n expect(sentBodies).toHaveLength(1);\n } finally {\n recorder.stop();\n warnSpy.mockRestore();\n localStorage.removeItem(storageKey);\n vi.useRealTimers();\n }\n });\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session-replay.test.js","names":["SessionRecorder","Result","vi","errors","KnownErrors"],"sources":["../../../../../src/lib/hexclave-app/apps/implementations/session-replay.test.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n// @vitest-environment jsdom\n\nimport { KnownErrors } from \"@hexclave/shared/dist/known-errors\";\nimport { describe, expect, it, vi } from \"vitest\";\nimport * as errors from \"@hexclave/shared/dist/utils/errors\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { analyticsOptionsFromJson, analyticsOptionsToJson, getSessionReplayOptions, SessionRecorder } from \"./session-replay\";\n\ndescribe(\"session replay options\", () => {\n it(\"enables replays by default\", () => {\n expect(getSessionReplayOptions(undefined).enabled).toBe(true);\n expect(getSessionReplayOptions({}).enabled).toBe(true);\n expect(getSessionReplayOptions({ replays: {} }).enabled).toBe(true);\n });\n\n it(\"preserves explicit replay opt-out\", () => {\n expect(getSessionReplayOptions({ replays: { enabled: false } }).enabled).toBe(false);\n });\n});\n\ndescribe(\"analytics option JSON conversion\", () => {\n it(\"preserves top-level analytics options when serializing replay block classes\", () => {\n const json = analyticsOptionsToJson({\n enabled: false,\n replays: {\n enabled: true,\n blockClass: /stack-sensitive/u,\n },\n });\n\n expect(json?.enabled).toBe(false);\n expect(json?.replays?.enabled).toBe(true);\n });\n\n it(\"preserves top-level analytics options when deserializing replay block classes\", () => {\n const roundTripped = analyticsOptionsFromJson(analyticsOptionsToJson({\n enabled: false,\n replays: {\n blockClass: /stack-sensitive/u,\n },\n }));\n\n expect(roundTripped?.enabled).toBe(false);\n expect(roundTripped?.replays?.blockClass).toEqual(/stack-sensitive/u);\n });\n});\n\ndescribe(\"SessionRecorder flush\", () => {\n it(\"silently ignores network errors caused by ad blockers\", async () => {\n vi.useFakeTimers();\n\n const storageKey = `hexclave:session-replay:v1:test-project`;\n localStorage.setItem(storageKey, JSON.stringify({\n session_id: \"test-session\",\n created_at_ms: Date.now(),\n last_activity_ms: Date.now(),\n }));\n\n const sentBodies: string[] = [];\n const recorder = new SessionRecorder(\n {\n projectId: \"test-project\",\n sendBatch: async (body) => {\n sentBodies.push(body);\n return Result.error(new TypeError(\"Failed to fetch\"));\n },\n },\n {},\n );\n\n const warnSpy = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n try {\n const event1 = { type: 2, timestamp: Date.now(), data: {} };\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [event1];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [JSON.stringify(event1).length];\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n\n expect(sentBodies).toHaveLength(1);\n expect(warnSpy).not.toHaveBeenCalled();\n\n // Unlike ANALYTICS_NOT_ENABLED, ad blocker errors do NOT disable the\n // recorder — subsequent flushes continue attempting delivery.\n const event2 = { type: 3, timestamp: Date.now(), data: {} };\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [event2];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [JSON.stringify(event2).length];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n expect(sentBodies).toHaveLength(2);\n expect(warnSpy).not.toHaveBeenCalled();\n } finally {\n recorder.stop();\n warnSpy.mockRestore();\n localStorage.removeItem(storageKey);\n vi.useRealTimers();\n }\n });\n\n it(\"splits large batches into multiple requests to stay under server 1MB limit\", async () => {\n vi.useFakeTimers();\n\n const storageKey = `hexclave:session-replay:v1:test-project`;\n localStorage.setItem(storageKey, JSON.stringify({\n session_id: \"test-session\",\n created_at_ms: Date.now(),\n last_activity_ms: Date.now(),\n }));\n\n const sentBodies: string[] = [];\n const recorder = new SessionRecorder(\n {\n projectId: \"test-project\",\n sendBatch: async (body) => {\n sentBodies.push(body);\n return Result.ok(new Response(\"ok\", { status: 200 }));\n },\n },\n {},\n );\n\n try {\n // Create events that together exceed 900KB (the per-batch cap).\n // Each event is ~500KB, so two events (~1MB) must be split into two batches.\n const largeData = \"x\".repeat(500_000);\n const event1 = { type: 2, timestamp: Date.now(), data: largeData };\n const event2 = { type: 3, timestamp: Date.now(), data: largeData };\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [event1, event2];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [JSON.stringify(event1).length, JSON.stringify(event2).length];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._approxBytes = JSON.stringify(event1).length + JSON.stringify(event2).length;\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n\n // Should have sent two separate batches\n expect(sentBodies).toHaveLength(2);\n\n // Each batch should contain exactly one event\n const batch1 = JSON.parse(sentBodies[0]);\n const batch2 = JSON.parse(sentBodies[1]);\n expect(batch1.events).toHaveLength(1);\n expect(batch2.events).toHaveLength(1);\n\n // They should have different batch IDs\n expect(batch1.batch_id).not.toBe(batch2.batch_id);\n } finally {\n recorder.stop();\n localStorage.removeItem(storageKey);\n vi.useRealTimers();\n }\n });\n\n it(\"sends a single oversized event alone without dropping it\", async () => {\n vi.useFakeTimers();\n\n const storageKey = `hexclave:session-replay:v1:test-project`;\n localStorage.setItem(storageKey, JSON.stringify({\n session_id: \"test-session\",\n created_at_ms: Date.now(),\n last_activity_ms: Date.now(),\n }));\n\n const sentBodies: string[] = [];\n const recorder = new SessionRecorder(\n {\n projectId: \"test-project\",\n sendBatch: async (body) => {\n sentBodies.push(body);\n return Result.ok(new Response(\"ok\", { status: 200 }));\n },\n },\n {},\n );\n\n try {\n // A single event larger than 900KB — should still be sent (not dropped)\n const hugeData = \"y\".repeat(1_000_000);\n const hugeEvent = { type: 2, timestamp: Date.now(), data: hugeData };\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [hugeEvent];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [JSON.stringify(hugeEvent).length];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._approxBytes = JSON.stringify(hugeEvent).length;\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n\n // Sent (not dropped): the transport gzips it under the wire limit.\n expect(sentBodies).toHaveLength(1);\n const batch = JSON.parse(sentBodies[0]);\n expect(batch.events).toHaveLength(1);\n } finally {\n recorder.stop();\n localStorage.removeItem(storageKey);\n vi.useRealTimers();\n }\n });\n\n it(\"drops a single event that exceeds the server's decompressed budget\", async () => {\n vi.useFakeTimers();\n\n const storageKey = `hexclave:session-replay:v1:test-project`;\n localStorage.setItem(storageKey, JSON.stringify({\n session_id: \"test-session\",\n created_at_ms: Date.now(),\n last_activity_ms: Date.now(),\n }));\n\n const sentBodies: string[] = [];\n const recorder = new SessionRecorder(\n {\n projectId: \"test-project\",\n sendBatch: async (body) => {\n sentBodies.push(body);\n return Result.ok(new Response(\"ok\", { status: 200 }));\n },\n },\n {},\n );\n\n try {\n // >8MB (MAX_SINGLE_EVENT_BYTES) is dropped; the next event still sends.\n const hugeEvent = { type: 2, timestamp: Date.now(), data: \"z\".repeat(9_000_000) };\n const smallEvent = { type: 3, timestamp: Date.now(), data: \"ok\" };\n const sizeOf = (e: unknown) => JSON.stringify(e).length;\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [hugeEvent, smallEvent];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [sizeOf(hugeEvent), sizeOf(smallEvent)];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._approxBytes = sizeOf(hugeEvent) + sizeOf(smallEvent);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n\n expect(sentBodies).toHaveLength(1);\n const batch = JSON.parse(sentBodies[0]);\n expect(batch.events).toHaveLength(1);\n expect(batch.events[0].type).toBe(3);\n } finally {\n recorder.stop();\n localStorage.removeItem(storageKey);\n vi.useRealTimers();\n }\n });\n\n it(\"on a keepalive flush, drops an event over the uncompressed batch target (it can't be gzipped before page tear-down)\", async () => {\n vi.useFakeTimers();\n\n const storageKey = `hexclave:session-replay:v1:test-project`;\n localStorage.setItem(storageKey, JSON.stringify({\n session_id: \"test-session\",\n created_at_ms: Date.now(),\n last_activity_ms: Date.now(),\n }));\n\n const sentBodies: string[] = [];\n const recorder = new SessionRecorder(\n {\n projectId: \"test-project\",\n sendBatch: async (body) => {\n sentBodies.push(body);\n return Result.ok(new Response(\"ok\", { status: 200 }));\n },\n },\n {},\n );\n\n try {\n // ~2MB is under the 8MB gzipped ceiling but over the 900KB uncompressed\n // batch target. Keepalive flushes skip gzip, so this event would 413 the\n // server's ~1MB raw body limit; it must be dropped, but the next event\n // (small enough to send raw) still goes out.\n const midEvent = { type: 2, timestamp: Date.now(), data: \"z\".repeat(2_000_000) };\n const smallEvent = { type: 3, timestamp: Date.now(), data: \"ok\" };\n const sizeOf = (e: unknown) => new TextEncoder().encode(JSON.stringify(e)).byteLength;\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [midEvent, smallEvent];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [sizeOf(midEvent), sizeOf(smallEvent)];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._approxBytes = sizeOf(midEvent) + sizeOf(smallEvent);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n await (recorder as any)._flush({ keepalive: true });\n\n expect(sentBodies).toHaveLength(1);\n const batch = JSON.parse(sentBodies[0]);\n expect(batch.events).toHaveLength(1);\n expect(batch.events[0].type).toBe(3);\n } finally {\n recorder.stop();\n localStorage.removeItem(storageKey);\n vi.useRealTimers();\n }\n });\n\n it(\"logs a distinct 413 warning and drops the buffered events when the server rejects a batch as too large\", async () => {\n vi.useFakeTimers();\n const captureWarningSpy = vi.spyOn(errors, \"captureWarning\").mockImplementation(() => {});\n\n const storageKey = `hexclave:session-replay:v1:test-project`;\n localStorage.setItem(storageKey, JSON.stringify({\n session_id: \"test-session\",\n created_at_ms: Date.now(),\n last_activity_ms: Date.now(),\n }));\n\n let calls = 0;\n const recorder = new SessionRecorder(\n {\n projectId: \"test-project\",\n sendBatch: async () => {\n calls += 1;\n // A poorly-compressible event can clear the client-side caps yet still\n // exceed the server's body limit after gzip → 413.\n return Result.ok(new Response(\"payload too large\", { status: 413 }));\n },\n },\n {},\n );\n\n try {\n const eventA = { type: 3, timestamp: Date.now(), data: \"a\" };\n const eventB = { type: 3, timestamp: Date.now(), data: \"b\" };\n const sizeOf = (e: unknown) => new TextEncoder().encode(JSON.stringify(e)).byteLength;\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [eventA, eventB];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [sizeOf(eventA), sizeOf(eventB)];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._approxBytes = sizeOf(eventA) + sizeOf(eventB);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n await (recorder as any)._flush({ keepalive: false });\n\n // The 413 stops the loop; no retry of the rejected batch.\n expect(calls).toBe(1);\n expect(captureWarningSpy).toHaveBeenCalledTimes(1);\n const warned = captureWarningSpy.mock.calls[0]?.[1];\n expect(warned).toBeInstanceOf(Error);\n expect((warned as Error).message).toContain(\"413\");\n // Both buffered events are reported as dropped.\n expect((warned as Error).message).toContain(\"2 buffered event\");\n } finally {\n recorder.stop();\n localStorage.removeItem(storageKey);\n captureWarningSpy.mockRestore();\n vi.useRealTimers();\n }\n });\n\n it(\"silently disables when client interface returns ANALYTICS_NOT_ENABLED as an error\", async () => {\n vi.useFakeTimers();\n\n const storageKey = `hexclave:session-replay:v1:test-project`;\n localStorage.setItem(storageKey, JSON.stringify({\n session_id: \"test-session\",\n created_at_ms: Date.now(),\n last_activity_ms: Date.now(),\n }));\n\n const sentBodies: string[] = [];\n const recorder = new SessionRecorder(\n {\n projectId: \"test-project\",\n sendBatch: async (body) => {\n sentBodies.push(body);\n return Result.error(new KnownErrors.AnalyticsNotEnabled());\n },\n },\n {},\n );\n\n const warnSpy = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n try {\n const event1 = { type: 2, timestamp: Date.now(), data: {} };\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [event1];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [JSON.stringify(event1).length];\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n\n expect(sentBodies).toHaveLength(1);\n expect(warnSpy).not.toHaveBeenCalled();\n\n const event2 = { type: 3, timestamp: Date.now(), data: {} };\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._events = [event2];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (recorder as any)._eventSizes = [JSON.stringify(event2).length];\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n (recorder as any)._tick();\n await vi.advanceTimersByTimeAsync(0);\n expect(sentBodies).toHaveLength(1);\n } finally {\n recorder.stop();\n warnSpy.mockRestore();\n localStorage.removeItem(storageKey);\n vi.useRealTimers();\n }\n });\n});\n"],"mappings":"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"}
|
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
import { HexclaveServerApp,
|
|
2
|
-
import { HexclaveAdminApp, HexclaveAdminAppConstructor, HexclaveAdminAppConstructorOptions, StackAdminApp, StackAdminAppConstructor, StackAdminAppConstructorOptions } from "./interfaces/admin-app.js";
|
|
3
|
-
import { HexclaveClientApp, HexclaveClientAppConstructor, HexclaveClientAppConstructorOptions, HexclaveClientAppJson, StackClientApp, StackClientAppConstructor, StackClientAppConstructorOptions, StackClientAppJson } from "./interfaces/client-app.js";
|
|
1
|
+
import { A as HexclaveServerApp, C as HexclaveAdminAppConstructorOptions, D as StackAdminApp, F as StackServerAppConstructorOptions, M as HexclaveServerAppConstructorOptions, N as StackServerApp, O as StackAdminAppConstructor, P as StackServerAppConstructor, S as HexclaveAdminAppConstructor, a as StackClientApp, c as StackClientAppJson, i as HexclaveClientAppJson, j as HexclaveServerAppConstructor, k as StackAdminAppConstructorOptions, n as HexclaveClientAppConstructor, o as StackClientAppConstructor, r as HexclaveClientAppConstructorOptions, s as StackClientAppConstructorOptions, t as HexclaveClientApp, x as HexclaveAdminApp } from "../../../client-app-D8-Lm4RT.js";
|
|
4
2
|
export { HexclaveAdminApp, type HexclaveAdminAppConstructor, type HexclaveAdminAppConstructorOptions, HexclaveClientApp, type HexclaveClientAppConstructor, type HexclaveClientAppConstructorOptions, type HexclaveClientAppJson, HexclaveServerApp, type HexclaveServerAppConstructor, type HexclaveServerAppConstructorOptions, StackAdminApp, type StackAdminAppConstructor, type StackAdminAppConstructorOptions, StackClientApp, type StackClientAppConstructor, type StackClientAppConstructorOptions, type StackClientAppJson, StackServerApp, type StackServerAppConstructor, type StackServerAppConstructorOptions };
|