@vio-live/web-sdk 0.4.2 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-KMIARP6X.js → chunk-7Q4MJ5NQ.js} +514 -6
- package/dist/chunk-7Q4MJ5NQ.js.map +1 -0
- package/dist/{chunk-D6IA4ZWL.js → chunk-LRXVUOEG.js} +93 -3
- package/dist/chunk-LRXVUOEG.js.map +1 -0
- package/dist/chunk-QYQQ5J6E.js +3 -0
- package/dist/{chunk-PBQSHIID.js.map → chunk-QYQQ5J6E.js.map} +1 -1
- package/dist/core/index.d.ts +168 -1
- package/dist/core/index.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +3 -3
- package/dist/{tokens-DjVcB-RL.d.ts → tokens-TIH4RkPl.d.ts} +8 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-D6IA4ZWL.js.map +0 -1
- package/dist/chunk-KMIARP6X.js.map +0 -1
- package/dist/chunk-PBQSHIID.js +0 -3
|
@@ -15,16 +15,19 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
15
15
|
var DEFAULT_URLS = {
|
|
16
16
|
development: {
|
|
17
17
|
apiBase: "https://api-dev.vio.live",
|
|
18
|
-
graphQLBase: "https://graph-ql-dev.vio.live"
|
|
18
|
+
graphQLBase: "https://graph-ql-dev.vio.live",
|
|
19
|
+
eventsBase: "https://events-dev.vio.live"
|
|
19
20
|
},
|
|
20
21
|
// No separate staging GraphQL deployment yet — shares dev's.
|
|
21
22
|
testing: {
|
|
22
23
|
apiBase: "https://api-staging.vio.live",
|
|
23
|
-
graphQLBase: "https://graph-ql-dev.vio.live"
|
|
24
|
+
graphQLBase: "https://graph-ql-dev.vio.live",
|
|
25
|
+
eventsBase: "https://events-staging.vio.live"
|
|
24
26
|
},
|
|
25
27
|
production: {
|
|
26
28
|
apiBase: "https://api.vio.live",
|
|
27
|
-
graphQLBase: "https://graph-ql.vio.live"
|
|
29
|
+
graphQLBase: "https://graph-ql.vio.live",
|
|
30
|
+
eventsBase: "https://events.vio.live"
|
|
28
31
|
}
|
|
29
32
|
};
|
|
30
33
|
var ConfigurationSingleton = class {
|
|
@@ -43,6 +46,7 @@ var ConfigurationSingleton = class {
|
|
|
43
46
|
environment: env,
|
|
44
47
|
apiBase: input.apiBase ?? defaults.apiBase,
|
|
45
48
|
graphQLBase: input.graphQLBase ?? defaults.graphQLBase,
|
|
49
|
+
eventsBase: input.eventsBase ?? defaults.eventsBase,
|
|
46
50
|
stripePublishableKey: input.stripePublishableKey,
|
|
47
51
|
klarnaClientId: input.klarnaClientId,
|
|
48
52
|
// Sandbox by default — live Klarna must be opted into explicitly.
|
|
@@ -3845,6 +3849,501 @@ function toSmallestUnit(amount, currency) {
|
|
|
3845
3849
|
return Math.round(amount * 100);
|
|
3846
3850
|
}
|
|
3847
3851
|
|
|
3852
|
+
// src/core/version.ts
|
|
3853
|
+
var SDK_VERSION = "0.5.1";
|
|
3854
|
+
|
|
3855
|
+
// src/core/analytics/analytics-manager.ts
|
|
3856
|
+
var SESSION_TTL_MS = 30 * 60 * 1e3;
|
|
3857
|
+
var FLUSH_INTERVAL_MS = 5e3;
|
|
3858
|
+
var FLUSH_AT_COUNT = 20;
|
|
3859
|
+
var MAX_QUEUE = 500;
|
|
3860
|
+
var RETRY_BACKOFF_MS = [2e3, 4e3, 8e3];
|
|
3861
|
+
var ANON_KEY = "vio.anon.v1";
|
|
3862
|
+
var SESSION_KEY = "vio.session.v1";
|
|
3863
|
+
var IMPRESSION_THRESHOLD = 0.5;
|
|
3864
|
+
var IMPRESSION_DWELL_MS = 1e3;
|
|
3865
|
+
var AnalyticsManager = class extends EventTarget {
|
|
3866
|
+
constructor() {
|
|
3867
|
+
super(...arguments);
|
|
3868
|
+
this.started = false;
|
|
3869
|
+
this.options = { host: "custom", collector: true, dataLayer: false, debug: false, autoTrack: true };
|
|
3870
|
+
this.queue = [];
|
|
3871
|
+
this.flushTimer = null;
|
|
3872
|
+
this.retryAttempt = 0;
|
|
3873
|
+
this.retryNotBefore = 0;
|
|
3874
|
+
this.sessionId = null;
|
|
3875
|
+
this.sessionStartedAt = 0;
|
|
3876
|
+
/** Impression once-per-(session, component) guard; reset on session rotation. */
|
|
3877
|
+
this.impressedComponents = /* @__PURE__ */ new Set();
|
|
3878
|
+
/** Last NON-EMPTY cart snapshot — `purchase` uses it because several flows
|
|
3879
|
+
* (Apple Pay) clear the cart BEFORE `vio:payment-success` fires. */
|
|
3880
|
+
this.lastCartSnapshot = null;
|
|
3881
|
+
this.teardown = [];
|
|
3882
|
+
}
|
|
3883
|
+
// MARK: - Public API
|
|
3884
|
+
/**
|
|
3885
|
+
* Install listeners + begin flushing. Explicit on purpose (no module
|
|
3886
|
+
* side-effects). Safe to call twice (second call only updates options).
|
|
3887
|
+
*/
|
|
3888
|
+
start(options = {}) {
|
|
3889
|
+
this.options = {
|
|
3890
|
+
host: options.host ?? this.options.host,
|
|
3891
|
+
collector: options.collector ?? this.options.collector,
|
|
3892
|
+
dataLayer: options.dataLayer ?? this.options.dataLayer,
|
|
3893
|
+
debug: options.debug ?? this.options.debug,
|
|
3894
|
+
autoTrack: options.autoTrack ?? this.options.autoTrack
|
|
3895
|
+
};
|
|
3896
|
+
if (this.started || !hasDom()) return;
|
|
3897
|
+
this.started = true;
|
|
3898
|
+
this.ensureSession();
|
|
3899
|
+
this.flushTimer = setInterval(() => void this.flush(), FLUSH_INTERVAL_MS);
|
|
3900
|
+
const onPageHide = () => {
|
|
3901
|
+
this.trackSessionEnd();
|
|
3902
|
+
this.flushWithBeacon();
|
|
3903
|
+
};
|
|
3904
|
+
window.addEventListener("pagehide", onPageHide);
|
|
3905
|
+
this.teardown.push(() => window.removeEventListener("pagehide", onPageHide));
|
|
3906
|
+
if (this.options.autoTrack) this.installAutoListeners();
|
|
3907
|
+
}
|
|
3908
|
+
/** Stop timers + listeners (tests / SPA unmount). Queue survives. */
|
|
3909
|
+
stop() {
|
|
3910
|
+
if (this.flushTimer) clearInterval(this.flushTimer);
|
|
3911
|
+
this.flushTimer = null;
|
|
3912
|
+
for (const fn of this.teardown.splice(0)) fn();
|
|
3913
|
+
this.started = false;
|
|
3914
|
+
}
|
|
3915
|
+
/** Attach the partner's user id (opaque) to all subsequent events. */
|
|
3916
|
+
identify(externalUserId) {
|
|
3917
|
+
this.externalUserId = externalUserId ?? void 0;
|
|
3918
|
+
}
|
|
3919
|
+
/** Queue one event. Works pre-init and pre-start (SSR: no-op). */
|
|
3920
|
+
track(name, opts = {}) {
|
|
3921
|
+
if (!hasDom()) return;
|
|
3922
|
+
const event = this.buildEvent(name, opts);
|
|
3923
|
+
if (!event) return;
|
|
3924
|
+
if (this.options.debug) {
|
|
3925
|
+
console.debug("[Vio.analytics]", event.name, event);
|
|
3926
|
+
}
|
|
3927
|
+
try {
|
|
3928
|
+
window.dispatchEvent(new CustomEvent("vio:analytics", { detail: event }));
|
|
3929
|
+
} catch {
|
|
3930
|
+
}
|
|
3931
|
+
if (this.options.dataLayer) pushGa4(event);
|
|
3932
|
+
this.queue.push(event);
|
|
3933
|
+
if (this.queue.length > MAX_QUEUE) this.queue.splice(0, this.queue.length - MAX_QUEUE);
|
|
3934
|
+
if (this.queue.length >= FLUSH_AT_COUNT) void this.flush();
|
|
3935
|
+
}
|
|
3936
|
+
/**
|
|
3937
|
+
* Impression helper: observes an element and tracks ONE
|
|
3938
|
+
* `component_impression` per (session, campaignComponentId) when the
|
|
3939
|
+
* contract's rule is met (≥50% visible, ≥1s dwell). Returns an
|
|
3940
|
+
* unobserve function.
|
|
3941
|
+
*/
|
|
3942
|
+
observeImpression(element, context = {}, opts = {}) {
|
|
3943
|
+
if (!hasDom() || typeof IntersectionObserver === "undefined") return () => {
|
|
3944
|
+
};
|
|
3945
|
+
const name = opts.name ?? "component_impression";
|
|
3946
|
+
const dedupe = opts.key ?? String(
|
|
3947
|
+
context.campaignComponentId ?? context.locationId ?? context.componentTemplateId ?? "component"
|
|
3948
|
+
);
|
|
3949
|
+
let dwellTimer = null;
|
|
3950
|
+
const observer = new IntersectionObserver(
|
|
3951
|
+
(entries) => {
|
|
3952
|
+
for (const entry of entries) {
|
|
3953
|
+
if (entry.isIntersecting && entry.intersectionRatio >= IMPRESSION_THRESHOLD) {
|
|
3954
|
+
if (dwellTimer) continue;
|
|
3955
|
+
dwellTimer = setTimeout(() => {
|
|
3956
|
+
const key = `${name}:${dedupe}`;
|
|
3957
|
+
this.ensureSession();
|
|
3958
|
+
if (this.impressedComponents.has(key)) return;
|
|
3959
|
+
this.impressedComponents.add(key);
|
|
3960
|
+
this.track(name, { context });
|
|
3961
|
+
opts.onImpression?.();
|
|
3962
|
+
observer.disconnect();
|
|
3963
|
+
}, IMPRESSION_DWELL_MS);
|
|
3964
|
+
} else if (dwellTimer) {
|
|
3965
|
+
clearTimeout(dwellTimer);
|
|
3966
|
+
dwellTimer = null;
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
},
|
|
3970
|
+
{ threshold: [IMPRESSION_THRESHOLD] }
|
|
3971
|
+
);
|
|
3972
|
+
observer.observe(element);
|
|
3973
|
+
return () => {
|
|
3974
|
+
if (dwellTimer) clearTimeout(dwellTimer);
|
|
3975
|
+
observer.disconnect();
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3978
|
+
/** Force a flush (mostly for tests). */
|
|
3979
|
+
async flush() {
|
|
3980
|
+
if (!this.options.collector) {
|
|
3981
|
+
this.queue = [];
|
|
3982
|
+
return;
|
|
3983
|
+
}
|
|
3984
|
+
if (this.queue.length === 0) return;
|
|
3985
|
+
if (!Configuration.isInitialized) return;
|
|
3986
|
+
if (Date.now() < this.retryNotBefore) return;
|
|
3987
|
+
const batch = this.queue.splice(0, MAX_QUEUE);
|
|
3988
|
+
const body = JSON.stringify({
|
|
3989
|
+
apiKey: Configuration.get().apiKey,
|
|
3990
|
+
sent_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3991
|
+
events: batch
|
|
3992
|
+
});
|
|
3993
|
+
try {
|
|
3994
|
+
const res = await fetch(`${eventsBase()}/v1/events`, {
|
|
3995
|
+
method: "POST",
|
|
3996
|
+
headers: { "Content-Type": "application/json" },
|
|
3997
|
+
body,
|
|
3998
|
+
keepalive: batch.length <= 20
|
|
3999
|
+
// keepalive caps payload at ~64KB
|
|
4000
|
+
});
|
|
4001
|
+
if (res.status === 401 || res.status === 400) {
|
|
4002
|
+
console.warn(`[Vio.analytics] collector rejected batch (HTTP ${res.status}) \u2014 dropped`);
|
|
4003
|
+
this.retryAttempt = 0;
|
|
4004
|
+
return;
|
|
4005
|
+
}
|
|
4006
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
4007
|
+
this.retryAttempt = 0;
|
|
4008
|
+
} catch {
|
|
4009
|
+
this.queue.unshift(...batch);
|
|
4010
|
+
const backoff = RETRY_BACKOFF_MS[Math.min(this.retryAttempt, RETRY_BACKOFF_MS.length - 1)] ?? 8e3;
|
|
4011
|
+
this.retryAttempt++;
|
|
4012
|
+
this.retryNotBefore = Date.now() + backoff;
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
// MARK: - Internals
|
|
4016
|
+
buildEvent(name, opts) {
|
|
4017
|
+
this.ensureSession();
|
|
4018
|
+
if (!this.sessionId) return null;
|
|
4019
|
+
const anonId = this.anonId();
|
|
4020
|
+
if (!anonId) return null;
|
|
4021
|
+
const boot = vioBootstrapCache();
|
|
4022
|
+
const ctx = opts.context ?? {};
|
|
4023
|
+
const context = compact({
|
|
4024
|
+
campaign_id: ctx.campaignId ?? boot?.campaignId,
|
|
4025
|
+
broadcast_id: ctx.broadcastId,
|
|
4026
|
+
campaign_component_id: ctx.campaignComponentId,
|
|
4027
|
+
app_placement_id: ctx.appPlacementId,
|
|
4028
|
+
location_id: ctx.locationId,
|
|
4029
|
+
component_template_id: ctx.componentTemplateId,
|
|
4030
|
+
sponsor_id: ctx.sponsorId,
|
|
4031
|
+
activation_id: ctx.activationId,
|
|
4032
|
+
content_url: ctx.contentUrl ?? (typeof location !== "undefined" ? location.href : void 0),
|
|
4033
|
+
// Deletion-proof reporting: sources have no trash bin — the title
|
|
4034
|
+
// snapshotted at event time keeps reports legible if content dies.
|
|
4035
|
+
content_title: ctx.contentTitle ?? (typeof document !== "undefined" && document.title ? document.title.slice(0, 255) : void 0),
|
|
4036
|
+
variant: ctx.variant
|
|
4037
|
+
});
|
|
4038
|
+
const com = opts.commerce;
|
|
4039
|
+
const commerce = com ? compact({
|
|
4040
|
+
items: com.items?.map(
|
|
4041
|
+
(i) => compact({
|
|
4042
|
+
product_id: String(i.productId),
|
|
4043
|
+
name: i.name,
|
|
4044
|
+
brand: i.brand,
|
|
4045
|
+
variant_id: i.variantId !== void 0 ? String(i.variantId) : void 0,
|
|
4046
|
+
price: i.price,
|
|
4047
|
+
quantity: i.quantity
|
|
4048
|
+
})
|
|
4049
|
+
),
|
|
4050
|
+
value: com.value,
|
|
4051
|
+
currency: com.currency,
|
|
4052
|
+
order_id: com.orderId,
|
|
4053
|
+
payment_method: com.paymentMethod
|
|
4054
|
+
}) : void 0;
|
|
4055
|
+
return {
|
|
4056
|
+
event_id: uuid(),
|
|
4057
|
+
name,
|
|
4058
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4059
|
+
surface: "web",
|
|
4060
|
+
host: this.options.host,
|
|
4061
|
+
sdk_version: SDK_VERSION,
|
|
4062
|
+
session_id: this.sessionId,
|
|
4063
|
+
anon_id: anonId,
|
|
4064
|
+
...this.externalUserId ? { external_user_id: this.externalUserId } : {},
|
|
4065
|
+
...Object.keys(context).length ? { context } : {},
|
|
4066
|
+
...commerce ? { commerce } : {},
|
|
4067
|
+
...opts.props ? { props: opts.props } : {}
|
|
4068
|
+
};
|
|
4069
|
+
}
|
|
4070
|
+
anonId() {
|
|
4071
|
+
const stored = safeStorageGet(ANON_KEY);
|
|
4072
|
+
if (stored) return stored;
|
|
4073
|
+
const id = `a-${uuid()}`;
|
|
4074
|
+
safeStorageSet(ANON_KEY, id);
|
|
4075
|
+
return safeStorageGet(ANON_KEY) ?? id;
|
|
4076
|
+
}
|
|
4077
|
+
/** Rolling session: renewed by activity, rotated after 30 min idle. */
|
|
4078
|
+
ensureSession() {
|
|
4079
|
+
const now = Date.now();
|
|
4080
|
+
if (!this.sessionId) {
|
|
4081
|
+
const stored = safeStorageGet(SESSION_KEY);
|
|
4082
|
+
if (stored) {
|
|
4083
|
+
try {
|
|
4084
|
+
const parsed = JSON.parse(stored);
|
|
4085
|
+
if (now - parsed.ts < SESSION_TTL_MS) {
|
|
4086
|
+
this.sessionId = parsed.id;
|
|
4087
|
+
this.sessionStartedAt = parsed.startedAt ?? now;
|
|
4088
|
+
}
|
|
4089
|
+
} catch {
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
} else {
|
|
4093
|
+
const stored = safeStorageGet(SESSION_KEY);
|
|
4094
|
+
if (stored) {
|
|
4095
|
+
try {
|
|
4096
|
+
if (now - JSON.parse(stored).ts >= SESSION_TTL_MS) {
|
|
4097
|
+
this.sessionId = null;
|
|
4098
|
+
}
|
|
4099
|
+
} catch {
|
|
4100
|
+
this.sessionId = null;
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
if (!this.sessionId) {
|
|
4105
|
+
this.sessionId = `s-${uuid()}`;
|
|
4106
|
+
this.sessionStartedAt = now;
|
|
4107
|
+
this.impressedComponents.clear();
|
|
4108
|
+
safeStorageSet(
|
|
4109
|
+
SESSION_KEY,
|
|
4110
|
+
JSON.stringify({ id: this.sessionId, ts: now, startedAt: now })
|
|
4111
|
+
);
|
|
4112
|
+
this.track("session_start");
|
|
4113
|
+
} else {
|
|
4114
|
+
safeStorageSet(
|
|
4115
|
+
SESSION_KEY,
|
|
4116
|
+
JSON.stringify({ id: this.sessionId, ts: now, startedAt: this.sessionStartedAt })
|
|
4117
|
+
);
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
trackSessionEnd() {
|
|
4121
|
+
if (!this.sessionId) return;
|
|
4122
|
+
this.track("session_end", {
|
|
4123
|
+
props: { duration_ms: Date.now() - this.sessionStartedAt }
|
|
4124
|
+
});
|
|
4125
|
+
}
|
|
4126
|
+
flushWithBeacon() {
|
|
4127
|
+
if (!this.options.collector || this.queue.length === 0) return;
|
|
4128
|
+
if (!Configuration.isInitialized) return;
|
|
4129
|
+
const batch = this.queue.splice(0, MAX_QUEUE);
|
|
4130
|
+
const body = JSON.stringify({
|
|
4131
|
+
apiKey: Configuration.get().apiKey,
|
|
4132
|
+
sent_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4133
|
+
events: batch
|
|
4134
|
+
});
|
|
4135
|
+
const ok = typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" && navigator.sendBeacon(`${eventsBase()}/v1/events`, new Blob([body], { type: "text/plain" }));
|
|
4136
|
+
if (!ok) this.queue.unshift(...batch);
|
|
4137
|
+
}
|
|
4138
|
+
/**
|
|
4139
|
+
* Auto-instrumentation over the SDK's own DOM events — the host app
|
|
4140
|
+
* writes ZERO tracking code for the commerce funnel.
|
|
4141
|
+
*/
|
|
4142
|
+
installAutoListeners() {
|
|
4143
|
+
const on = (type, handler) => {
|
|
4144
|
+
window.addEventListener(type, handler);
|
|
4145
|
+
this.teardown.push(() => window.removeEventListener(type, handler));
|
|
4146
|
+
};
|
|
4147
|
+
const cart = vioCart();
|
|
4148
|
+
if (cart) {
|
|
4149
|
+
const onChange = (ev) => {
|
|
4150
|
+
const detail = ev.detail;
|
|
4151
|
+
if (!detail) return;
|
|
4152
|
+
const items = [];
|
|
4153
|
+
detail.cartsBySponsor?.forEach((state) => items.push(...state.items));
|
|
4154
|
+
if (items.length > 0) {
|
|
4155
|
+
this.lastCartSnapshot = { items: items.map((i) => ({ ...i })), currency: items[0]?.currency };
|
|
4156
|
+
}
|
|
4157
|
+
};
|
|
4158
|
+
cart.addEventListener("change", onChange);
|
|
4159
|
+
this.teardown.push(() => cart.removeEventListener("change", onChange));
|
|
4160
|
+
}
|
|
4161
|
+
on("vio:added-to-cart", (ev) => {
|
|
4162
|
+
const d = ev.detail ?? {};
|
|
4163
|
+
const line = this.findLine(d.productId, d.variantId);
|
|
4164
|
+
this.track("add_to_cart", {
|
|
4165
|
+
context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0 },
|
|
4166
|
+
commerce: {
|
|
4167
|
+
items: [
|
|
4168
|
+
{
|
|
4169
|
+
productId: d.productId ?? line?.productId ?? "",
|
|
4170
|
+
name: line?.name,
|
|
4171
|
+
brand: line?.brand,
|
|
4172
|
+
variantId: d.variantId ?? line?.variantId,
|
|
4173
|
+
price: line?.unitPrice,
|
|
4174
|
+
quantity: d.quantity ?? 1
|
|
4175
|
+
}
|
|
4176
|
+
],
|
|
4177
|
+
value: line ? line.unitPrice * (d.quantity ?? 1) : void 0,
|
|
4178
|
+
currency: line?.currency
|
|
4179
|
+
}
|
|
4180
|
+
});
|
|
4181
|
+
});
|
|
4182
|
+
on("vio:product-click", (ev) => {
|
|
4183
|
+
const d = ev.detail ?? {};
|
|
4184
|
+
if (d.productId === void 0) return;
|
|
4185
|
+
const price = Number(d.price);
|
|
4186
|
+
this.track("select_item", {
|
|
4187
|
+
context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0 },
|
|
4188
|
+
commerce: {
|
|
4189
|
+
items: [
|
|
4190
|
+
{
|
|
4191
|
+
productId: d.productId,
|
|
4192
|
+
name: d.name,
|
|
4193
|
+
brand: d.brand,
|
|
4194
|
+
price: Number.isFinite(price) ? price : void 0
|
|
4195
|
+
}
|
|
4196
|
+
]
|
|
4197
|
+
}
|
|
4198
|
+
});
|
|
4199
|
+
});
|
|
4200
|
+
on("vio:open-cart", () => {
|
|
4201
|
+
const snap = this.cartCommerce();
|
|
4202
|
+
this.track("view_cart", snap ? { commerce: snap } : {});
|
|
4203
|
+
});
|
|
4204
|
+
on("vio:checkout-open", (ev) => {
|
|
4205
|
+
const d = ev.detail ?? {};
|
|
4206
|
+
const snap = this.cartCommerce(d.sponsorId);
|
|
4207
|
+
this.track("begin_checkout", {
|
|
4208
|
+
context: { sponsorId: d.sponsorId },
|
|
4209
|
+
...snap ? { commerce: snap } : {}
|
|
4210
|
+
});
|
|
4211
|
+
});
|
|
4212
|
+
on("vio:payment-success", (ev) => {
|
|
4213
|
+
const d = ev.detail ?? {};
|
|
4214
|
+
const sponsorId = d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0;
|
|
4215
|
+
const snap = this.cartCommerce(sponsorId);
|
|
4216
|
+
this.track("purchase", {
|
|
4217
|
+
context: { sponsorId },
|
|
4218
|
+
commerce: {
|
|
4219
|
+
...snap ?? {},
|
|
4220
|
+
orderId: d.orderId,
|
|
4221
|
+
paymentMethod: d.method
|
|
4222
|
+
}
|
|
4223
|
+
});
|
|
4224
|
+
});
|
|
4225
|
+
}
|
|
4226
|
+
/** Current cart → commerce payload; falls back to the pre-clear snapshot. */
|
|
4227
|
+
cartCommerce(sponsorId) {
|
|
4228
|
+
let items = [];
|
|
4229
|
+
const cart = vioCart();
|
|
4230
|
+
if (cart) {
|
|
4231
|
+
cart.getAllCarts().forEach((state, id) => {
|
|
4232
|
+
if (sponsorId === void 0 || id === sponsorId) items.push(...state.items);
|
|
4233
|
+
});
|
|
4234
|
+
}
|
|
4235
|
+
if (items.length === 0 && this.lastCartSnapshot) {
|
|
4236
|
+
items = this.lastCartSnapshot.items.filter(
|
|
4237
|
+
(i) => sponsorId === void 0 || i.sponsorId === sponsorId
|
|
4238
|
+
);
|
|
4239
|
+
}
|
|
4240
|
+
if (items.length === 0) return null;
|
|
4241
|
+
return {
|
|
4242
|
+
items: items.map((i) => ({
|
|
4243
|
+
productId: i.productId,
|
|
4244
|
+
name: i.name,
|
|
4245
|
+
brand: i.brand,
|
|
4246
|
+
variantId: i.variantId,
|
|
4247
|
+
price: i.unitPrice,
|
|
4248
|
+
quantity: i.quantity
|
|
4249
|
+
})),
|
|
4250
|
+
value: items.reduce((sum, i) => sum + i.unitPrice * (i.quantity ?? 1), 0),
|
|
4251
|
+
currency: items[0]?.currency ?? this.lastCartSnapshot?.currency
|
|
4252
|
+
};
|
|
4253
|
+
}
|
|
4254
|
+
findLine(productId, variantId) {
|
|
4255
|
+
const pool = [];
|
|
4256
|
+
vioCart()?.getAllCarts().forEach((state) => pool.push(...state.items));
|
|
4257
|
+
if (pool.length === 0 && this.lastCartSnapshot) pool.push(...this.lastCartSnapshot.items);
|
|
4258
|
+
return pool.find(
|
|
4259
|
+
(i) => String(i.productId) === String(productId) && (variantId === void 0 || String(i.variantId) === String(variantId))
|
|
4260
|
+
);
|
|
4261
|
+
}
|
|
4262
|
+
};
|
|
4263
|
+
function hasDom() {
|
|
4264
|
+
return typeof window !== "undefined";
|
|
4265
|
+
}
|
|
4266
|
+
function uuid() {
|
|
4267
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
4268
|
+
return crypto.randomUUID();
|
|
4269
|
+
}
|
|
4270
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
4271
|
+
const r = Math.random() * 16 | 0;
|
|
4272
|
+
return (c === "x" ? r : r & 3 | 8).toString(16);
|
|
4273
|
+
});
|
|
4274
|
+
}
|
|
4275
|
+
function safeStorageGet(key) {
|
|
4276
|
+
try {
|
|
4277
|
+
return window.localStorage.getItem(key);
|
|
4278
|
+
} catch {
|
|
4279
|
+
return null;
|
|
4280
|
+
}
|
|
4281
|
+
}
|
|
4282
|
+
function safeStorageSet(key, value) {
|
|
4283
|
+
try {
|
|
4284
|
+
window.localStorage.setItem(key, value);
|
|
4285
|
+
} catch {
|
|
4286
|
+
}
|
|
4287
|
+
}
|
|
4288
|
+
function compact(obj) {
|
|
4289
|
+
const out = {};
|
|
4290
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
4291
|
+
if (v !== void 0 && v !== null && v !== "") out[k] = v;
|
|
4292
|
+
}
|
|
4293
|
+
return out;
|
|
4294
|
+
}
|
|
4295
|
+
function eventsBase() {
|
|
4296
|
+
return Configuration.get().eventsBase;
|
|
4297
|
+
}
|
|
4298
|
+
function vioFacade() {
|
|
4299
|
+
const g = globalThis;
|
|
4300
|
+
return g.__VIO_FACADE__ ?? null;
|
|
4301
|
+
}
|
|
4302
|
+
function vioBootstrapCache() {
|
|
4303
|
+
return vioFacade()?.bootstrapCache ?? null;
|
|
4304
|
+
}
|
|
4305
|
+
function vioCart() {
|
|
4306
|
+
try {
|
|
4307
|
+
return vioFacade()?.cart ?? null;
|
|
4308
|
+
} catch {
|
|
4309
|
+
return null;
|
|
4310
|
+
}
|
|
4311
|
+
}
|
|
4312
|
+
function pushGa4(event) {
|
|
4313
|
+
const commerceNames = /* @__PURE__ */ new Set([
|
|
4314
|
+
"view_item_list",
|
|
4315
|
+
"select_item",
|
|
4316
|
+
"view_item",
|
|
4317
|
+
"add_to_cart",
|
|
4318
|
+
"view_cart",
|
|
4319
|
+
"begin_checkout",
|
|
4320
|
+
"purchase"
|
|
4321
|
+
]);
|
|
4322
|
+
if (!commerceNames.has(event.name)) return;
|
|
4323
|
+
const w = window;
|
|
4324
|
+
w.dataLayer = w.dataLayer || [];
|
|
4325
|
+
const commerce = event.commerce ?? {};
|
|
4326
|
+
w.dataLayer.push({ ecommerce: null });
|
|
4327
|
+
w.dataLayer.push({
|
|
4328
|
+
event: event.name,
|
|
4329
|
+
ecommerce: compact({
|
|
4330
|
+
currency: commerce.currency,
|
|
4331
|
+
value: commerce.value,
|
|
4332
|
+
transaction_id: commerce.order_id,
|
|
4333
|
+
items: commerce.items?.map(
|
|
4334
|
+
(i) => compact({
|
|
4335
|
+
item_id: i.product_id,
|
|
4336
|
+
item_name: i.name,
|
|
4337
|
+
item_brand: i.brand,
|
|
4338
|
+
item_variant: i.variant_id,
|
|
4339
|
+
price: i.price,
|
|
4340
|
+
quantity: i.quantity
|
|
4341
|
+
})
|
|
4342
|
+
)
|
|
4343
|
+
})
|
|
4344
|
+
});
|
|
4345
|
+
}
|
|
4346
|
+
|
|
3848
4347
|
// src/core/client.ts
|
|
3849
4348
|
var VioFacade = class {
|
|
3850
4349
|
constructor() {
|
|
@@ -3859,6 +4358,7 @@ var VioFacade = class {
|
|
|
3859
4358
|
// need Vio.init() to work (state is purely local).
|
|
3860
4359
|
this.cartManager = null;
|
|
3861
4360
|
this.checkoutManager = null;
|
|
4361
|
+
this.analyticsManager = null;
|
|
3862
4362
|
}
|
|
3863
4363
|
init(config) {
|
|
3864
4364
|
Configuration.init(config);
|
|
@@ -3886,6 +4386,14 @@ var VioFacade = class {
|
|
|
3886
4386
|
if (!this.checkoutManager) this.checkoutManager = new CheckoutManager(this.cart);
|
|
3887
4387
|
return this.checkoutManager;
|
|
3888
4388
|
}
|
|
4389
|
+
/**
|
|
4390
|
+
* Analytics — track() queues even pre-init; nothing is sent (and no
|
|
4391
|
+
* listeners are installed) until an explicit `Vio.analytics.start()`.
|
|
4392
|
+
*/
|
|
4393
|
+
get analytics() {
|
|
4394
|
+
if (!this.analyticsManager) this.analyticsManager = new AnalyticsManager();
|
|
4395
|
+
return this.analyticsManager;
|
|
4396
|
+
}
|
|
3889
4397
|
// MARK: - Backend-bound
|
|
3890
4398
|
/** Lazy VioApi for REST endpoints on `api-dev.vio.live`. */
|
|
3891
4399
|
get api() {
|
|
@@ -3955,6 +4463,6 @@ var VioFacade = class {
|
|
|
3955
4463
|
var _vioGlobal = globalThis;
|
|
3956
4464
|
var Vio = _vioGlobal.__VIO_FACADE__ ?? (_vioGlobal.__VIO_FACADE__ = new VioFacade());
|
|
3957
4465
|
|
|
3958
|
-
export { ADD_ITEM_MUTATION, CREATE_CART_MUTATION, CREATE_CHECKOUT_MUTATION, CREATE_PAYMENT_STRIPE_MUTATION, CREATE_PAYMENT_VIPPS_MUTATION, CartManager, CheckoutManager, CommerceClient, Configuration, DELETE_ITEM_MUTATION, GET_AVAILABLE_PAYMENT_METHODS_QUERY, GET_CART_QUERY, GET_CHECKOUT_QUERY, GET_LINE_ITEMS_BY_SUPPLIER_QUERY, GET_VIPPS_STATUS_QUERY, UPDATE_CHECKOUT_MUTATION, UPDATE_ITEM_MUTATION, UPDATE_SHIPPINGS_BY_SUPPLIER_MUTATION, Vio, VioApi, VioApiError, __decorateClass, addItem, checkApplePayAvailability, createCart, createCheckout, createCommerceClient, createPaymentStripe, createPaymentVipps, createVioApi, deleteItem, displayPrice, executeCartGraphQL, formatPrice, getAvailablePaymentMethods, getCart, getCartGraphQLOptions, getCheckout, getCustomerSessionId, getGlobalCurrency, getLineItemsBySupplier, getVippsStatus, primaryImageUrl, showApplePaySheet, updateCheckout, updateItem, updateShippingsBySupplier };
|
|
3959
|
-
//# sourceMappingURL=chunk-
|
|
3960
|
-
//# sourceMappingURL=chunk-
|
|
4466
|
+
export { ADD_ITEM_MUTATION, AnalyticsManager, CREATE_CART_MUTATION, CREATE_CHECKOUT_MUTATION, CREATE_PAYMENT_STRIPE_MUTATION, CREATE_PAYMENT_VIPPS_MUTATION, CartManager, CheckoutManager, CommerceClient, Configuration, DELETE_ITEM_MUTATION, GET_AVAILABLE_PAYMENT_METHODS_QUERY, GET_CART_QUERY, GET_CHECKOUT_QUERY, GET_LINE_ITEMS_BY_SUPPLIER_QUERY, GET_VIPPS_STATUS_QUERY, SDK_VERSION, UPDATE_CHECKOUT_MUTATION, UPDATE_ITEM_MUTATION, UPDATE_SHIPPINGS_BY_SUPPLIER_MUTATION, Vio, VioApi, VioApiError, __decorateClass, addItem, checkApplePayAvailability, createCart, createCheckout, createCommerceClient, createPaymentStripe, createPaymentVipps, createVioApi, deleteItem, displayPrice, executeCartGraphQL, formatPrice, getAvailablePaymentMethods, getCart, getCartGraphQLOptions, getCheckout, getCustomerSessionId, getGlobalCurrency, getLineItemsBySupplier, getVippsStatus, primaryImageUrl, showApplePaySheet, updateCheckout, updateItem, updateShippingsBySupplier };
|
|
4467
|
+
//# sourceMappingURL=chunk-7Q4MJ5NQ.js.map
|
|
4468
|
+
//# sourceMappingURL=chunk-7Q4MJ5NQ.js.map
|