@active-reach/web-sdk 1.10.0 → 1.11.1
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/aegis.min.js +1 -1
- package/dist/aegis.min.js.map +1 -1
- package/dist/{analytics-Cc4-QQBf.mjs → analytics-6PR9ERDS.mjs} +190 -4
- package/dist/analytics-6PR9ERDS.mjs.map +1 -0
- package/dist/core/analytics.d.ts +1 -0
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/governance/index.d.ts +2 -0
- package/dist/governance/index.d.ts.map +1 -1
- package/dist/governance/trait-governor.d.ts +63 -0
- package/dist/governance/trait-governor.d.ts.map +1 -0
- package/dist/index.js +234 -10
- package/dist/index.js.map +1 -1
- package/dist/react.js +1 -1
- package/dist/runtime/AegisMessageRuntime.d.ts +0 -1
- package/dist/runtime/AegisMessageRuntime.d.ts.map +1 -1
- package/dist/state/intent_ledger.d.ts +136 -0
- package/dist/state/intent_ledger.d.ts.map +1 -0
- package/dist/triggers/IntentRuleEvaluator.d.ts +167 -0
- package/dist/triggers/IntentRuleEvaluator.d.ts.map +1 -0
- package/dist/triggers/IntentSnapshotCollector.d.ts +83 -0
- package/dist/triggers/IntentSnapshotCollector.d.ts.map +1 -0
- package/dist/triggers/SelectorBinder.d.ts +118 -0
- package/dist/triggers/SelectorBinder.d.ts.map +1 -0
- package/dist/triggers/TriggerEngine.d.ts +177 -0
- package/dist/triggers/TriggerEngine.d.ts.map +1 -1
- package/dist/triggers/index.d.ts +7 -1
- package/dist/triggers/index.d.ts.map +1 -1
- package/dist/widgets/AegisWidgetManager.d.ts +6 -0
- package/dist/widgets/AegisWidgetManager.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/analytics-Cc4-QQBf.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Storage, l as logger, A as Aegis } from "./analytics-
|
|
2
|
-
import { B, E, N, R, m } from "./analytics-
|
|
1
|
+
import { S as Storage, l as logger, A as Aegis } from "./analytics-6PR9ERDS.mjs";
|
|
2
|
+
import { B, E, N, R, m } from "./analytics-6PR9ERDS.mjs";
|
|
3
3
|
import { AegisWebPush } from "./push/AegisWebPush.js";
|
|
4
4
|
function debounce(func, wait) {
|
|
5
5
|
let timeoutId = null;
|
|
@@ -1024,10 +1024,10 @@ const _AegisInAppManager = class _AegisInAppManager {
|
|
|
1024
1024
|
this.log(`Error parsing SSE event: ${error}`, "error");
|
|
1025
1025
|
}
|
|
1026
1026
|
});
|
|
1027
|
-
this.eventSource.addEventListener("heartbeat", (
|
|
1027
|
+
this.eventSource.addEventListener("heartbeat", () => {
|
|
1028
1028
|
this.log("SSE heartbeat received");
|
|
1029
1029
|
});
|
|
1030
|
-
this.eventSource.addEventListener("error", (
|
|
1030
|
+
this.eventSource.addEventListener("error", () => {
|
|
1031
1031
|
var _a;
|
|
1032
1032
|
this.log("SSE connection error", "error");
|
|
1033
1033
|
if (((_a = this.eventSource) == null ? void 0 : _a.readyState) === EventSource.CLOSED) {
|
|
@@ -3216,7 +3216,7 @@ class AegisPlacementManager {
|
|
|
3216
3216
|
wrapper.innerHTML = this.sanitizeHTML(html);
|
|
3217
3217
|
const links = wrapper.querySelectorAll("a");
|
|
3218
3218
|
links.forEach((link) => {
|
|
3219
|
-
link.addEventListener("click", (
|
|
3219
|
+
link.addEventListener("click", () => {
|
|
3220
3220
|
this.trackEvent(content.placement_id, content.variant_id, "click");
|
|
3221
3221
|
});
|
|
3222
3222
|
});
|
|
@@ -3349,10 +3349,10 @@ class AegisPlacementManager {
|
|
|
3349
3349
|
this.log(`Error parsing SSE event: ${error}`, true);
|
|
3350
3350
|
}
|
|
3351
3351
|
});
|
|
3352
|
-
this.eventSource.addEventListener("heartbeat", (
|
|
3352
|
+
this.eventSource.addEventListener("heartbeat", () => {
|
|
3353
3353
|
this.log("SSE heartbeat received");
|
|
3354
3354
|
});
|
|
3355
|
-
this.eventSource.addEventListener("error", (
|
|
3355
|
+
this.eventSource.addEventListener("error", () => {
|
|
3356
3356
|
var _a;
|
|
3357
3357
|
this.log("SSE connection error", true);
|
|
3358
3358
|
if (((_a = this.eventSource) == null ? void 0 : _a.readyState) === EventSource.CLOSED) {
|
|
@@ -3519,6 +3519,28 @@ class TriggerEngine {
|
|
|
3519
3519
|
this.visibilityChangeEnabled = false;
|
|
3520
3520
|
this.backButtonEnabled = false;
|
|
3521
3521
|
this.backButtonFired = false;
|
|
3522
|
+
this.rageClickEnabled = false;
|
|
3523
|
+
this.rageClickConfig = { threshold: 3, windowMs: 1e3 };
|
|
3524
|
+
this.rageClickBuffer = /* @__PURE__ */ new Map();
|
|
3525
|
+
this.rageClickFiredInBurst = /* @__PURE__ */ new Set();
|
|
3526
|
+
this.hoverEnabled = false;
|
|
3527
|
+
this.hoverStartAt = /* @__PURE__ */ new Map();
|
|
3528
|
+
this.hoverLastMs = /* @__PURE__ */ new Map();
|
|
3529
|
+
this.hoverThresholds = /* @__PURE__ */ new Map([
|
|
3530
|
+
["price", [1500]],
|
|
3531
|
+
["cta", [1500]]
|
|
3532
|
+
]);
|
|
3533
|
+
this.hoverThresholdsFired = /* @__PURE__ */ new Map();
|
|
3534
|
+
this.hoverThresholdTimers = /* @__PURE__ */ new Map();
|
|
3535
|
+
this.mouseVelEnabled = false;
|
|
3536
|
+
this.mouseVelConfig = {
|
|
3537
|
+
threshold: 1.5,
|
|
3538
|
+
windowMs: 400,
|
|
3539
|
+
cooldownMs: 5e3
|
|
3540
|
+
};
|
|
3541
|
+
this.mouseSamples = [];
|
|
3542
|
+
this.mouseVelLast = 0;
|
|
3543
|
+
this.mouseVelLastFiredAt = 0;
|
|
3522
3544
|
this.handleScroll = () => {
|
|
3523
3545
|
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
3524
3546
|
const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;
|
|
@@ -3663,6 +3685,204 @@ class TriggerEngine {
|
|
|
3663
3685
|
registerBackButton() {
|
|
3664
3686
|
this.backButtonEnabled = true;
|
|
3665
3687
|
}
|
|
3688
|
+
/**
|
|
3689
|
+
* Enable rage-click detection (P2 Task 1).
|
|
3690
|
+
*
|
|
3691
|
+
* After calling this, feed click events via `noteClick(intent)`.
|
|
3692
|
+
* SelectorBinder (P1 Task 5) is the upstream source; the wiring lives
|
|
3693
|
+
* in IntentSnapshotCollector (P2 Task 5). Calling `noteClick` without
|
|
3694
|
+
* a prior `registerRageClick()` is a no-op so legacy callers don't
|
|
3695
|
+
* accidentally enable the feature.
|
|
3696
|
+
*/
|
|
3697
|
+
registerRageClick(config) {
|
|
3698
|
+
this.rageClickEnabled = true;
|
|
3699
|
+
if (config) {
|
|
3700
|
+
this.rageClickConfig = {
|
|
3701
|
+
threshold: config.threshold ?? this.rageClickConfig.threshold,
|
|
3702
|
+
windowMs: config.windowMs ?? this.rageClickConfig.windowMs
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
}
|
|
3706
|
+
/**
|
|
3707
|
+
* Feed a click event for rage-click detection. Returns the current
|
|
3708
|
+
* count within-window for the intent — useful for the snapshot
|
|
3709
|
+
* collector (P2 Task 5) which updates IntentRuleEvaluator with
|
|
3710
|
+
* `rage_click_count` continuously, not just at the burst threshold.
|
|
3711
|
+
*
|
|
3712
|
+
* Idempotent w.r.t. `registerRageClick` — when not enabled, this
|
|
3713
|
+
* returns 0 without buffering.
|
|
3714
|
+
*/
|
|
3715
|
+
noteClick(intent, timestamp = Date.now()) {
|
|
3716
|
+
if (!this.rageClickEnabled) return 0;
|
|
3717
|
+
const buf = this.rageClickBuffer.get(intent) ?? [];
|
|
3718
|
+
const cutoff = timestamp - this.rageClickConfig.windowMs;
|
|
3719
|
+
let trimStart = 0;
|
|
3720
|
+
while (trimStart < buf.length && buf[trimStart] < cutoff) trimStart++;
|
|
3721
|
+
const recent = trimStart === 0 ? buf : buf.slice(trimStart);
|
|
3722
|
+
recent.push(timestamp);
|
|
3723
|
+
this.rageClickBuffer.set(intent, recent);
|
|
3724
|
+
if (recent.length < this.rageClickConfig.threshold) {
|
|
3725
|
+
this.rageClickFiredInBurst.delete(intent);
|
|
3726
|
+
}
|
|
3727
|
+
if (recent.length >= this.rageClickConfig.threshold && !this.rageClickFiredInBurst.has(intent)) {
|
|
3728
|
+
this.rageClickFiredInBurst.add(intent);
|
|
3729
|
+
this.emit("rage_click", {
|
|
3730
|
+
intent,
|
|
3731
|
+
count: recent.length,
|
|
3732
|
+
window_ms: this.rageClickConfig.windowMs
|
|
3733
|
+
});
|
|
3734
|
+
}
|
|
3735
|
+
return recent.length;
|
|
3736
|
+
}
|
|
3737
|
+
/** Snapshot the current rage_click count for an intent. The
|
|
3738
|
+
* IntentSnapshotCollector (P2 Task 5) calls this when building the
|
|
3739
|
+
* `rage_click_count` signal value for the IntentRuleEvaluator. */
|
|
3740
|
+
getRageClickCount(intent) {
|
|
3741
|
+
if (!this.rageClickEnabled) return 0;
|
|
3742
|
+
const buf = this.rageClickBuffer.get(intent);
|
|
3743
|
+
if (!buf) return 0;
|
|
3744
|
+
const cutoff = Date.now() - this.rageClickConfig.windowMs;
|
|
3745
|
+
let live = 0;
|
|
3746
|
+
for (const ts of buf) {
|
|
3747
|
+
if (ts >= cutoff) live++;
|
|
3748
|
+
}
|
|
3749
|
+
return live;
|
|
3750
|
+
}
|
|
3751
|
+
/**
|
|
3752
|
+
* Enable hover-dwell tracking (P2 Task 2). After registration, feed
|
|
3753
|
+
* hover events via `noteHoverStart(intent, ts)` / `noteHoverEnd`.
|
|
3754
|
+
* SelectorBinder (P1 Task 5) is the upstream source; wiring lands in
|
|
3755
|
+
* P2 Task 5.
|
|
3756
|
+
*/
|
|
3757
|
+
registerHoverDwell(config) {
|
|
3758
|
+
this.hoverEnabled = true;
|
|
3759
|
+
if (config == null ? void 0 : config.thresholdsByIntent) {
|
|
3760
|
+
for (const [intent, thresholds] of Object.entries(config.thresholdsByIntent)) {
|
|
3761
|
+
if (thresholds) this.hoverThresholds.set(intent, thresholds);
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3765
|
+
/** Record the start of a hover on the named intent. Schedules
|
|
3766
|
+
* per-threshold timers that emit `<intent>_hover_dwell_<ms>` when
|
|
3767
|
+
* the active hover passes each registered threshold. */
|
|
3768
|
+
noteHoverStart(intent, timestamp = Date.now()) {
|
|
3769
|
+
if (!this.hoverEnabled) return;
|
|
3770
|
+
if (this.hoverStartAt.has(intent)) {
|
|
3771
|
+
this.noteHoverEnd(intent, timestamp);
|
|
3772
|
+
}
|
|
3773
|
+
this.hoverStartAt.set(intent, timestamp);
|
|
3774
|
+
this.hoverThresholdsFired.set(intent, /* @__PURE__ */ new Set());
|
|
3775
|
+
const thresholds = this.hoverThresholds.get(intent);
|
|
3776
|
+
if (thresholds) {
|
|
3777
|
+
const timers = [];
|
|
3778
|
+
for (const ms of thresholds) {
|
|
3779
|
+
const timerId = setTimeout(() => {
|
|
3780
|
+
if (!this.hoverStartAt.has(intent)) return;
|
|
3781
|
+
const firedSet = this.hoverThresholdsFired.get(intent);
|
|
3782
|
+
if (firedSet == null ? void 0 : firedSet.has(ms)) return;
|
|
3783
|
+
firedSet == null ? void 0 : firedSet.add(ms);
|
|
3784
|
+
this.emit(`${intent}_hover_dwell_${ms}`, {
|
|
3785
|
+
intent,
|
|
3786
|
+
threshold_ms: ms,
|
|
3787
|
+
actual_ms: Date.now() - timestamp
|
|
3788
|
+
});
|
|
3789
|
+
}, ms);
|
|
3790
|
+
timers.push(timerId);
|
|
3791
|
+
}
|
|
3792
|
+
this.hoverThresholdTimers.set(intent, timers);
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
/** Record the end of a hover on the named intent. Updates the sticky
|
|
3796
|
+
* `lastHoverMs` so `getHoverMs` keeps returning the last-known
|
|
3797
|
+
* duration until the next hover_start. */
|
|
3798
|
+
noteHoverEnd(intent, timestamp = Date.now()) {
|
|
3799
|
+
if (!this.hoverEnabled) return;
|
|
3800
|
+
const startAt = this.hoverStartAt.get(intent);
|
|
3801
|
+
if (startAt === void 0) return;
|
|
3802
|
+
const dwell = Math.max(0, timestamp - startAt);
|
|
3803
|
+
this.hoverLastMs.set(intent, dwell);
|
|
3804
|
+
this.hoverStartAt.delete(intent);
|
|
3805
|
+
const pending = this.hoverThresholdTimers.get(intent);
|
|
3806
|
+
if (pending) {
|
|
3807
|
+
for (const id of pending) clearTimeout(id);
|
|
3808
|
+
this.hoverThresholdTimers.delete(intent);
|
|
3809
|
+
}
|
|
3810
|
+
this.hoverThresholdsFired.delete(intent);
|
|
3811
|
+
}
|
|
3812
|
+
/**
|
|
3813
|
+
* Enable mouse-velocity-to-top tracking (P2 Task 3). After
|
|
3814
|
+
* registration, feed positions via `noteMousePosition(x, y, ts?)`.
|
|
3815
|
+
* IntentSnapshotCollector (P2 Task 5) wires window 'mousemove' to
|
|
3816
|
+
* this method, with throttling to ~100ms per the plan.
|
|
3817
|
+
*/
|
|
3818
|
+
registerMouseVelocityToTop(config) {
|
|
3819
|
+
this.mouseVelEnabled = true;
|
|
3820
|
+
if (config) {
|
|
3821
|
+
this.mouseVelConfig = {
|
|
3822
|
+
threshold: config.threshold ?? this.mouseVelConfig.threshold,
|
|
3823
|
+
windowMs: config.windowMs ?? this.mouseVelConfig.windowMs,
|
|
3824
|
+
cooldownMs: config.cooldownMs ?? this.mouseVelConfig.cooldownMs
|
|
3825
|
+
};
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
/** Feed a mouse-position sample. Recomputes the rolling-window
|
|
3829
|
+
* velocity-to-top and fires `mouse_velocity_to_top` when the value
|
|
3830
|
+
* crosses the configured threshold (subject to cooldown). */
|
|
3831
|
+
noteMousePosition(x, y, timestamp = Date.now()) {
|
|
3832
|
+
if (!this.mouseVelEnabled) return 0;
|
|
3833
|
+
const cutoff = timestamp - this.mouseVelConfig.windowMs;
|
|
3834
|
+
let trimStart = 0;
|
|
3835
|
+
while (trimStart < this.mouseSamples.length && this.mouseSamples[trimStart].ts < cutoff) {
|
|
3836
|
+
trimStart++;
|
|
3837
|
+
}
|
|
3838
|
+
if (trimStart > 0) this.mouseSamples = this.mouseSamples.slice(trimStart);
|
|
3839
|
+
this.mouseSamples.push({ x, y, ts: timestamp });
|
|
3840
|
+
if (this.mouseSamples.length < 2) {
|
|
3841
|
+
this.mouseVelLast = 0;
|
|
3842
|
+
return 0;
|
|
3843
|
+
}
|
|
3844
|
+
const oldest = this.mouseSamples[0];
|
|
3845
|
+
const newest = this.mouseSamples[this.mouseSamples.length - 1];
|
|
3846
|
+
const dt = newest.ts - oldest.ts;
|
|
3847
|
+
if (dt <= 0) {
|
|
3848
|
+
this.mouseVelLast = 0;
|
|
3849
|
+
return 0;
|
|
3850
|
+
}
|
|
3851
|
+
const velocity = -(newest.y - oldest.y) / dt;
|
|
3852
|
+
this.mouseVelLast = velocity;
|
|
3853
|
+
const cooldownOk = this.mouseVelLastFiredAt === 0 || timestamp - this.mouseVelLastFiredAt >= this.mouseVelConfig.cooldownMs;
|
|
3854
|
+
if (velocity >= this.mouseVelConfig.threshold && cooldownOk) {
|
|
3855
|
+
this.mouseVelLastFiredAt = timestamp;
|
|
3856
|
+
this.emit("mouse_velocity_to_top", {
|
|
3857
|
+
velocity,
|
|
3858
|
+
threshold: this.mouseVelConfig.threshold,
|
|
3859
|
+
window_ms: this.mouseVelConfig.windowMs,
|
|
3860
|
+
samples: this.mouseSamples.length
|
|
3861
|
+
});
|
|
3862
|
+
}
|
|
3863
|
+
return velocity;
|
|
3864
|
+
}
|
|
3865
|
+
/** Snapshot the current velocity-to-top in px/ms (positive = up).
|
|
3866
|
+
* Returns 0 when not enabled or insufficient samples. */
|
|
3867
|
+
getMouseVelocityToTop() {
|
|
3868
|
+
return this.mouseVelEnabled ? this.mouseVelLast : 0;
|
|
3869
|
+
}
|
|
3870
|
+
/**
|
|
3871
|
+
* Snapshot the hover dwell for an intent.
|
|
3872
|
+
* - Currently hovering: returns `Date.now() - hover_start_at`.
|
|
3873
|
+
* - Hovered before, not currently: returns the most recent hover's
|
|
3874
|
+
* duration (sticky — the rule evaluator queries this AFTER
|
|
3875
|
+
* mouseleave to see "did Priya hover the price ≥ 1.5s?").
|
|
3876
|
+
* - Never hovered: returns 0.
|
|
3877
|
+
*/
|
|
3878
|
+
getHoverMs(intent) {
|
|
3879
|
+
if (!this.hoverEnabled) return 0;
|
|
3880
|
+
const startAt = this.hoverStartAt.get(intent);
|
|
3881
|
+
if (startAt !== void 0) {
|
|
3882
|
+
return Math.max(0, Date.now() - startAt);
|
|
3883
|
+
}
|
|
3884
|
+
return this.hoverLastMs.get(intent) ?? 0;
|
|
3885
|
+
}
|
|
3666
3886
|
start() {
|
|
3667
3887
|
if (this.isStarted) {
|
|
3668
3888
|
return;
|
|
@@ -4543,7 +4763,14 @@ class AegisWidgetManager {
|
|
|
4543
4763
|
this.widgets = [];
|
|
4544
4764
|
this.isInitialized = false;
|
|
4545
4765
|
this.log("AegisWidgets destroyed");
|
|
4766
|
+
this.emitEvent("destroyed", {});
|
|
4546
4767
|
}
|
|
4768
|
+
/**
|
|
4769
|
+
* Fire a lifecycle event to the consumer-supplied callback. Public-API
|
|
4770
|
+
* hook configured via `AegisWidgetConfig.onEvent`. Currently only used
|
|
4771
|
+
* by external callers passing their own logger; internal widget lifecycle
|
|
4772
|
+
* events get wired through here as they're added.
|
|
4773
|
+
*/
|
|
4547
4774
|
emitEvent(eventType, data) {
|
|
4548
4775
|
if (this.onEvent) {
|
|
4549
4776
|
try {
|
|
@@ -6395,10 +6622,8 @@ class AegisWidgetManager {
|
|
|
6395
6622
|
class AegisMessageRuntime {
|
|
6396
6623
|
constructor(config) {
|
|
6397
6624
|
this.initialized = false;
|
|
6398
|
-
this.ownedTriggerEngine = null;
|
|
6399
6625
|
const triggerEngine = config.triggerEngine ?? new TriggerEngine();
|
|
6400
6626
|
const ownsTriggerEngine = !config.triggerEngine;
|
|
6401
|
-
if (ownsTriggerEngine) this.ownedTriggerEngine = triggerEngine;
|
|
6402
6627
|
this.widgets = new AegisWidgetManager({
|
|
6403
6628
|
writeKey: config.writeKey,
|
|
6404
6629
|
apiHost: config.apiHost,
|
|
@@ -6495,7 +6720,6 @@ class AegisMessageRuntime {
|
|
|
6495
6720
|
var _a, _b;
|
|
6496
6721
|
(_b = (_a = this.inApp).destroy) == null ? void 0 : _b.call(_a);
|
|
6497
6722
|
this.widgets.destroy();
|
|
6498
|
-
this.ownedTriggerEngine = null;
|
|
6499
6723
|
this.initialized = false;
|
|
6500
6724
|
}
|
|
6501
6725
|
/**
|