@active-reach/web-sdk 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aegis.min.js +1 -1
- package/dist/aegis.min.js.map +1 -1
- package/dist/{analytics-6PR9ERDS.mjs → analytics-DGt-CSgi.mjs} +333 -6
- package/dist/analytics-DGt-CSgi.mjs.map +1 -0
- package/dist/cdn.d.ts.map +1 -1
- package/dist/core/analytics.d.ts +102 -0
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/core/bootstrap.d.ts +9 -0
- package/dist/core/bootstrap.d.ts.map +1 -1
- package/dist/core/user-namespace.d.ts +48 -0
- package/dist/core/user-namespace.d.ts.map +1 -0
- package/dist/ecommerce/index.d.ts +7 -2
- package/dist/ecommerce/index.d.ts.map +1 -1
- package/dist/ecommerce/types.d.ts +4 -0
- package/dist/ecommerce/types.d.ts.map +1 -1
- package/dist/inapp/AegisInAppManager.d.ts +11 -0
- package/dist/inapp/AegisInAppManager.d.ts.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -6
- package/dist/index.js.map +1 -1
- package/dist/placements/AegisPlacementManager.d.ts +9 -0
- package/dist/placements/AegisPlacementManager.d.ts.map +1 -1
- package/dist/push/AegisWebPush.d.ts +11 -0
- package/dist/push/AegisWebPush.d.ts.map +1 -1
- package/dist/push/AegisWebPush.js +52 -1
- package/dist/push/AegisWebPush.js.map +1 -1
- package/dist/react.js +1 -1
- package/dist/runtime/AegisMessageRuntime.d.ts.map +1 -1
- package/dist/snippet.min.js +1 -1
- package/dist/widgets/AegisWidgetManager.d.ts +9 -0
- package/dist/widgets/AegisWidgetManager.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/analytics-6PR9ERDS.mjs.map +0 -1
|
@@ -1764,6 +1764,24 @@ class EcommerceTracker {
|
|
|
1764
1764
|
variant_id: wishlist.product.variant_id
|
|
1765
1765
|
});
|
|
1766
1766
|
}
|
|
1767
|
+
// -- Back-in-stock waitlist --
|
|
1768
|
+
//
|
|
1769
|
+
// Server-side substrate: contact_events row keyed on
|
|
1770
|
+
// (organization_id, contact_id, event_name='product_waitlisted',
|
|
1771
|
+
// event_properties['product_id']). Resolved by
|
|
1772
|
+
// product_event_trigger_service._get_waitlisted_contacts and fanned out via
|
|
1773
|
+
// catalog.back_in_stock journey trigger when stock_event_handler_worker
|
|
1774
|
+
// detects the SKU flipping back into stock.
|
|
1775
|
+
productWaitlisted(waitlist) {
|
|
1776
|
+
this.aegis.track("product_waitlisted", {
|
|
1777
|
+
product_id: waitlist.product.product_id,
|
|
1778
|
+
sku: waitlist.product.sku ?? waitlist.product.product_id,
|
|
1779
|
+
variant_id: waitlist.product.variant_id,
|
|
1780
|
+
name: waitlist.product.name,
|
|
1781
|
+
price: waitlist.product.price,
|
|
1782
|
+
channels: waitlist.channels
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1767
1785
|
// -- Promotions --
|
|
1768
1786
|
promotionViewed(promo) {
|
|
1769
1787
|
this.aegis.track("promotion_viewed", { ...promo });
|
|
@@ -2215,6 +2233,134 @@ const _TraitGovernor = class _TraitGovernor {
|
|
|
2215
2233
|
};
|
|
2216
2234
|
_TraitGovernor.WARN_CAP = 3;
|
|
2217
2235
|
let TraitGovernor = _TraitGovernor;
|
|
2236
|
+
const VALID_CHANNELS = /* @__PURE__ */ new Set([
|
|
2237
|
+
"email",
|
|
2238
|
+
"sms",
|
|
2239
|
+
"push",
|
|
2240
|
+
"webpush",
|
|
2241
|
+
"whatsapp",
|
|
2242
|
+
"rcs",
|
|
2243
|
+
"inapp"
|
|
2244
|
+
]);
|
|
2245
|
+
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2246
|
+
const PHONE_RE = /^\+[1-9]\d{7,14}$/;
|
|
2247
|
+
const SHA256_HEX_RE = /^[a-f0-9]{64}$/i;
|
|
2248
|
+
const ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
2249
|
+
class UserNamespace {
|
|
2250
|
+
constructor(aegis) {
|
|
2251
|
+
this.pendingTraits = {};
|
|
2252
|
+
this.aegis = aegis;
|
|
2253
|
+
}
|
|
2254
|
+
/**
|
|
2255
|
+
* Authoritative identity write. Flushes any pending pre-login traits
|
|
2256
|
+
* along with the supplied `traits` argument as a single identify call.
|
|
2257
|
+
*/
|
|
2258
|
+
login(userId, traits) {
|
|
2259
|
+
if (typeof userId !== "string" || userId.length === 0) {
|
|
2260
|
+
logger.warn("[user.login] userId must be a non-empty string");
|
|
2261
|
+
return;
|
|
2262
|
+
}
|
|
2263
|
+
const merged = { ...this.pendingTraits, ...traits || {} };
|
|
2264
|
+
this.pendingTraits = {};
|
|
2265
|
+
this.aegis.identify(userId, merged);
|
|
2266
|
+
}
|
|
2267
|
+
/**
|
|
2268
|
+
* Drops the current userId and any pending pre-login traits. Does NOT
|
|
2269
|
+
* fire a server-side logout event — that's a separate explicit `track`.
|
|
2270
|
+
*/
|
|
2271
|
+
logout() {
|
|
2272
|
+
this.pendingTraits = {};
|
|
2273
|
+
this.aegis.reset();
|
|
2274
|
+
}
|
|
2275
|
+
setAttribute(key, value) {
|
|
2276
|
+
if (typeof key !== "string" || key.length === 0) {
|
|
2277
|
+
logger.warn("[user.setAttribute] key must be a non-empty string");
|
|
2278
|
+
return;
|
|
2279
|
+
}
|
|
2280
|
+
this.writeTraits({ [key]: value });
|
|
2281
|
+
}
|
|
2282
|
+
setAttributes(map) {
|
|
2283
|
+
if (!map || typeof map !== "object") {
|
|
2284
|
+
logger.warn("[user.setAttributes] map must be an object");
|
|
2285
|
+
return;
|
|
2286
|
+
}
|
|
2287
|
+
this.writeTraits(map);
|
|
2288
|
+
}
|
|
2289
|
+
setEmail(email) {
|
|
2290
|
+
if (!EMAIL_RE.test(email)) {
|
|
2291
|
+
logger.warn("[user.setEmail] invalid email format");
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
this.writeTraits({ email: email.toLowerCase() });
|
|
2295
|
+
}
|
|
2296
|
+
setPhone(phone) {
|
|
2297
|
+
if (!PHONE_RE.test(phone)) {
|
|
2298
|
+
logger.warn("[user.setPhone] phone must be E.164 (e.g. +15551234567)");
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
2301
|
+
this.writeTraits({ phone });
|
|
2302
|
+
}
|
|
2303
|
+
setHashedEmail(sha256Hex) {
|
|
2304
|
+
if (!SHA256_HEX_RE.test(sha256Hex)) {
|
|
2305
|
+
logger.warn("[user.setHashedEmail] expected 64-char hex SHA-256");
|
|
2306
|
+
return;
|
|
2307
|
+
}
|
|
2308
|
+
this.writeTraits({ email_sha256: sha256Hex.toLowerCase() });
|
|
2309
|
+
}
|
|
2310
|
+
setHashedPhone(sha256Hex) {
|
|
2311
|
+
if (!SHA256_HEX_RE.test(sha256Hex)) {
|
|
2312
|
+
logger.warn("[user.setHashedPhone] expected 64-char hex SHA-256");
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
this.writeTraits({ phone_sha256: sha256Hex.toLowerCase() });
|
|
2316
|
+
}
|
|
2317
|
+
setBirthDate(iso) {
|
|
2318
|
+
if (!ISO_DATE_RE.test(iso)) {
|
|
2319
|
+
logger.warn("[user.setBirthDate] expected YYYY-MM-DD");
|
|
2320
|
+
return;
|
|
2321
|
+
}
|
|
2322
|
+
this.writeTraits({ birth_date: iso });
|
|
2323
|
+
}
|
|
2324
|
+
setOptIn(channel, granted) {
|
|
2325
|
+
if (!VALID_CHANNELS.has(channel)) {
|
|
2326
|
+
logger.warn(`[user.setOptIn] unknown channel: ${channel}`);
|
|
2327
|
+
return;
|
|
2328
|
+
}
|
|
2329
|
+
if (typeof granted !== "boolean") {
|
|
2330
|
+
logger.warn("[user.setOptIn] granted must be a boolean");
|
|
2331
|
+
return;
|
|
2332
|
+
}
|
|
2333
|
+
this.writeTraits({ [`opt_in_${channel}`]: granted });
|
|
2334
|
+
}
|
|
2335
|
+
/**
|
|
2336
|
+
* HMAC-signed identity token. Ingress-side verification not yet wired —
|
|
2337
|
+
* the trait is forwarded as-is and persisted on the contact record
|
|
2338
|
+
* until that lands.
|
|
2339
|
+
*/
|
|
2340
|
+
setSecureToken(token) {
|
|
2341
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
2342
|
+
logger.warn("[user.setSecureToken] token must be a non-empty string");
|
|
2343
|
+
return;
|
|
2344
|
+
}
|
|
2345
|
+
this.writeTraits({ _secure_token: token });
|
|
2346
|
+
}
|
|
2347
|
+
/**
|
|
2348
|
+
* Test/inspection hook — returns a copy of the pending traits buffer.
|
|
2349
|
+
* Used by the e2e smoke test to assert pre-login accumulation.
|
|
2350
|
+
*/
|
|
2351
|
+
_getPendingTraits() {
|
|
2352
|
+
return { ...this.pendingTraits };
|
|
2353
|
+
}
|
|
2354
|
+
// ---------------------------------------------------------------------------
|
|
2355
|
+
writeTraits(traits) {
|
|
2356
|
+
const userId = this.aegis.getUserId();
|
|
2357
|
+
if (userId) {
|
|
2358
|
+
this.aegis.identify(userId, traits);
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
Object.assign(this.pendingTraits, traits);
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2218
2364
|
class Aegis {
|
|
2219
2365
|
constructor() {
|
|
2220
2366
|
this.config = null;
|
|
@@ -2225,6 +2371,7 @@ class Aegis {
|
|
|
2225
2371
|
this.initPromise = null;
|
|
2226
2372
|
this.consent = null;
|
|
2227
2373
|
this._ecommerce = null;
|
|
2374
|
+
this._user = null;
|
|
2228
2375
|
this.rateLimiter = null;
|
|
2229
2376
|
this.nameGovernor = new NameGovernor();
|
|
2230
2377
|
this.traitGovernor = new TraitGovernor();
|
|
@@ -2233,6 +2380,8 @@ class Aegis {
|
|
|
2233
2380
|
this._originalPushState = null;
|
|
2234
2381
|
this._originalReplaceState = null;
|
|
2235
2382
|
this._lastEventIds = /* @__PURE__ */ new Map();
|
|
2383
|
+
this._workspaceCodes = /* @__PURE__ */ new Set();
|
|
2384
|
+
this._runtimeWorkspace = null;
|
|
2236
2385
|
}
|
|
2237
2386
|
async init(writeKey, config) {
|
|
2238
2387
|
if (this.initPromise) {
|
|
@@ -2400,6 +2549,151 @@ class Aegis {
|
|
|
2400
2549
|
});
|
|
2401
2550
|
}
|
|
2402
2551
|
}
|
|
2552
|
+
/**
|
|
2553
|
+
* Symmetric counterpart to `use(plugin)`. Calls the plugin's `destroy()`
|
|
2554
|
+
* hook (if defined) and removes it from the registry so its hooks no
|
|
2555
|
+
* longer fire on subsequent events. Safe to call with a name that
|
|
2556
|
+
* isn't registered — logs a warning and returns.
|
|
2557
|
+
*
|
|
2558
|
+
* Primary use case: React components that register a plugin in
|
|
2559
|
+
* `useEffect` and need to unregister in the cleanup function so HMR /
|
|
2560
|
+
* provider remount doesn't leak listeners. See cashier-portal's
|
|
2561
|
+
* MetaPixelInjector for an example.
|
|
2562
|
+
*/
|
|
2563
|
+
removePlugin(name) {
|
|
2564
|
+
this.plugins.unregister(name);
|
|
2565
|
+
}
|
|
2566
|
+
/**
|
|
2567
|
+
* Plugin-handshake P1 — ingest the workspace_code allowlist from the
|
|
2568
|
+
* bootstrap response. Pass `result.workspaceCodes` from `bootstrap()`
|
|
2569
|
+
* here so the SDK can do path-segment workspace detection on
|
|
2570
|
+
* customer-facing pages like `/south/rewards`, `/ghatkopar/feedback`.
|
|
2571
|
+
*
|
|
2572
|
+
* Empty array is the right answer for single-outlet tenants — SDK
|
|
2573
|
+
* will skip the path cascade and fall through to query param /
|
|
2574
|
+
* setWorkspace() / gateway origin lookup.
|
|
2575
|
+
*
|
|
2576
|
+
* Safe to call before init() — the set is consulted at event-fire time.
|
|
2577
|
+
*/
|
|
2578
|
+
ingestWorkspaceCodes(codes) {
|
|
2579
|
+
this._workspaceCodes = new Set(
|
|
2580
|
+
(codes ?? []).filter((c) => typeof c === "string" && c.length > 0)
|
|
2581
|
+
);
|
|
2582
|
+
logger.debug("Workspace codes ingested", { count: this._workspaceCodes.size });
|
|
2583
|
+
}
|
|
2584
|
+
/**
|
|
2585
|
+
* Plugin-handshake P1 (Track E) — explicitly set the workspace for
|
|
2586
|
+
* subsequent events. Used by:
|
|
2587
|
+
* - SPAs where workspace context changes via in-app routing
|
|
2588
|
+
* - Mobile / RN apps that have no URL
|
|
2589
|
+
* - Custom integrations that resolve workspace from their own state
|
|
2590
|
+
*
|
|
2591
|
+
* Pass null to clear (or call clearWorkspace()).
|
|
2592
|
+
*
|
|
2593
|
+
* Persisted to sessionStorage so SPA reloads / page transitions on the
|
|
2594
|
+
* same outlet inherit it without re-calling.
|
|
2595
|
+
*/
|
|
2596
|
+
setWorkspace(codeOrId) {
|
|
2597
|
+
this._runtimeWorkspace = codeOrId;
|
|
2598
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
2599
|
+
try {
|
|
2600
|
+
if (codeOrId) {
|
|
2601
|
+
window.sessionStorage.setItem("aegis_runtime_ws", codeOrId);
|
|
2602
|
+
} else {
|
|
2603
|
+
window.sessionStorage.removeItem("aegis_runtime_ws");
|
|
2604
|
+
}
|
|
2605
|
+
} catch {
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
logger.debug("Runtime workspace set", { value: codeOrId });
|
|
2609
|
+
}
|
|
2610
|
+
/** Symmetric helper to clear the runtime workspace. */
|
|
2611
|
+
clearWorkspace() {
|
|
2612
|
+
this.setWorkspace(null);
|
|
2613
|
+
}
|
|
2614
|
+
/**
|
|
2615
|
+
* Inspect the URL's first path segment and return it if it's in the
|
|
2616
|
+
* org's workspace_code allowlist. Skips well-known non-workspace prefixes
|
|
2617
|
+
* (e.g. `b` for bill short codes; `s` is the storefront app slug).
|
|
2618
|
+
*
|
|
2619
|
+
* Returns undefined when:
|
|
2620
|
+
* - window is undefined (SSR / Node)
|
|
2621
|
+
* - path is empty / root
|
|
2622
|
+
* - first segment isn't in `_workspaceCodes` (e.g. /products, /cart)
|
|
2623
|
+
*
|
|
2624
|
+
* The returned value is a workspace CODE (slug like "south"), not a
|
|
2625
|
+
* UUID. The gateway normalizes code → UUID server-side via
|
|
2626
|
+
* workspace_subaccounts lookup with Redis cache.
|
|
2627
|
+
*/
|
|
2628
|
+
getPathWorkspaceCode() {
|
|
2629
|
+
if (typeof window === "undefined") return void 0;
|
|
2630
|
+
if (this._workspaceCodes.size === 0) return void 0;
|
|
2631
|
+
try {
|
|
2632
|
+
const segments = window.location.pathname.split("/").filter(Boolean);
|
|
2633
|
+
if (segments.length === 0) return void 0;
|
|
2634
|
+
const first = segments[0].toLowerCase();
|
|
2635
|
+
return this._workspaceCodes.has(first) ? first : void 0;
|
|
2636
|
+
} catch {
|
|
2637
|
+
return void 0;
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
/**
|
|
2641
|
+
* Resolve the effective `workspace_id` for the next event.
|
|
2642
|
+
*
|
|
2643
|
+
* Cascade (highest precedence first):
|
|
2644
|
+
* 1. `this.config.workspace_id` — explicit operator config (headless
|
|
2645
|
+
* SDK usage, server-rendered apps, native shells).
|
|
2646
|
+
* 2. `?ws=` query param on the current URL — the P3 storefront URL
|
|
2647
|
+
* contract. The outlet picker writes this via `router.replace`.
|
|
2648
|
+
* 3. URL path segment (P1 Track A) — `/south/rewards` → "south" when
|
|
2649
|
+
* "south" is in the org's workspace_code allowlist.
|
|
2650
|
+
* 4. `aegis.setWorkspace()` runtime override (P1 Track E) — SPAs +
|
|
2651
|
+
* mobile + custom integrations.
|
|
2652
|
+
* 5. sessionStorage `aegis_runtime_ws` — persists setWorkspace across
|
|
2653
|
+
* reloads/SPA route changes.
|
|
2654
|
+
* 6. `undefined` — gateway falls back to `resolveByOrigin` lookup
|
|
2655
|
+
* against the property's allowed_origins.
|
|
2656
|
+
*
|
|
2657
|
+
* The returned value can be EITHER a UUID (from config / ?ws=) or a
|
|
2658
|
+
* workspace CODE slug (from path / setWorkspace). The gateway
|
|
2659
|
+
* normalizes slug → UUID server-side; analytics layer never needs to
|
|
2660
|
+
* worry about the difference.
|
|
2661
|
+
*
|
|
2662
|
+
* See docs/architecture/MULTI_OUTLET_URL_STRATEGY.md §2.3 and
|
|
2663
|
+
* docs/architecture/PLUGIN_HANDSHAKE_AUTOMATION.md §2.
|
|
2664
|
+
*/
|
|
2665
|
+
/**
|
|
2666
|
+
* Public so peer SDK surfaces (AegisMessageRuntime / AegisInAppManager /
|
|
2667
|
+
* AegisPlacementManager / AegisWidgetManager) can plumb the same
|
|
2668
|
+
* resolved workspace into their own gateway POSTs. Each manager calls
|
|
2669
|
+
* the gateway on its own endpoint (`/v1/in_app/events`,
|
|
2670
|
+
* `/v1/placements/track`, `/v1/widgets/track-event`); without this,
|
|
2671
|
+
* those events arrive at event-ingress with no workspace_id stamped
|
|
2672
|
+
* → impressions/clicks fall back to the org's primary workspace and
|
|
2673
|
+
* cross-outlet attribution breaks.
|
|
2674
|
+
*/
|
|
2675
|
+
getEffectiveWorkspaceId() {
|
|
2676
|
+
var _a;
|
|
2677
|
+
const configured = (_a = this.config) == null ? void 0 : _a.workspace_id;
|
|
2678
|
+
if (configured) return configured;
|
|
2679
|
+
if (typeof window === "undefined") return void 0;
|
|
2680
|
+
try {
|
|
2681
|
+
const ws = new URLSearchParams(window.location.search).get("ws");
|
|
2682
|
+
if (ws && ws.length > 0) return ws;
|
|
2683
|
+
} catch {
|
|
2684
|
+
}
|
|
2685
|
+
const pathWs = this.getPathWorkspaceCode();
|
|
2686
|
+
if (pathWs) return pathWs;
|
|
2687
|
+
if (this._runtimeWorkspace) return this._runtimeWorkspace;
|
|
2688
|
+
try {
|
|
2689
|
+
if (window.sessionStorage) {
|
|
2690
|
+
const cached = window.sessionStorage.getItem("aegis_runtime_ws");
|
|
2691
|
+
if (cached && cached.length > 0) return cached;
|
|
2692
|
+
}
|
|
2693
|
+
} catch {
|
|
2694
|
+
}
|
|
2695
|
+
return void 0;
|
|
2696
|
+
}
|
|
2403
2697
|
track(eventName, properties) {
|
|
2404
2698
|
if (!this.assertInitialized()) return;
|
|
2405
2699
|
const messageId = generateMessageId();
|
|
@@ -2412,7 +2706,7 @@ class Aegis {
|
|
|
2412
2706
|
anonymousId: this.identity.getAnonymousId(),
|
|
2413
2707
|
userId: this.identity.getUserId() || void 0,
|
|
2414
2708
|
sessionId: this.session.getSessionId(),
|
|
2415
|
-
workspace_id: this.
|
|
2709
|
+
workspace_id: this.getEffectiveWorkspaceId(),
|
|
2416
2710
|
context: buildContext(this.config, this.session)
|
|
2417
2711
|
};
|
|
2418
2712
|
this._lastEventIds.set(eventName, messageId);
|
|
@@ -2420,7 +2714,7 @@ class Aegis {
|
|
|
2420
2714
|
}
|
|
2421
2715
|
identify(userId, traits) {
|
|
2422
2716
|
if (!this.assertInitialized()) return;
|
|
2423
|
-
const wsForGovernor = this.
|
|
2717
|
+
const wsForGovernor = this.getEffectiveWorkspaceId() || null;
|
|
2424
2718
|
const { sanitized: governedTraits } = this.traitGovernor.process(traits, wsForGovernor);
|
|
2425
2719
|
this.identity.setUserId(userId, governedTraits);
|
|
2426
2720
|
const event = {
|
|
@@ -2431,7 +2725,7 @@ class Aegis {
|
|
|
2431
2725
|
anonymousId: this.identity.getAnonymousId(),
|
|
2432
2726
|
userId,
|
|
2433
2727
|
sessionId: this.session.getSessionId(),
|
|
2434
|
-
workspace_id: this.
|
|
2728
|
+
workspace_id: this.getEffectiveWorkspaceId(),
|
|
2435
2729
|
context: buildContext(this.config, this.session)
|
|
2436
2730
|
};
|
|
2437
2731
|
this.captureEvent(event);
|
|
@@ -2447,14 +2741,14 @@ class Aegis {
|
|
|
2447
2741
|
anonymousId: this.identity.getAnonymousId(),
|
|
2448
2742
|
userId: this.identity.getUserId() || void 0,
|
|
2449
2743
|
sessionId: this.session.getSessionId(),
|
|
2450
|
-
workspace_id: this.
|
|
2744
|
+
workspace_id: this.getEffectiveWorkspaceId(),
|
|
2451
2745
|
context: buildContext(this.config, this.session)
|
|
2452
2746
|
};
|
|
2453
2747
|
this.captureEvent(event);
|
|
2454
2748
|
}
|
|
2455
2749
|
group(groupId, traits) {
|
|
2456
2750
|
if (!this.assertInitialized()) return;
|
|
2457
|
-
const wsForGovernor = this.
|
|
2751
|
+
const wsForGovernor = this.getEffectiveWorkspaceId() || null;
|
|
2458
2752
|
const { sanitized: governedTraits } = this.traitGovernor.process(traits, wsForGovernor);
|
|
2459
2753
|
const event = {
|
|
2460
2754
|
type: "group",
|
|
@@ -2484,6 +2778,31 @@ class Aegis {
|
|
|
2484
2778
|
};
|
|
2485
2779
|
this.captureEvent(event);
|
|
2486
2780
|
}
|
|
2781
|
+
/**
|
|
2782
|
+
* SPA-logical screen view. Distinct from `page()` (URL-bound) — fires
|
|
2783
|
+
* when the user enters a logical surface like cart / checkout / drawer
|
|
2784
|
+
* without a URL change. Gateway maps `screen` → `engagement.screen_view`.
|
|
2785
|
+
*/
|
|
2786
|
+
screen(name, properties) {
|
|
2787
|
+
if (!this.assertInitialized()) return;
|
|
2788
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
2789
|
+
logger.warn("aegis.screen: name must be a non-empty string");
|
|
2790
|
+
return;
|
|
2791
|
+
}
|
|
2792
|
+
const event = {
|
|
2793
|
+
type: "screen",
|
|
2794
|
+
name,
|
|
2795
|
+
properties: properties || {},
|
|
2796
|
+
messageId: generateMessageId(),
|
|
2797
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2798
|
+
anonymousId: this.identity.getAnonymousId(),
|
|
2799
|
+
userId: this.identity.getUserId() || void 0,
|
|
2800
|
+
sessionId: this.session.getSessionId(),
|
|
2801
|
+
workspace_id: this.getEffectiveWorkspaceId(),
|
|
2802
|
+
context: buildContext(this.config, this.session)
|
|
2803
|
+
};
|
|
2804
|
+
this.captureEvent(event);
|
|
2805
|
+
}
|
|
2487
2806
|
async captureEvent(event) {
|
|
2488
2807
|
var _a;
|
|
2489
2808
|
if (((_a = this.config) == null ? void 0 : _a.enable_consent_mode) && this.consent) {
|
|
@@ -2570,6 +2889,7 @@ class Aegis {
|
|
|
2570
2889
|
reset() {
|
|
2571
2890
|
if (!this.assertInitialized()) return;
|
|
2572
2891
|
this.identity.reset();
|
|
2892
|
+
this._user = null;
|
|
2573
2893
|
logger.info("User identity reset");
|
|
2574
2894
|
}
|
|
2575
2895
|
/**
|
|
@@ -2754,6 +3074,12 @@ class Aegis {
|
|
|
2754
3074
|
}
|
|
2755
3075
|
return this._ecommerce;
|
|
2756
3076
|
}
|
|
3077
|
+
get user() {
|
|
3078
|
+
if (!this._user) {
|
|
3079
|
+
this._user = new UserNamespace(this);
|
|
3080
|
+
}
|
|
3081
|
+
return this._user;
|
|
3082
|
+
}
|
|
2757
3083
|
destroy() {
|
|
2758
3084
|
this.stopSPATracking();
|
|
2759
3085
|
if (this.queue) {
|
|
@@ -2780,7 +3106,8 @@ export {
|
|
|
2780
3106
|
NameGovernor as N,
|
|
2781
3107
|
RateLimiter as R,
|
|
2782
3108
|
Storage as S,
|
|
3109
|
+
UserNamespace as U,
|
|
2783
3110
|
logger as l,
|
|
2784
3111
|
murmurhash3_x86_32 as m
|
|
2785
3112
|
};
|
|
2786
|
-
//# sourceMappingURL=analytics-
|
|
3113
|
+
//# sourceMappingURL=analytics-DGt-CSgi.mjs.map
|