@xenia-martech/sdk-js 1.0.1-dev.1 → 1.1.0-dev.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 +54 -4
- package/dist/{actions-CcBYV--3.d.cts → actions-CKeiofxJ.d.cts} +19 -1
- package/dist/{actions-CcBYV--3.d.ts → actions-CKeiofxJ.d.ts} +19 -1
- package/dist/{cookie-names-B3b0XNc2.d.cts → api-URhxxi4x.d.cts} +19 -42
- package/dist/{cookie-names-B3b0XNc2.d.ts → api-URhxxi4x.d.ts} +19 -42
- package/dist/chunk-5D7SJIO4.cjs +109 -0
- package/dist/chunk-5D7SJIO4.cjs.map +1 -0
- package/dist/{chunk-XXNDHYAS.js → chunk-FYSEKFIY.js} +45 -103
- package/dist/chunk-FYSEKFIY.js.map +1 -0
- package/dist/{chunk-WYM4CQVG.cjs → chunk-JEKVN7HK.cjs} +46 -106
- package/dist/chunk-JEKVN7HK.cjs.map +1 -0
- package/dist/chunk-TLTG3BTC.js +104 -0
- package/dist/chunk-TLTG3BTC.js.map +1 -0
- package/dist/chunk-U2WBBOYT.cjs +233 -0
- package/dist/chunk-U2WBBOYT.cjs.map +1 -0
- package/dist/chunk-WYAMPEVV.js +227 -0
- package/dist/chunk-WYAMPEVV.js.map +1 -0
- package/dist/client/index.cjs +60 -6
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +25 -3
- package/dist/client/index.d.ts +25 -3
- package/dist/client/index.js +59 -7
- package/dist/client/index.js.map +1 -1
- package/dist/client/react/index.cjs +58 -6
- package/dist/client/react/index.cjs.map +1 -1
- package/dist/client/react/index.d.cts +2 -2
- package/dist/client/react/index.d.ts +2 -2
- package/dist/client/react/index.js +58 -6
- package/dist/client/react/index.js.map +1 -1
- package/dist/cookie-names-8FxbGKGS.d.ts +127 -0
- package/dist/cookie-names-QYMiEMAq.d.cts +127 -0
- package/dist/index.cjs +41 -32
- package/dist/index.d.cts +4 -71
- package/dist/index.d.ts +4 -71
- package/dist/index.js +2 -1
- package/dist/next/index.cjs +59 -0
- package/dist/next/index.cjs.map +1 -0
- package/dist/next/index.d.cts +45 -0
- package/dist/next/index.d.ts +45 -0
- package/dist/next/index.js +54 -0
- package/dist/next/index.js.map +1 -0
- package/dist/server/index.cjs +38 -214
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +5 -104
- package/dist/server/index.d.ts +5 -104
- package/dist/server/index.js +5 -204
- package/dist/server/index.js.map +1 -1
- package/dist/xenia-sdk.iife.js +3 -3
- package/dist/xenia-sdk.iife.js.map +1 -1
- package/dist/xenia-server-D-ozqSgv.d.ts +126 -0
- package/dist/xenia-server-rs3Gpbw7.d.cts +126 -0
- package/package.json +26 -7
- package/dist/chunk-WYM4CQVG.cjs.map +0 -1
- package/dist/chunk-XXNDHYAS.js.map +0 -1
package/dist/server/index.d.ts
CHANGED
|
@@ -1,106 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* Extra per-call identity/context for server-side webhook calls — an override on top of whatever the
|
|
6
|
-
* client already carries.
|
|
7
|
-
*
|
|
8
|
-
* There's no ambient `window` on the server to auto-collect `path`/`query`/`url`/UTM params the way
|
|
9
|
-
* the browser client does, so bind them ONCE per request via {@link XeniaConfig.context} (see
|
|
10
|
-
* `contextFromUrl`) and every call attaches them automatically. These per-call fields are for the
|
|
11
|
-
* remainder: values known only at this call site, or a deliberate override of the ambient ones.
|
|
12
|
-
*/
|
|
13
|
-
interface ServerWebhookOptions {
|
|
14
|
-
method?: "GET" | "POST";
|
|
15
|
-
journeyId?: string;
|
|
16
|
-
identity?: EventIdentity;
|
|
17
|
-
context?: EventContext;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Options for a server-side unified event (`POST /api/events/v1`). There's no
|
|
21
|
-
* ambient request context on the server — pass whatever's known at the call
|
|
22
|
-
* site (e.g. pathname/user-agent from the current request, session id from a
|
|
23
|
-
* cookie) via `identity`/`context`.
|
|
24
|
-
*/
|
|
25
|
-
interface ServerTrackOptions {
|
|
26
|
-
/** When true, also match/advance a journey (never sampled/shed). Default false. */
|
|
27
|
-
trigger?: boolean;
|
|
28
|
-
/** Stable dedup key (e.g. `"ord-1001"`) for idempotent conversions. */
|
|
29
|
-
eventId?: string;
|
|
30
|
-
eventVersion?: number;
|
|
31
|
-
occurredAtMs?: number;
|
|
32
|
-
/** Origin tag. Default `"xenia-server-sdk"`. */
|
|
33
|
-
source?: string;
|
|
34
|
-
/** Identity fields for this event (except the SDK-owned `xeniaVisitorId`,
|
|
35
|
-
* which comes from `config.visitorId` and can't be set per call). */
|
|
36
|
-
identity?: CallerIdentity;
|
|
37
|
-
context?: AnalyticsContext;
|
|
38
|
-
properties?: Record<string, JsonValue | undefined>;
|
|
39
|
-
consent?: ConsentState;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Server-native Xenia client. Same method names as the browser client, but
|
|
43
|
-
* every call is stateless: identity and context are passed explicitly rather
|
|
44
|
-
* than read from the DOM/localStorage. Uses global `fetch` (Node ≥18).
|
|
45
|
-
*/
|
|
46
|
-
declare class XeniaServer {
|
|
47
|
-
readonly api: XeniaApi;
|
|
48
|
-
/** SDK-owned visitor anchor for this (per-request) client — see {@link XeniaConfig.visitorId}. */
|
|
49
|
-
private readonly visitorId?;
|
|
50
|
-
/** Ambient request context attached to every call — see {@link XeniaConfig.context}. */
|
|
51
|
-
private readonly context?;
|
|
52
|
-
constructor(config: XeniaConfig);
|
|
53
|
-
/**
|
|
54
|
-
* Send a unified event server-side (`POST /api/events/v1`) and return the
|
|
55
|
-
* ingest response (including per-result `triggered`). `opts.trigger: true`
|
|
56
|
-
* also matches/advances a journey; pass `opts.eventId` (e.g. `"ord-1001"`)
|
|
57
|
-
* as a stable dedup key for idempotent conversions.
|
|
58
|
-
*/
|
|
59
|
-
track(eventName: AnalyticsEventName, opts?: ServerTrackOptions): Promise<AnalyticsIngestResponse>;
|
|
60
|
-
/**
|
|
61
|
-
* Send a webhook request and await the synchronous response. Merges
|
|
62
|
-
* `opts.identity`/`opts.context` into the payload the same way the browser
|
|
63
|
-
* client's auto-attached fields do (`path`, `query`, `url`, `referrer`,
|
|
64
|
-
* `deviceId`, `visitorId`, etc.) — see `ServerWebhookOptions`.
|
|
65
|
-
*/
|
|
66
|
-
webhook(triggerId: string, data?: Record<string, JsonValue | undefined>, opts?: ServerWebhookOptions): Promise<WebhookResponse>;
|
|
67
|
-
/** Bind visitor→user server-side — `POST /api/events/v1/identify`. */
|
|
68
|
-
identify(body: IdentifyRequest): Promise<IdentifyResponse>;
|
|
69
|
-
/**
|
|
70
|
-
* Resolve in-entry content personalization for one content key
|
|
71
|
-
* (`POST /api/personalization/resolve`). Requires a configured SDK token
|
|
72
|
-
* (`token`/`getToken`) — resolve is auth-required. With `merge: true` the
|
|
73
|
-
* returned `content` is server-spliced.
|
|
74
|
-
*
|
|
75
|
-
* Pass `opts.identity`/`opts.context` to attach the same SDK-provided
|
|
76
|
-
* fields (`deviceId`, `customerId`, `email`, `path`, `url`, `referrer`,
|
|
77
|
-
* `utm*`, …) the browser client collects ambiently — there's no ambient
|
|
78
|
-
* `window`/`document` server-side, so a server caller must supply whatever
|
|
79
|
-
* it knows (e.g. from the current request) explicitly. See
|
|
80
|
-
* {@link ResolveOptions}.
|
|
81
|
-
*/
|
|
82
|
-
resolve(contentKey: string, opts?: ResolveOptions): Promise<ResolveResult>;
|
|
83
|
-
/** Resolve several content keys in one round-trip (`POST /api/personalization/resolve-batch`). */
|
|
84
|
-
resolveBatch(items: ResolveBatchItem[], opts?: ResolveOptions): Promise<ResolveBatchResult[]>;
|
|
85
|
-
/**
|
|
86
|
-
* Merge the ambient `config.context` and `opts.identity`/`opts.context` into
|
|
87
|
-
* the resolve `data`, then stamp the SDK-owned visitor id
|
|
88
|
-
* (`config.visitorId`) under both the reserved `_xenia_vid` key (read by the
|
|
89
|
-
* backend for holdout/exposure bucketing) and the flat `visitorId` field —
|
|
90
|
-
* mirrors `webhook()`'s merge order/precedence exactly: caller `data` <
|
|
91
|
-
* ambient `config.context` < caller `context`/`identity` < SDK-owned
|
|
92
|
-
* `visitorId`/`_xenia_vid`, set LAST so neither can be overridden per call.
|
|
93
|
-
*
|
|
94
|
-
* `config.context` is what makes the SDK-provided fields automatic on the
|
|
95
|
-
* server: bound once per request, every `resolve()` carries `path`/`query`/
|
|
96
|
-
* `url`/`referrer`/UTMs with no per-call wiring, matching what the browser
|
|
97
|
-
* client collects from `window` on its own. A no-op when nothing is bound and
|
|
98
|
-
* nothing was passed — preserving today's behavior for callers using neither.
|
|
99
|
-
*/
|
|
100
|
-
private withVisitor;
|
|
101
|
-
}
|
|
102
|
-
/** Create a server-native client. */
|
|
103
|
-
declare function createXeniaServer(config: XeniaConfig): XeniaServer;
|
|
1
|
+
export { S as ServerResolveOptions, a as ServerTrackOptions, b as ServerWebhookOptions, W as WithFallback, X as XeniaServer, c as createXeniaServer } from '../xenia-server-D-ozqSgv.js';
|
|
2
|
+
import { d as ConsentState } from '../api-URhxxi4x.js';
|
|
3
|
+
export { A as AnalyticsContext, a as AnalyticsEventName, b as AnalyticsIdentity, c as AnalyticsIngestResponse, C as CircuitBreakerConfig, E as EventContext, e as EventIdentity, I as IdentifyRequest, f as IdentifyResponse, J as JsonValue, P as PersonalizationSwap, R as ResolveBatchItem, j as ResolveBatchResult, k as ResolveOptions, l as ResolveResult, W as WebhookResponse, s as XeniaApi, t as XeniaConfig, u as XeniaEvent } from '../api-URhxxi4x.js';
|
|
4
|
+
export { C as CONSENT_COOKIE, a as CircuitOpenError, V as VISITOR_COOKIE, f as consentCookie, g as consentCookieName, p as parseActions, v as visitorCookieName } from '../cookie-names-8FxbGKGS.js';
|
|
104
5
|
|
|
105
6
|
interface RefreshTokenOptions {
|
|
106
7
|
/**
|
|
@@ -168,4 +69,4 @@ declare function getVisitorId(cookieHeader: string | null | undefined, prefix?:
|
|
|
168
69
|
*/
|
|
169
70
|
declare function getConsent(cookieHeader: string | null | undefined, prefix?: string): ConsentState;
|
|
170
71
|
|
|
171
|
-
export {
|
|
72
|
+
export { ConsentState, type RefreshTokenOptions, type RefreshedToken, createTokenRoute, getConsent, getVisitorId, parseCookies, refreshSdkToken };
|
package/dist/server/index.js
CHANGED
|
@@ -1,174 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var RESERVED = /* @__PURE__ */ new Set([
|
|
6
|
-
"eventId",
|
|
7
|
-
"eventType",
|
|
8
|
-
"journeyId",
|
|
9
|
-
"deviceId",
|
|
10
|
-
"visitorId",
|
|
11
|
-
"customerId",
|
|
12
|
-
"email",
|
|
13
|
-
"utmSource",
|
|
14
|
-
"utmMedium",
|
|
15
|
-
"utmCampaign",
|
|
16
|
-
"utmContent",
|
|
17
|
-
"utmId",
|
|
18
|
-
"url",
|
|
19
|
-
"referrer",
|
|
20
|
-
"path",
|
|
21
|
-
"query"
|
|
22
|
-
]);
|
|
23
|
-
function compact2(obj) {
|
|
24
|
-
const out = {};
|
|
25
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
26
|
-
if (v !== void 0) out[k] = v;
|
|
27
|
-
}
|
|
28
|
-
return out;
|
|
29
|
-
}
|
|
30
|
-
function buildEventPayload(input) {
|
|
31
|
-
const passthrough = {};
|
|
32
|
-
const reserved = {};
|
|
33
|
-
if (input.props) {
|
|
34
|
-
for (const [k, v] of Object.entries(input.props)) {
|
|
35
|
-
if (v === void 0) continue;
|
|
36
|
-
if (RESERVED.has(k)) reserved[k] = v;
|
|
37
|
-
else passthrough[k] = v;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const payload = {
|
|
41
|
-
...passthrough,
|
|
42
|
-
// Reserved-named props fill first-class fields (e.g. `email`, `customerId`)
|
|
43
|
-
// the SDK didn't auto-collect. Auto-collected context/identity still win for
|
|
44
|
-
// what they reliably know (real page url, visitor/device id), so a stray prop
|
|
45
|
-
// can't override the true page context — see merge order below.
|
|
46
|
-
...reserved,
|
|
47
|
-
...compact2(input.context ?? {}),
|
|
48
|
-
...compact2(input.identity ?? {}),
|
|
49
|
-
eventId: input.eventId,
|
|
50
|
-
...input.eventType ? { eventType: input.eventType } : {},
|
|
51
|
-
...input.journeyId ? { journeyId: input.journeyId } : {}
|
|
52
|
-
};
|
|
53
|
-
return payload;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// src/server/xenia-server.ts
|
|
57
|
-
var XeniaServer = class {
|
|
58
|
-
api;
|
|
59
|
-
/** SDK-owned visitor anchor for this (per-request) client — see {@link XeniaConfig.visitorId}. */
|
|
60
|
-
visitorId;
|
|
61
|
-
/** Ambient request context attached to every call — see {@link XeniaConfig.context}. */
|
|
62
|
-
context;
|
|
63
|
-
constructor(config) {
|
|
64
|
-
this.api = new XeniaApi(config);
|
|
65
|
-
this.visitorId = config.visitorId;
|
|
66
|
-
this.context = config.context;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Send a unified event server-side (`POST /api/events/v1`) and return the
|
|
70
|
-
* ingest response (including per-result `triggered`). `opts.trigger: true`
|
|
71
|
-
* also matches/advances a journey; pass `opts.eventId` (e.g. `"ord-1001"`)
|
|
72
|
-
* as a stable dedup key for idempotent conversions.
|
|
73
|
-
*/
|
|
74
|
-
async track(eventName, opts = {}) {
|
|
75
|
-
const event = {
|
|
76
|
-
eventName,
|
|
77
|
-
eventId: opts.eventId ? toEventId(opts.eventId) : generateUlid(),
|
|
78
|
-
eventVersion: opts.eventVersion ?? 1,
|
|
79
|
-
occurredAtMs: opts.occurredAtMs ?? Date.now(),
|
|
80
|
-
source: opts.source ?? "xenia-server-sdk",
|
|
81
|
-
identity: {
|
|
82
|
-
...opts.identity,
|
|
83
|
-
// SDK-owned, set LAST so it can't be overridden per call. Comes from
|
|
84
|
-
// `config.visitorId` (bind it per request from the `xenia_vid` cookie).
|
|
85
|
-
xeniaVisitorId: this.visitorId
|
|
86
|
-
},
|
|
87
|
-
context: opts.context,
|
|
88
|
-
properties: opts.properties,
|
|
89
|
-
consent: opts.consent,
|
|
90
|
-
trigger: opts.trigger === true ? true : void 0
|
|
91
|
-
};
|
|
92
|
-
return this.api.sendEvents([event]);
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Send a webhook request and await the synchronous response. Merges
|
|
96
|
-
* `opts.identity`/`opts.context` into the payload the same way the browser
|
|
97
|
-
* client's auto-attached fields do (`path`, `query`, `url`, `referrer`,
|
|
98
|
-
* `deviceId`, `visitorId`, etc.) — see `ServerWebhookOptions`.
|
|
99
|
-
*/
|
|
100
|
-
async webhook(triggerId, data, opts = {}) {
|
|
101
|
-
const payload = buildEventPayload({
|
|
102
|
-
eventId: triggerId,
|
|
103
|
-
journeyId: opts.journeyId,
|
|
104
|
-
// SDK-owned visitor anchor wins: config.visitorId (bind the `xenia_vid`
|
|
105
|
-
// cookie per request) is attached automatically. Falls back to a
|
|
106
|
-
// per-call visitorId only when none is bound.
|
|
107
|
-
identity: { ...opts.identity, visitorId: this.visitorId ?? opts.identity?.visitorId },
|
|
108
|
-
// Ambient request context (config.context, bound once per request) under a per-call
|
|
109
|
-
// override — same precedence resolve() uses, so both surfaces carry the same fields.
|
|
110
|
-
context: { ...this.context, ...opts.context },
|
|
111
|
-
props: data
|
|
112
|
-
});
|
|
113
|
-
return this.api.webhook(triggerId, payload, opts.method ?? "POST");
|
|
114
|
-
}
|
|
115
|
-
/** Bind visitor→user server-side — `POST /api/events/v1/identify`. */
|
|
116
|
-
async identify(body) {
|
|
117
|
-
return this.api.identify(body);
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Resolve in-entry content personalization for one content key
|
|
121
|
-
* (`POST /api/personalization/resolve`). Requires a configured SDK token
|
|
122
|
-
* (`token`/`getToken`) — resolve is auth-required. With `merge: true` the
|
|
123
|
-
* returned `content` is server-spliced.
|
|
124
|
-
*
|
|
125
|
-
* Pass `opts.identity`/`opts.context` to attach the same SDK-provided
|
|
126
|
-
* fields (`deviceId`, `customerId`, `email`, `path`, `url`, `referrer`,
|
|
127
|
-
* `utm*`, …) the browser client collects ambiently — there's no ambient
|
|
128
|
-
* `window`/`document` server-side, so a server caller must supply whatever
|
|
129
|
-
* it knows (e.g. from the current request) explicitly. See
|
|
130
|
-
* {@link ResolveOptions}.
|
|
131
|
-
*/
|
|
132
|
-
async resolve(contentKey, opts) {
|
|
133
|
-
return this.api.resolve(contentKey, this.withVisitor(opts));
|
|
134
|
-
}
|
|
135
|
-
/** Resolve several content keys in one round-trip (`POST /api/personalization/resolve-batch`). */
|
|
136
|
-
async resolveBatch(items, opts) {
|
|
137
|
-
return this.api.resolveBatch(items, this.withVisitor(opts));
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Merge the ambient `config.context` and `opts.identity`/`opts.context` into
|
|
141
|
-
* the resolve `data`, then stamp the SDK-owned visitor id
|
|
142
|
-
* (`config.visitorId`) under both the reserved `_xenia_vid` key (read by the
|
|
143
|
-
* backend for holdout/exposure bucketing) and the flat `visitorId` field —
|
|
144
|
-
* mirrors `webhook()`'s merge order/precedence exactly: caller `data` <
|
|
145
|
-
* ambient `config.context` < caller `context`/`identity` < SDK-owned
|
|
146
|
-
* `visitorId`/`_xenia_vid`, set LAST so neither can be overridden per call.
|
|
147
|
-
*
|
|
148
|
-
* `config.context` is what makes the SDK-provided fields automatic on the
|
|
149
|
-
* server: bound once per request, every `resolve()` carries `path`/`query`/
|
|
150
|
-
* `url`/`referrer`/UTMs with no per-call wiring, matching what the browser
|
|
151
|
-
* client collects from `window` on its own. A no-op when nothing is bound and
|
|
152
|
-
* nothing was passed — preserving today's behavior for callers using neither.
|
|
153
|
-
*/
|
|
154
|
-
withVisitor(opts) {
|
|
155
|
-
const identity = compact({
|
|
156
|
-
...opts?.identity,
|
|
157
|
-
visitorId: this.visitorId ?? opts?.identity?.visitorId
|
|
158
|
-
});
|
|
159
|
-
const merged = {
|
|
160
|
-
...compact(this.context ?? {}),
|
|
161
|
-
...compact(opts?.context ?? {}),
|
|
162
|
-
...identity
|
|
163
|
-
};
|
|
164
|
-
if (Object.keys(merged).length === 0 && !this.visitorId) return opts;
|
|
165
|
-
if (this.visitorId) merged._xenia_vid = this.visitorId;
|
|
166
|
-
return { ...opts, data: { ...opts?.data ?? {}, ...merged } };
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
function createXeniaServer(config) {
|
|
170
|
-
return new XeniaServer(config);
|
|
171
|
-
}
|
|
1
|
+
import '../chunk-TLTG3BTC.js';
|
|
2
|
+
export { XeniaServer, createXeniaServer, getConsent, getVisitorId, parseCookies } from '../chunk-WYAMPEVV.js';
|
|
3
|
+
import { HttpClient } from '../chunk-FYSEKFIY.js';
|
|
4
|
+
export { CONSENT_COOKIE, CircuitOpenError, VISITOR_COOKIE, XeniaApi, consentCookie, consentCookieName, parseActions, visitorCookieName } from '../chunk-FYSEKFIY.js';
|
|
172
5
|
|
|
173
6
|
// src/server/token.ts
|
|
174
7
|
async function refreshSdkToken(opts) {
|
|
@@ -203,38 +36,6 @@ function createTokenRoute(config) {
|
|
|
203
36
|
};
|
|
204
37
|
}
|
|
205
38
|
|
|
206
|
-
|
|
207
|
-
function parseCookies(header) {
|
|
208
|
-
const out = {};
|
|
209
|
-
if (!header) return out;
|
|
210
|
-
for (const part of header.split(";")) {
|
|
211
|
-
const idx = part.indexOf("=");
|
|
212
|
-
if (idx === -1) continue;
|
|
213
|
-
const key = part.slice(0, idx).trim();
|
|
214
|
-
const value = part.slice(idx + 1).trim();
|
|
215
|
-
if (key) out[key] = decodeURIComponent(value);
|
|
216
|
-
}
|
|
217
|
-
return out;
|
|
218
|
-
}
|
|
219
|
-
function getVisitorId(cookieHeader, prefix) {
|
|
220
|
-
return parseCookies(cookieHeader)[visitorCookieName(prefix)] ?? null;
|
|
221
|
-
}
|
|
222
|
-
function getConsent(cookieHeader, prefix) {
|
|
223
|
-
const raw = parseCookies(cookieHeader)[consentCookieName(prefix)];
|
|
224
|
-
if (!raw) return { analytics: false, marketing: false, personalization: false };
|
|
225
|
-
try {
|
|
226
|
-
const parsed = JSON.parse(raw);
|
|
227
|
-
return parsed;
|
|
228
|
-
} catch {
|
|
229
|
-
const scopes = new Set(raw.split(",").map((s) => s.trim()));
|
|
230
|
-
return {
|
|
231
|
-
analytics: scopes.has("analytics"),
|
|
232
|
-
marketing: scopes.has("marketing"),
|
|
233
|
-
personalization: scopes.has("personalization")
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
export { XeniaServer, createTokenRoute, createXeniaServer, getConsent, getVisitorId, parseCookies, refreshSdkToken };
|
|
39
|
+
export { createTokenRoute, refreshSdkToken };
|
|
239
40
|
//# sourceMappingURL=index.js.map
|
|
240
41
|
//# sourceMappingURL=index.js.map
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/event-payload.ts","../../src/server/xenia-server.ts","../../src/server/token.ts","../../src/server/cookies.ts"],"names":["compact"],"mappings":"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","file":"index.js","sourcesContent":["import type { EventContext, EventIdentity, EventPayload, JsonValue } from \"./types\";\n\n/** Reserved keys that map to first-class fields (not passthrough data). */\nconst RESERVED = new Set([\n \"eventId\",\n \"eventType\",\n \"journeyId\",\n \"deviceId\",\n \"visitorId\",\n \"customerId\",\n \"email\",\n \"utmSource\",\n \"utmMedium\",\n \"utmCampaign\",\n \"utmContent\",\n \"utmId\",\n \"url\",\n \"referrer\",\n \"path\",\n \"query\",\n]);\n\nexport interface BuildEventInput {\n eventId: string;\n eventType?: string;\n journeyId?: string;\n identity?: EventIdentity;\n context?: EventContext;\n /** Arbitrary extra properties merged as passthrough data. */\n props?: Record<string, JsonValue | undefined>;\n}\n\n/** Drop `undefined` values so the JSON body stays clean. */\nfunction compact(obj: Record<string, JsonValue | undefined>): Record<string, JsonValue> {\n const out: Record<string, JsonValue> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (v !== undefined) out[k] = v;\n }\n return out;\n}\n\n/**\n * Build the request body for the webhook `/flow` call (`XeniaApi.webhook`).\n *\n * Merge order (later wins): passthrough props < context < identity < core.\n * Field names match the backend `EventTriggerRequest` DTO exactly.\n *\n * @internal Used only by the webhook request/response path — the unified event\n * surface (`track()` → `POST /api/events/v1`) builds {@link XeniaEvent} instead.\n */\nexport function buildEventPayload(input: BuildEventInput): EventPayload {\n const passthrough: Record<string, JsonValue> = {};\n const reserved: Record<string, JsonValue> = {};\n if (input.props) {\n for (const [k, v] of Object.entries(input.props)) {\n if (v === undefined) continue;\n // A prop whose key matches a reserved field (e.g. `email`, `customerId`,\n // `utmSource`) is promoted to that first-class field; everything else is\n // custom passthrough data. Previously reserved-named props were dropped\n // outright — so `track(\"sign_up\", { email })` never sent `email` at all.\n if (RESERVED.has(k)) reserved[k] = v;\n else passthrough[k] = v;\n }\n }\n\n const payload = {\n ...passthrough,\n // Reserved-named props fill first-class fields (e.g. `email`, `customerId`)\n // the SDK didn't auto-collect. Auto-collected context/identity still win for\n // what they reliably know (real page url, visitor/device id), so a stray prop\n // can't override the true page context — see merge order below.\n ...reserved,\n ...compact((input.context ?? {}) as Record<string, JsonValue | undefined>),\n ...compact((input.identity ?? {}) as Record<string, JsonValue | undefined>),\n eventId: input.eventId,\n ...(input.eventType ? { eventType: input.eventType } : {}),\n ...(input.journeyId ? { journeyId: input.journeyId } : {}),\n };\n\n return payload as EventPayload;\n}\n","import { compact, XeniaApi } from \"../core/api\";\nimport { buildEventPayload } from \"../core/event-payload\";\nimport { generateUlid, toEventId } from \"../core/id\";\nimport type {\n AnalyticsContext,\n AnalyticsEventName,\n CallerIdentity,\n AnalyticsIngestResponse,\n ConsentState,\n EventContext,\n EventIdentity,\n IdentifyRequest,\n IdentifyResponse,\n JsonValue,\n ResolveBatchItem,\n ResolveBatchResult,\n ResolveOptions,\n ResolveResult,\n WebhookResponse,\n XeniaConfig,\n XeniaEvent,\n} from \"../core/types\";\n\n/**\n * Extra per-call identity/context for server-side webhook calls — an override on top of whatever the\n * client already carries.\n *\n * There's no ambient `window` on the server to auto-collect `path`/`query`/`url`/UTM params the way\n * the browser client does, so bind them ONCE per request via {@link XeniaConfig.context} (see\n * `contextFromUrl`) and every call attaches them automatically. These per-call fields are for the\n * remainder: values known only at this call site, or a deliberate override of the ambient ones.\n */\nexport interface ServerWebhookOptions {\n method?: \"GET\" | \"POST\";\n journeyId?: string;\n identity?: EventIdentity;\n context?: EventContext;\n}\n\n/**\n * Options for a server-side unified event (`POST /api/events/v1`). There's no\n * ambient request context on the server — pass whatever's known at the call\n * site (e.g. pathname/user-agent from the current request, session id from a\n * cookie) via `identity`/`context`.\n */\nexport interface ServerTrackOptions {\n /** When true, also match/advance a journey (never sampled/shed). Default false. */\n trigger?: boolean;\n /** Stable dedup key (e.g. `\"ord-1001\"`) for idempotent conversions. */\n eventId?: string;\n eventVersion?: number;\n occurredAtMs?: number;\n /** Origin tag. Default `\"xenia-server-sdk\"`. */\n source?: string;\n /** Identity fields for this event (except the SDK-owned `xeniaVisitorId`,\n * which comes from `config.visitorId` and can't be set per call). */\n identity?: CallerIdentity;\n context?: AnalyticsContext;\n properties?: Record<string, JsonValue | undefined>;\n consent?: ConsentState;\n}\n\n/**\n * Server-native Xenia client. Same method names as the browser client, but\n * every call is stateless: identity and context are passed explicitly rather\n * than read from the DOM/localStorage. Uses global `fetch` (Node ≥18).\n */\nexport class XeniaServer {\n readonly api: XeniaApi;\n /** SDK-owned visitor anchor for this (per-request) client — see {@link XeniaConfig.visitorId}. */\n private readonly visitorId?: string;\n /** Ambient request context attached to every call — see {@link XeniaConfig.context}. */\n private readonly context?: EventContext;\n\n constructor(config: XeniaConfig) {\n this.api = new XeniaApi(config);\n this.visitorId = config.visitorId;\n this.context = config.context;\n }\n\n /**\n * Send a unified event server-side (`POST /api/events/v1`) and return the\n * ingest response (including per-result `triggered`). `opts.trigger: true`\n * also matches/advances a journey; pass `opts.eventId` (e.g. `\"ord-1001\"`)\n * as a stable dedup key for idempotent conversions.\n */\n async track(\n eventName: AnalyticsEventName,\n opts: ServerTrackOptions = {},\n ): Promise<AnalyticsIngestResponse> {\n const event: XeniaEvent = {\n eventName,\n eventId: opts.eventId ? toEventId(opts.eventId) : generateUlid(),\n eventVersion: opts.eventVersion ?? 1,\n occurredAtMs: opts.occurredAtMs ?? Date.now(),\n source: opts.source ?? \"xenia-server-sdk\",\n identity: {\n ...opts.identity,\n // SDK-owned, set LAST so it can't be overridden per call. Comes from\n // `config.visitorId` (bind it per request from the `xenia_vid` cookie).\n xeniaVisitorId: this.visitorId,\n },\n context: opts.context,\n properties: opts.properties,\n consent: opts.consent,\n trigger: opts.trigger === true ? true : undefined,\n };\n return this.api.sendEvents([event]);\n }\n\n /**\n * Send a webhook request and await the synchronous response. Merges\n * `opts.identity`/`opts.context` into the payload the same way the browser\n * client's auto-attached fields do (`path`, `query`, `url`, `referrer`,\n * `deviceId`, `visitorId`, etc.) — see `ServerWebhookOptions`.\n */\n async webhook(\n triggerId: string,\n data?: Record<string, JsonValue | undefined>,\n opts: ServerWebhookOptions = {},\n ): Promise<WebhookResponse> {\n const payload = buildEventPayload({\n eventId: triggerId,\n journeyId: opts.journeyId,\n // SDK-owned visitor anchor wins: config.visitorId (bind the `xenia_vid`\n // cookie per request) is attached automatically. Falls back to a\n // per-call visitorId only when none is bound.\n identity: { ...opts.identity, visitorId: this.visitorId ?? opts.identity?.visitorId },\n // Ambient request context (config.context, bound once per request) under a per-call\n // override — same precedence resolve() uses, so both surfaces carry the same fields.\n context: { ...this.context, ...opts.context },\n props: data,\n });\n return this.api.webhook(triggerId, payload as JsonValue, opts.method ?? \"POST\");\n }\n\n /** Bind visitor→user server-side — `POST /api/events/v1/identify`. */\n async identify(body: IdentifyRequest): Promise<IdentifyResponse> {\n return this.api.identify(body);\n }\n\n /**\n * Resolve in-entry content personalization for one content key\n * (`POST /api/personalization/resolve`). Requires a configured SDK token\n * (`token`/`getToken`) — resolve is auth-required. With `merge: true` the\n * returned `content` is server-spliced.\n *\n * Pass `opts.identity`/`opts.context` to attach the same SDK-provided\n * fields (`deviceId`, `customerId`, `email`, `path`, `url`, `referrer`,\n * `utm*`, …) the browser client collects ambiently — there's no ambient\n * `window`/`document` server-side, so a server caller must supply whatever\n * it knows (e.g. from the current request) explicitly. See\n * {@link ResolveOptions}.\n */\n async resolve(contentKey: string, opts?: ResolveOptions): Promise<ResolveResult> {\n return this.api.resolve(contentKey, this.withVisitor(opts));\n }\n\n /** Resolve several content keys in one round-trip (`POST /api/personalization/resolve-batch`). */\n async resolveBatch(\n items: ResolveBatchItem[],\n opts?: ResolveOptions,\n ): Promise<ResolveBatchResult[]> {\n return this.api.resolveBatch(items, this.withVisitor(opts));\n }\n\n /**\n * Merge the ambient `config.context` and `opts.identity`/`opts.context` into\n * the resolve `data`, then stamp the SDK-owned visitor id\n * (`config.visitorId`) under both the reserved `_xenia_vid` key (read by the\n * backend for holdout/exposure bucketing) and the flat `visitorId` field —\n * mirrors `webhook()`'s merge order/precedence exactly: caller `data` <\n * ambient `config.context` < caller `context`/`identity` < SDK-owned\n * `visitorId`/`_xenia_vid`, set LAST so neither can be overridden per call.\n *\n * `config.context` is what makes the SDK-provided fields automatic on the\n * server: bound once per request, every `resolve()` carries `path`/`query`/\n * `url`/`referrer`/UTMs with no per-call wiring, matching what the browser\n * client collects from `window` on its own. A no-op when nothing is bound and\n * nothing was passed — preserving today's behavior for callers using neither.\n */\n private withVisitor(opts?: ResolveOptions): ResolveOptions | undefined {\n const identity = compact({\n ...opts?.identity,\n visitorId: this.visitorId ?? opts?.identity?.visitorId,\n });\n const merged = {\n ...compact((this.context ?? {}) as Record<string, JsonValue | undefined>),\n ...compact((opts?.context ?? {}) as Record<string, JsonValue | undefined>),\n ...identity,\n };\n if (Object.keys(merged).length === 0 && !this.visitorId) return opts;\n if (this.visitorId) merged._xenia_vid = this.visitorId;\n return { ...opts, data: { ...(opts?.data ?? {}), ...merged } };\n }\n}\n\n/** Create a server-native client. */\nexport function createXeniaServer(config: XeniaConfig): XeniaServer {\n return new XeniaServer(config);\n}\n","import { HttpClient } from \"../core/http-client\";\nimport type { JsonObject } from \"../core/types\";\n\nexport interface RefreshTokenOptions {\n /**\n * Xenia base URL. Defaults to the hosted engine,\n * `https://engine.xeniamartech.com/xenia` — omit unless self-hosting.\n */\n baseUrl?: string;\n /**\n * The personalization SDK token created once in the dashboard (API Tokens →\n * Create token). Stays server-side; the browser never sees it — only the\n * short-lived token this exchanges it for does.\n */\n sdkToken: string;\n /** Lifetime of the returned token, in minutes. Default 15, capped server-side at 60. */\n ttlMinutes?: number;\n timeout?: number;\n}\n\nexport interface RefreshedToken {\n token: string;\n expiresAt?: string;\n}\n\n/**\n * Exchange the long-lived personalization SDK token for a short-lived one with\n * the same tenant/env scope, via `POST /api/personalization/token/refresh`.\n * Self-service — no dashboard credential needed, just the SDK token you\n * already have. Call this from your backend and hand the result to the\n * browser (e.g. from a token route the client SDK calls via `getToken`).\n */\nexport async function refreshSdkToken(opts: RefreshTokenOptions): Promise<RefreshedToken> {\n const http = new HttpClient({ baseUrl: opts.baseUrl, timeout: opts.timeout });\n const { data } = await http.request({\n method: \"POST\",\n path: \"/api/personalization/token/refresh\",\n headers: { Authorization: `Bearer ${opts.sdkToken}` },\n body: { ttlMinutes: opts.ttlMinutes ?? null },\n });\n const obj = (data ?? {}) as JsonObject;\n return {\n token: String(obj.token ?? \"\"),\n expiresAt: typeof obj.expiresAt === \"string\" ? obj.expiresAt : undefined,\n };\n}\n\n/**\n * Build a framework-agnostic request handler that hands the browser SDK a\n * personalization token — mount it at the endpoint `getToken` calls. Needs\n * only the SDK token you already created in the dashboard; mints a fresh\n * short-lived token per request so the long-lived one never reaches the\n * browser.\n *\n * @example\n * ```ts\n * export const GET = createTokenRoute({\n * sdkToken: process.env.XENIA_SDK_TOKEN!,\n * // baseUrl: process.env.XENIA_URL, // only to override the default hosted engine\n * });\n * ```\n */\nexport function createTokenRoute(config: RefreshTokenOptions): (request: Request) => Promise<Response> {\n return async (): Promise<Response> => {\n try {\n const refreshed = await refreshSdkToken(config);\n return new Response(JSON.stringify(refreshed), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" },\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"token refresh failed\";\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n };\n}\n","import {\n CONSENT_COOKIE,\n VISITOR_COOKIE,\n consentCookieName,\n visitorCookieName,\n} from \"../core/cookie-names\";\nimport type { ConsentState } from \"../core/types\";\n\n/**\n * Cookie names the SDK reads server-side (set by the browser SDK / your app).\n *\n * The constants are the DEFAULT-prefix names; the functions build the name for any prefix. If you\n * pass `config.prefix` to the browser client, pass the same prefix here — the browser namespaces\n * the cookie by it, so the default name won't be there to find.\n */\nexport { CONSENT_COOKIE, VISITOR_COOKIE, consentCookieName, visitorCookieName };\n\n/**\n * Parse a raw `Cookie` header into a map. Framework-agnostic so it works with\n * `Request.headers.get(\"cookie\")`, Node `req.headers.cookie`, etc.\n */\nexport function parseCookies(header: string | null | undefined): Record<string, string> {\n const out: Record<string, string> = {};\n if (!header) return out;\n for (const part of header.split(\";\")) {\n const idx = part.indexOf(\"=\");\n if (idx === -1) continue;\n const key = part.slice(0, idx).trim();\n const value = part.slice(idx + 1).trim();\n if (key) out[key] = decodeURIComponent(value);\n }\n return out;\n}\n\n/**\n * Read the visitor id the browser SDK wrote, so a server-side event can be bound to the same\n * visitor (`createXeniaServer({ visitorId })`) and attribute to a browser-entered journey.\n *\n * @param prefix Must match the browser client's `config.prefix`. The cookie is namespaced by it, so\n * a mismatch finds nothing and quietly leaves the server with no visitor.\n */\nexport function getVisitorId(\n cookieHeader: string | null | undefined,\n prefix?: string,\n): string | null {\n return parseCookies(cookieHeader)[visitorCookieName(prefix)] ?? null;\n}\n\n/**\n * Read consent from a cookie header. Accepts a JSON blob or a comma-list of\n * granted scopes (e.g. `analytics,personalization`). Absent → all denied.\n *\n * @param prefix Must match the browser client's `config.prefix` — see {@link getVisitorId}.\n */\nexport function getConsent(\n cookieHeader: string | null | undefined,\n prefix?: string,\n): ConsentState {\n const raw = parseCookies(cookieHeader)[consentCookieName(prefix)];\n if (!raw) return { analytics: false, marketing: false, personalization: false };\n try {\n const parsed = JSON.parse(raw) as ConsentState;\n return parsed;\n } catch {\n const scopes = new Set(raw.split(\",\").map((s) => s.trim()));\n return {\n analytics: scopes.has(\"analytics\"),\n marketing: scopes.has(\"marketing\"),\n personalization: scopes.has(\"personalization\"),\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/token.ts"],"names":[],"mappings":";;;;;;AAgCA,eAAsB,gBAAgB,IAAA,EAAoD;AACxF,EAAA,MAAM,IAAA,GAAO,IAAI,UAAA,CAAW,EAAE,OAAA,EAAS,KAAK,OAAA,EAAS,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,CAAA;AAC5E,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,KAAK,OAAA,CAAQ;AAAA,IAClC,MAAA,EAAQ,MAAA;AAAA,IACR,IAAA,EAAM,oCAAA;AAAA,IACN,SAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,QAAQ,CAAA,CAAA,EAAG;AAAA,IACpD,IAAA,EAAM,EAAE,UAAA,EAAY,IAAA,CAAK,cAAc,IAAA;AAAK,GAC7C,CAAA;AACD,EAAA,MAAM,GAAA,GAAO,QAAQ,EAAC;AACtB,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,MAAA,CAAO,GAAA,CAAI,KAAA,IAAS,EAAE,CAAA;AAAA,IAC7B,WAAW,OAAO,GAAA,CAAI,SAAA,KAAc,QAAA,GAAW,IAAI,SAAA,GAAY;AAAA,GACjE;AACF;AAiBO,SAAS,iBAAiB,MAAA,EAAsE;AACrG,EAAA,OAAO,YAA+B;AACpC,IAAA,IAAI;AACF,MAAA,MAAM,SAAA,GAAY,MAAM,eAAA,CAAgB,MAAM,CAAA;AAC9C,MAAA,OAAO,IAAI,QAAA,CAAS,IAAA,CAAK,SAAA,CAAU,SAAS,CAAA,EAAG;AAAA,QAC7C,MAAA,EAAQ,GAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAoB,iBAAiB,UAAA;AAAW,OAC5E,CAAA;AAAA,IACH,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,OAAA,GAAU,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,sBAAA;AACrD,MAAA,OAAO,IAAI,SAAS,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,OAAA,EAAS,CAAA,EAAG;AAAA,QACtD,MAAA,EAAQ,GAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAAmB,OAC/C,CAAA;AAAA,IACH;AAAA,EACF,CAAA;AACF","file":"index.js","sourcesContent":["import { HttpClient } from \"../core/http-client\";\nimport type { JsonObject } from \"../core/types\";\n\nexport interface RefreshTokenOptions {\n /**\n * Xenia base URL. Defaults to the hosted engine,\n * `https://engine.xeniamartech.com/xenia` — omit unless self-hosting.\n */\n baseUrl?: string;\n /**\n * The personalization SDK token created once in the dashboard (API Tokens →\n * Create token). Stays server-side; the browser never sees it — only the\n * short-lived token this exchanges it for does.\n */\n sdkToken: string;\n /** Lifetime of the returned token, in minutes. Default 15, capped server-side at 60. */\n ttlMinutes?: number;\n timeout?: number;\n}\n\nexport interface RefreshedToken {\n token: string;\n expiresAt?: string;\n}\n\n/**\n * Exchange the long-lived personalization SDK token for a short-lived one with\n * the same tenant/env scope, via `POST /api/personalization/token/refresh`.\n * Self-service — no dashboard credential needed, just the SDK token you\n * already have. Call this from your backend and hand the result to the\n * browser (e.g. from a token route the client SDK calls via `getToken`).\n */\nexport async function refreshSdkToken(opts: RefreshTokenOptions): Promise<RefreshedToken> {\n const http = new HttpClient({ baseUrl: opts.baseUrl, timeout: opts.timeout });\n const { data } = await http.request({\n method: \"POST\",\n path: \"/api/personalization/token/refresh\",\n headers: { Authorization: `Bearer ${opts.sdkToken}` },\n body: { ttlMinutes: opts.ttlMinutes ?? null },\n });\n const obj = (data ?? {}) as JsonObject;\n return {\n token: String(obj.token ?? \"\"),\n expiresAt: typeof obj.expiresAt === \"string\" ? obj.expiresAt : undefined,\n };\n}\n\n/**\n * Build a framework-agnostic request handler that hands the browser SDK a\n * personalization token — mount it at the endpoint `getToken` calls. Needs\n * only the SDK token you already created in the dashboard; mints a fresh\n * short-lived token per request so the long-lived one never reaches the\n * browser.\n *\n * @example\n * ```ts\n * export const GET = createTokenRoute({\n * sdkToken: process.env.XENIA_SDK_TOKEN!,\n * // baseUrl: process.env.XENIA_URL, // only to override the default hosted engine\n * });\n * ```\n */\nexport function createTokenRoute(config: RefreshTokenOptions): (request: Request) => Promise<Response> {\n return async (): Promise<Response> => {\n try {\n const refreshed = await refreshSdkToken(config);\n return new Response(JSON.stringify(refreshed), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" },\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : \"token refresh failed\";\n return new Response(JSON.stringify({ error: message }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n };\n}\n"]}
|
package/dist/xenia-sdk.iife.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var Xenia=(function(exports){'use strict';var He=new Set(["banner","popup","modal","inline"]),We=new Set(["top-left","top-right","bottom-left","bottom-right","top","bottom"]),Ve=new Set(["standard","spin-wheel"]),Le=new Set(["swap","hide","show","setContent","redirect",...He]);function S(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function u(n){return typeof n=="string"?n:void 0}function le(n){return typeof n=="number"?n:void 0}function $e(n){if(!S(n))return null;let e=u(n.type);if(!e||!Le.has(e))return null;if(e==="redirect"){let i=u(n.url);return i?{type:e,url:i}:null}if(e==="hide"||e==="show"){let i=u(n.target);return i?{type:e,target:i}:null}if(e==="swap"||e==="setContent"){let i=u(n.target);return i?{type:e,target:i,html:u(n.html),text:u(n.text)}:null}let t=S(n.cta)?{label:u(n.cta.label)??"",url:u(n.cta.url),event:u(n.cta.event)}:void 0;return {type:e,target:u(n.target),title:u(n.title),message:u(n.message),imageUrl:u(n.imageUrl),html:u(n.html),cta:t&&(t.label||t.url)?t:void 0,dismissAfterMs:le(n.dismissAfterMs),position:qe(n.position),widgetType:Je(n.widgetType),segments:De(n.segments),formSubmit:ze(n.formSubmit),style:Fe(n.style),spinnerHtml:u(n.spinnerHtml),winHtml:u(n.winHtml),lossHtml:u(n.lossHtml)}}function qe(n){let e=u(n);return e&&We.has(e)?e:void 0}function Je(n){let e=u(n);return e&&Ve.has(e)?e:void 0}function De(n){if(!Array.isArray(n))return;let e=[];for(let t of n){if(!S(t))continue;let i=u(t.id),r=u(t.label);!i||!r||e.push({id:i,label:r,colorHex:u(t.colorHex),isLoss:t.isLoss===true});}return e.length?e:void 0}function ze(n){if(!S(n))return;let e=u(n.mode);if(e)return {requiresResolve:n.requiresResolve===true,mode:e,flowRef:u(n.flowRef),bindingRef:u(n.bindingRef),successDisplayMs:le(n.successDisplayMs)}}function Fe(n){if(!S(n))return;let e={hideCloseButton:n.hideCloseButton===true,hideOverlay:n.hideOverlay===true};return e.hideCloseButton||e.hideOverlay?e:void 0}function j(n){let e;if(Array.isArray(n)?e=n:S(n)&&Array.isArray(n.xeniaActions)&&(e=n.xeniaActions),!Array.isArray(e))return [];let t=[];for(let i of e){let r=$e(i);r&&t.push(r);}return t}var _e="https://engine.xeniamartech.com/xenia";function H(){return typeof window<"u"&&typeof document<"u"}function Ne(n,e){if(!e||Object.keys(e).length===0)return n;let t=new URLSearchParams(e),i=n.includes("?")?"&":"?";return `${n}${i}${t.toString()}`}function de(n){return new Promise(e=>setTimeout(e,n))}var E=class extends Error{constructor(t,i,r){super(r??`Request failed with status ${t}`);this.status=t;this.data=i;this.name="HttpError";}status;data},O=class{baseUrl;defaultHeaders;timeout;retry;debug;constructor(e){this.baseUrl=(e.baseUrl??_e).replace(/\/+$/,""),this.defaultHeaders=e.defaultHeaders??{},this.timeout=e.timeout??8e3,this.retry={attempts:e.retry?.attempts??2,baseDelayMs:e.retry?.baseDelayMs??300},this.debug=e.debug??false;}async request(e){let t=Ne(this.baseUrl+e.path,e.query),i=e.method??"GET",r={"Content-Type":"application/json",...this.defaultHeaders,...e.headers},s=e.body!==void 0?JSON.stringify(e.body):void 0,o=e.timeout??this.timeout,a=e.noRetry?1:(e.retry?.attempts??this.retry.attempts)+1,l=e.retry?.baseDelayMs??this.retry.baseDelayMs,c;for(let d=0;d<a;d++)try{let p=await this.fetchWithTimeout(t,{method:i,headers:r,body:s,...e.keepalive?{keepalive:!0}:{}},o),h=await this.parseBody(p);if(p.ok)return {response:p,data:h};if(this.isRetryableStatus(p.status)&&d<a-1){let I=this.retryAfterMs(p,l,d);this.log(`retrying ${e.path} after ${I}ms (status ${p.status})`),await de(I);continue}throw new E(p.status,h)}catch(p){if(c=p,p instanceof E)throw p;if(d<a-1){let h=l*2**d;this.log(`retrying ${e.path} after ${h}ms (network error)`),await de(h);continue}}throw c instanceof Error?c:new Error("Request failed")}async fetchWithTimeout(e,t,i){let r=new AbortController,s=setTimeout(()=>r.abort(),i);try{return await fetch(e,{...t,signal:r.signal})}catch(o){throw o instanceof Error&&o.name==="AbortError"?new Error(`Request timeout after ${i}ms`,{cause:o}):o}finally{clearTimeout(s);}}async parseBody(e){let t=await e.text().catch(()=>"");if(!t)return null;try{return JSON.parse(t)}catch{return t}}isRetryableStatus(e){return e===429||e>=500&&e<=599}retryAfterMs(e,t,i){let r=e.headers.get("Retry-After");if(r){let s=Number(r);if(!Number.isNaN(s))return s*1e3}return t*2**i}log(e){this.debug&&console.warn(`[xenia] ${e}`);}};function Xe(n){try{let e=n.split(".")[1];if(!e)return;let t=JSON.parse(atob(e.replace(/-/g,"+").replace(/_/g,"/")));return typeof t.exp=="number"?t.exp*1e3:void 0}catch{return}}var W=class{config;cached=null;inflight=null;constructor(e){this.config=e;}hasSource(){return !!(this.config.token||this.config.getToken)}getToken=async()=>{if(this.config.token&&!this.config.getToken)return this.config.token;let e=Date.now();if(this.cached&&this.cached.expiresAt>e)return this.cached.token;if(this.inflight)return this.inflight;if(!this.config.getToken)throw new Error("No token source: set config.token or config.getToken.");return this.inflight=Promise.resolve(this.config.getToken()).then(t=>{let i=Xe(t),r=i?i-3e4:e+5*6e4;return this.cached={token:t,expiresAt:r},t}).finally(()=>{this.inflight=null;}),this.inflight}};function ce(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function Be(n){return n!==void 0&&ce(n)?n:void 0}function x(n){let e={};for(let[t,i]of Object.entries(n))i!==void 0&&(e[t]=i);return e}var V=class{http;tenantId;environmentKey;tokenProvider;constructor(e){this.tenantId=e.tenantId,this.environmentKey=e.environmentKey,this.tokenProvider=new W(e),this.http=new O({baseUrl:e.baseUrl,defaultHeaders:e.headers,timeout:e.timeout,retry:e.retry,debug:e.debug});}tenantHeaders(){return {"X-Tenant-Id":this.tenantId,"X-Environment":this.environmentKey}}async authHeaders(){let e=this.tenantHeaders();if(!this.tokenProvider.hasSource())return e;let t=await this.tokenProvider.getToken();return t&&(e.Authorization=`Bearer ${t}`),e}async webhook(e,t,i="POST"){let{response:r,data:s}=await this.http.request({method:i,path:"/flow",query:{id:e},headers:await this.authHeaders(),body:i==="POST"?t??{}:void 0,noRetry:true});return {status:r.status,body:s,actions:j(s),receivedAt:Date.now()}}async submitWidget(e,t){let{response:i,data:r}=await this.http.request({method:"POST",path:"/widget-submissions",query:{ref:e},headers:await this.authHeaders(),body:t??{},noRetry:true}),s=ce(r)?r:{};return {status:i.status,body:r,actions:j(r),prize:Be(s.prize),alreadyPlayed:s.alreadyPlayed===true,dispatched:s.dispatched===true,dispatchError:typeof s.dispatchError=="string"?s.dispatchError:void 0,widgetType:typeof s.widgetType=="string"?s.widgetType:void 0,receivedAt:Date.now()}}async sendEvents(e,t){let{data:i}=await this.http.request({method:"POST",path:"/api/events/v1",headers:await this.authHeaders(),body:{events:e},keepalive:t?.keepalive});return i}async identify(e){let{data:t}=await this.http.request({method:"POST",path:"/api/events/v1/identify",headers:await this.authHeaders(),body:e});return t}async consent(e,t){let{data:i}=await this.http.request({method:"POST",path:"/api/events/v1/consent",headers:await this.authHeaders(),body:x({xeniaVisitorId:e,analytics:t.analytics===true,marketing:t.marketing===true,personalization:t.personalization===true,consentSource:t.consentSource,consentVersion:t.consentVersion})});return i}async resolve(e,t={}){let{data:i}=await this.http.request({method:"POST",path:"/api/personalization/resolve",query:t.merge!==void 0?{merge:String(t.merge)}:void 0,headers:await this.authHeaders(),body:x({contentKey:e,data:t.data,locale:t.locale})});return i}async resolveBatch(e,t={}){let{data:i}=await this.http.request({method:"POST",path:"/api/personalization/resolve-batch",query:t.merge!==void 0?{merge:String(t.merge)}:void 0,headers:await this.authHeaders(),body:x({items:e,data:t.data,locale:t.locale})});return i}};var je=new Set(["eventId","eventType","journeyId","deviceId","visitorId","customerId","email","utmSource","utmMedium","utmCampaign","utmContent","utmId","url","referrer","path","query"]);function ue(n){let e={};for(let[t,i]of Object.entries(n))i!==void 0&&(e[t]=i);return e}function pe(n){let e={},t={};if(n.props)for(let[r,s]of Object.entries(n.props))s!==void 0&&(je.has(r)?t[r]=s:e[r]=s);return {...e,...t,...ue(n.context??{}),...ue(n.identity??{}),eventId:n.eventId,...n.eventType?{eventType:n.eventType}:{},...n.journeyId?{journeyId:n.journeyId}:{}}}var T="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Ue(n){let e=new Uint8Array(n);if(typeof crypto<"u"&&crypto.getRandomValues)crypto.getRandomValues(e);else for(let t=0;t<n;t++)e[t]=Math.floor(Math.random()*256);return e}function g(n=Date.now()){let e="",t=n;for(let s=9;s>=0;s--)e=T[t%32]+e,t=Math.floor(t/32);let i=Ue(16),r="";for(let s=0;s<16;s++)r+=T[i[s]%32];return e+r}var U=26;function me(n){if(n.length<=U)return n;let e="";for(let i of [2166136261,16777619]){let r=i>>>0;for(let s=0;s<n.length;s++)r^=n.charCodeAt(s),r=Math.imul(r,16777619)>>>0,e+=T[r%32];}let t="";for(let i=0;i<U;i++){let r=i+1;for(let s=i;s<e.length;s+=U)r=r*31+T.indexOf(e[s])>>>0;t+=T[r%32];}return t}var L=class{map=new Map;set(e,t){this.map.set(e,t);}get(e){return this.map.get(e)}keys(){return [...this.map.keys()]}clear(){this.map.clear();}},$=class{nsKey;constructor(e="xenia"){this.nsKey=`${e}_responses`;}read(){try{let e=localStorage.getItem(this.nsKey);return e?JSON.parse(e):{}}catch{return {}}}write(e){try{localStorage.setItem(this.nsKey,JSON.stringify(e));}catch{}}set(e,t){let i=this.read();i[e]=t,this.write(i);}get(e){return this.read()[e]}keys(){return Object.keys(this.read())}clear(){try{localStorage.removeItem(this.nsKey);}catch{}}};var q=class{queue=[];send;batchSize;flushIntervalMs;maxQueueSize;debug;timer=null;flushing=false;listenersAttached=false;allowed=false;onPageHide=()=>this.flushOnUnload();onVisibilityChange=()=>{document.visibilityState==="hidden"&&this.flushOnUnload();};constructor(e){this.send=e.send,this.batchSize=e.batchSize??20,this.flushIntervalMs=e.flushIntervalMs??3e3,this.maxQueueSize=e.maxQueueSize??200,this.debug=e.debug??false;}start(){this.allowed=true,!(this.timer||typeof window>"u")&&(this.timer=setInterval(()=>{this.flush();},this.flushIntervalMs),window.addEventListener("pagehide",this.onPageHide),document.addEventListener("visibilitychange",this.onVisibilityChange),this.listenersAttached=true);}stop(){this.allowed=false,this.timer&&clearInterval(this.timer),this.timer=null,this.listenersAttached&&(window.removeEventListener("pagehide",this.onPageHide),document.removeEventListener("visibilitychange",this.onVisibilityChange),this.listenersAttached=false);}enqueue(e){this.queue.length>=this.maxQueueSize&&this.queue.shift(),this.queue.push(e),this.allowed&&this.queue.length>=this.batchSize&&this.flush();}async sendImmediate(e){try{await this.send([e],{keepalive:!0});}catch(t){this.isPermanentFailure(t)||this.requeue([e]),this.debug&&console.warn("[xenia] immediate send failed",t);}}async flush(){if(!this.allowed||this.flushing||this.queue.length===0)return;this.flushing=true;let e=this.queue.splice(0,this.batchSize);try{await this.send(e);}catch(t){this.isPermanentFailure(t)||this.requeue(e),this.debug&&console.warn("[xenia] analytics flush failed",t);}finally{this.flushing=false;}}isPermanentFailure(e){return e instanceof E&&e.status>=400&&e.status<500}flushOnUnload(){if(!this.allowed||this.queue.length===0)return;let e=this.queue.splice(0,this.queue.length);this.send(e,{keepalive:true}).catch(()=>{});}requeue(e){let t=this.maxQueueSize-this.queue.length;t<=0||this.queue.unshift(...e.slice(0,t));}};var Ke=new Set(["click","view-port","page-load"]);function he(n){let e=n.indexOf("&");if(e===-1)return null;let t=n.slice(0,e),i=n.slice(e+1);return !t||!Ke.has(i)?null:{eventId:t,interactionType:i}}function fe(n){let e=n.getAttribute("data-xenia-content-key");if(!e)return null;let t=n.getAttribute("data-xenia-content-entry-id"),i=n.getAttribute("data-xenia-base-entry-id"),r=n.getAttribute("data-xenia-personalized");return {contentKey:e,contentEntryId:t??void 0,baseEntryId:i??void 0,personalized:r==null?void 0:r==="true"}}var J=class{opts;throttleTimers=new Map;observers=new Map;slotObservers=new Map;triggered=new Set;slotImpressed=new Set;clickHandler=null;mutationObserver=null;constructor(e){this.opts=e;}start(){this.setupClicks(),this.scan(document),this.setupMutations();}stop(){this.clickHandler&&document.removeEventListener("click",this.clickHandler),this.clickHandler=null,this.mutationObserver?.disconnect(),this.mutationObserver=null,this.observers.forEach(e=>e.disconnect()),this.observers.clear(),this.slotObservers.forEach(e=>e.disconnect()),this.slotObservers.clear(),this.throttleTimers.clear(),this.triggered.clear(),this.slotImpressed.clear();}attr(){return `data-${this.opts.prefix}`}throttled(e){if(this.opts.throttleMs<=0)return false;let t=Date.now(),i=this.throttleTimers.get(e)??0;return t-i>=this.opts.throttleMs?(this.throttleTimers.set(e,t),false):true}setupClicks(){let e=this.attr();this.clickHandler=t=>{let i=t.target;if(!i)return;let r=i.closest(`[${e}]`);if(r){let s=r.getAttribute(e),o=s?he(s):null;o&&o.interactionType==="click"&&!this.throttled(r)&&this.opts.fire(o.eventId,"click");}if(this.opts.fireSlot){let s=i.closest("[data-xenia-slot]");if(s){let o=fe(s);o&&!this.throttled(s)&&this.opts.fireSlot("click",o);}}},document.addEventListener("click",this.clickHandler);}observeSlot(e){if(!this.opts.fireSlot||this.slotObservers.has(e))return;let t=fe(e);if(!t)return;let i=this.opts.fireSlot,{threshold:r,rootMargin:s}=this.opts.viewport,o=new IntersectionObserver(a=>{for(let l of a)l.isIntersecting&&(this.slotImpressed.has(e)||(this.slotImpressed.add(e),i("impression",t),o.disconnect(),this.slotObservers.delete(e)));},{threshold:r,rootMargin:s});o.observe(e),this.slotObservers.set(e,o);}observeViewport(e,t){if(this.observers.has(e))return;let{threshold:i,rootMargin:r,triggerOnce:s}=this.opts.viewport,o=new IntersectionObserver(a=>{for(let l of a)l.isIntersecting&&(s&&this.triggered.has(e)||this.throttled(e)||(this.opts.fire(t,"view-port"),s&&(this.triggered.add(e),o.disconnect(),this.observers.delete(e))));},{threshold:i,rootMargin:r});o.observe(e),this.observers.set(e,o);}handleElement(e){let t=e.getAttribute(this.attr());if(!t)return;let i=he(t);i&&(i.interactionType==="view-port"?this.observeViewport(e,i.eventId):i.interactionType==="page-load"&&this.opts.fire(i.eventId,"page-load"));}scan(e){e.querySelectorAll(`[${this.attr()}]`).forEach(t=>this.handleElement(t)),this.opts.fireSlot&&e.querySelectorAll("[data-xenia-slot]").forEach(t=>this.observeSlot(t));}setupMutations(){this.mutationObserver=new MutationObserver(e=>{for(let t of e)for(let i of t.addedNodes){if(i.nodeType!==Node.ELEMENT_NODE)continue;let r=i;this.handleElement(r),this.opts.fireSlot&&r.matches("[data-xenia-slot]")&&this.observeSlot(r),this.scan(r);}}),this.mutationObserver.observe(document.body,{childList:true,subtree:true});}};var D="xenia";function K(n=D){return `${n||D}_vid`}var Ge=1800*1e3,z=class{prefix;cookie;deviceId=null;journeyId=null;sessionId=null;constructor(e="xenia",t=true){this.prefix=e,this.cookie=Ye(t);}key(e){return `${this.prefix}_${e}`}cookieName(){return K(this.prefix)}writeVisitorCookie(e){if(!(!this.cookie||!e))try{if(typeof document>"u")return;let t=[`${this.cookieName()}=${encodeURIComponent(e)}`,"path=/",`max-age=${Math.floor(this.cookie.maxAgeDays*86400)}`,`SameSite=${this.cookie.sameSite}`];this.cookie.domain&&t.push(`domain=${this.cookie.domain}`),this.cookie.secure&&t.push("Secure"),document.cookie=t.join("; ");}catch{}}async resolveDeviceId(){if(this.deviceId)return this.writeVisitorCookie(this.getVisitorId()??this.deviceId),this.deviceId;let e=this.key("device_id");try{let i=localStorage.getItem(e);if(i)return this.deviceId=i,this.writeVisitorCookie(this.getVisitorId()??i),i}catch{}let t=await this.mediaDeviceId();t||(t=await this.fingerprint()),this.deviceId=t;try{localStorage.setItem(e,t);}catch{}return this.writeVisitorCookie(this.getVisitorId()??t),t}getDeviceId(){return this.deviceId}async mediaDeviceId(){try{if(!navigator.mediaDevices?.enumerateDevices)return null;let e=await navigator.mediaDevices.enumerateDevices();return (e.find(i=>i.kind==="videoinput"&&i.deviceId)||e.find(i=>i.kind==="audioinput"&&i.deviceId)||e.find(i=>i.kind==="audiooutput"&&i.deviceId))?.deviceId||null}catch{return null}}async fingerprint(){let e=[navigator.userAgent,navigator.language,screen.width,screen.height,screen.colorDepth,new Date().getTimezoneOffset()].join("|"),t=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e));return Array.from(new Uint8Array(t)).map(i=>i.toString(16).padStart(2,"0")).join("")}identify(e){if(e){try{localStorage.setItem(this.key("visitor_id"),e);}catch{}this.writeVisitorCookie(e);}}getVisitorId(){try{return localStorage.getItem(this.key("visitor_id"))}catch{return null}}setCustomerId(e){try{e?localStorage.setItem(this.key("customer_id"),e):localStorage.removeItem(this.key("customer_id"));}catch{}}getCustomerId(){try{return localStorage.getItem(this.key("customer_id"))}catch{return null}}setEmail(e){try{e?localStorage.setItem(this.key("email"),e):localStorage.removeItem(this.key("email"));}catch{}}getEmail(){try{return localStorage.getItem(this.key("email"))}catch{return null}}getSessionId(){let e=this.key("session_id"),t=this.key("session_last_activity"),i=Date.now();try{let r=sessionStorage.getItem(e),s=Number(sessionStorage.getItem(t)),a=r&&Number.isFinite(s)&&i-s<Ge?r:g(i);return sessionStorage.setItem(e,a),sessionStorage.setItem(t,String(i)),this.sessionId=a,a}catch{return this.sessionId||(this.sessionId=g(i)),this.sessionId}}setJourneyId(e){this.journeyId=e&&e.trim()?e.trim():null;}getJourneyId(){return this.journeyId||this.utmParam("utm_id")||null}utmParam(e){try{let t=new URLSearchParams(window.location.search).get(e);if(t){try{sessionStorage.setItem(this.key(e),t);}catch{}return t}try{return sessionStorage.getItem(this.key(e))}catch{}}catch{}return null}collectContext(){let e={},t=this.utmParam("utm_source"),i=this.utmParam("utm_medium"),r=this.utmParam("utm_campaign"),s=this.utmParam("utm_content"),o=this.utmParam("utm_id");t&&(e.utmSource=t),i&&(e.utmMedium=i),r&&(e.utmCampaign=r),s&&(e.utmContent=s),o&&(e.utmId=o);try{e.url=window.location.href,e.path=window.location.pathname,e.query=Object.fromEntries(new URLSearchParams(window.location.search)),document.referrer&&(e.referrer=document.referrer);}catch{}let a=this.getVisitorId();return a&&(e.visitorId=a),e}};function Ye(n){if(n===false)return null;let e=n===true?{}:n,t=e.sameSite??"Lax",i=t==="None"?true:e.secure??false;return {sameSite:t,secure:i,maxAgeDays:e.maxAgeDays??365,domain:e.domain}}function F(n){let e={};return n.querySelectorAll("[data-xenia-field]").forEach(t=>{let i=t.getAttribute("data-xenia-field");i&&(e[i]=Ze(t));}),e}function Ze(n){return n instanceof HTMLInputElement?n.type==="checkbox"?n.checked?n.value||"on":"":n.type==="radio"?n.checked?n.value:"":n.value:n instanceof HTMLTextAreaElement||n instanceof HTMLSelectElement?n.value:n.textContent?.trim()??""}function ge(n,e){n&&(n instanceof HTMLButtonElement||n instanceof HTMLInputElement||n instanceof HTMLSelectElement||n instanceof HTMLTextAreaElement||n instanceof HTMLFieldSetElement?n.disabled=e:e?n.setAttribute("aria-disabled","true"):n.removeAttribute("aria-disabled"));}function Q(n,e){let t=n.querySelector("[data-xenia-error]");t&&(t.textContent=e);}var ye="xenia-form-submit-style";function et(n){if(n.querySelector(`#${ye}`))return;let e=document.createElement("style");e.id=ye,e.textContent="@keyframes xenia-submit-spin{to{transform:rotate(360deg)}}[data-xenia-submitting]::after{content:'';display:inline-block;width:0.85em;height:0.85em;margin-left:0.5em;vertical-align:-0.15em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:xenia-submit-spin 0.6s linear infinite}",n.prepend(e);}function G(n,e){n&&(e?n.setAttribute("data-xenia-submitting",""):n.removeAttribute("data-xenia-submitting"));}function tt(n,e){if(!e)return;let t=n instanceof ShadowRoot?n.host:n;t instanceof HTMLElement?(t.style.transition="opacity 220ms ease, transform 220ms ease",t.style.opacity="0",t.style.transform="scale(0.96)",window.setTimeout(e,220)):e();}var nt=900;function it(n,e){let t=n.querySelector("[data-xenia-success]");return t?(t instanceof HTMLElement?t.hidden=false:t.removeAttribute("hidden"),e?e.hidden=true:n.querySelectorAll("[data-xenia-field], [data-xenia-submit]").forEach(i=>{i.hidden=true;}),true):false}function ve(n,e,t){let i=n.querySelector("form"),r=false;et(n);let s=(o,a)=>{if(r)return;if(!e.formSubmit){Q(n,"This form isn't connected yet.");return}r=true,Q(n,""),ge(o,true),G(o,true);let l=Date.now(),c=e.formSubmit.successDisplayMs??nt;t.submitWidgetForm(a).then(()=>{G(o,false),it(n,i);let d=Math.max(0,c-(Date.now()-l));window.setTimeout(()=>tt(n,t.dismiss),d);}).catch(()=>{G(o,false),ge(o,false),Q(n,"Something went wrong. Please try again.");}).finally(()=>{r=false;});};i&&i.addEventListener("submit",o=>{o.preventDefault();let a={};new FormData(i).forEach((c,d)=>{typeof c=="string"&&(a[d]=c);});let l=o.submitter??i.querySelector('[type="submit"], button:not([type])');s(l,{...F(n),...a});}),n.querySelectorAll("[data-xenia-submit]").forEach(o=>{i?.contains(o)||o.addEventListener("click",()=>s(o,F(n)));});}function Y(n){return n.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function be(n){return Y(n).replace(/"/g,""")}function _(n,e){e&&n.querySelectorAll(".xenia-close, [data-xenia-close], [class*='close' i], [id*='close' i], [class*='backdrop' i], [id*='backdrop' i]").forEach(i=>{i.removeAttribute("onclick"),i.addEventListener("click",e);}),n.querySelectorAll("[class*='backdrop' i], [id*='backdrop' i]").forEach(t=>{t instanceof HTMLElement&&t.children.length===0&&!t.textContent?.trim()&&(t.style.opacity="0.5");});}var R=260,A=R/2,C=R/2,xe=["#7c3aed","#2563eb","#059669","#d97706","#dc2626","#0891b2"],rt="transform 3.2s cubic-bezier(0.15, 0.85, 0.25, 1)",st='<div class="xenia-wheel-pointer" aria-hidden="true"></div>',ee=320,we=`opacity ${ee}ms ease, transform ${ee}ms ease`,ot=900,Z=(n,e)=>`perspective(${ot}px) translateZ(${e}px) scale(${n})`;function te(n,e){let t=n*Math.PI/180;return {x:C+e*Math.sin(t),y:C-e*Math.cos(t)}}function at(n,e){let t=te(n,A),i=te(e,A),r=e-n>180?1:0;return `M ${C},${C} L ${t.x},${t.y} A ${A},${A} 0 ${r} 1 ${i.x},${i.y} Z`}function lt(n,e,t=0){if(e<=0)return 0;let i=360/e,s=(360-(n*i+i/2))%360;return t*360+s}function Ee(n,e){let t=n?Array.from(n.children):[],i=e?Array.from(e.children):[];e&&(e.hidden=false),i.forEach(r=>{r instanceof HTMLElement&&(r.style.transition="none",r.style.opacity="0",r.style.transform=Z(.94,-60));}),i.forEach(r=>{r instanceof HTMLElement&&r.offsetHeight;}),Re(()=>{t.forEach(r=>{r instanceof HTMLElement&&(r.style.transition=we,r.style.opacity="0",r.style.transform=Z(.94,-60));}),i.forEach(r=>{r instanceof HTMLElement&&(r.style.transition=we,r.style.opacity="1",r.style.transform=Z(1,0));});}),n&&setTimeout(()=>{n.hidden=true;},ee);}function Re(n){typeof requestAnimationFrame=="function"?requestAnimationFrame(n):setTimeout(n,0);}function dt(n){let t=360/(n.length||1),i=n.map((r,s)=>{let o=s*t,a=o+t,l=r.colorHex||xe[s%xe.length],c=o+t/2,d=te(c,A*.62);return `<path d="${at(o,a)}" fill="${be(l)}" stroke="#fff" stroke-width="1"></path><text x="${d.x}" y="${d.y}" fill="#fff" font-size="12" text-anchor="middle" transform="rotate(${c} ${d.x} ${d.y})">${Y(r.label)}</text>`}).join("");return `<svg class="xenia-wheel-svg" viewBox="0 0 ${R} ${R}" width="${R}" height="${R}"><g class="xenia-wheel-dial">${i}</g><circle class="xenia-wheel-hub" cx="${C}" cy="${C}" r="6"></circle></svg>`}var Ce={html(n){return `<div class="xenia-spin-wheel"><div data-xenia-wheel-face>${n.spinnerHtml??""}</div><div class="xenia-widget-result" data-xenia-wheel-result hidden>${n.winHtml??""}</div><div class="xenia-widget-result" data-xenia-wheel-loss hidden>${n.lossHtml??""}</div></div>`},wire(n,e,t){_(n,t.dismiss);let i=e.segments??[],r=n.querySelector("[data-xenia-wheel-face]"),s=n.querySelector("[data-xenia-spin]"),o=n.querySelector("[data-xenia-wheel-result]"),a=n.querySelector("[data-xenia-wheel-loss]"),l=n.querySelector("[data-xenia-error]"),c=n.querySelector("[data-xenia-wheel-slot]");c&&c.insertAdjacentHTML("afterbegin",dt(i)+st);let d=n.querySelector(".xenia-wheel-dial");if(!s||!e.formSubmit)return;n.querySelectorAll("[data-xenia-wheel-cta]").forEach(v=>{v.addEventListener("click",()=>t.fireCta());});let p=(v,f,b)=>{n.querySelectorAll("[data-xenia-wheel-prize]").forEach(w=>{w.textContent=v;});let P=f?a:o,k=f?o:a;k&&(k.hidden=true),Ee(r,P),b||(I=true,n.querySelectorAll("[data-xenia-spin-again]").forEach(w=>{w.hidden=true;}));},h=false,I=false;n.querySelectorAll("[data-xenia-spin-again]").forEach(v=>{v.addEventListener("click",()=>{if(I)return;let f=o&&!o.hidden?o:a&&!a.hidden?a:null;f&&(h=false,s.disabled=false,l&&(l.textContent=""),Ee(f,r));});}),s.addEventListener("click",()=>{if(h)return;h=true,s.disabled=true,l&&(l.textContent="");let v=F(n);t.submitWidgetForm(v).then(f=>{let b=i.findIndex(Oe=>Oe.id===f.prize?.segmentId),P=f.prize?.label??i[b]?.label??"",k=b>=0&&!!i[b]?.isLoss,w=f.alreadyPlayed!==true;if(b<0||!d){p(P,k,w);return}let Ae=3+Math.floor(Math.random()*3),Me=lt(b,i.length||1,Ae),oe=/rotate\(([-\d.]+)deg\)/.exec(d.style.transform),Pe=oe?parseFloat(oe[1]):0;d.style.transition="none",d.style.transform=`rotate(${Pe%360}deg)`,d.getBoundingClientRect(),d.style.transition=rt;let ae=()=>{d.removeEventListener("transitionend",ae),p(P,k,w);};d.addEventListener("transitionend",ae),Re(()=>{d.style.transform=`rotate(${Me}deg)`;});}).catch(()=>{h=false,s.disabled=false,l&&(l.textContent="Something went wrong. Please try again.");});});}};var ne={"spin-wheel":Ce};function B(n,e=document){if(!n)return null;let t=e.querySelector(`[data-xenia-slot="${ct(n)}"]`);if(t)return t;try{return e.querySelector(n)}catch{return null}}function ct(n){let e=window;return e.CSS?.escape?e.CSS.escape(n):n.replace(/["\\]/g,"\\$&")}function N(n,e){let t=B(n);return t?(e.html!==void 0?t.innerHTML=e.html:e.text!==void 0&&(t.textContent=e.text),t.removeAttribute("hidden"),true):false}function ie(n){let e=B(n);return !e||!(e instanceof HTMLElement)?false:(e.dataset.xeniaHidden="1",e.style.setProperty("display","none","important"),true)}function re(n){let e=B(n);return !e||!(e instanceof HTMLElement)?false:(delete e.dataset.xeniaHidden,e.style.removeProperty("display"),true)}var ut=new Set(["banner","popup","modal","inline"]);function pt(n){return ut.has(n)}var X=class{host=null;shadow=null;onCta;messageTemplate;messageStyles;messageComponent;submitWidgetFormFn;mounted=new Map;constructor(e,t){this.onCta=e,this.messageTemplate=t?.messageTemplate,this.messageStyles=t?.messageStyles,this.messageComponent=t?.messageComponent,this.submitWidgetFormFn=t?.submitWidgetForm;}apply(e){let t=0;for(let i of e)this.applyOne(i)&&t++;return t}applyOne(e){switch(e.type){case "swap":return N(e.target,{html:e.html,text:e.text});case "setContent":return N(e.target,{html:e.html,text:e.text});case "hide":return ie(e.target);case "show":return re(e.target);case "redirect":try{return window.location.assign(e.url),!0}catch{return false}default:return pt(e.type)?this.renderMessage(e):false}}destroy(){this.mounted.forEach(e=>e()),this.mounted.clear(),this.host?.remove(),this.host=null,this.shadow=null;}ensureShadow(){if(this.shadow)return this.shadow;let e=document.createElement("div");e.setAttribute("data-xenia-ui",""),document.body.appendChild(e);let t=e.attachShadow({mode:"open"});return t.appendChild(this.styleEl()),this.host=e,this.shadow=t,t}styleEl(){let e=document.createElement("style");return e.textContent=this.messageStyles?`${ke}
|
|
2
|
-
${this.messageStyles}`:
|
|
1
|
+
var Xenia=(function(exports){'use strict';var qe=new Set(["banner","popup","modal","inline"]),Je=new Set(["top-left","top-right","bottom-left","bottom-right","top","bottom"]),De=new Set(["standard","spin-wheel"]),_e=new Set(["swap","hide","show","setContent","redirect",...qe]);function E(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function u(n){return typeof n=="string"?n:void 0}function ce(n){return typeof n=="number"?n:void 0}function ze(n){if(!E(n))return null;let e=u(n.type);if(!e||!_e.has(e))return null;if(e==="redirect"){let i=u(n.url);return i?{type:e,url:i}:null}if(e==="hide"||e==="show"){let i=u(n.target);return i?{type:e,target:i}:null}if(e==="swap"||e==="setContent"){let i=u(n.target);return i?{type:e,target:i,html:u(n.html),text:u(n.text)}:null}let t=E(n.cta)?{label:u(n.cta.label)??"",url:u(n.cta.url),event:u(n.cta.event)}:void 0;return {type:e,target:u(n.target),title:u(n.title),message:u(n.message),imageUrl:u(n.imageUrl),html:u(n.html),cta:t&&(t.label||t.url)?t:void 0,dismissAfterMs:ce(n.dismissAfterMs),position:Fe(n.position),widgetType:Ne(n.widgetType),segments:Xe(n.segments),formSubmit:Be(n.formSubmit),style:je(n.style),spinnerHtml:u(n.spinnerHtml),winHtml:u(n.winHtml),lossHtml:u(n.lossHtml)}}function Fe(n){let e=u(n);return e&&Je.has(e)?e:void 0}function Ne(n){let e=u(n);return e&&De.has(e)?e:void 0}function Xe(n){if(!Array.isArray(n))return;let e=[];for(let t of n){if(!E(t))continue;let i=u(t.id),r=u(t.label);!i||!r||e.push({id:i,label:r,colorHex:u(t.colorHex),isLoss:t.isLoss===true});}return e.length?e:void 0}function Be(n){if(!E(n))return;let e=u(n.mode);if(e)return {requiresResolve:n.requiresResolve===true,mode:e,flowRef:u(n.flowRef),bindingRef:u(n.bindingRef),successDisplayMs:ce(n.successDisplayMs)}}function je(n){if(!E(n))return;let e={hideCloseButton:n.hideCloseButton===true,hideOverlay:n.hideOverlay===true};return e.hideCloseButton||e.hideOverlay?e:void 0}function U(n){let e;if(Array.isArray(n)?e=n:E(n)&&Array.isArray(n.xeniaActions)&&(e=n.xeniaActions),!Array.isArray(e))return [];let t=[];for(let i of e){let r=ze(i);r&&t.push(r);}return t}var Ue="https://engine.xeniamartech.com/xenia";function T(){return typeof window<"u"&&typeof document<"u"}function ue(n,e){if(!e||Object.keys(e).length===0)return n;let t=new URLSearchParams(e),i=n.includes("?")?"&":"?";return `${n}${i}${t.toString()}`}function pe(n){return new Promise(e=>setTimeout(e,n))}var x=class extends Error{constructor(t,i,r){super(r??`Request failed with status ${t}`);this.status=t;this.data=i;this.name="HttpError";}status;data},K=class extends Error{constructor(t){super(`Circuit open for ${t}: failing fast until the cooldown passes`);this.endpoint=t;this.name="CircuitOpenError";}endpoint},H=new Map,W=class{baseUrl;defaultHeaders;timeout;retry;debug;breaker;constructor(e){this.baseUrl=(e.baseUrl??Ue).replace(/\/+$/,""),this.defaultHeaders=e.defaultHeaders??{},this.timeout=e.timeout??8e3,this.retry={attempts:e.retry?.attempts??2,baseDelayMs:e.retry?.baseDelayMs??300},this.debug=e.debug??false;let t=e.circuitBreaker===true?{}:e.circuitBreaker||null;this.breaker=t&&{failureThreshold:t.failureThreshold??3,cooldownMs:t.cooldownMs??3e4};}async request(e){if(!this.breaker)return this.send(e);let t=ue(this.baseUrl+e.path,e.query),i=H.get(t)??{failures:0,openedAt:0};if(i.openedAt){if(Date.now()-i.openedAt<this.breaker.cooldownMs)throw new K(t);i.openedAt=0,i.failures=this.breaker.failureThreshold-1;}try{let r=await this.send(e);return H.delete(t),r}catch(r){throw r instanceof x&&!this.isRetryableStatus(r.status)?(H.delete(t),r):(i.failures+=1,i.failures>=this.breaker.failureThreshold&&(i.openedAt=Date.now(),this.log(`circuit opened for ${e.path} after ${i.failures} failures`)),H.set(t,i),r)}}async send(e){let t=ue(this.baseUrl+e.path,e.query),i=e.method??"GET",r={"Content-Type":"application/json",...this.defaultHeaders,...e.headers},s=e.body!==void 0?JSON.stringify(e.body):void 0,o=e.timeout??this.timeout,a=e.noRetry?1:(e.retry?.attempts??this.retry.attempts)+1,l=e.retry?.baseDelayMs??this.retry.baseDelayMs,c;for(let d=0;d<a;d++)try{let p=await this.fetchWithTimeout(t,{method:i,headers:r,body:s,...e.keepalive?{keepalive:!0}:{}},o),m=await this.parseBody(p);if(p.ok)return {response:p,data:m};if(this.isRetryableStatus(p.status)&&d<a-1){let k=this.retryAfterMs(p,l,d);this.log(`retrying ${e.path} after ${k}ms (status ${p.status})`),await pe(k);continue}throw new x(p.status,m)}catch(p){if(c=p,p instanceof x)throw p;if(d<a-1){let m=l*2**d;this.log(`retrying ${e.path} after ${m}ms (network error)`),await pe(m);continue}}throw c instanceof Error?c:new Error("Request failed")}async fetchWithTimeout(e,t,i){let r=new AbortController,s=setTimeout(()=>r.abort(),i);try{return await fetch(e,{...t,signal:r.signal})}catch(o){throw o instanceof Error&&o.name==="AbortError"?new Error(`Request timeout after ${i}ms`,{cause:o}):o}finally{clearTimeout(s);}}async parseBody(e){let t=await e.text().catch(()=>"");if(!t)return null;try{return JSON.parse(t)}catch{return t}}isRetryableStatus(e){return e===429||e>=500&&e<=599}retryAfterMs(e,t,i){let r=e.headers.get("Retry-After");if(r){let s=Number(r);if(!Number.isNaN(s))return s*1e3}return t*2**i}log(e){this.debug&&console.warn(`[xenia] ${e}`);}};function Ke(n){try{let e=n.split(".")[1];if(!e)return;let t=JSON.parse(atob(e.replace(/-/g,"+").replace(/_/g,"/")));return typeof t.exp=="number"?t.exp*1e3:void 0}catch{return}}var V=class{config;cached=null;inflight=null;constructor(e){this.config=e;}hasSource(){return !!(this.config.token||this.config.getToken)}getToken=async()=>{if(this.config.token&&!this.config.getToken)return this.config.token;let e=Date.now();if(this.cached&&this.cached.expiresAt>e)return this.cached.token;if(this.inflight)return this.inflight;if(!this.config.getToken)throw new Error("No token source: set config.token or config.getToken.");return this.inflight=Promise.resolve(this.config.getToken()).then(t=>{let i=Ke(t),r=i?i-3e4:e+5*6e4;return this.cached={token:t,expiresAt:r},t}).finally(()=>{this.inflight=null;}),this.inflight}};function he(n){return typeof n=="object"&&n!==null&&!Array.isArray(n)}function Qe(n){return n!==void 0&&he(n)?n:void 0}function w(n){let e={};for(let[t,i]of Object.entries(n))i!==void 0&&(e[t]=i);return e}var L=class{http;tenantId;environmentKey;tokenProvider;constructor(e){this.tenantId=e.tenantId,this.environmentKey=e.environmentKey,this.tokenProvider=new V(e),this.http=new W({baseUrl:e.baseUrl,defaultHeaders:e.headers,timeout:e.timeout,retry:e.retry,circuitBreaker:e.circuitBreaker,debug:e.debug});}tenantHeaders(){return {"X-Tenant-Id":this.tenantId,"X-Environment":this.environmentKey}}async authHeaders(){let e=this.tenantHeaders();if(!this.tokenProvider.hasSource())return e;let t=await this.tokenProvider.getToken();return t&&(e.Authorization=`Bearer ${t}`),e}async webhook(e,t,i="POST"){let{response:r,data:s}=await this.http.request({method:i,path:"/flow",query:{id:e},headers:await this.authHeaders(),body:i==="POST"?t??{}:void 0,noRetry:true});return {status:r.status,body:s,actions:U(s),receivedAt:Date.now()}}async submitWidget(e,t){let{response:i,data:r}=await this.http.request({method:"POST",path:"/widget-submissions",query:{ref:e},headers:await this.authHeaders(),body:t??{},noRetry:true}),s=he(r)?r:{};return {status:i.status,body:r,actions:U(r),prize:Qe(s.prize),alreadyPlayed:s.alreadyPlayed===true,dispatched:s.dispatched===true,dispatchError:typeof s.dispatchError=="string"?s.dispatchError:void 0,widgetType:typeof s.widgetType=="string"?s.widgetType:void 0,receivedAt:Date.now()}}async sendEvents(e,t){let{data:i}=await this.http.request({method:"POST",path:"/api/events/v1",headers:await this.authHeaders(),body:{events:e},keepalive:t?.keepalive});return i}async identify(e){let{data:t}=await this.http.request({method:"POST",path:"/api/events/v1/identify",headers:await this.authHeaders(),body:e});return t}async consent(e,t){let{data:i}=await this.http.request({method:"POST",path:"/api/events/v1/consent",headers:await this.authHeaders(),body:w({xeniaVisitorId:e,analytics:t.analytics===true,marketing:t.marketing===true,personalization:t.personalization===true,consentSource:t.consentSource,consentVersion:t.consentVersion})});return i}async resolve(e,t={}){let{data:i}=await this.http.request({method:"POST",path:"/api/personalization/resolve",query:t.merge!==void 0?{merge:String(t.merge)}:void 0,headers:await this.authHeaders(),body:w({contentKey:e,data:t.data,locale:t.locale})});return i}async resolveBatch(e,t={}){let{data:i}=await this.http.request({method:"POST",path:"/api/personalization/resolve-batch",query:t.merge!==void 0?{merge:String(t.merge)}:void 0,headers:await this.authHeaders(),body:w({items:e,data:t.data,locale:t.locale})});return i}};var $="xenia";function Q(n=$){return `${n||$}_vid`}function me(n=$){return `${n||$}_consent`}function fe(n,e,t=365){return `${me(e)}=${encodeURIComponent(JSON.stringify(n))}; path=/; max-age=${Math.floor(t*86400)}; SameSite=Lax`}var Ge=new Set(["eventId","eventType","journeyId","deviceId","visitorId","customerId","email","utmSource","utmMedium","utmCampaign","utmContent","utmId","url","referrer","path","query"]);function ge(n){let e={};for(let[t,i]of Object.entries(n))i!==void 0&&(e[t]=i);return e}function ye(n){let e={},t={};if(n.props)for(let[r,s]of Object.entries(n.props))s!==void 0&&(Ge.has(r)?t[r]=s:e[r]=s);return {...e,...t,...ge(n.context??{}),...ge(n.identity??{}),eventId:n.eventId,...n.eventType?{eventType:n.eventType}:{},...n.journeyId?{journeyId:n.journeyId}:{}}}var A="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Ye(n){let e=new Uint8Array(n);if(typeof crypto<"u"&&crypto.getRandomValues)crypto.getRandomValues(e);else for(let t=0;t<n;t++)e[t]=Math.floor(Math.random()*256);return e}function g(n=Date.now()){let e="",t=n;for(let s=9;s>=0;s--)e=A[t%32]+e,t=Math.floor(t/32);let i=Ye(16),r="";for(let s=0;s<16;s++)r+=A[i[s]%32];return e+r}var G=26;function ve(n){if(n.length<=G)return n;let e="";for(let i of [2166136261,16777619]){let r=i>>>0;for(let s=0;s<n.length;s++)r^=n.charCodeAt(s),r=Math.imul(r,16777619)>>>0,e+=A[r%32];}let t="";for(let i=0;i<G;i++){let r=i+1;for(let s=i;s<e.length;s+=G)r=r*31+A.indexOf(e[s])>>>0;t+=A[r%32];}return t}var q=class{map=new Map;set(e,t){this.map.set(e,t);}get(e){return this.map.get(e)}keys(){return [...this.map.keys()]}clear(){this.map.clear();}},J=class{nsKey;constructor(e="xenia"){this.nsKey=`${e}_responses`;}read(){try{let e=localStorage.getItem(this.nsKey);return e?JSON.parse(e):{}}catch{return {}}}write(e){try{localStorage.setItem(this.nsKey,JSON.stringify(e));}catch{}}set(e,t){let i=this.read();i[e]=t,this.write(i);}get(e){return this.read()[e]}keys(){return Object.keys(this.read())}clear(){try{localStorage.removeItem(this.nsKey);}catch{}}};var D=class{queue=[];send;batchSize;flushIntervalMs;maxQueueSize;debug;timer=null;flushing=false;listenersAttached=false;allowed=false;onPageHide=()=>this.flushOnUnload();onVisibilityChange=()=>{document.visibilityState==="hidden"&&this.flushOnUnload();};constructor(e){this.send=e.send,this.batchSize=e.batchSize??20,this.flushIntervalMs=e.flushIntervalMs??3e3,this.maxQueueSize=e.maxQueueSize??200,this.debug=e.debug??false;}start(){this.allowed=true,!(this.timer||typeof window>"u")&&(this.timer=setInterval(()=>{this.flush();},this.flushIntervalMs),window.addEventListener("pagehide",this.onPageHide),document.addEventListener("visibilitychange",this.onVisibilityChange),this.listenersAttached=true);}stop(){this.allowed=false,this.timer&&clearInterval(this.timer),this.timer=null,this.listenersAttached&&(window.removeEventListener("pagehide",this.onPageHide),document.removeEventListener("visibilitychange",this.onVisibilityChange),this.listenersAttached=false);}enqueue(e){this.queue.length>=this.maxQueueSize&&this.queue.shift(),this.queue.push(e),this.allowed&&this.queue.length>=this.batchSize&&this.flush();}async sendImmediate(e){try{await this.send([e],{keepalive:!0});}catch(t){this.isPermanentFailure(t)||this.requeue([e]),this.debug&&console.warn("[xenia] immediate send failed",t);}}async flush(){if(!this.allowed||this.flushing||this.queue.length===0)return;this.flushing=true;let e=this.queue.splice(0,this.batchSize);try{await this.send(e);}catch(t){this.isPermanentFailure(t)||this.requeue(e),this.debug&&console.warn("[xenia] analytics flush failed",t);}finally{this.flushing=false;}}isPermanentFailure(e){return e instanceof x&&e.status>=400&&e.status<500}flushOnUnload(){if(!this.allowed||this.queue.length===0)return;let e=this.queue.splice(0,this.queue.length);this.send(e,{keepalive:true}).catch(()=>{});}requeue(e){let t=this.maxQueueSize-this.queue.length;t<=0||this.queue.unshift(...e.slice(0,t));}};var Ze=new Set(["click","view-port","page-load"]);function be(n){let e=n.indexOf("&");if(e===-1)return null;let t=n.slice(0,e),i=n.slice(e+1);return !t||!Ze.has(i)?null:{eventId:t,interactionType:i}}function xe(n){let e=n.getAttribute("data-xenia-content-key");if(!e)return null;let t=n.getAttribute("data-xenia-content-entry-id"),i=n.getAttribute("data-xenia-base-entry-id"),r=n.getAttribute("data-xenia-personalized");return {contentKey:e,contentEntryId:t??void 0,baseEntryId:i??void 0,personalized:r==null?void 0:r==="true"}}var _=class{opts;throttleTimers=new Map;observers=new Map;slotObservers=new Map;triggered=new Set;slotImpressed=new Set;clickHandler=null;mutationObserver=null;constructor(e){this.opts=e;}start(){this.setupClicks(),this.scan(document),this.setupMutations();}stop(){this.clickHandler&&document.removeEventListener("click",this.clickHandler),this.clickHandler=null,this.mutationObserver?.disconnect(),this.mutationObserver=null,this.observers.forEach(e=>e.disconnect()),this.observers.clear(),this.slotObservers.forEach(e=>e.disconnect()),this.slotObservers.clear(),this.throttleTimers.clear(),this.triggered.clear(),this.slotImpressed.clear();}attr(){return `data-${this.opts.prefix}`}throttled(e){if(this.opts.throttleMs<=0)return false;let t=Date.now(),i=this.throttleTimers.get(e)??0;return t-i>=this.opts.throttleMs?(this.throttleTimers.set(e,t),false):true}setupClicks(){let e=this.attr();this.clickHandler=t=>{let i=t.target;if(!i)return;let r=i.closest(`[${e}]`);if(r){let s=r.getAttribute(e),o=s?be(s):null;o&&o.interactionType==="click"&&!this.throttled(r)&&this.opts.fire(o.eventId,"click");}if(this.opts.fireSlot){let s=i.closest("[data-xenia-slot]");if(s){let o=xe(s);o&&!this.throttled(s)&&this.opts.fireSlot("click",o);}}},document.addEventListener("click",this.clickHandler);}observeSlot(e){if(!this.opts.fireSlot||this.slotObservers.has(e))return;let t=xe(e);if(!t)return;let i=this.opts.fireSlot,{threshold:r,rootMargin:s}=this.opts.viewport,o=new IntersectionObserver(a=>{for(let l of a)l.isIntersecting&&(this.slotImpressed.has(e)||(this.slotImpressed.add(e),i("impression",t),o.disconnect(),this.slotObservers.delete(e)));},{threshold:r,rootMargin:s});o.observe(e),this.slotObservers.set(e,o);}observeViewport(e,t){if(this.observers.has(e))return;let{threshold:i,rootMargin:r,triggerOnce:s}=this.opts.viewport,o=new IntersectionObserver(a=>{for(let l of a)l.isIntersecting&&(s&&this.triggered.has(e)||this.throttled(e)||(this.opts.fire(t,"view-port"),s&&(this.triggered.add(e),o.disconnect(),this.observers.delete(e))));},{threshold:i,rootMargin:r});o.observe(e),this.observers.set(e,o);}handleElement(e){let t=e.getAttribute(this.attr());if(!t)return;let i=be(t);i&&(i.interactionType==="view-port"?this.observeViewport(e,i.eventId):i.interactionType==="page-load"&&this.opts.fire(i.eventId,"page-load"));}scan(e){e.querySelectorAll(`[${this.attr()}]`).forEach(t=>this.handleElement(t)),this.opts.fireSlot&&e.querySelectorAll("[data-xenia-slot]").forEach(t=>this.observeSlot(t));}setupMutations(){this.mutationObserver=new MutationObserver(e=>{for(let t of e)for(let i of t.addedNodes){if(i.nodeType!==Node.ELEMENT_NODE)continue;let r=i;this.handleElement(r),this.opts.fireSlot&&r.matches("[data-xenia-slot]")&&this.observeSlot(r),this.scan(r);}}),this.mutationObserver.observe(document.body,{childList:true,subtree:true});}};var et=1800*1e3,z=class{prefix;cookie;deviceId=null;journeyId=null;sessionId=null;constructor(e="xenia",t=true){this.prefix=e,this.cookie=tt(t);}key(e){return `${this.prefix}_${e}`}cookieName(){return Q(this.prefix)}writeVisitorCookie(e){if(!(!this.cookie||!e))try{if(typeof document>"u")return;let t=[`${this.cookieName()}=${encodeURIComponent(e)}`,"path=/",`max-age=${Math.floor(this.cookie.maxAgeDays*86400)}`,`SameSite=${this.cookie.sameSite}`];this.cookie.domain&&t.push(`domain=${this.cookie.domain}`),this.cookie.secure&&t.push("Secure"),document.cookie=t.join("; ");}catch{}}async resolveDeviceId(){if(this.deviceId)return this.writeVisitorCookie(this.getVisitorId()??this.deviceId),this.deviceId;let e=this.key("device_id");try{let i=localStorage.getItem(e);if(i)return this.deviceId=i,this.writeVisitorCookie(this.getVisitorId()??i),i}catch{}let t=await this.mediaDeviceId();t||(t=await this.fingerprint()),this.deviceId=t;try{localStorage.setItem(e,t);}catch{}return this.writeVisitorCookie(this.getVisitorId()??t),t}getDeviceId(){return this.deviceId}async mediaDeviceId(){try{if(!navigator.mediaDevices?.enumerateDevices)return null;let e=await navigator.mediaDevices.enumerateDevices();return (e.find(i=>i.kind==="videoinput"&&i.deviceId)||e.find(i=>i.kind==="audioinput"&&i.deviceId)||e.find(i=>i.kind==="audiooutput"&&i.deviceId))?.deviceId||null}catch{return null}}async fingerprint(){let e=[navigator.userAgent,navigator.language,screen.width,screen.height,screen.colorDepth,new Date().getTimezoneOffset()].join("|"),t=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e));return Array.from(new Uint8Array(t)).map(i=>i.toString(16).padStart(2,"0")).join("")}identify(e){if(e){try{localStorage.setItem(this.key("visitor_id"),e);}catch{}this.writeVisitorCookie(e);}}getVisitorId(){try{return localStorage.getItem(this.key("visitor_id"))}catch{return null}}setCustomerId(e){try{e?localStorage.setItem(this.key("customer_id"),e):localStorage.removeItem(this.key("customer_id"));}catch{}}getCustomerId(){try{return localStorage.getItem(this.key("customer_id"))}catch{return null}}setEmail(e){try{e?localStorage.setItem(this.key("email"),e):localStorage.removeItem(this.key("email"));}catch{}}getEmail(){try{return localStorage.getItem(this.key("email"))}catch{return null}}getSessionId(){let e=this.key("session_id"),t=this.key("session_last_activity"),i=Date.now();try{let r=sessionStorage.getItem(e),s=Number(sessionStorage.getItem(t)),a=r&&Number.isFinite(s)&&i-s<et?r:g(i);return sessionStorage.setItem(e,a),sessionStorage.setItem(t,String(i)),this.sessionId=a,a}catch{return this.sessionId||(this.sessionId=g(i)),this.sessionId}}setJourneyId(e){this.journeyId=e&&e.trim()?e.trim():null;}getJourneyId(){return this.journeyId||this.utmParam("utm_id")||null}utmParam(e){try{let t=new URLSearchParams(window.location.search).get(e);if(t){try{sessionStorage.setItem(this.key(e),t);}catch{}return t}try{return sessionStorage.getItem(this.key(e))}catch{}}catch{}return null}collectContext(){let e={},t=this.utmParam("utm_source"),i=this.utmParam("utm_medium"),r=this.utmParam("utm_campaign"),s=this.utmParam("utm_content"),o=this.utmParam("utm_id");t&&(e.utmSource=t),i&&(e.utmMedium=i),r&&(e.utmCampaign=r),s&&(e.utmContent=s),o&&(e.utmId=o);try{e.url=window.location.href,e.path=window.location.pathname,e.query=Object.fromEntries(new URLSearchParams(window.location.search)),document.referrer&&(e.referrer=document.referrer);}catch{}let a=this.getVisitorId();return a&&(e.visitorId=a),e}};function tt(n){if(n===false)return null;let e=n===true?{}:n,t=e.sameSite??"Lax",i=t==="None"?true:e.secure??false;return {sameSite:t,secure:i,maxAgeDays:e.maxAgeDays??365,domain:e.domain}}function F(n){let e={};return n.querySelectorAll("[data-xenia-field]").forEach(t=>{let i=t.getAttribute("data-xenia-field");i&&(e[i]=nt(t));}),e}function nt(n){return n instanceof HTMLInputElement?n.type==="checkbox"?n.checked?n.value||"on":"":n.type==="radio"?n.checked?n.value:"":n.value:n instanceof HTMLTextAreaElement||n instanceof HTMLSelectElement?n.value:n.textContent?.trim()??""}function we(n,e){n&&(n instanceof HTMLButtonElement||n instanceof HTMLInputElement||n instanceof HTMLSelectElement||n instanceof HTMLTextAreaElement||n instanceof HTMLFieldSetElement?n.disabled=e:e?n.setAttribute("aria-disabled","true"):n.removeAttribute("aria-disabled"));}function Y(n,e){let t=n.querySelector("[data-xenia-error]");t&&(t.textContent=e);}var Se="xenia-form-submit-style";function it(n){if(n.querySelector(`#${Se}`))return;let e=document.createElement("style");e.id=Se,e.textContent="@keyframes xenia-submit-spin{to{transform:rotate(360deg)}}[data-xenia-submitting]::after{content:'';display:inline-block;width:0.85em;height:0.85em;margin-left:0.5em;vertical-align:-0.15em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:xenia-submit-spin 0.6s linear infinite}",n.prepend(e);}function Z(n,e){n&&(e?n.setAttribute("data-xenia-submitting",""):n.removeAttribute("data-xenia-submitting"));}function rt(n,e){if(!e)return;let t=n instanceof ShadowRoot?n.host:n;t instanceof HTMLElement?(t.style.transition="opacity 220ms ease, transform 220ms ease",t.style.opacity="0",t.style.transform="scale(0.96)",window.setTimeout(e,220)):e();}var st=900;function ot(n,e){let t=n.querySelector("[data-xenia-success]");return t?(t instanceof HTMLElement?t.hidden=false:t.removeAttribute("hidden"),e?e.hidden=true:n.querySelectorAll("[data-xenia-field], [data-xenia-submit]").forEach(i=>{i.hidden=true;}),true):false}function Ee(n,e,t){let i=n.querySelector("form"),r=false;it(n);let s=(o,a)=>{if(r)return;if(!e.formSubmit){Y(n,"This form isn't connected yet.");return}r=true,Y(n,""),we(o,true),Z(o,true);let l=Date.now(),c=e.formSubmit.successDisplayMs??st;t.submitWidgetForm(a).then(()=>{Z(o,false),ot(n,i);let d=Math.max(0,c-(Date.now()-l));window.setTimeout(()=>rt(n,t.dismiss),d);}).catch(()=>{Z(o,false),we(o,false),Y(n,"Something went wrong. Please try again.");}).finally(()=>{r=false;});};i&&i.addEventListener("submit",o=>{o.preventDefault();let a={};new FormData(i).forEach((c,d)=>{typeof c=="string"&&(a[d]=c);});let l=o.submitter??i.querySelector('[type="submit"], button:not([type])');s(l,{...F(n),...a});}),n.querySelectorAll("[data-xenia-submit]").forEach(o=>{i?.contains(o)||o.addEventListener("click",()=>s(o,F(n)));});}function ee(n){return n.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function Ce(n){return ee(n).replace(/"/g,""")}function N(n,e){e&&n.querySelectorAll(".xenia-close, [data-xenia-close], [class*='close' i], [id*='close' i], [class*='backdrop' i], [id*='backdrop' i]").forEach(i=>{i.removeAttribute("onclick"),i.addEventListener("click",e);}),n.querySelectorAll("[class*='backdrop' i], [id*='backdrop' i]").forEach(t=>{t instanceof HTMLElement&&t.children.length===0&&!t.textContent?.trim()&&(t.style.opacity="0.5");});}var C=260,M=C/2,R=C/2,Re=["#7c3aed","#2563eb","#059669","#d97706","#dc2626","#0891b2"],at="transform 3.2s cubic-bezier(0.15, 0.85, 0.25, 1)",lt='<div class="xenia-wheel-pointer" aria-hidden="true"></div>',ne=320,ke=`opacity ${ne}ms ease, transform ${ne}ms ease`,dt=900,te=(n,e)=>`perspective(${dt}px) translateZ(${e}px) scale(${n})`;function ie(n,e){let t=n*Math.PI/180;return {x:R+e*Math.sin(t),y:R-e*Math.cos(t)}}function ct(n,e){let t=ie(n,M),i=ie(e,M),r=e-n>180?1:0;return `M ${R},${R} L ${t.x},${t.y} A ${M},${M} 0 ${r} 1 ${i.x},${i.y} Z`}function ut(n,e,t=0){if(e<=0)return 0;let i=360/e,s=(360-(n*i+i/2))%360;return t*360+s}function Te(n,e){let t=n?Array.from(n.children):[],i=e?Array.from(e.children):[];e&&(e.hidden=false),i.forEach(r=>{r instanceof HTMLElement&&(r.style.transition="none",r.style.opacity="0",r.style.transform=te(.94,-60));}),i.forEach(r=>{r instanceof HTMLElement&&r.offsetHeight;}),Ae(()=>{t.forEach(r=>{r instanceof HTMLElement&&(r.style.transition=ke,r.style.opacity="0",r.style.transform=te(.94,-60));}),i.forEach(r=>{r instanceof HTMLElement&&(r.style.transition=ke,r.style.opacity="1",r.style.transform=te(1,0));});}),n&&setTimeout(()=>{n.hidden=true;},ne);}function Ae(n){typeof requestAnimationFrame=="function"?requestAnimationFrame(n):setTimeout(n,0);}function pt(n){let t=360/(n.length||1),i=n.map((r,s)=>{let o=s*t,a=o+t,l=r.colorHex||Re[s%Re.length],c=o+t/2,d=ie(c,M*.62);return `<path d="${ct(o,a)}" fill="${Ce(l)}" stroke="#fff" stroke-width="1"></path><text x="${d.x}" y="${d.y}" fill="#fff" font-size="12" text-anchor="middle" transform="rotate(${c} ${d.x} ${d.y})">${ee(r.label)}</text>`}).join("");return `<svg class="xenia-wheel-svg" viewBox="0 0 ${C} ${C}" width="${C}" height="${C}"><g class="xenia-wheel-dial">${i}</g><circle class="xenia-wheel-hub" cx="${R}" cy="${R}" r="6"></circle></svg>`}var Me={html(n){return `<div class="xenia-spin-wheel"><div data-xenia-wheel-face>${n.spinnerHtml??""}</div><div class="xenia-widget-result" data-xenia-wheel-result hidden>${n.winHtml??""}</div><div class="xenia-widget-result" data-xenia-wheel-loss hidden>${n.lossHtml??""}</div></div>`},wire(n,e,t){N(n,t.dismiss);let i=e.segments??[],r=n.querySelector("[data-xenia-wheel-face]"),s=n.querySelector("[data-xenia-spin]"),o=n.querySelector("[data-xenia-wheel-result]"),a=n.querySelector("[data-xenia-wheel-loss]"),l=n.querySelector("[data-xenia-error]"),c=n.querySelector("[data-xenia-wheel-slot]");c&&c.insertAdjacentHTML("afterbegin",pt(i)+lt);let d=n.querySelector(".xenia-wheel-dial");if(!s||!e.formSubmit)return;n.querySelectorAll("[data-xenia-wheel-cta]").forEach(v=>{v.addEventListener("click",()=>t.fireCta());});let p=(v,f,b)=>{n.querySelectorAll("[data-xenia-wheel-prize]").forEach(S=>{S.textContent=v;});let O=f?a:o,I=f?o:a;I&&(I.hidden=true),Te(r,O),b||(k=true,n.querySelectorAll("[data-xenia-spin-again]").forEach(S=>{S.hidden=true;}));},m=false,k=false;n.querySelectorAll("[data-xenia-spin-again]").forEach(v=>{v.addEventListener("click",()=>{if(k)return;let f=o&&!o.hidden?o:a&&!a.hidden?a:null;f&&(m=false,s.disabled=false,l&&(l.textContent=""),Te(f,r));});}),s.addEventListener("click",()=>{if(m)return;m=true,s.disabled=true,l&&(l.textContent="");let v=F(n);t.submitWidgetForm(v).then(f=>{let b=i.findIndex($e=>$e.id===f.prize?.segmentId),O=f.prize?.label??i[b]?.label??"",I=b>=0&&!!i[b]?.isLoss,S=f.alreadyPlayed!==true;if(b<0||!d){p(O,I,S);return}let We=3+Math.floor(Math.random()*3),Ve=ut(b,i.length||1,We),le=/rotate\(([-\d.]+)deg\)/.exec(d.style.transform),Le=le?parseFloat(le[1]):0;d.style.transition="none",d.style.transform=`rotate(${Le%360}deg)`,d.getBoundingClientRect(),d.style.transition=at;let de=()=>{d.removeEventListener("transitionend",de),p(O,I,S);};d.addEventListener("transitionend",de),Ae(()=>{d.style.transform=`rotate(${Ve}deg)`;});}).catch(()=>{m=false,s.disabled=false,l&&(l.textContent="Something went wrong. Please try again.");});});}};var re={"spin-wheel":Me};function j(n,e=document){if(!n)return null;let t=e.querySelector(`[data-xenia-slot="${ht(n)}"]`);if(t)return t;try{return e.querySelector(n)}catch{return null}}function ht(n){let e=window;return e.CSS?.escape?e.CSS.escape(n):n.replace(/["\\]/g,"\\$&")}function X(n,e){let t=j(n);return t?(e.html!==void 0?t.innerHTML=e.html:e.text!==void 0&&(t.textContent=e.text),t.removeAttribute("hidden"),true):false}function se(n){let e=j(n);return !e||!(e instanceof HTMLElement)?false:(e.dataset.xeniaHidden="1",e.style.setProperty("display","none","important"),true)}function oe(n){let e=j(n);return !e||!(e instanceof HTMLElement)?false:(delete e.dataset.xeniaHidden,e.style.removeProperty("display"),true)}var mt=new Set(["banner","popup","modal","inline"]);function ft(n){return mt.has(n)}var B=class{host=null;shadow=null;onCta;messageTemplate;messageStyles;messageComponent;submitWidgetFormFn;mounted=new Map;constructor(e,t){this.onCta=e,this.messageTemplate=t?.messageTemplate,this.messageStyles=t?.messageStyles,this.messageComponent=t?.messageComponent,this.submitWidgetFormFn=t?.submitWidgetForm;}apply(e){let t=0;for(let i of e)this.applyOne(i)&&t++;return t}applyOne(e){switch(e.type){case "swap":return X(e.target,{html:e.html,text:e.text});case "setContent":return X(e.target,{html:e.html,text:e.text});case "hide":return se(e.target);case "show":return oe(e.target);case "redirect":try{return window.location.assign(e.url),!0}catch{return false}default:return ft(e.type)?this.renderMessage(e):false}}destroy(){this.mounted.forEach(e=>e()),this.mounted.clear(),this.host?.remove(),this.host=null,this.shadow=null;}ensureShadow(){if(this.shadow)return this.shadow;let e=document.createElement("div");e.setAttribute("data-xenia-ui",""),document.body.appendChild(e);let t=e.attachShadow({mode:"open"});return t.appendChild(this.styleEl()),this.host=e,this.shadow=t,t}styleEl(){let e=document.createElement("style");return e.textContent=this.messageStyles?`${Oe}
|
|
2
|
+
${this.messageStyles}`:Oe,e}renderMessage(e){let t=e.html===void 0&&!!this.messageComponent;if(e.type==="inline"){if(!e.target)return false;let c=j(e.target);return c?(this.unmount(c),t?(c.innerHTML="",this.mountComponent(c,e,"inline",()=>this.unmount(c))):(c.innerHTML=this.cardHtml(e,"inline"),this.wireCard(c,e)),true):false}let i=this.ensureShadow(),r=document.createElement("div"),s=this.positionClass(e.type,e.position),o=e.html!==void 0?" xenia-custom-bg":"",a=e.style?.hideOverlay?" xenia-no-overlay":"";r.className=`xenia-msg xenia-${e.type}${s?` ${s}`:""}${o}${a}`,i.appendChild(r);let l=()=>{this.unmount(r),r.remove();};if(t?this.mountComponent(r,e,e.type,l):(r.innerHTML=this.cardHtml(e,e.type),this.wireCard(r,e,l)),e.dismissAfterMs&&e.dismissAfterMs>0){let c=setTimeout(l,e.dismissAfterMs),d=()=>clearTimeout(c);r.addEventListener("click",d,{once:true}),r.addEventListener("focusin",d,{once:true});}return true}mountComponent(e,t,i,r){if(!this.messageComponent)return;let s={action:t,variant:i,close:r,fireCta:()=>{t.cta?.event&&this.onCta?.(t.cta.event,t.cta),t.cta?.url&&window.location.assign(t.cta.url),r();}},o=this.messageComponent(e,s);o&&this.mounted.set(e,o);}unmount(e){let t=this.mounted.get(e);t&&(t(),this.mounted.delete(e));}positionClass(e,t){return e==="popup"?`xenia-pos-${t==="top-left"||t==="top-right"||t==="bottom-left"?t:"bottom-right"}`:e==="banner"?`xenia-pos-${t==="top"?"top":"bottom"}`:""}cardHtml(e,t){let i=e.widgetType?re[e.widgetType]:void 0;if(i)return i.html(e,t);if(e.html!==void 0)return gt(e.html,t);if(this.messageTemplate){let c={action:e,variant:t,escapeHtml:P,escapeAttr:Pe};return this.messageTemplate(c)}let r=e.imageUrl?`<img class="xenia-img" src="${Pe(e.imageUrl)}" alt="" />`:"",s=e.title?`<div class="xenia-title">${P(e.title)}</div>`:"",o=e.message?`<div class="xenia-body">${P(e.message)}</div>`:"",a=e.cta?`<button class="xenia-cta" type="button">${P(e.cta.label)}</button>`:"";return `<div class="xenia-card">${t==="inline"?"":'<button class="xenia-close" type="button" aria-label="Close">\xD7</button>'}${r}<div class="xenia-content">${s}${o}${a}</div></div>`}wireCard(e,t,i){let r=t.widgetType?re[t.widgetType]:void 0;if(r){r.wire(e,t,this.buildWidgetCtx(t,i));return}N(e,i);let s=e.querySelector(".xenia-cta, [data-xenia-cta]");s&&t.cta&&s.addEventListener("click",()=>{t.cta?.event&&this.onCta?.(t.cta.event,t.cta),t.cta?.url&&window.location.assign(t.cta.url),i?.();}),Ee(e,t,this.buildWidgetCtx(t,i));}buildWidgetCtx(e,t){let i=g();return {dismiss:t,fireCta:()=>{e.cta?.event&&this.onCta?.(e.cta.event,e.cta),e.cta?.url&&window.location.assign(e.cta.url),t?.();},submitWidgetForm:r=>!e.formSubmit||!this.submitWidgetFormFn?Promise.reject(new Error("xenia: this action has no formSubmit config, or no submitWidgetForm handler is wired (construct the ActionRenderer via XeniaClient to get one)")):this.submitWidgetFormFn(e.formSubmit,r,{idempotencyKey:i})}}};function gt(n,e){return e==="modal"?`<div class="xenia-custom-html">${n}</div>`:`<div class="xenia-custom-html"><style>.xenia-custom-html{position:relative}.xenia-custom-html>*{position:static!important;inset:auto!important;top:auto!important;right:auto!important;bottom:auto!important;left:auto!important}</style>${n}</div>`}function P(n){return n.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function Pe(n){return P(n).replace(/"/g,""")}var Oe=`
|
|
3
3
|
:host { all: initial; }
|
|
4
4
|
.xenia-msg { position: fixed; z-index: var(--xenia-z-index, 2147483000);
|
|
5
5
|
font-family: var(--xenia-font-family, system-ui, sans-serif); }
|
|
@@ -76,6 +76,6 @@ ${this.messageStyles}`:ke,e}renderMessage(e){let t=e.html===void 0&&!!this.messa
|
|
|
76
76
|
.xenia-widget-result-cta { margin-top: 8px; background: var(--xenia-cta-bg, #111); color: var(--xenia-cta-color, #fff);
|
|
77
77
|
border: 0; border-radius: var(--xenia-cta-radius, 8px); padding: var(--xenia-cta-padding, 8px 14px);
|
|
78
78
|
font-size: var(--xenia-cta-size, 14px); font-family: inherit; cursor: pointer; }
|
|
79
|
-
`;var
|
|
79
|
+
`;var ae=class{api;identity;responses;options;renderer;analyticsQueue;tracker=null;ready=false;consentState;listeners=new Map;constructor(e){this.options=e;let t=e.prefix??"xenia";this.identity=new z(t,e.visitorCookie??true),e.visitorId&&this.identity.identify(e.visitorId),this.api=new L(e),this.responses=e.persistResponses===false||!T()?new q:new J(t),this.renderer=new B((i,r)=>this.onCtaEvent(i,r),{messageTemplate:e.messageTemplate,messageStyles:e.messageStyles,messageComponent:e.messageComponent,submitWidgetForm:(i,r,s)=>this.submitWidgetForm(i,r,s)}),this.consentState={...e.consent},this.analyticsQueue=new D({debug:e.debug,send:(i,r)=>this.api.sendEvents(i.map(s=>({...s,consent:this.consentState})),r)});}async init(){this.ready||!T()||(await this.identity.resolveDeviceId(),this.ready=true,this.analyticsAllowed()&&this.analyticsQueue.start(),this.options.autoTrack!==false&&(this.tracker=new _({prefix:this.options.prefix??"xenia",throttleMs:this.options.throttleMs??1e3,viewport:{threshold:this.options.viewport?.threshold??.5,rootMargin:this.options.viewport?.rootMargin,triggerOnce:this.options.viewport?.triggerOnce??true},fire:(e,t)=>this.track(e,{interactionType:t},{trigger:true}),fireSlot:(e,t)=>{this.track("customEvent",{customEventName:e==="impression"?"personalization_impression":"personalization_click",contentKey:t.contentKey,contentEntryId:t.contentEntryId??null,baseEntryId:t.baseEntryId??null},{trigger:false});}}),this.tracker.start()));}destroy(){this.tracker?.stop(),this.tracker=null,this.renderer.destroy(),this.analyticsQueue.stop(),this.ready=false;}identify(e,t){this.identity.identify(e),t?.customerId!==void 0&&this.identity.setCustomerId(t.customerId),t?.email!==void 0&&this.identity.setEmail(t.email),this.api.identify({xeniaVisitorId:e,userId:t?.customerId,email:t?.email}).then(i=>this.emit("identify",i)).catch(i=>this.emitError(i));}setJourneyId(e){this.identity.setJourneyId(e);}track(e,t,i={}){if(!T())return;let r=i.trigger===true;if(!r&&this.dntBlocked())return;let s=this.buildEvent(e,t,i);this.emit("event",{eventName:e,event:s}),r?this.analyticsQueue.sendImmediate(s).catch(o=>this.emitError(o)):(this.analyticsQueue.enqueue(s),this.analyticsAllowed()&&this.analyticsQueue.start());}buildEvent(e,t,i){let r=this.identity.collectContext(),s;try{s=Intl.DateTimeFormat().resolvedOptions().timeZone;}catch{}return {eventName:e,eventId:i.eventId?ve(i.eventId):g(),eventVersion:1,occurredAtMs:Date.now(),source:"xenia-js-sdk",identity:{sessionId:this.identity.getSessionId(),...i.identity,xeniaVisitorId:r.visitorId??this.identity.getDeviceId()??void 0},context:{pagePath:r.path,referrer:r.referrer,userAgent:navigator.userAgent,locale:navigator.language,timezone:s,journeyId:this.identity.getJourneyId()??void 0,utmSource:r.utmSource,utmMedium:r.utmMedium,utmCampaign:r.utmCampaign,utmContent:r.utmContent,...i.context},properties:this.withUtmId(t,r.utmId),trigger:i.trigger===true?true:void 0}}withUtmId(e,t){return !t||e&&(e.utm_id!==void 0||e.utmId!==void 0)?e:{...e,utm_id:t}}async resolve(e,t){return this.api.resolve(e,this.withVisitor(t))}async resolveBatch(e,t){return this.api.resolveBatch(e,this.withVisitor(t))}resolveVisitorId(){return this.identity.getVisitorId()??this.identity.getDeviceId()??void 0}withVisitor(e){let t=this.identity.collectContext(),i=this.resolveVisitorId(),r=w({path:t.path,query:t.query,deviceId:this.identity.getDeviceId()??void 0,visitorId:i,customerId:this.identity.getCustomerId()??void 0,email:this.identity.getEmail()??void 0,url:t.url,referrer:t.referrer,utmSource:t.utmSource,utmMedium:t.utmMedium,utmCampaign:t.utmCampaign,utmContent:t.utmContent,utmId:t.utmId});return i&&(r._xenia_vid=i),Object.keys(r).length===0?e:{...e,data:{...e?.data??{},...r}}}dntBlocked(){if(this.options.respectDnt===false)return false;try{let e=navigator,t=navigator.doNotTrack??window.doNotTrack;if(t==="1"||t==="yes"||e.globalPrivacyControl===!0)return !0}catch{}return false}analyticsAllowed(){return this.consentState.analytics===true}async request(e,t,i){let r=this.identity.collectContext(),s=ye({eventId:e,journeyId:this.identity.getJourneyId()??void 0,identity:{deviceId:this.identity.getDeviceId()??void 0,visitorId:this.resolveVisitorId()},context:r,props:t}),o=await this.api.webhook(e,s,i?.method??"POST");return this.responses.set(e,o),this.emit("response",{triggerId:e,response:o}),(i?.apply??this.options.autoApplyActions!==false)&&o.actions.length&&this.applyActions(o.actions),o}getResponse(e){return this.responses.get(e)}async submitWidgetForm(e,t,i){let r=this.identity.collectContext(),s=i?.idempotencyKey??g(),o=e.flowRef??e.bindingRef,a={visitorId:this.resolveVisitorId(),deviceId:this.identity.getDeviceId()??void 0,idempotencyKey:s,mode:e.mode,ref:o,url:r.url,referrer:r.referrer,path:r.path,query:r.query,utmSource:r.utmSource,utmMedium:r.utmMedium,utmCampaign:r.utmCampaign,utmContent:r.utmContent,utmId:r.utmId},l=e.flowRef?await this.api.webhook(e.flowRef,w({...a,...t}),"POST"):await this.api.submitWidget(e.bindingRef??"",w({...a,fields:t}));return this.emit("response",{triggerId:o,response:l}),(i?.apply??this.options.autoApplyActions!==false)&&l.actions.length&&this.applyActions(l.actions),l}swapContent(e,t){return X(e,t)}hideContent(e){return se(e)}showContent(e){return oe(e)}applyActions(e){let t=this.renderer.apply(e);return this.emit("actions",{actions:e,applied:t}),t}async consent(e){let t=this.analyticsAllowed();if(this.consentState={...this.consentState,...e},T())try{document.cookie=fe(this.consentState,this.options.prefix);}catch{}this.analyticsAllowed()&&!t?(this.analyticsQueue.start(),this.analyticsQueue.flush()):!this.analyticsAllowed()&&t&&this.analyticsQueue.stop();let i=this.identity.getVisitorId()??this.identity.getDeviceId();if(i)try{await this.api.consent(i,e);}catch(r){this.emitError(r);}}on(e,t){let i=this.listeners.get(e)??new Set;return i.add(t),this.listeners.set(e,i),()=>i.delete(t)}emit(e,t){this.listeners.get(e)?.forEach(i=>{try{i(t);}catch{}});}emitError(e){let t=e instanceof Error?e:new Error(String(e));this.options.debug&&console.error("[xenia]",t),this.emit("error",t);}onCtaEvent(e,t){this.track(e,{ctaLabel:t.label,ctaUrl:t.url??null},{trigger:true});}};function He(n){return new ae(n)}var y=null;function h(n){if(!y)throw console.warn(`[xenia] ${n}() called before init().`),new Error("Xenia not initialized");return y}var an={init(n){y&&y.destroy(),y=He(n),y.init();},destroy(){y?.destroy(),y=null;},track(n,e,t){h("track").track(n,e,t);},identify(n){h("identify").identify(n);},consent(n){return h("consent").consent(n)},resolve(n,e){return h("resolve").resolve(n,e)},resolveBatch(n,e){return h("resolveBatch").resolveBatch(n,e)},setJourneyId(n){h("setJourneyId").setJourneyId(n);},request(n,e){return h("request").request(n,e)},getResponse(n){return h("getResponse").getResponse(n)},swapContent(n,e){return h("swapContent").swapContent(n,e)},hideContent(n){return h("hideContent").hideContent(n)},showContent(n){return h("showContent").showContent(n)}};exports.Xenia=an;return exports;})({});
|
|
80
80
|
Xenia=Xenia&&(Xenia.Xenia||Xenia.default)||Xenia;if(typeof window!=='undefined'){window.Xenia=Xenia;}
|
|
81
81
|
//# sourceMappingURL=xenia-sdk.iife.js.map
|