@interfere/react 0.1.0-alpha.6 → 0.2.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -0
- package/dist/error-boundary.d.mts +27 -0
- package/dist/error-boundary.d.mts.map +1 -0
- package/dist/error-boundary.mjs +42 -0
- package/dist/error-boundary.mjs.map +1 -0
- package/dist/internal/client.d.mts +13 -0
- package/dist/internal/client.d.mts.map +1 -0
- package/dist/internal/client.mjs +80 -0
- package/dist/internal/client.mjs.map +1 -0
- package/dist/internal/config.d.mts +9 -0
- package/dist/internal/config.d.mts.map +1 -0
- package/dist/internal/config.mjs +27 -0
- package/dist/internal/config.mjs.map +1 -0
- package/dist/internal/context.d.mts +6 -0
- package/dist/internal/context.d.mts.map +1 -0
- package/dist/internal/context.mjs +32 -0
- package/dist/internal/context.mjs.map +1 -0
- package/dist/internal/envelope.d.mts +14 -0
- package/dist/internal/envelope.d.mts.map +1 -0
- package/dist/internal/envelope.mjs +20 -0
- package/dist/internal/envelope.mjs.map +1 -0
- package/dist/internal/errors.d.mts +4 -0
- package/dist/internal/errors.d.mts.map +1 -0
- package/dist/internal/errors.mjs +4 -0
- package/dist/internal/errors.mjs.map +1 -0
- package/dist/internal/sw.d.mts +4 -0
- package/dist/internal/sw.d.mts.map +1 -0
- package/dist/internal/sw.mjs +10 -0
- package/dist/internal/sw.mjs.map +1 -0
- package/dist/plugins/errors.d.mts +6 -0
- package/dist/plugins/errors.d.mts.map +1 -0
- package/dist/plugins/errors.mjs +59 -0
- package/dist/plugins/errors.mjs.map +1 -0
- package/dist/plugins/lib/loader.d.mts +9 -0
- package/dist/plugins/lib/loader.d.mts.map +1 -0
- package/dist/plugins/lib/loader.mjs +47 -0
- package/dist/plugins/lib/loader.mjs.map +1 -0
- package/dist/plugins/lib/types.d.mts +14 -0
- package/dist/plugins/lib/types.d.mts.map +1 -0
- package/dist/plugins/lib/types.mjs +1 -0
- package/dist/plugins/pages.d.mts +6 -0
- package/dist/plugins/pages.d.mts.map +1 -0
- package/dist/plugins/pages.mjs +102 -0
- package/dist/plugins/pages.mjs.map +1 -0
- package/dist/plugins/rage-clicks.d.mts +6 -0
- package/dist/plugins/rage-clicks.d.mts.map +1 -0
- package/dist/plugins/rage-clicks.mjs +53 -0
- package/dist/plugins/rage-clicks.mjs.map +1 -0
- package/dist/plugins/replay.d.mts +6 -0
- package/dist/plugins/replay.d.mts.map +1 -0
- package/dist/plugins/replay.mjs +62 -0
- package/dist/plugins/replay.mjs.map +1 -0
- package/dist/provider.d.mts +17 -11
- package/dist/provider.d.mts.map +1 -1
- package/dist/provider.mjs +18 -17
- package/dist/provider.mjs.map +1 -1
- package/dist/tracking/api.d.mts +22 -0
- package/dist/tracking/api.d.mts.map +1 -0
- package/dist/tracking/api.mjs +88 -0
- package/dist/tracking/api.mjs.map +1 -0
- package/dist/tracking/session.d.mts +19 -0
- package/dist/tracking/session.d.mts.map +1 -0
- package/dist/tracking/session.mjs +92 -0
- package/dist/tracking/session.mjs.map +1 -0
- package/dist/tracking/visitor.d.mts +6 -0
- package/dist/tracking/visitor.d.mts.map +1 -0
- package/dist/tracking/visitor.mjs +35 -0
- package/dist/tracking/visitor.mjs.map +1 -0
- package/dist/transport/http.d.mts +15 -0
- package/dist/transport/http.d.mts.map +1 -0
- package/dist/transport/http.mjs +56 -0
- package/dist/transport/http.mjs.map +1 -0
- package/dist/transport/queue.d.mts +25 -0
- package/dist/transport/queue.d.mts.map +1 -0
- package/dist/transport/queue.mjs +60 -0
- package/dist/transport/queue.mjs.map +1 -0
- package/dist/util/log.d.mts +13 -0
- package/dist/util/log.d.mts.map +1 -0
- package/dist/util/log.mjs +37 -0
- package/dist/util/log.mjs.map +1 -0
- package/package.json +38 -64
- package/dist/client.d.mts +0 -8
- package/dist/client.d.mts.map +0 -1
- package/dist/client.mjs +0 -14
- package/dist/client.mjs.map +0 -1
- package/dist/core/events/define-event.d.mts +0 -12
- package/dist/core/events/define-event.d.mts.map +0 -1
- package/dist/core/events/define-event.mjs +0 -39
- package/dist/core/events/define-event.mjs.map +0 -1
- package/dist/core/events/event-registry.d.mts +0 -23
- package/dist/core/events/event-registry.d.mts.map +0 -1
- package/dist/core/events/event-registry.mjs +0 -32
- package/dist/core/events/event-registry.mjs.map +0 -1
- package/dist/core/events/plugin-event-types.d.mts +0 -130
- package/dist/core/events/plugin-event-types.d.mts.map +0 -1
- package/dist/core/events/plugin-event-types.mjs +0 -25
- package/dist/core/events/plugin-event-types.mjs.map +0 -1
- package/dist/core/plugins/define-plugin.d.mts +0 -43
- package/dist/core/plugins/define-plugin.d.mts.map +0 -1
- package/dist/core/plugins/define-plugin.mjs +0 -23
- package/dist/core/plugins/define-plugin.mjs.map +0 -1
- package/dist/core/plugins/dom-utils.d.mts +0 -9
- package/dist/core/plugins/dom-utils.d.mts.map +0 -1
- package/dist/core/plugins/dom-utils.mjs +0 -25
- package/dist/core/plugins/dom-utils.mjs.map +0 -1
- package/dist/core/plugins/impl/ai-summary/ai-summary-plugin-api.d.mts +0 -18
- package/dist/core/plugins/impl/ai-summary/ai-summary-plugin-api.d.mts.map +0 -1
- package/dist/core/plugins/impl/ai-summary/ai-summary-plugin-api.mjs +0 -17
- package/dist/core/plugins/impl/ai-summary/ai-summary-plugin-api.mjs.map +0 -1
- package/dist/core/plugins/impl/ai-summary/plugin.d.mts +0 -6
- package/dist/core/plugins/impl/ai-summary/plugin.d.mts.map +0 -1
- package/dist/core/plugins/impl/ai-summary/plugin.mjs +0 -151
- package/dist/core/plugins/impl/ai-summary/plugin.mjs.map +0 -1
- package/dist/core/plugins/impl/errors/errors-plugin-api.d.mts +0 -17
- package/dist/core/plugins/impl/errors/errors-plugin-api.d.mts.map +0 -1
- package/dist/core/plugins/impl/errors/errors-plugin-api.mjs +0 -58
- package/dist/core/plugins/impl/errors/errors-plugin-api.mjs.map +0 -1
- package/dist/core/plugins/impl/errors/logic.d.mts +0 -15
- package/dist/core/plugins/impl/errors/logic.d.mts.map +0 -1
- package/dist/core/plugins/impl/errors/logic.mjs +0 -48
- package/dist/core/plugins/impl/errors/logic.mjs.map +0 -1
- package/dist/core/plugins/impl/errors/patches.d.mts +0 -13
- package/dist/core/plugins/impl/errors/patches.d.mts.map +0 -1
- package/dist/core/plugins/impl/errors/patches.mjs +0 -43
- package/dist/core/plugins/impl/errors/patches.mjs.map +0 -1
- package/dist/core/plugins/impl/errors/plugin.d.mts +0 -9
- package/dist/core/plugins/impl/errors/plugin.d.mts.map +0 -1
- package/dist/core/plugins/impl/errors/plugin.mjs +0 -91
- package/dist/core/plugins/impl/errors/plugin.mjs.map +0 -1
- package/dist/core/plugins/impl/page-events/page-events-plugin-api.d.mts +0 -19
- package/dist/core/plugins/impl/page-events/page-events-plugin-api.d.mts.map +0 -1
- package/dist/core/plugins/impl/page-events/page-events-plugin-api.mjs +0 -38
- package/dist/core/plugins/impl/page-events/page-events-plugin-api.mjs.map +0 -1
- package/dist/core/plugins/impl/page-events/plugin.d.mts +0 -6
- package/dist/core/plugins/impl/page-events/plugin.d.mts.map +0 -1
- package/dist/core/plugins/impl/page-events/plugin.mjs +0 -95
- package/dist/core/plugins/impl/page-events/plugin.mjs.map +0 -1
- package/dist/core/plugins/impl/rage-click/plugin.d.mts +0 -6
- package/dist/core/plugins/impl/rage-click/plugin.d.mts.map +0 -1
- package/dist/core/plugins/impl/rage-click/plugin.mjs +0 -38
- package/dist/core/plugins/impl/rage-click/plugin.mjs.map +0 -1
- package/dist/core/plugins/impl/rage-click/rage-click.layer.d.mts +0 -9
- package/dist/core/plugins/impl/rage-click/rage-click.layer.d.mts.map +0 -1
- package/dist/core/plugins/impl/rage-click/rage-click.layer.mjs +0 -35
- package/dist/core/plugins/impl/rage-click/rage-click.layer.mjs.map +0 -1
- package/dist/core/plugins/impl/rage-click/rage-click.service.d.mts +0 -16
- package/dist/core/plugins/impl/rage-click/rage-click.service.d.mts.map +0 -1
- package/dist/core/plugins/impl/rage-click/rage-click.service.mjs +0 -7
- package/dist/core/plugins/impl/rage-click/rage-click.service.mjs.map +0 -1
- package/dist/core/plugins/impl/rage-click/rage-click.test-layer.d.mts +0 -16
- package/dist/core/plugins/impl/rage-click/rage-click.test-layer.d.mts.map +0 -1
- package/dist/core/plugins/impl/rage-click/rage-click.test-layer.mjs +0 -18
- package/dist/core/plugins/impl/rage-click/rage-click.test-layer.mjs.map +0 -1
- package/dist/core/plugins/impl/replay/plugin.d.mts +0 -9
- package/dist/core/plugins/impl/replay/plugin.d.mts.map +0 -1
- package/dist/core/plugins/impl/replay/plugin.mjs +0 -83
- package/dist/core/plugins/impl/replay/plugin.mjs.map +0 -1
- package/dist/core/plugins/impl/replay/replay-plugin-api.d.mts +0 -18
- package/dist/core/plugins/impl/replay/replay-plugin-api.d.mts.map +0 -1
- package/dist/core/plugins/impl/replay/replay-plugin-api.mjs +0 -50
- package/dist/core/plugins/impl/replay/replay-plugin-api.mjs.map +0 -1
- package/dist/core/plugins/impl/server-tracing/plugin.d.mts +0 -13
- package/dist/core/plugins/impl/server-tracing/plugin.d.mts.map +0 -1
- package/dist/core/plugins/impl/server-tracing/plugin.mjs +0 -75
- package/dist/core/plugins/impl/server-tracing/plugin.mjs.map +0 -1
- package/dist/core/plugins/impl/server-tracing/server-tracing-plugin-api.d.mts +0 -16
- package/dist/core/plugins/impl/server-tracing/server-tracing-plugin-api.d.mts.map +0 -1
- package/dist/core/plugins/impl/server-tracing/server-tracing-plugin-api.mjs +0 -17
- package/dist/core/plugins/impl/server-tracing/server-tracing-plugin-api.mjs.map +0 -1
- package/dist/core/plugins/impl/server-tracing/tracing-logic.d.mts +0 -29
- package/dist/core/plugins/impl/server-tracing/tracing-logic.d.mts.map +0 -1
- package/dist/core/plugins/impl/server-tracing/tracing-logic.mjs +0 -55
- package/dist/core/plugins/impl/server-tracing/tracing-logic.mjs.map +0 -1
- package/dist/core/plugins/plugin-loader.d.mts +0 -75
- package/dist/core/plugins/plugin-loader.d.mts.map +0 -1
- package/dist/core/plugins/plugin-loader.mjs +0 -79
- package/dist/core/plugins/plugin-loader.mjs.map +0 -1
- package/dist/core/plugins/services/event-capture.d.mts +0 -12
- package/dist/core/plugins/services/event-capture.d.mts.map +0 -1
- package/dist/core/plugins/services/event-capture.layer.d.mts +0 -11
- package/dist/core/plugins/services/event-capture.layer.d.mts.map +0 -1
- package/dist/core/plugins/services/event-capture.layer.mjs +0 -79
- package/dist/core/plugins/services/event-capture.layer.mjs.map +0 -1
- package/dist/core/plugins/services/event-capture.mjs +0 -7
- package/dist/core/plugins/services/event-capture.mjs.map +0 -1
- package/dist/core/plugins/services/event-capture.test-layer.d.mts +0 -17
- package/dist/core/plugins/services/event-capture.test-layer.d.mts.map +0 -1
- package/dist/core/plugins/services/event-capture.test-layer.mjs +0 -21
- package/dist/core/plugins/services/event-capture.test-layer.mjs.map +0 -1
- package/dist/core/plugins/services/plugin-config.d.mts +0 -11
- package/dist/core/plugins/services/plugin-config.d.mts.map +0 -1
- package/dist/core/plugins/services/plugin-config.mjs +0 -7
- package/dist/core/plugins/services/plugin-config.mjs.map +0 -1
- package/dist/core/plugins/services/plugin-config.test-layer.d.mts +0 -60
- package/dist/core/plugins/services/plugin-config.test-layer.d.mts.map +0 -1
- package/dist/core/plugins/services/plugin-config.test-layer.mjs +0 -8
- package/dist/core/plugins/services/plugin-config.test-layer.mjs.map +0 -1
- package/dist/core/plugins/services/plugin-logger.d.mts +0 -14
- package/dist/core/plugins/services/plugin-logger.d.mts.map +0 -1
- package/dist/core/plugins/services/plugin-logger.mjs +0 -7
- package/dist/core/plugins/services/plugin-logger.mjs.map +0 -1
- package/dist/core/plugins/services/plugin-logger.test-layer.d.mts +0 -18
- package/dist/core/plugins/services/plugin-logger.test-layer.d.mts.map +0 -1
- package/dist/core/plugins/services/plugin-logger.test-layer.mjs +0 -28
- package/dist/core/plugins/services/plugin-logger.test-layer.mjs.map +0 -1
- package/dist/core/plugins/services/plugin-runtime.d.mts +0 -10
- package/dist/core/plugins/services/plugin-runtime.d.mts.map +0 -1
- package/dist/core/plugins/services/plugin-runtime.mjs +0 -7
- package/dist/core/plugins/services/plugin-runtime.mjs.map +0 -1
- package/dist/core/plugins/services/plugin-runtime.test-layer.d.mts +0 -16
- package/dist/core/plugins/services/plugin-runtime.test-layer.d.mts.map +0 -1
- package/dist/core/plugins/services/plugin-runtime.test-layer.mjs +0 -21
- package/dist/core/plugins/services/plugin-runtime.test-layer.mjs.map +0 -1
- package/dist/core/plugins/services/session-info.d.mts +0 -11
- package/dist/core/plugins/services/session-info.d.mts.map +0 -1
- package/dist/core/plugins/services/session-info.mjs +0 -7
- package/dist/core/plugins/services/session-info.mjs.map +0 -1
- package/dist/core/plugins/services/session-info.test-layer.d.mts +0 -18
- package/dist/core/plugins/services/session-info.test-layer.d.mts.map +0 -1
- package/dist/core/plugins/services/session-info.test-layer.mjs +0 -20
- package/dist/core/plugins/services/session-info.test-layer.mjs.map +0 -1
- package/dist/core/runtime/config.d.mts +0 -14
- package/dist/core/runtime/config.d.mts.map +0 -1
- package/dist/core/runtime/config.mjs +0 -33
- package/dist/core/runtime/config.mjs.map +0 -1
- package/dist/core/runtime/context.d.mts +0 -50
- package/dist/core/runtime/context.d.mts.map +0 -1
- package/dist/core/runtime/context.mjs +0 -46
- package/dist/core/runtime/context.mjs.map +0 -1
- package/dist/core/runtime/ingest-target.d.mts +0 -10
- package/dist/core/runtime/ingest-target.d.mts.map +0 -1
- package/dist/core/runtime/ingest-target.mjs +0 -15
- package/dist/core/runtime/ingest-target.mjs.map +0 -1
- package/dist/core/runtime/native-fetch.d.mts +0 -32
- package/dist/core/runtime/native-fetch.d.mts.map +0 -1
- package/dist/core/runtime/native-fetch.mjs +0 -49
- package/dist/core/runtime/native-fetch.mjs.map +0 -1
- package/dist/core/schemas.d.mts +0 -26
- package/dist/core/schemas.d.mts.map +0 -1
- package/dist/core/schemas.mjs +0 -1
- package/dist/effect/errors.d.mts +0 -22
- package/dist/effect/errors.d.mts.map +0 -1
- package/dist/effect/errors.mjs +0 -17
- package/dist/effect/errors.mjs.map +0 -1
- package/dist/effect/layers/circuit-breaker.layer.d.mts +0 -9
- package/dist/effect/layers/circuit-breaker.layer.d.mts.map +0 -1
- package/dist/effect/layers/circuit-breaker.layer.mjs +0 -9
- package/dist/effect/layers/circuit-breaker.layer.mjs.map +0 -1
- package/dist/effect/layers/circuit-breaker.layer.test-layer.d.mts +0 -18
- package/dist/effect/layers/circuit-breaker.layer.test-layer.d.mts.map +0 -1
- package/dist/effect/layers/circuit-breaker.layer.test-layer.mjs +0 -43
- package/dist/effect/layers/circuit-breaker.layer.test-layer.mjs.map +0 -1
- package/dist/effect/layers/config.layer.d.mts +0 -13
- package/dist/effect/layers/config.layer.d.mts.map +0 -1
- package/dist/effect/layers/config.layer.mjs +0 -21
- package/dist/effect/layers/config.layer.mjs.map +0 -1
- package/dist/effect/layers/context.layer.d.mts +0 -12
- package/dist/effect/layers/context.layer.d.mts.map +0 -1
- package/dist/effect/layers/context.layer.mjs +0 -14
- package/dist/effect/layers/context.layer.mjs.map +0 -1
- package/dist/effect/layers/http.layer.d.mts +0 -21
- package/dist/effect/layers/http.layer.d.mts.map +0 -1
- package/dist/effect/layers/http.layer.mjs +0 -118
- package/dist/effect/layers/http.layer.mjs.map +0 -1
- package/dist/effect/layers/queue.layer.d.mts +0 -31
- package/dist/effect/layers/queue.layer.d.mts.map +0 -1
- package/dist/effect/layers/queue.layer.mjs +0 -257
- package/dist/effect/layers/queue.layer.mjs.map +0 -1
- package/dist/effect/layers/queue.layer.test-layer.d.mts +0 -19
- package/dist/effect/layers/queue.layer.test-layer.d.mts.map +0 -1
- package/dist/effect/layers/queue.layer.test-layer.mjs +0 -44
- package/dist/effect/layers/queue.layer.test-layer.mjs.map +0 -1
- package/dist/effect/layers/session.layer.d.mts +0 -34
- package/dist/effect/layers/session.layer.d.mts.map +0 -1
- package/dist/effect/layers/session.layer.mjs +0 -127
- package/dist/effect/layers/session.layer.mjs.map +0 -1
- package/dist/effect/layers/storage.layer.d.mts +0 -50
- package/dist/effect/layers/storage.layer.d.mts.map +0 -1
- package/dist/effect/layers/storage.layer.mjs +0 -180
- package/dist/effect/layers/storage.layer.mjs.map +0 -1
- package/dist/effect/layers/test-utils.d.mts +0 -19
- package/dist/effect/layers/test-utils.d.mts.map +0 -1
- package/dist/effect/layers/test-utils.mjs +0 -32
- package/dist/effect/layers/test-utils.mjs.map +0 -1
- package/dist/effect/runtime-services.d.mts +0 -23
- package/dist/effect/runtime-services.d.mts.map +0 -1
- package/dist/effect/runtime-services.mjs +0 -79
- package/dist/effect/runtime-services.mjs.map +0 -1
- package/dist/effect/tags.d.mts +0 -58
- package/dist/effect/tags.d.mts.map +0 -1
- package/dist/effect/tags.mjs +0 -7
- package/dist/effect/tags.mjs.map +0 -1
- package/dist/hooks/use-runtime-and-plugins.d.mts +0 -7
- package/dist/hooks/use-runtime-and-plugins.d.mts.map +0 -1
- package/dist/hooks/use-runtime-and-plugins.mjs +0 -121
- package/dist/hooks/use-runtime-and-plugins.mjs.map +0 -1
- package/dist/hooks/use-session.d.mts +0 -40
- package/dist/hooks/use-session.d.mts.map +0 -1
- package/dist/hooks/use-session.mjs +0 -96
- package/dist/hooks/use-session.mjs.map +0 -1
- package/dist/package.mjs +0 -103
- package/dist/package.mjs.map +0 -1
- package/dist/server/auth.d.mts +0 -15
- package/dist/server/auth.d.mts.map +0 -1
- package/dist/server/auth.mjs +0 -45
- package/dist/server/auth.mjs.map +0 -1
- package/dist/server/capture.d.mts +0 -34
- package/dist/server/capture.d.mts.map +0 -1
- package/dist/server/capture.mjs +0 -172
- package/dist/server/capture.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../src/plugins/lib/types.ts"],"mappings":";;;KAEY,aAAA;AAAA,UAEK,aAAA;EACf,OAAA,WAAkB,SAAA,EAAW,IAAA,EAAM,CAAA,EAAG,OAAA,EAAS,eAAA,CAAgB,CAAA;EAC/D,YAAA;AAAA;AAAA,UAGe,MAAA;EAAA,SACN,IAAA;EACT,KAAA,CAAM,GAAA,EAAK,aAAA,GAAgB,aAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pages.d.mts","names":[],"sources":["../../src/plugins/pages.ts"],"mappings":";;;cAmDa,WAAA,EAAa,MAAA"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//#region src/plugins/pages.ts
|
|
2
|
+
const INTERACTIVE_TAGS = new Set([
|
|
3
|
+
"A",
|
|
4
|
+
"BUTTON",
|
|
5
|
+
"INPUT",
|
|
6
|
+
"SELECT",
|
|
7
|
+
"TEXTAREA"
|
|
8
|
+
]);
|
|
9
|
+
const INTERACTIVE_ROLES = new Set([
|
|
10
|
+
"button",
|
|
11
|
+
"link",
|
|
12
|
+
"tab",
|
|
13
|
+
"menuitem",
|
|
14
|
+
"checkbox",
|
|
15
|
+
"radio",
|
|
16
|
+
"switch"
|
|
17
|
+
]);
|
|
18
|
+
function isInteractive(el) {
|
|
19
|
+
if (INTERACTIVE_TAGS.has(el.tagName)) return true;
|
|
20
|
+
const role = el.getAttribute("role");
|
|
21
|
+
return role !== null && INTERACTIVE_ROLES.has(role);
|
|
22
|
+
}
|
|
23
|
+
function closestInteractive(target) {
|
|
24
|
+
if (!(target instanceof Element)) return null;
|
|
25
|
+
let el = target;
|
|
26
|
+
while (el) {
|
|
27
|
+
if (isInteractive(el)) return el;
|
|
28
|
+
el = el.parentElement;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
function elementDescriptor(el) {
|
|
33
|
+
return {
|
|
34
|
+
tag: el.tagName.toLowerCase(),
|
|
35
|
+
text: el.textContent?.trim().slice(0, 120) ?? null,
|
|
36
|
+
...el.id && { id: el.id },
|
|
37
|
+
...el.getAttribute("role") && { role: el.getAttribute("role") },
|
|
38
|
+
...el instanceof HTMLAnchorElement && { href: el.href }
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const pagesPlugin = {
|
|
42
|
+
name: "pages",
|
|
43
|
+
setup(ctx) {
|
|
44
|
+
let pageEnteredAt = Date.now();
|
|
45
|
+
let currentUrl = location.href;
|
|
46
|
+
const emitPageview = () => {
|
|
47
|
+
currentUrl = location.href;
|
|
48
|
+
pageEnteredAt = Date.now();
|
|
49
|
+
ctx.capture("pageview", {
|
|
50
|
+
url: currentUrl,
|
|
51
|
+
title: document.title || void 0
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
const emitPageleave = () => {
|
|
55
|
+
ctx.capture("pageleave", {
|
|
56
|
+
url: currentUrl,
|
|
57
|
+
durationMs: Date.now() - pageEnteredAt
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
const originalPushState = history.pushState;
|
|
61
|
+
const originalReplaceState = history.replaceState;
|
|
62
|
+
history.pushState = function(...args) {
|
|
63
|
+
emitPageleave();
|
|
64
|
+
originalPushState.apply(this, args);
|
|
65
|
+
emitPageview();
|
|
66
|
+
};
|
|
67
|
+
history.replaceState = function(...args) {
|
|
68
|
+
originalReplaceState.apply(this, args);
|
|
69
|
+
currentUrl = location.href;
|
|
70
|
+
};
|
|
71
|
+
const onPopState = () => {
|
|
72
|
+
emitPageleave();
|
|
73
|
+
emitPageview();
|
|
74
|
+
};
|
|
75
|
+
globalThis.addEventListener("popstate", onPopState);
|
|
76
|
+
const onBeforeUnload = () => {
|
|
77
|
+
emitPageleave();
|
|
78
|
+
};
|
|
79
|
+
globalThis.addEventListener("beforeunload", onBeforeUnload);
|
|
80
|
+
const onVisibilityChange = () => {
|
|
81
|
+
if (document.visibilityState === "hidden") emitPageleave();
|
|
82
|
+
};
|
|
83
|
+
globalThis.addEventListener("visibilitychange", onVisibilityChange);
|
|
84
|
+
const onClick = (event) => {
|
|
85
|
+
const el = closestInteractive(event.target);
|
|
86
|
+
if (!el) return;
|
|
87
|
+
ctx.capture("ui_event", { event: elementDescriptor(el) });
|
|
88
|
+
};
|
|
89
|
+
document.addEventListener("click", onClick, { capture: true });
|
|
90
|
+
emitPageview();
|
|
91
|
+
return () => {
|
|
92
|
+
history.pushState = originalPushState;
|
|
93
|
+
history.replaceState = originalReplaceState;
|
|
94
|
+
globalThis.removeEventListener("popstate", onPopState);
|
|
95
|
+
globalThis.removeEventListener("beforeunload", onBeforeUnload);
|
|
96
|
+
globalThis.removeEventListener("visibilitychange", onVisibilityChange);
|
|
97
|
+
document.removeEventListener("click", onClick, { capture: true });
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
//#endregion
|
|
102
|
+
export { pagesPlugin as default, pagesPlugin };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pages.mjs","names":[],"sources":["../../src/plugins/pages.ts"],"sourcesContent":["import type { Plugin } from \"./lib/types.js\";\n\nconst INTERACTIVE_TAGS = new Set([\n \"A\",\n \"BUTTON\",\n \"INPUT\",\n \"SELECT\",\n \"TEXTAREA\",\n]);\nconst INTERACTIVE_ROLES = new Set([\n \"button\",\n \"link\",\n \"tab\",\n \"menuitem\",\n \"checkbox\",\n \"radio\",\n \"switch\",\n]);\n\nfunction isInteractive(el: Element): boolean {\n if (INTERACTIVE_TAGS.has(el.tagName)) {\n return true;\n }\n const role = el.getAttribute(\"role\");\n return role !== null && INTERACTIVE_ROLES.has(role);\n}\n\nfunction closestInteractive(target: EventTarget | null): Element | null {\n if (!(target instanceof Element)) {\n return null;\n }\n let el: Element | null = target;\n while (el) {\n if (isInteractive(el)) {\n return el;\n }\n el = el.parentElement;\n }\n return null;\n}\n\nfunction elementDescriptor(el: Element): Record<string, unknown> {\n return {\n tag: el.tagName.toLowerCase(),\n text: el.textContent?.trim().slice(0, 120) ?? null,\n ...(el.id && { id: el.id }),\n ...(el.getAttribute(\"role\") && { role: el.getAttribute(\"role\") }),\n ...(el instanceof HTMLAnchorElement && { href: el.href }),\n };\n}\n\nexport const pagesPlugin: Plugin = {\n name: \"pages\",\n\n setup(ctx) {\n let pageEnteredAt = Date.now();\n let currentUrl = location.href;\n\n const emitPageview = () => {\n currentUrl = location.href;\n pageEnteredAt = Date.now();\n ctx.capture(\"pageview\", {\n url: currentUrl,\n title: document.title || undefined,\n });\n };\n\n const emitPageleave = () => {\n ctx.capture(\"pageleave\", {\n url: currentUrl,\n durationMs: Date.now() - pageEnteredAt,\n });\n };\n\n // Patch history API for SPA navigation\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n history.pushState = function (...args) {\n emitPageleave();\n originalPushState.apply(this, args);\n emitPageview();\n };\n\n history.replaceState = function (...args) {\n originalReplaceState.apply(this, args);\n // replaceState doesn't create a new entry — update URL tracking but skip pageview\n currentUrl = location.href;\n };\n\n const onPopState = () => {\n emitPageleave();\n emitPageview();\n };\n globalThis.addEventListener(\"popstate\", onPopState);\n\n const onBeforeUnload = () => {\n emitPageleave();\n };\n globalThis.addEventListener(\"beforeunload\", onBeforeUnload);\n\n const onVisibilityChange = () => {\n if (document.visibilityState === \"hidden\") {\n emitPageleave();\n }\n };\n globalThis.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n const onClick = (event: MouseEvent) => {\n const el = closestInteractive(event.target);\n if (!el) {\n return;\n }\n ctx.capture(\"ui_event\", { event: elementDescriptor(el) });\n };\n document.addEventListener(\"click\", onClick, { capture: true });\n\n // Initial pageview\n emitPageview();\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n globalThis.removeEventListener(\"popstate\", onPopState);\n globalThis.removeEventListener(\"beforeunload\", onBeforeUnload);\n globalThis.removeEventListener(\"visibilitychange\", onVisibilityChange);\n document.removeEventListener(\"click\", onClick, { capture: true });\n };\n },\n};\n\nexport default pagesPlugin;\n"],"mappings":";AAEA,MAAM,mBAAmB,IAAI,IAAI;CAC/B;CACA;CACA;CACA;CACA;CACD,CAAC;AACF,MAAM,oBAAoB,IAAI,IAAI;CAChC;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,SAAS,cAAc,IAAsB;AAC3C,KAAI,iBAAiB,IAAI,GAAG,QAAQ,CAClC,QAAO;CAET,MAAM,OAAO,GAAG,aAAa,OAAO;AACpC,QAAO,SAAS,QAAQ,kBAAkB,IAAI,KAAK;;AAGrD,SAAS,mBAAmB,QAA4C;AACtE,KAAI,EAAE,kBAAkB,SACtB,QAAO;CAET,IAAI,KAAqB;AACzB,QAAO,IAAI;AACT,MAAI,cAAc,GAAG,CACnB,QAAO;AAET,OAAK,GAAG;;AAEV,QAAO;;AAGT,SAAS,kBAAkB,IAAsC;AAC/D,QAAO;EACL,KAAK,GAAG,QAAQ,aAAa;EAC7B,MAAM,GAAG,aAAa,MAAM,CAAC,MAAM,GAAG,IAAI,IAAI;EAC9C,GAAI,GAAG,MAAM,EAAE,IAAI,GAAG,IAAI;EAC1B,GAAI,GAAG,aAAa,OAAO,IAAI,EAAE,MAAM,GAAG,aAAa,OAAO,EAAE;EAChE,GAAI,cAAc,qBAAqB,EAAE,MAAM,GAAG,MAAM;EACzD;;AAGH,MAAa,cAAsB;CACjC,MAAM;CAEN,MAAM,KAAK;EACT,IAAI,gBAAgB,KAAK,KAAK;EAC9B,IAAI,aAAa,SAAS;EAE1B,MAAM,qBAAqB;AACzB,gBAAa,SAAS;AACtB,mBAAgB,KAAK,KAAK;AAC1B,OAAI,QAAQ,YAAY;IACtB,KAAK;IACL,OAAO,SAAS,SAAS,KAAA;IAC1B,CAAC;;EAGJ,MAAM,sBAAsB;AAC1B,OAAI,QAAQ,aAAa;IACvB,KAAK;IACL,YAAY,KAAK,KAAK,GAAG;IAC1B,CAAC;;EAIJ,MAAM,oBAAoB,QAAQ;EAClC,MAAM,uBAAuB,QAAQ;AAErC,UAAQ,YAAY,SAAU,GAAG,MAAM;AACrC,kBAAe;AACf,qBAAkB,MAAM,MAAM,KAAK;AACnC,iBAAc;;AAGhB,UAAQ,eAAe,SAAU,GAAG,MAAM;AACxC,wBAAqB,MAAM,MAAM,KAAK;AAEtC,gBAAa,SAAS;;EAGxB,MAAM,mBAAmB;AACvB,kBAAe;AACf,iBAAc;;AAEhB,aAAW,iBAAiB,YAAY,WAAW;EAEnD,MAAM,uBAAuB;AAC3B,kBAAe;;AAEjB,aAAW,iBAAiB,gBAAgB,eAAe;EAE3D,MAAM,2BAA2B;AAC/B,OAAI,SAAS,oBAAoB,SAC/B,gBAAe;;AAGnB,aAAW,iBAAiB,oBAAoB,mBAAmB;EAEnE,MAAM,WAAW,UAAsB;GACrC,MAAM,KAAK,mBAAmB,MAAM,OAAO;AAC3C,OAAI,CAAC,GACH;AAEF,OAAI,QAAQ,YAAY,EAAE,OAAO,kBAAkB,GAAG,EAAE,CAAC;;AAE3D,WAAS,iBAAiB,SAAS,SAAS,EAAE,SAAS,MAAM,CAAC;AAG9D,gBAAc;AAEd,eAAa;AACX,WAAQ,YAAY;AACpB,WAAQ,eAAe;AACvB,cAAW,oBAAoB,YAAY,WAAW;AACtD,cAAW,oBAAoB,gBAAgB,eAAe;AAC9D,cAAW,oBAAoB,oBAAoB,mBAAmB;AACtE,YAAS,oBAAoB,SAAS,SAAS,EAAE,SAAS,MAAM,CAAC;;;CAGtE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rage-clicks.d.mts","names":[],"sources":["../../src/plugins/rage-clicks.ts"],"mappings":";;;cA6Ba,gBAAA,EAAkB,MAAA"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/plugins/rage-clicks.ts
|
|
2
|
+
const CLICK_THRESHOLD = 3;
|
|
3
|
+
const TIME_WINDOW_MS = 800;
|
|
4
|
+
const PROXIMITY_PX = 30;
|
|
5
|
+
function distance(a, b) {
|
|
6
|
+
return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2);
|
|
7
|
+
}
|
|
8
|
+
function selectorFor(el) {
|
|
9
|
+
if (!el) return "unknown";
|
|
10
|
+
if (el.id) return `#${el.id}`;
|
|
11
|
+
const classes = [...el.classList].slice(0, 3).join(".");
|
|
12
|
+
const tag = el.tagName.toLowerCase();
|
|
13
|
+
return classes ? `${tag}.${classes}` : tag;
|
|
14
|
+
}
|
|
15
|
+
const rageClicksPlugin = {
|
|
16
|
+
name: "rage-clicks",
|
|
17
|
+
setup(ctx) {
|
|
18
|
+
const clicks = [];
|
|
19
|
+
const onClick = (event) => {
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
clicks.push({
|
|
22
|
+
x: event.clientX,
|
|
23
|
+
y: event.clientY,
|
|
24
|
+
ts: now,
|
|
25
|
+
target: event.target instanceof Element ? event.target : null
|
|
26
|
+
});
|
|
27
|
+
while (clicks.length > 0 && now - (clicks[0]?.ts ?? 0) > TIME_WINDOW_MS) clicks.shift();
|
|
28
|
+
if (clicks.length < CLICK_THRESHOLD) return;
|
|
29
|
+
const anchor = clicks[0];
|
|
30
|
+
if (!anchor) return;
|
|
31
|
+
const clustered = clicks.filter((c) => distance(anchor, c) <= PROXIMITY_PX);
|
|
32
|
+
if (clustered.length < CLICK_THRESHOLD) return;
|
|
33
|
+
const last = clustered.at(-1);
|
|
34
|
+
if (!last) return;
|
|
35
|
+
ctx.capture("rage_click", {
|
|
36
|
+
count: clustered.length,
|
|
37
|
+
timeWindow: last.ts - anchor.ts,
|
|
38
|
+
selector: selectorFor(last.target),
|
|
39
|
+
text: last.target?.textContent?.trim().slice(0, 120) ?? "",
|
|
40
|
+
x: last.x,
|
|
41
|
+
y: last.y,
|
|
42
|
+
timestamp: now
|
|
43
|
+
});
|
|
44
|
+
clicks.length = 0;
|
|
45
|
+
};
|
|
46
|
+
document.addEventListener("click", onClick, { capture: true });
|
|
47
|
+
return () => {
|
|
48
|
+
document.removeEventListener("click", onClick, { capture: true });
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
//#endregion
|
|
53
|
+
export { rageClicksPlugin as default, rageClicksPlugin };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rage-clicks.mjs","names":[],"sources":["../../src/plugins/rage-clicks.ts"],"sourcesContent":["import type { Plugin } from \"./lib/types.js\";\n\nconst CLICK_THRESHOLD = 3;\nconst TIME_WINDOW_MS = 800;\nconst PROXIMITY_PX = 30;\n\ninterface Click {\n target: Element | null;\n ts: number;\n x: number;\n y: number;\n}\n\nfunction distance(a: Click, b: Click): number {\n return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2);\n}\n\nfunction selectorFor(el: Element | null): string {\n if (!el) {\n return \"unknown\";\n }\n if (el.id) {\n return `#${el.id}`;\n }\n const classes = [...el.classList].slice(0, 3).join(\".\");\n const tag = el.tagName.toLowerCase();\n return classes ? `${tag}.${classes}` : tag;\n}\n\nexport const rageClicksPlugin: Plugin = {\n name: \"rage-clicks\",\n\n setup(ctx) {\n const clicks: Click[] = [];\n\n const onClick = (event: MouseEvent) => {\n const now = Date.now();\n clicks.push({\n x: event.clientX,\n y: event.clientY,\n ts: now,\n target: event.target instanceof Element ? event.target : null,\n });\n\n // Prune stale clicks\n while (clicks.length > 0 && now - (clicks[0]?.ts ?? 0) > TIME_WINDOW_MS) {\n clicks.shift();\n }\n\n if (clicks.length < CLICK_THRESHOLD) {\n return;\n }\n\n // Check proximity — all clicks within PROXIMITY_PX of the first\n const anchor = clicks[0];\n if (!anchor) {\n return;\n }\n const clustered = clicks.filter(\n (c) => distance(anchor, c) <= PROXIMITY_PX\n );\n if (clustered.length < CLICK_THRESHOLD) {\n return;\n }\n\n const last = clustered.at(-1);\n if (!last) {\n return;\n }\n ctx.capture(\"rage_click\", {\n count: clustered.length,\n timeWindow: last.ts - anchor.ts,\n selector: selectorFor(last.target),\n text: last.target?.textContent?.trim().slice(0, 120) ?? \"\",\n x: last.x,\n y: last.y,\n timestamp: now,\n });\n\n clicks.length = 0;\n };\n\n document.addEventListener(\"click\", onClick, { capture: true });\n\n return () => {\n document.removeEventListener(\"click\", onClick, { capture: true });\n };\n },\n};\n\nexport default rageClicksPlugin;\n"],"mappings":";AAEA,MAAM,kBAAkB;AACxB,MAAM,iBAAiB;AACvB,MAAM,eAAe;AASrB,SAAS,SAAS,GAAU,GAAkB;AAC5C,QAAO,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE;;AAGvD,SAAS,YAAY,IAA4B;AAC/C,KAAI,CAAC,GACH,QAAO;AAET,KAAI,GAAG,GACL,QAAO,IAAI,GAAG;CAEhB,MAAM,UAAU,CAAC,GAAG,GAAG,UAAU,CAAC,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI;CACvD,MAAM,MAAM,GAAG,QAAQ,aAAa;AACpC,QAAO,UAAU,GAAG,IAAI,GAAG,YAAY;;AAGzC,MAAa,mBAA2B;CACtC,MAAM;CAEN,MAAM,KAAK;EACT,MAAM,SAAkB,EAAE;EAE1B,MAAM,WAAW,UAAsB;GACrC,MAAM,MAAM,KAAK,KAAK;AACtB,UAAO,KAAK;IACV,GAAG,MAAM;IACT,GAAG,MAAM;IACT,IAAI;IACJ,QAAQ,MAAM,kBAAkB,UAAU,MAAM,SAAS;IAC1D,CAAC;AAGF,UAAO,OAAO,SAAS,KAAK,OAAO,OAAO,IAAI,MAAM,KAAK,eACvD,QAAO,OAAO;AAGhB,OAAI,OAAO,SAAS,gBAClB;GAIF,MAAM,SAAS,OAAO;AACtB,OAAI,CAAC,OACH;GAEF,MAAM,YAAY,OAAO,QACtB,MAAM,SAAS,QAAQ,EAAE,IAAI,aAC/B;AACD,OAAI,UAAU,SAAS,gBACrB;GAGF,MAAM,OAAO,UAAU,GAAG,GAAG;AAC7B,OAAI,CAAC,KACH;AAEF,OAAI,QAAQ,cAAc;IACxB,OAAO,UAAU;IACjB,YAAY,KAAK,KAAK,OAAO;IAC7B,UAAU,YAAY,KAAK,OAAO;IAClC,MAAM,KAAK,QAAQ,aAAa,MAAM,CAAC,MAAM,GAAG,IAAI,IAAI;IACxD,GAAG,KAAK;IACR,GAAG,KAAK;IACR,WAAW;IACZ,CAAC;AAEF,UAAO,SAAS;;AAGlB,WAAS,iBAAiB,SAAS,SAAS,EAAE,SAAS,MAAM,CAAC;AAE9D,eAAa;AACX,YAAS,oBAAoB,SAAS,SAAS,EAAE,SAAS,MAAM,CAAC;;;CAGtE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.d.mts","names":[],"sources":["../../src/plugins/replay.ts"],"mappings":";;;cAOa,YAAA,EAAc,MAAA"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createLogger } from "../util/log.mjs";
|
|
2
|
+
//#region src/plugins/replay.ts
|
|
3
|
+
const log = createLogger("replay");
|
|
4
|
+
const FLUSH_INTERVAL_MS = 1e4;
|
|
5
|
+
const replayPlugin = {
|
|
6
|
+
name: "replay",
|
|
7
|
+
setup(ctx) {
|
|
8
|
+
let stopFn = null;
|
|
9
|
+
let events = [];
|
|
10
|
+
let flushTimer = null;
|
|
11
|
+
let firstTs = null;
|
|
12
|
+
let lastTs = null;
|
|
13
|
+
const flush = () => {
|
|
14
|
+
if (events.length === 0) return;
|
|
15
|
+
const chunk = events;
|
|
16
|
+
events = [];
|
|
17
|
+
const fts = firstTs;
|
|
18
|
+
const lts = lastTs;
|
|
19
|
+
firstTs = null;
|
|
20
|
+
lastTs = null;
|
|
21
|
+
ctx.capture("replay_chunk", {
|
|
22
|
+
ts: Date.now(),
|
|
23
|
+
count: chunk.length,
|
|
24
|
+
events: chunk,
|
|
25
|
+
...fts !== null && { first_event_ts: fts },
|
|
26
|
+
...lts !== null && { last_event_ts: lts }
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
const onVisibilityChange = () => {
|
|
30
|
+
if (document.visibilityState === "hidden") flush();
|
|
31
|
+
};
|
|
32
|
+
const onBeforeUnload = () => {
|
|
33
|
+
flush();
|
|
34
|
+
};
|
|
35
|
+
const init = async () => {
|
|
36
|
+
try {
|
|
37
|
+
stopFn = (await import("rrweb")).record({ emit(event) {
|
|
38
|
+
const ts = Date.now();
|
|
39
|
+
if (firstTs === null) firstTs = ts;
|
|
40
|
+
lastTs = ts;
|
|
41
|
+
events.push(JSON.stringify(event));
|
|
42
|
+
} }) ?? null;
|
|
43
|
+
flushTimer = setInterval(flush, FLUSH_INTERVAL_MS);
|
|
44
|
+
globalThis.addEventListener("visibilitychange", onVisibilityChange);
|
|
45
|
+
globalThis.addEventListener("beforeunload", onBeforeUnload);
|
|
46
|
+
log.debug("recording started");
|
|
47
|
+
} catch {
|
|
48
|
+
log.error("rrweb failed to load, replay disabled");
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
init().catch(() => {});
|
|
52
|
+
return () => {
|
|
53
|
+
flush();
|
|
54
|
+
stopFn?.();
|
|
55
|
+
if (flushTimer) clearInterval(flushTimer);
|
|
56
|
+
globalThis.removeEventListener("visibilitychange", onVisibilityChange);
|
|
57
|
+
globalThis.removeEventListener("beforeunload", onBeforeUnload);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
62
|
+
export { replayPlugin as default, replayPlugin };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.mjs","names":[],"sources":["../../src/plugins/replay.ts"],"sourcesContent":["import { createLogger } from \"../util/log.js\";\nimport type { Plugin } from \"./lib/types.js\";\n\nconst log = createLogger(\"replay\");\n\nconst FLUSH_INTERVAL_MS = 10_000;\n\nexport const replayPlugin: Plugin = {\n name: \"replay\",\n\n setup(ctx) {\n let stopFn: (() => void) | null = null;\n let events: string[] = [];\n let flushTimer: ReturnType<typeof setInterval> | null = null;\n let firstTs: number | null = null;\n let lastTs: number | null = null;\n\n const flush = () => {\n if (events.length === 0) {\n return;\n }\n const chunk = events;\n events = [];\n const fts = firstTs;\n const lts = lastTs;\n firstTs = null;\n lastTs = null;\n\n ctx.capture(\"replay_chunk\", {\n ts: Date.now(),\n count: chunk.length,\n events: chunk,\n ...(fts !== null && { first_event_ts: fts }),\n ...(lts !== null && { last_event_ts: lts }),\n });\n };\n\n const onVisibilityChange = () => {\n if (document.visibilityState === \"hidden\") {\n flush();\n }\n };\n const onBeforeUnload = () => {\n flush();\n };\n\n const init = async () => {\n try {\n const rrweb = await import(\"rrweb\");\n stopFn =\n rrweb.record({\n emit(event) {\n const ts = Date.now();\n if (firstTs === null) {\n firstTs = ts;\n }\n lastTs = ts;\n events.push(JSON.stringify(event));\n },\n }) ?? null;\n\n flushTimer = setInterval(flush, FLUSH_INTERVAL_MS);\n globalThis.addEventListener(\"visibilitychange\", onVisibilityChange);\n globalThis.addEventListener(\"beforeunload\", onBeforeUnload);\n log.debug(\"recording started\");\n } catch {\n log.error(\"rrweb failed to load, replay disabled\");\n }\n };\n\n init().catch(() => {\n // rrweb load failure is non-fatal\n });\n\n return () => {\n flush();\n stopFn?.();\n if (flushTimer) {\n clearInterval(flushTimer);\n }\n globalThis.removeEventListener(\"visibilitychange\", onVisibilityChange);\n globalThis.removeEventListener(\"beforeunload\", onBeforeUnload);\n };\n },\n};\n\nexport default replayPlugin;\n"],"mappings":";;AAGA,MAAM,MAAM,aAAa,SAAS;AAElC,MAAM,oBAAoB;AAE1B,MAAa,eAAuB;CAClC,MAAM;CAEN,MAAM,KAAK;EACT,IAAI,SAA8B;EAClC,IAAI,SAAmB,EAAE;EACzB,IAAI,aAAoD;EACxD,IAAI,UAAyB;EAC7B,IAAI,SAAwB;EAE5B,MAAM,cAAc;AAClB,OAAI,OAAO,WAAW,EACpB;GAEF,MAAM,QAAQ;AACd,YAAS,EAAE;GACX,MAAM,MAAM;GACZ,MAAM,MAAM;AACZ,aAAU;AACV,YAAS;AAET,OAAI,QAAQ,gBAAgB;IAC1B,IAAI,KAAK,KAAK;IACd,OAAO,MAAM;IACb,QAAQ;IACR,GAAI,QAAQ,QAAQ,EAAE,gBAAgB,KAAK;IAC3C,GAAI,QAAQ,QAAQ,EAAE,eAAe,KAAK;IAC3C,CAAC;;EAGJ,MAAM,2BAA2B;AAC/B,OAAI,SAAS,oBAAoB,SAC/B,QAAO;;EAGX,MAAM,uBAAuB;AAC3B,UAAO;;EAGT,MAAM,OAAO,YAAY;AACvB,OAAI;AAEF,cADc,MAAM,OAAO,UAEnB,OAAO,EACX,KAAK,OAAO;KACV,MAAM,KAAK,KAAK,KAAK;AACrB,SAAI,YAAY,KACd,WAAU;AAEZ,cAAS;AACT,YAAO,KAAK,KAAK,UAAU,MAAM,CAAC;OAErC,CAAC,IAAI;AAER,iBAAa,YAAY,OAAO,kBAAkB;AAClD,eAAW,iBAAiB,oBAAoB,mBAAmB;AACnE,eAAW,iBAAiB,gBAAgB,eAAe;AAC3D,QAAI,MAAM,oBAAoB;WACxB;AACN,QAAI,MAAM,wCAAwC;;;AAItD,QAAM,CAAC,YAAY,GAEjB;AAEF,eAAa;AACX,UAAO;AACP,aAAU;AACV,OAAI,WACF,eAAc,WAAW;AAE3B,cAAW,oBAAoB,oBAAoB,mBAAmB;AACtE,cAAW,oBAAoB,gBAAgB,eAAe;;;CAGnE"}
|
package/dist/provider.d.mts
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
|
-
import { InterfereProviderValue, Plugin } from "./core/schemas.mjs";
|
|
2
|
-
import { Config } from "@interfere/types/sdk/config";
|
|
3
1
|
import { PropsWithChildren, ReactNode } from "react";
|
|
2
|
+
import { EnvelopePayload, EventType } from "@interfere/types/sdk/envelope";
|
|
3
|
+
import { IdentifyParams } from "@interfere/types/sdk/identify";
|
|
4
4
|
|
|
5
5
|
//#region src/provider.d.ts
|
|
6
|
-
interface
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
interface InterfereContextValue {
|
|
7
|
+
capture<T extends EventType>(type: T, payload: EnvelopePayload<T>): void;
|
|
8
|
+
identity: {
|
|
9
|
+
get(): IdentifyParams | null;
|
|
10
|
+
set(params: IdentifyParams): void;
|
|
11
|
+
};
|
|
12
|
+
session: {
|
|
13
|
+
getId(): string | null;
|
|
14
|
+
getWindowId(): string | null;
|
|
15
|
+
};
|
|
9
16
|
}
|
|
10
17
|
declare function InterfereProvider({
|
|
11
|
-
children
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
declare function useInterfere(): InterfereProviderValue;
|
|
18
|
+
children
|
|
19
|
+
}: PropsWithChildren): ReactNode;
|
|
20
|
+
declare function useInterfere(): InterfereContextValue;
|
|
21
|
+
declare function useSession(): string | null;
|
|
16
22
|
//#endregion
|
|
17
|
-
export { InterfereProvider,
|
|
23
|
+
export { InterfereProvider, useInterfere, useSession };
|
package/dist/provider.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.mts","names":[],"sources":["../src/provider.tsx"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"provider.d.mts","names":[],"sources":["../src/provider.tsx"],"mappings":";;;;;UAeU,qBAAA;EACR,OAAA,WAAkB,SAAA,EAAW,IAAA,EAAM,CAAA,EAAG,OAAA,EAAS,eAAA,CAAgB,CAAA;EAC/D,QAAA;IACE,GAAA,IAAO,cAAA;IACP,GAAA,CAAI,MAAA,EAAQ,cAAA;EAAA;EAEd,OAAA;IACE,KAAA;IACA,WAAA;EAAA;AAAA;AAAA,iBAMY,iBAAA,CAAA;EAAoB;AAAA,GAAY,iBAAA,GAAoB,SAAA;AAAA,iBAUpD,YAAA,CAAA,GAAgB,qBAAA;AAAA,iBAQhB,UAAA,CAAA"}
|
package/dist/provider.mjs
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
|
|
3
|
-
import { capture } from "./client.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import { createContext, useContext, useMemo } from "react";
|
|
2
|
+
import { identity, session } from "./tracking/api.mjs";
|
|
3
|
+
import { capture } from "./internal/client.mjs";
|
|
4
|
+
import { createContext, useContext } from "react";
|
|
6
5
|
import { jsx } from "react/jsx-runtime";
|
|
7
|
-
|
|
8
6
|
//#region src/provider.tsx
|
|
9
|
-
const InterfereContext = createContext(
|
|
10
|
-
function InterfereProvider({ children
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
value: contextValue,
|
|
7
|
+
const InterfereContext = createContext(null);
|
|
8
|
+
function InterfereProvider({ children }) {
|
|
9
|
+
return /* @__PURE__ */ jsx(InterfereContext, {
|
|
10
|
+
value: {
|
|
11
|
+
capture,
|
|
12
|
+
identity,
|
|
13
|
+
session
|
|
14
|
+
},
|
|
18
15
|
children
|
|
19
16
|
});
|
|
20
17
|
}
|
|
21
18
|
function useInterfere() {
|
|
22
|
-
|
|
19
|
+
const ctx = useContext(InterfereContext);
|
|
20
|
+
if (!ctx) throw new Error("useInterfere must be used within <InterfereProvider>");
|
|
21
|
+
return ctx;
|
|
22
|
+
}
|
|
23
|
+
function useSession() {
|
|
24
|
+
return useInterfere().session.getId();
|
|
23
25
|
}
|
|
24
|
-
|
|
25
26
|
//#endregion
|
|
26
|
-
export { InterfereProvider, useInterfere };
|
|
27
|
+
export { InterfereProvider, useInterfere, useSession };
|
package/dist/provider.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.mjs","names":[],"sources":["../src/provider.tsx"],"sourcesContent":["\"use client\";\n\nimport type {
|
|
1
|
+
{"version":3,"file":"provider.mjs","names":[],"sources":["../src/provider.tsx"],"sourcesContent":["\"use client\";\n\nimport type { EnvelopePayload, EventType } from \"@interfere/types/sdk/envelope\";\nimport type { IdentifyParams } from \"@interfere/types/sdk/identify\";\n\nimport {\n createContext,\n type PropsWithChildren,\n type ReactNode,\n useContext,\n} from \"react\";\n\nimport { capture } from \"./internal/client.js\";\nimport { identity, session } from \"./tracking/api.js\";\n\ninterface InterfereContextValue {\n capture<T extends EventType>(type: T, payload: EnvelopePayload<T>): void;\n identity: {\n get(): IdentifyParams | null;\n set(params: IdentifyParams): void;\n };\n session: {\n getId(): string | null;\n getWindowId(): string | null;\n };\n}\n\nconst InterfereContext = createContext<InterfereContextValue | null>(null);\n\nexport function InterfereProvider({ children }: PropsWithChildren): ReactNode {\n const value: InterfereContextValue = {\n capture,\n identity,\n session,\n };\n\n return <InterfereContext value={value}>{children}</InterfereContext>;\n}\n\nexport function useInterfere(): InterfereContextValue {\n const ctx = useContext(InterfereContext);\n if (!ctx) {\n throw new Error(\"useInterfere must be used within <InterfereProvider>\");\n }\n return ctx;\n}\n\nexport function useSession(): string | null {\n return useInterfere().session.getId();\n}\n"],"mappings":";;;;;;AA2BA,MAAM,mBAAmB,cAA4C,KAAK;AAE1E,SAAgB,kBAAkB,EAAE,YAA0C;AAO5E,QAAO,oBAAC,kBAAD;EAAkB,OANY;GACnC;GACA;GACA;GACD;EAEuC;EAA4B,CAAA;;AAGtE,SAAgB,eAAsC;CACpD,MAAM,MAAM,WAAW,iBAAiB;AACxC,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,uDAAuD;AAEzE,QAAO;;AAGT,SAAgB,aAA4B;AAC1C,QAAO,cAAc,CAAC,QAAQ,OAAO"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PluginOverrides } from "../plugins/lib/loader.mjs";
|
|
2
|
+
import { IngestTarget } from "../transport/http.mjs";
|
|
3
|
+
import { IdentifyParams } from "@interfere/types/sdk/identify";
|
|
4
|
+
|
|
5
|
+
//#region src/tracking/api.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Bootstraps the tracking subsystem: creates the session manager,
|
|
8
|
+
* resolves the session target, and optionally starts Fingerprint Pro.
|
|
9
|
+
*/
|
|
10
|
+
declare function bootstrap(sessionTarget: IngestTarget, plugins?: PluginOverrides): void;
|
|
11
|
+
declare const session: {
|
|
12
|
+
getId(): string | null;
|
|
13
|
+
getWindowId(): string | null;
|
|
14
|
+
};
|
|
15
|
+
declare const identity: {
|
|
16
|
+
get(): IdentifyParams | null;
|
|
17
|
+
set(params: IdentifyParams): void;
|
|
18
|
+
clear(): void;
|
|
19
|
+
};
|
|
20
|
+
declare function teardown(): void;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { bootstrap, identity, session, teardown };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.mts","names":[],"sources":["../../src/tracking/api.ts"],"mappings":";;;;;;;AA4DA;;iBAAgB,SAAA,CACd,aAAA,EAAe,YAAA,EACf,OAAA,GAAU,eAAA;AAAA,cAgBC,OAAA;EAQZ,KAAA;EAAA,WAAA;AAAA;AAAA,cAEY,QAAA;SACJ,cAAA;cAIK,cAAA;;;iBA4BE,QAAA,CAAA"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { createLogger } from "../util/log.mjs";
|
|
2
|
+
import { resolveFeatures } from "../plugins/lib/loader.mjs";
|
|
3
|
+
import { SessionManager } from "./session.mjs";
|
|
4
|
+
import { getVisitorId, initVisitor, whenVisitorReady } from "./visitor.mjs";
|
|
5
|
+
import { make } from "tctx/traceparent";
|
|
6
|
+
//#region src/tracking/api.ts
|
|
7
|
+
const log = createLogger("tracking");
|
|
8
|
+
let mgr = null;
|
|
9
|
+
let target = null;
|
|
10
|
+
let currentIdentity = null;
|
|
11
|
+
let identifiedSessionId = null;
|
|
12
|
+
function fire(url, method, headers, body) {
|
|
13
|
+
const h = Object.fromEntries(headers.entries());
|
|
14
|
+
h.traceparent = String(make());
|
|
15
|
+
const sid = mgr?.getSessionId();
|
|
16
|
+
if (sid) h["x-interfere-session"] = sid;
|
|
17
|
+
const vid = getVisitorId();
|
|
18
|
+
if (vid) h["x-interfere-visitor"] = vid;
|
|
19
|
+
fetch(url, {
|
|
20
|
+
method,
|
|
21
|
+
headers: h,
|
|
22
|
+
body: JSON.stringify(body),
|
|
23
|
+
keepalive: true
|
|
24
|
+
}).catch(() => {
|
|
25
|
+
log.warn("fire-and-forget %s failed", method);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async function onRotate(sessionId) {
|
|
29
|
+
if (!target) return;
|
|
30
|
+
const visitorId = await whenVisitorReady();
|
|
31
|
+
log.debug("POST session %s (visitor=%s)", sessionId, visitorId ?? "pending");
|
|
32
|
+
fire(target.url, "POST", target.headers, {
|
|
33
|
+
sessionId,
|
|
34
|
+
visitorId
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Bootstraps the tracking subsystem: creates the session manager,
|
|
39
|
+
* resolves the session target, and optionally starts Fingerprint Pro.
|
|
40
|
+
*/
|
|
41
|
+
function bootstrap(sessionTarget, plugins) {
|
|
42
|
+
target = sessionTarget;
|
|
43
|
+
if (resolveFeatures(plugins).fingerprint) initVisitor();
|
|
44
|
+
mgr = new SessionManager((id) => {
|
|
45
|
+
onRotate(id).catch(() => {});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const session = {
|
|
49
|
+
getId() {
|
|
50
|
+
return mgr?.getSessionId() ?? null;
|
|
51
|
+
},
|
|
52
|
+
getWindowId() {
|
|
53
|
+
return mgr?.getWindowId() ?? null;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const identity = {
|
|
57
|
+
get() {
|
|
58
|
+
return currentIdentity;
|
|
59
|
+
},
|
|
60
|
+
set(params) {
|
|
61
|
+
if (!(mgr && target)) return;
|
|
62
|
+
const sessionId = mgr.getSessionId();
|
|
63
|
+
if (identifiedSessionId === sessionId) {
|
|
64
|
+
log.debug("skipped, already identified for session %s", sessionId);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
currentIdentity = params;
|
|
68
|
+
identifiedSessionId = sessionId;
|
|
69
|
+
const visitorId = getVisitorId();
|
|
70
|
+
log.info("PUT session %s → user %s", sessionId, params.identifier);
|
|
71
|
+
fire(target.url, "PUT", target.headers, {
|
|
72
|
+
sessionId,
|
|
73
|
+
visitorId,
|
|
74
|
+
...params
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
clear() {
|
|
78
|
+
currentIdentity = null;
|
|
79
|
+
identifiedSessionId = null;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
function teardown() {
|
|
83
|
+
identity.clear();
|
|
84
|
+
mgr = null;
|
|
85
|
+
target = null;
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
export { bootstrap, identity, session, teardown };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.mjs","names":[],"sources":["../../src/tracking/api.ts"],"sourcesContent":["import type { IdentifyParams } from \"@interfere/types/sdk/identify\";\n\nimport { make } from \"tctx/traceparent\";\n\nimport type { PluginOverrides } from \"../plugins/lib/loader.js\";\nimport { resolveFeatures } from \"../plugins/lib/loader.js\";\nimport type { IngestTarget } from \"../transport/http.js\";\nimport { createLogger } from \"../util/log.js\";\nimport { SessionManager } from \"./session.js\";\nimport { getVisitorId, initVisitor, whenVisitorReady } from \"./visitor.js\";\n\nconst log = createLogger(\"tracking\");\n\nlet mgr: SessionManager | null = null;\nlet target: IngestTarget | null = null;\nlet currentIdentity: IdentifyParams | null = null;\nlet identifiedSessionId: string | null = null;\n\nfunction fire(\n url: string,\n method: \"POST\" | \"PUT\",\n headers: Headers,\n body: unknown\n): void {\n const h: Record<string, string> = Object.fromEntries(headers.entries());\n h.traceparent = String(make());\n\n const sid = mgr?.getSessionId();\n if (sid) {\n h[\"x-interfere-session\"] = sid;\n }\n\n const vid = getVisitorId();\n if (vid) {\n h[\"x-interfere-visitor\"] = vid;\n }\n\n fetch(url, {\n method,\n headers: h,\n body: JSON.stringify(body),\n keepalive: true,\n }).catch(() => {\n log.warn(\"fire-and-forget %s failed\", method);\n });\n}\n\nasync function onRotate(sessionId: string): Promise<void> {\n if (!target) {\n return;\n }\n const visitorId = await whenVisitorReady();\n log.debug(\"POST session %s (visitor=%s)\", sessionId, visitorId ?? \"pending\");\n fire(target.url, \"POST\", target.headers, { sessionId, visitorId });\n}\n\n/**\n * Bootstraps the tracking subsystem: creates the session manager,\n * resolves the session target, and optionally starts Fingerprint Pro.\n */\nexport function bootstrap(\n sessionTarget: IngestTarget,\n plugins?: PluginOverrides\n): void {\n target = sessionTarget;\n\n const features = resolveFeatures(plugins);\n if (features.fingerprint) {\n initVisitor();\n }\n\n mgr = new SessionManager((id) => {\n onRotate(id).catch(() => {\n /* best-effort */\n });\n });\n}\n\nexport const session = {\n getId(): string | null {\n return mgr?.getSessionId() ?? null;\n },\n\n getWindowId(): string | null {\n return mgr?.getWindowId() ?? null;\n },\n};\n\nexport const identity = {\n get(): IdentifyParams | null {\n return currentIdentity;\n },\n\n set(params: IdentifyParams): void {\n if (!(mgr && target)) {\n return;\n }\n const sessionId = mgr.getSessionId();\n if (identifiedSessionId === sessionId) {\n log.debug(\"skipped, already identified for session %s\", sessionId);\n return;\n }\n\n currentIdentity = params;\n identifiedSessionId = sessionId;\n\n const visitorId = getVisitorId();\n log.info(\"PUT session %s → user %s\", sessionId, params.identifier);\n fire(target.url, \"PUT\", target.headers, {\n sessionId,\n visitorId,\n ...params,\n });\n },\n\n clear(): void {\n currentIdentity = null;\n identifiedSessionId = null;\n },\n};\n\nexport function teardown(): void {\n identity.clear();\n mgr = null;\n target = null;\n}\n"],"mappings":";;;;;;AAWA,MAAM,MAAM,aAAa,WAAW;AAEpC,IAAI,MAA6B;AACjC,IAAI,SAA8B;AAClC,IAAI,kBAAyC;AAC7C,IAAI,sBAAqC;AAEzC,SAAS,KACP,KACA,QACA,SACA,MACM;CACN,MAAM,IAA4B,OAAO,YAAY,QAAQ,SAAS,CAAC;AACvE,GAAE,cAAc,OAAO,MAAM,CAAC;CAE9B,MAAM,MAAM,KAAK,cAAc;AAC/B,KAAI,IACF,GAAE,yBAAyB;CAG7B,MAAM,MAAM,cAAc;AAC1B,KAAI,IACF,GAAE,yBAAyB;AAG7B,OAAM,KAAK;EACT;EACA,SAAS;EACT,MAAM,KAAK,UAAU,KAAK;EAC1B,WAAW;EACZ,CAAC,CAAC,YAAY;AACb,MAAI,KAAK,6BAA6B,OAAO;GAC7C;;AAGJ,eAAe,SAAS,WAAkC;AACxD,KAAI,CAAC,OACH;CAEF,MAAM,YAAY,MAAM,kBAAkB;AAC1C,KAAI,MAAM,gCAAgC,WAAW,aAAa,UAAU;AAC5E,MAAK,OAAO,KAAK,QAAQ,OAAO,SAAS;EAAE;EAAW;EAAW,CAAC;;;;;;AAOpE,SAAgB,UACd,eACA,SACM;AACN,UAAS;AAGT,KADiB,gBAAgB,QAAQ,CAC5B,YACX,cAAa;AAGf,OAAM,IAAI,gBAAgB,OAAO;AAC/B,WAAS,GAAG,CAAC,YAAY,GAEvB;GACF;;AAGJ,MAAa,UAAU;CACrB,QAAuB;AACrB,SAAO,KAAK,cAAc,IAAI;;CAGhC,cAA6B;AAC3B,SAAO,KAAK,aAAa,IAAI;;CAEhC;AAED,MAAa,WAAW;CACtB,MAA6B;AAC3B,SAAO;;CAGT,IAAI,QAA8B;AAChC,MAAI,EAAE,OAAO,QACX;EAEF,MAAM,YAAY,IAAI,cAAc;AACpC,MAAI,wBAAwB,WAAW;AACrC,OAAI,MAAM,8CAA8C,UAAU;AAClE;;AAGF,oBAAkB;AAClB,wBAAsB;EAEtB,MAAM,YAAY,cAAc;AAChC,MAAI,KAAK,4BAA4B,WAAW,OAAO,WAAW;AAClE,OAAK,OAAO,KAAK,OAAO,OAAO,SAAS;GACtC;GACA;GACA,GAAG;GACJ,CAAC;;CAGJ,QAAc;AACZ,oBAAkB;AAClB,wBAAsB;;CAEzB;AAED,SAAgB,WAAiB;AAC/B,UAAS,OAAO;AAChB,OAAM;AACN,UAAS"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/tracking/session.d.ts
|
|
2
|
+
type OnRotate = (sessionId: string) => void;
|
|
3
|
+
declare class SessionManager {
|
|
4
|
+
sessionId: string | null;
|
|
5
|
+
windowId: string | null;
|
|
6
|
+
private readonly local;
|
|
7
|
+
private readonly session;
|
|
8
|
+
private readonly onRotate;
|
|
9
|
+
constructor(onRotate?: OnRotate);
|
|
10
|
+
getSessionId(): string;
|
|
11
|
+
getWindowId(): string;
|
|
12
|
+
whenReady(signal?: AbortSignal): Promise<string>;
|
|
13
|
+
private restore;
|
|
14
|
+
private rotate;
|
|
15
|
+
private touch;
|
|
16
|
+
private isExpired;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { OnRotate, SessionManager };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.mts","names":[],"sources":["../../src/tracking/session.ts"],"mappings":";KAqBY,QAAA,IAAY,SAAA;AAAA,cAEX,cAAA;EACX,SAAA;EACA,QAAA;EAAA,iBACiB,KAAA;EAAA,iBACA,OAAA;EAAA,iBACA,QAAA;cAEL,QAAA,GAAW,QAAA;EAOvB,YAAA,CAAA;EAQA,WAAA,CAAA;EAgBA,SAAA,CAAU,MAAA,GAAS,WAAA,GAAc,OAAA;EAAA,QAsBzB,OAAA;EAAA,QAWA,MAAA;EAAA,QAQA,KAAA;EAAA,QAIA,SAAA;AAAA"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { nanoid } from "nanoid";
|
|
2
|
+
import { v7 } from "uuid";
|
|
3
|
+
//#region src/tracking/session.ts
|
|
4
|
+
const SESSION_ID_KEY = "interfere:session_id";
|
|
5
|
+
const LAST_ACTIVITY_KEY = "interfere:last_activity";
|
|
6
|
+
const WINDOW_ID_KEY = "interfere:window_id";
|
|
7
|
+
const SESSION_TIMEOUT_MS = 1800 * 1e3;
|
|
8
|
+
const POLL_INTERVAL_MS = 250;
|
|
9
|
+
function tryStorage(type) {
|
|
10
|
+
try {
|
|
11
|
+
const s = globalThis[type];
|
|
12
|
+
const key = "__interfere_probe__";
|
|
13
|
+
s.setItem(key, "1");
|
|
14
|
+
s.removeItem(key);
|
|
15
|
+
return s;
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
var SessionManager = class {
|
|
21
|
+
sessionId = null;
|
|
22
|
+
windowId = null;
|
|
23
|
+
local;
|
|
24
|
+
session;
|
|
25
|
+
onRotate = null;
|
|
26
|
+
constructor(onRotate) {
|
|
27
|
+
this.local = tryStorage("localStorage");
|
|
28
|
+
this.session = tryStorage("sessionStorage");
|
|
29
|
+
this.onRotate = onRotate ?? null;
|
|
30
|
+
this.restore();
|
|
31
|
+
}
|
|
32
|
+
getSessionId() {
|
|
33
|
+
if (this.sessionId && !this.isExpired()) {
|
|
34
|
+
this.touch();
|
|
35
|
+
return this.sessionId;
|
|
36
|
+
}
|
|
37
|
+
return this.rotate();
|
|
38
|
+
}
|
|
39
|
+
getWindowId() {
|
|
40
|
+
if (this.windowId) return this.windowId;
|
|
41
|
+
const stored = this.session?.getItem(WINDOW_ID_KEY);
|
|
42
|
+
if (stored) {
|
|
43
|
+
this.windowId = stored;
|
|
44
|
+
return stored;
|
|
45
|
+
}
|
|
46
|
+
this.windowId = `win_${nanoid(10)}`;
|
|
47
|
+
this.session?.setItem(WINDOW_ID_KEY, this.windowId);
|
|
48
|
+
return this.windowId;
|
|
49
|
+
}
|
|
50
|
+
whenReady(signal) {
|
|
51
|
+
const id = this.sessionId;
|
|
52
|
+
if (id && !this.isExpired()) return Promise.resolve(id);
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const check = setInterval(() => {
|
|
55
|
+
if (signal?.aborted) {
|
|
56
|
+
clearInterval(check);
|
|
57
|
+
reject(signal.reason);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const current = this.getSessionId();
|
|
61
|
+
if (current) {
|
|
62
|
+
clearInterval(check);
|
|
63
|
+
resolve(current);
|
|
64
|
+
}
|
|
65
|
+
}, POLL_INTERVAL_MS);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
restore() {
|
|
69
|
+
const stored = this.local?.getItem(SESSION_ID_KEY);
|
|
70
|
+
if (stored && !this.isExpired()) {
|
|
71
|
+
this.sessionId = stored;
|
|
72
|
+
this.touch();
|
|
73
|
+
} else this.rotate();
|
|
74
|
+
}
|
|
75
|
+
rotate() {
|
|
76
|
+
this.sessionId = v7();
|
|
77
|
+
this.local?.setItem(SESSION_ID_KEY, this.sessionId);
|
|
78
|
+
this.touch();
|
|
79
|
+
this.onRotate?.(this.sessionId);
|
|
80
|
+
return this.sessionId;
|
|
81
|
+
}
|
|
82
|
+
touch() {
|
|
83
|
+
this.local?.setItem(LAST_ACTIVITY_KEY, String(Date.now()));
|
|
84
|
+
}
|
|
85
|
+
isExpired() {
|
|
86
|
+
const raw = this.local?.getItem(LAST_ACTIVITY_KEY);
|
|
87
|
+
if (!raw) return true;
|
|
88
|
+
return Date.now() - Number(raw) > SESSION_TIMEOUT_MS;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
//#endregion
|
|
92
|
+
export { SessionManager };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.mjs","names":["uuidv7"],"sources":["../../src/tracking/session.ts"],"sourcesContent":["import { nanoid } from \"nanoid\";\nimport { v7 as uuidv7 } from \"uuid\";\n\nconst SESSION_ID_KEY = \"interfere:session_id\";\nconst LAST_ACTIVITY_KEY = \"interfere:last_activity\";\nconst WINDOW_ID_KEY = \"interfere:window_id\";\nconst SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes\nconst POLL_INTERVAL_MS = 250;\n\nfunction tryStorage(type: \"localStorage\" | \"sessionStorage\"): Storage | null {\n try {\n const s = globalThis[type];\n const key = \"__interfere_probe__\";\n s.setItem(key, \"1\");\n s.removeItem(key);\n return s;\n } catch {\n return null;\n }\n}\n\nexport type OnRotate = (sessionId: string) => void;\n\nexport class SessionManager {\n sessionId: string | null = null;\n windowId: string | null = null;\n private readonly local: Storage | null;\n private readonly session: Storage | null;\n private readonly onRotate: OnRotate | null = null;\n\n constructor(onRotate?: OnRotate) {\n this.local = tryStorage(\"localStorage\");\n this.session = tryStorage(\"sessionStorage\");\n this.onRotate = onRotate ?? null;\n this.restore();\n }\n\n getSessionId(): string {\n if (this.sessionId && !this.isExpired()) {\n this.touch();\n return this.sessionId;\n }\n return this.rotate();\n }\n\n getWindowId(): string {\n if (this.windowId) {\n return this.windowId;\n }\n\n const stored = this.session?.getItem(WINDOW_ID_KEY);\n if (stored) {\n this.windowId = stored;\n return stored;\n }\n\n this.windowId = `win_${nanoid(10)}`;\n this.session?.setItem(WINDOW_ID_KEY, this.windowId);\n return this.windowId;\n }\n\n whenReady(signal?: AbortSignal): Promise<string> {\n const id = this.sessionId;\n if (id && !this.isExpired()) {\n return Promise.resolve(id);\n }\n\n return new Promise((resolve, reject) => {\n const check = setInterval(() => {\n if (signal?.aborted) {\n clearInterval(check);\n reject(signal.reason as Error);\n return;\n }\n const current = this.getSessionId();\n if (current) {\n clearInterval(check);\n resolve(current);\n }\n }, POLL_INTERVAL_MS);\n });\n }\n\n private restore(): void {\n const stored = this.local?.getItem(SESSION_ID_KEY);\n\n if (stored && !this.isExpired()) {\n this.sessionId = stored;\n this.touch();\n } else {\n this.rotate();\n }\n }\n\n private rotate(): string {\n this.sessionId = uuidv7();\n this.local?.setItem(SESSION_ID_KEY, this.sessionId);\n this.touch();\n this.onRotate?.(this.sessionId);\n return this.sessionId;\n }\n\n private touch(): void {\n this.local?.setItem(LAST_ACTIVITY_KEY, String(Date.now()));\n }\n\n private isExpired(): boolean {\n const raw = this.local?.getItem(LAST_ACTIVITY_KEY);\n\n if (!raw) {\n return true;\n }\n return Date.now() - Number(raw) > SESSION_TIMEOUT_MS;\n }\n}\n"],"mappings":";;;AAGA,MAAM,iBAAiB;AACvB,MAAM,oBAAoB;AAC1B,MAAM,gBAAgB;AACtB,MAAM,qBAAqB,OAAU;AACrC,MAAM,mBAAmB;AAEzB,SAAS,WAAW,MAAyD;AAC3E,KAAI;EACF,MAAM,IAAI,WAAW;EACrB,MAAM,MAAM;AACZ,IAAE,QAAQ,KAAK,IAAI;AACnB,IAAE,WAAW,IAAI;AACjB,SAAO;SACD;AACN,SAAO;;;AAMX,IAAa,iBAAb,MAA4B;CAC1B,YAA2B;CAC3B,WAA0B;CAC1B;CACA;CACA,WAA6C;CAE7C,YAAY,UAAqB;AAC/B,OAAK,QAAQ,WAAW,eAAe;AACvC,OAAK,UAAU,WAAW,iBAAiB;AAC3C,OAAK,WAAW,YAAY;AAC5B,OAAK,SAAS;;CAGhB,eAAuB;AACrB,MAAI,KAAK,aAAa,CAAC,KAAK,WAAW,EAAE;AACvC,QAAK,OAAO;AACZ,UAAO,KAAK;;AAEd,SAAO,KAAK,QAAQ;;CAGtB,cAAsB;AACpB,MAAI,KAAK,SACP,QAAO,KAAK;EAGd,MAAM,SAAS,KAAK,SAAS,QAAQ,cAAc;AACnD,MAAI,QAAQ;AACV,QAAK,WAAW;AAChB,UAAO;;AAGT,OAAK,WAAW,OAAO,OAAO,GAAG;AACjC,OAAK,SAAS,QAAQ,eAAe,KAAK,SAAS;AACnD,SAAO,KAAK;;CAGd,UAAU,QAAuC;EAC/C,MAAM,KAAK,KAAK;AAChB,MAAI,MAAM,CAAC,KAAK,WAAW,CACzB,QAAO,QAAQ,QAAQ,GAAG;AAG5B,SAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,QAAQ,kBAAkB;AAC9B,QAAI,QAAQ,SAAS;AACnB,mBAAc,MAAM;AACpB,YAAO,OAAO,OAAgB;AAC9B;;IAEF,MAAM,UAAU,KAAK,cAAc;AACnC,QAAI,SAAS;AACX,mBAAc,MAAM;AACpB,aAAQ,QAAQ;;MAEjB,iBAAiB;IACpB;;CAGJ,UAAwB;EACtB,MAAM,SAAS,KAAK,OAAO,QAAQ,eAAe;AAElD,MAAI,UAAU,CAAC,KAAK,WAAW,EAAE;AAC/B,QAAK,YAAY;AACjB,QAAK,OAAO;QAEZ,MAAK,QAAQ;;CAIjB,SAAyB;AACvB,OAAK,YAAYA,IAAQ;AACzB,OAAK,OAAO,QAAQ,gBAAgB,KAAK,UAAU;AACnD,OAAK,OAAO;AACZ,OAAK,WAAW,KAAK,UAAU;AAC/B,SAAO,KAAK;;CAGd,QAAsB;AACpB,OAAK,OAAO,QAAQ,mBAAmB,OAAO,KAAK,KAAK,CAAC,CAAC;;CAG5D,YAA6B;EAC3B,MAAM,MAAM,KAAK,OAAO,QAAQ,kBAAkB;AAElD,MAAI,CAAC,IACH,QAAO;AAET,SAAO,KAAK,KAAK,GAAG,OAAO,IAAI,GAAG"}
|