@vio-live/web-sdk 0.24.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,6 +17,19 @@ var SLOT_DECLARATION_TTL_MS = 24 * 60 * 60 * 1e3;
17
17
  var SLOT_DECLARATION_FAILURE_TTL_MS = 60 * 60 * 1e3;
18
18
  var MAX_LOCATIONS_PER_DECLARATION = 100;
19
19
  var MAX_DECLARATIONS_PER_LOAD = 3;
20
+ var SLOT_KIND_MAX_LENGTH = 32;
21
+ function componentKindOf(templateId, slot) {
22
+ const hint = typeof templateId === "string" ? templateId.trim().toLowerCase() : "";
23
+ if (hint) return hint.replace(/^(product|vev)-/, "").slice(0, SLOT_KIND_MAX_LENGTH) || void 0;
24
+ const id = typeof slot === "string" ? slot.toLowerCase() : "";
25
+ const m = id.match(/(?:^|-)(card|carousel|grid|detail|banner|list|hero|stepper)(?:-|$)/);
26
+ return m?.[1];
27
+ }
28
+ function normalizeSlotKind(value) {
29
+ if (typeof value !== "string") return void 0;
30
+ const kind = value.trim().toLowerCase().slice(0, SLOT_KIND_MAX_LENGTH).trim();
31
+ return kind || void 0;
32
+ }
20
33
  function resolveSlotCampaign(boot, slot, now = Date.now(), platform, pageCampaignId, sponsorId) {
21
34
  if (!boot) return null;
22
35
  const page = activeCampaignEntry(boot, pageCampaignId);
@@ -163,9 +176,11 @@ function normalizeSlotLocations(slots) {
163
176
  seen.add(id);
164
177
  const displayName = raw && typeof raw === "object" && typeof raw.displayName === "string" ? raw.displayName.trim() : "";
165
178
  const campaignId = raw && typeof raw === "object" ? toCampaignId(raw.campaignId) : void 0;
179
+ const kind = raw && typeof raw === "object" ? normalizeSlotKind(raw.kind) : void 0;
166
180
  const location2 = { id };
167
181
  if (displayName) location2.displayName = displayName;
168
182
  if (campaignId !== void 0) location2.campaignId = campaignId;
183
+ if (kind) location2.kind = kind;
169
184
  out.push(location2);
170
185
  }
171
186
  return out;
@@ -189,7 +204,8 @@ function normalizeScope(scope) {
189
204
  var SLOT_DECLARATION_MEMORY_CAP = 500;
190
205
  function slotDeclarationKey(location2, pageCampaignId) {
191
206
  const campaignId = toCampaignId(location2.campaignId) ?? toCampaignId(pageCampaignId);
192
- return location2.id + (location2.displayName ? `${location2.displayName}` : "") + (campaignId !== void 0 ? `${campaignId}` : "");
207
+ const kind = normalizeSlotKind(location2.kind);
208
+ return location2.id + (location2.displayName ? `${location2.displayName}` : "") + (campaignId !== void 0 ? `${campaignId}` : "") + (kind !== void 0 ? `${kind}` : "");
193
209
  }
194
210
  function pendingSlotKeys(memory, keys, now = Date.now(), ttl = SLOT_DECLARATION_TTL_MS) {
195
211
  return keys.filter((key) => {
@@ -199,8 +215,8 @@ function pendingSlotKeys(memory, keys, now = Date.now(), ttl = SLOT_DECLARATION_
199
215
  });
200
216
  }
201
217
  function slotKeyId(key) {
202
- const cut = key.search(/[\u001f\u001e]/);
203
- return cut >= 0 ? key.slice(0, cut) : key;
218
+ const cut4 = key.search(/[\u001f\u001e\u001d]/);
219
+ return cut4 >= 0 ? key.slice(0, cut4) : key;
204
220
  }
205
221
  function rememberSlotKeys(memory, keys, at, cap = SLOT_DECLARATION_MEMORY_CAP) {
206
222
  const next = { ...memory ?? {} };
@@ -290,6 +306,8 @@ var ConfigurationSingleton = class {
290
306
  contentTitle: input.contentTitle,
291
307
  campaignId: toCampaignId(input.campaignId),
292
308
  debug: input.debug === true,
309
+ // Only the two words the backend knows; anything else is "not said".
310
+ privacyMode: input.privacyMode === "minimal" || input.privacyMode === "standard" ? input.privacyMode : void 0,
293
311
  apiBase: input.apiBase ?? defaults.apiBase,
294
312
  graphQLBase: input.graphQLBase ?? defaults.graphQLBase,
295
313
  eventsBase: input.eventsBase ?? defaults.eventsBase,
@@ -379,6 +397,14 @@ var VioApi = class {
379
397
  async declareManifest(body) {
380
398
  return this.post("/v2/mobile/components/manifest", body);
381
399
  }
400
+ /**
401
+ * The surface of the key (`GET /v2/mobile/surface`): its id, name, status
402
+ * and `privacyMode` — what decides whether the order attribution may carry
403
+ * the entry context (ADR-0035). A 404 means a service that predates it.
404
+ */
405
+ async surface() {
406
+ return this.get("/v2/mobile/surface");
407
+ }
382
408
  /**
383
409
  * Get placement components for a campaign. Used by the carousel auto-fetch
384
410
  * mode (`<vio-product-carousel campaign-id="36" location="news-favorites">`).
@@ -698,6 +724,214 @@ function createCommerceClient(options) {
698
724
  return new CommerceClient(options);
699
725
  }
700
726
 
727
+ // src/core/content.ts
728
+ var CONTENT_ID_QUERY = "vio_content";
729
+ var CONTENT_TITLE_QUERY = "vio_content_title";
730
+ var CONTENT_ID_MAX_LENGTH = 128;
731
+ var CONTENT_TITLE_MAX_LENGTH = 255;
732
+ var CONTENT_URL_MAX_LENGTH = 2048;
733
+ function originAndPath(raw, max = CONTENT_URL_MAX_LENGTH) {
734
+ const value = text(raw);
735
+ if (!value) return void 0;
736
+ try {
737
+ const url = new URL(value);
738
+ return cutTo(`${url.origin}${url.pathname}`, max);
739
+ } catch {
740
+ const end = value.search(/[?#]/);
741
+ return cutTo(end >= 0 ? value.slice(0, end) : value, max);
742
+ }
743
+ }
744
+ function cutTo(value, max) {
745
+ const out = value.length > max ? value.slice(0, max) : value;
746
+ return out || void 0;
747
+ }
748
+ function capContentUrl(value) {
749
+ const url = text(value);
750
+ if (!url) return void 0;
751
+ if (url.length <= CONTENT_URL_MAX_LENGTH) return url;
752
+ const cut4 = url.search(/[?#]/);
753
+ const withoutQuery = cut4 >= 0 ? url.slice(0, cut4) : url;
754
+ return withoutQuery.length <= CONTENT_URL_MAX_LENGTH ? withoutQuery : withoutQuery.slice(0, CONTENT_URL_MAX_LENGTH);
755
+ }
756
+ function resolveContent(init, doc = typeof document !== "undefined" ? document : null, loc = typeof location !== "undefined" ? location : null) {
757
+ const out = {};
758
+ try {
759
+ const url = originAndPath(loc?.href);
760
+ if (url !== void 0) out.url = url;
761
+ } catch {
762
+ }
763
+ try {
764
+ const query = parseQuery(loc?.search);
765
+ const id = fits(text(init?.contentId)) ?? fits(text(query.get(CONTENT_ID_QUERY))) ?? fits(text(meta(doc, "name", "vio:content-id"))) ?? fits(stripQueryAndHash(text(attr(doc, 'link[rel="canonical"]', "href")))) ?? fits(stripQueryAndHash(text(meta(doc, "property", "og:url")))) ?? fits(text(loc?.pathname));
766
+ const title = text(init?.contentTitle) ?? text(query.get(CONTENT_TITLE_QUERY)) ?? text(meta(doc, "name", "vio:content-title")) ?? text(meta(doc, "property", "og:title")) ?? text(doc?.title);
767
+ if (id !== void 0) out.id = id;
768
+ if (title !== void 0) out.title = title.slice(0, CONTENT_TITLE_MAX_LENGTH);
769
+ return out;
770
+ } catch {
771
+ return out;
772
+ }
773
+ }
774
+ function fits(value) {
775
+ return value !== void 0 && value.length <= CONTENT_ID_MAX_LENGTH ? value : void 0;
776
+ }
777
+ function text(value) {
778
+ if (value === void 0 || value === null) return void 0;
779
+ const s = String(value).trim();
780
+ return s ? s : void 0;
781
+ }
782
+ function parseQuery(search) {
783
+ try {
784
+ return new URLSearchParams(typeof search === "string" ? search : "");
785
+ } catch {
786
+ return new URLSearchParams();
787
+ }
788
+ }
789
+ function meta(doc, key, value) {
790
+ return attr(doc, `meta[${key}="${value}"]`, "content");
791
+ }
792
+ function attr(doc, selector, name) {
793
+ try {
794
+ if (!doc || typeof doc.querySelector !== "function") return void 0;
795
+ const el = doc.querySelector(selector);
796
+ const v = el?.getAttribute?.(name);
797
+ return typeof v === "string" ? v : void 0;
798
+ } catch {
799
+ return void 0;
800
+ }
801
+ }
802
+ function stripQueryAndHash(url) {
803
+ if (!url) return void 0;
804
+ const cut4 = url.search(/[?#]/);
805
+ const clean = cut4 >= 0 ? url.slice(0, cut4) : url;
806
+ return clean.trim() || void 0;
807
+ }
808
+
809
+ // src/core/cart/origin-wire.ts
810
+ var GRAPHQL_INT_MAX = 2147483647;
811
+ var LINE_ORIGIN_CAPS = Object.freeze({
812
+ locationId: SLOT_ID_MAX_LENGTH,
813
+ componentKind: SLOT_KIND_MAX_LENGTH,
814
+ contentUrl: 2048,
815
+ contentTitle: CONTENT_TITLE_MAX_LENGTH
816
+ });
817
+ function lineOriginWire(origin, options = {}) {
818
+ if (!origin || typeof origin !== "object") return void 0;
819
+ const page = options.page ?? {};
820
+ const standard = options.privacyMode === "standard";
821
+ const out = {};
822
+ const campaignId = Number(origin.campaignId);
823
+ if (Number.isInteger(campaignId) && campaignId > 0 && campaignId <= GRAPHQL_INT_MAX) out.campaign_id = campaignId;
824
+ const locationId = cut(origin.slot, LINE_ORIGIN_CAPS.locationId);
825
+ if (locationId && standard) out.location_id = locationId;
826
+ const kind = cut(origin.componentKind, LINE_ORIGIN_CAPS.componentKind) ?? componentKindOf(void 0, origin.slot);
827
+ if (kind) out.component_kind = kind;
828
+ if (!out.campaign_id && !out.location_id && !out.component_kind) return void 0;
829
+ if (!standard) return out;
830
+ const url = originAndPath(origin.contentUrl, LINE_ORIGIN_CAPS.contentUrl) ?? originAndPath(page.contentUrl, LINE_ORIGIN_CAPS.contentUrl);
831
+ if (url) out.content_url = url;
832
+ const title = cut(origin.contentTitle, LINE_ORIGIN_CAPS.contentTitle) ?? cut(page.contentTitle, LINE_ORIGIN_CAPS.contentTitle);
833
+ if (title) out.content_title = title;
834
+ return out;
835
+ }
836
+ function currentOriginPage() {
837
+ const out = {};
838
+ try {
839
+ const facade = globalThis.__VIO_FACADE__;
840
+ const content = typeof facade?.content === "function" ? facade.content() : resolveContent();
841
+ const url = originAndPath(content?.url, LINE_ORIGIN_CAPS.contentUrl);
842
+ if (url) out.contentUrl = url;
843
+ const title = cut(content?.title, LINE_ORIGIN_CAPS.contentTitle);
844
+ if (title) out.contentTitle = title;
845
+ } catch {
846
+ }
847
+ return out;
848
+ }
849
+ function cut(value, max) {
850
+ if (typeof value !== "string" && typeof value !== "number") return void 0;
851
+ const text3 = String(value).trim();
852
+ if (!text3) return void 0;
853
+ return text3.length > max ? text3.slice(0, max).trim() || void 0 : text3;
854
+ }
855
+ async function currentPrivacyMode() {
856
+ try {
857
+ const facade = globalThis.__VIO_FACADE__;
858
+ if (typeof facade?.privacyMode !== "function") return "unknown";
859
+ const mode = await facade.privacyMode();
860
+ return mode === "standard" || mode === "minimal" ? mode : "unknown";
861
+ } catch {
862
+ return "unknown";
863
+ }
864
+ }
865
+
866
+ // src/core/buyer-ref.ts
867
+ var BUYER_REF_MAX_LENGTH = 128;
868
+ var BUYER_REF_PATTERN = /^[A-Za-z0-9._~-]+$/;
869
+ function normalizeBuyerRef(value) {
870
+ if (typeof value !== "string") return void 0;
871
+ const ref = value.trim();
872
+ if (!ref || ref.length > BUYER_REF_MAX_LENGTH || !BUYER_REF_PATTERN.test(ref)) return void 0;
873
+ return ref;
874
+ }
875
+
876
+ // src/core/api/gateway-features.ts
877
+ var FEATURE_TOKENS = {
878
+ buyer_ref: ["buyer_ref"],
879
+ line_origin: ["origin", "CartLineOriginInput"],
880
+ checkout_attribution: ["attribution", "CheckoutAttributionInput", "CheckoutAttributionEntryInput"]
881
+ };
882
+ var UNKNOWN_PATTERN = /cannot query field|unknown field|unknown argument|unknown type|is not defined by type|not defined by type|field "[^"]+" is not defined/i;
883
+ var refused = /* @__PURE__ */ new Set();
884
+ var warned = /* @__PURE__ */ new Set();
885
+ function gatewayHas(feature) {
886
+ return !refused.has(feature);
887
+ }
888
+ function resetGatewayFeatures(feature) {
889
+ if (feature) {
890
+ refused.delete(feature);
891
+ warned.delete(feature);
892
+ return;
893
+ }
894
+ refused.clear();
895
+ warned.clear();
896
+ }
897
+ function messagesOf(err) {
898
+ if (!err || typeof err !== "object") return [];
899
+ const { message, graphql } = err;
900
+ return [message, ...(graphql?.errors ?? []).map((e) => e?.message)].filter(
901
+ (m) => typeof m === "string"
902
+ );
903
+ }
904
+ function ranSomething(err) {
905
+ if (!err || typeof err !== "object") return false;
906
+ const graphql = err.graphql;
907
+ if (graphql?.hasData === true) return true;
908
+ return (graphql?.errors ?? []).some((e) => Array.isArray(e?.path) && e.path.length > 0);
909
+ }
910
+ function isUnknownTokenError(err, token) {
911
+ if (ranSomething(err)) return false;
912
+ return messagesOf(err).some((m) => m.includes(`"${token}"`) && UNKNOWN_PATTERN.test(m));
913
+ }
914
+ function isUnknownFeatureError(err, feature) {
915
+ return FEATURE_TOKENS[feature].some((token) => isUnknownTokenError(err, token));
916
+ }
917
+ async function withGatewayFeature(feature, request) {
918
+ if (!gatewayHas(feature)) return request(false);
919
+ try {
920
+ return await request(true);
921
+ } catch (err) {
922
+ if (!isUnknownFeatureError(err, feature)) throw err;
923
+ refused.add(feature);
924
+ if (!warned.has(feature) && typeof console !== "undefined") {
925
+ warned.add(feature);
926
+ console.warn(
927
+ `[Vio] the commerce gateway does not know "${FEATURE_TOKENS[feature][0]}" (${feature}) yet \u2014 sent without it on this page load:`,
928
+ messagesOf(err)[0] ?? ""
929
+ );
930
+ }
931
+ return request(false);
932
+ }
933
+ }
934
+
701
935
  // src/core/api/cart-queries.ts
702
936
  var CREATE_CART_MUTATION = `
703
937
  mutation CreateCart($customerSessionId: String!, $currency: String!, $shippingCountry: String) {
@@ -915,10 +1149,10 @@ mutation DeleteItem($cartId: String!, $cartItemId: String!) {
915
1149
  }
916
1150
  }
917
1151
  `;
918
- var UPDATE_ITEM_MUTATION = `
919
- mutation UpdateItem($cartId: String!, $cartItemId: String!, $shippingId: String, $qty: Int) {
1152
+ var updateItemMutation = (withExtra) => `
1153
+ mutation UpdateItem($cartId: String!, $cartItemId: String!, $shippingId: String, $qty: Int${withExtra ? ", $origin: CartLineOriginInput" : ""}) {
920
1154
  Cart {
921
- UpdateItem(cart_id: $cartId, cart_item_id: $cartItemId, shipping_id: $shippingId, qty: $qty) {
1155
+ UpdateItem(cart_id: $cartId, cart_item_id: $cartItemId, shipping_id: $shippingId, qty: $qty${withExtra ? ", origin: $origin" : ""}) {
922
1156
  cart_id
923
1157
  customer_session_id
924
1158
  shipping_country
@@ -987,6 +1221,8 @@ mutation UpdateItem($cartId: String!, $cartItemId: String!, $shippingId: String,
987
1221
  }
988
1222
  }
989
1223
  `;
1224
+ var UPDATE_ITEM_MUTATION = updateItemMutation(false);
1225
+ var UPDATE_ITEM_WITH_ORIGIN_MUTATION = updateItemMutation(true);
990
1226
  var GET_CART_QUERY = `
991
1227
  query GetCart($cartId: String!) {
992
1228
  Cart {
@@ -1200,10 +1436,10 @@ mutation UpdateShippingsBySupplier($cartId: String!, $data: [DataUpdateShippings
1200
1436
  }
1201
1437
  }
1202
1438
  `;
1203
- var CREATE_CHECKOUT_MUTATION = `
1204
- mutation CreateCheckout($cartId: String!) {
1439
+ var createCheckoutMutation = (withExtra) => `
1440
+ mutation CreateCheckout($cartId: String!${withExtra ? ", $attribution: CheckoutAttributionInput" : ""}) {
1205
1441
  Checkout {
1206
- CreateCheckout(cart_id: $cartId) {
1442
+ CreateCheckout(cart_id: $cartId${withExtra ? ", attribution: $attribution" : ""}) {
1207
1443
  buyer_accepts_purchase_conditions
1208
1444
  buyer_accepts_terms_conditions
1209
1445
  created_at
@@ -1332,10 +1568,12 @@ mutation CreateCheckout($cartId: String!) {
1332
1568
  }
1333
1569
  }
1334
1570
  `;
1335
- var UPDATE_CHECKOUT_MUTATION = `
1336
- mutation UpdateCheckout($checkoutId: String!, $status: String, $successUrl: String, $email: String, $cancelUrl: String, $paymentMethod: String, $shippingAddress: AddressArgs, $billingAddress: AddressArgs, $buyerAcceptsPurchaseConditions: Boolean, $buyerAcceptsTermsConditions: Boolean) {
1571
+ var CREATE_CHECKOUT_MUTATION = createCheckoutMutation(false);
1572
+ var CREATE_CHECKOUT_WITH_ATTRIBUTION_MUTATION = createCheckoutMutation(true);
1573
+ var updateCheckoutMutation = (withExtra) => `
1574
+ mutation UpdateCheckout($checkoutId: String!, $status: String, $successUrl: String, $email: String, $cancelUrl: String, $paymentMethod: String, $shippingAddress: AddressArgs, $billingAddress: AddressArgs, $buyerAcceptsPurchaseConditions: Boolean, $buyerAcceptsTermsConditions: Boolean${withExtra ? ", $attribution: CheckoutAttributionInput" : ""}) {
1337
1575
  Checkout {
1338
- UpdateCheckout(checkout_id: $checkoutId, status: $status, success_url: $successUrl, email: $email, cancel_url: $cancelUrl, payment_method: $paymentMethod, shipping_address: $shippingAddress, billing_address: $billingAddress, buyer_accepts_purchase_conditions: $buyerAcceptsPurchaseConditions, buyer_accepts_terms_conditions: $buyerAcceptsTermsConditions) {
1576
+ UpdateCheckout(checkout_id: $checkoutId, status: $status, success_url: $successUrl, email: $email, cancel_url: $cancelUrl, payment_method: $paymentMethod, shipping_address: $shippingAddress, billing_address: $billingAddress, buyer_accepts_purchase_conditions: $buyerAcceptsPurchaseConditions, buyer_accepts_terms_conditions: $buyerAcceptsTermsConditions${withExtra ? ", attribution: $attribution" : ""}) {
1339
1577
  buyer_accepts_purchase_conditions
1340
1578
  buyer_accepts_terms_conditions
1341
1579
  created_at
@@ -1464,6 +1702,8 @@ mutation UpdateCheckout($checkoutId: String!, $status: String, $successUrl: Stri
1464
1702
  }
1465
1703
  }
1466
1704
  `;
1705
+ var UPDATE_CHECKOUT_MUTATION = updateCheckoutMutation(false);
1706
+ var UPDATE_CHECKOUT_WITH_ATTRIBUTION_MUTATION = updateCheckoutMutation(true);
1467
1707
  var GET_CHECKOUT_QUERY = `
1468
1708
  query GetCheckout($checkoutId: String!) {
1469
1709
  Checkout {
@@ -1686,7 +1926,9 @@ async function executeCartGraphQL(query, variables, options = {}) {
1686
1926
  const json = await Promise.race([res.json().catch(() => ({})), timedOut]);
1687
1927
  if (!res.ok || json.errors) {
1688
1928
  const errMsg = json.errors?.[0]?.message || `GraphQL request failed (HTTP ${res.status})`;
1689
- throw new Error(errMsg);
1929
+ throw Object.assign(new Error(errMsg), {
1930
+ graphql: { hasData: json.data !== void 0 && json.data !== null, errors: json.errors ?? [] }
1931
+ });
1690
1932
  }
1691
1933
  return json;
1692
1934
  } finally {
@@ -1706,19 +1948,37 @@ async function createCart(customerSessionId, currency, shippingCountry, options)
1706
1948
  return json?.data?.Cart?.CreateCart;
1707
1949
  }
1708
1950
  async function addItem(cartId, lineItems, options) {
1709
- const json = await executeCartGraphQL(ADD_ITEM_MUTATION, { cartId, lineItems }, options);
1951
+ const attributed = lineItems.some((li) => !!li?.origin);
1952
+ const json = attributed ? await withGatewayFeature(
1953
+ "line_origin",
1954
+ (supported) => executeCartGraphQL(
1955
+ ADD_ITEM_MUTATION,
1956
+ { cartId, lineItems: supported ? lineItems : lineItems.map(withoutOrigin) },
1957
+ options
1958
+ )
1959
+ ) : await executeCartGraphQL(ADD_ITEM_MUTATION, { cartId, lineItems }, options);
1710
1960
  return json?.data?.Cart?.AddItem;
1711
1961
  }
1962
+ function withoutOrigin(lineItem) {
1963
+ if (!lineItem || typeof lineItem !== "object") return lineItem;
1964
+ const { origin: _origin, ...rest } = lineItem;
1965
+ return rest;
1966
+ }
1712
1967
  async function deleteItem(cartId, cartItemId, options) {
1713
1968
  const json = await executeCartGraphQL(DELETE_ITEM_MUTATION, { cartId, cartItemId }, options);
1714
1969
  return json?.data?.Cart?.DeleteItem;
1715
1970
  }
1716
- async function updateItem(cartId, cartItemId, shippingId, qty, options) {
1717
- const json = await executeCartGraphQL(
1718
- UPDATE_ITEM_MUTATION,
1719
- { cartId, cartItemId, shippingId: shippingId || null, qty: qty != null ? qty : null },
1720
- options
1721
- );
1971
+ async function updateItem(cartId, cartItemId, shippingId, qty, options, extra = {}) {
1972
+ const variables = { cartId, cartItemId, shippingId: shippingId || null, qty: qty != null ? qty : null };
1973
+ const origin = extra.origin;
1974
+ const json = origin ? await withGatewayFeature(
1975
+ "line_origin",
1976
+ (supported) => executeCartGraphQL(
1977
+ supported ? UPDATE_ITEM_WITH_ORIGIN_MUTATION : UPDATE_ITEM_MUTATION,
1978
+ supported ? { ...variables, origin } : variables,
1979
+ options
1980
+ )
1981
+ ) : await executeCartGraphQL(UPDATE_ITEM_MUTATION, variables, options);
1722
1982
  return json?.data?.Cart?.UpdateItem;
1723
1983
  }
1724
1984
  async function getCart(cartId, options) {
@@ -1737,32 +1997,68 @@ async function updateShippingsBySupplier(cartId, data, options) {
1737
1997
  );
1738
1998
  return json?.data?.Cart?.UpdateShippingsBySupplier;
1739
1999
  }
1740
- async function createCheckout(cartId, options) {
1741
- const json = await executeCartGraphQL(CREATE_CHECKOUT_MUTATION, { cartId }, options);
2000
+ async function createCheckout(cartId, options, extra = {}) {
2001
+ const attribution = extra.attribution;
2002
+ const json = attribution ? await withGatewayFeature(
2003
+ "checkout_attribution",
2004
+ (supported) => executeCartGraphQL(
2005
+ supported ? CREATE_CHECKOUT_WITH_ATTRIBUTION_MUTATION : CREATE_CHECKOUT_MUTATION,
2006
+ supported ? { cartId, attribution } : { cartId },
2007
+ options
2008
+ )
2009
+ ) : await executeCartGraphQL(CREATE_CHECKOUT_MUTATION, { cartId }, options);
1742
2010
  return json?.data?.Checkout?.CreateCheckout;
1743
2011
  }
1744
2012
  async function updateCheckout(variables, options) {
1745
- const json = await executeCartGraphQL(UPDATE_CHECKOUT_MUTATION, variables, options);
2013
+ const { attribution, ...legacy } = variables;
2014
+ const json = attribution ? await withGatewayFeature(
2015
+ "checkout_attribution",
2016
+ (supported) => executeCartGraphQL(
2017
+ supported ? UPDATE_CHECKOUT_WITH_ATTRIBUTION_MUTATION : UPDATE_CHECKOUT_MUTATION,
2018
+ supported ? variables : legacy,
2019
+ options
2020
+ )
2021
+ ) : await executeCartGraphQL(UPDATE_CHECKOUT_MUTATION, legacy, options);
1746
2022
  return json?.data?.Checkout?.UpdateCheckout;
1747
2023
  }
1748
2024
  async function getCheckout(checkoutId, options) {
1749
2025
  const json = await executeCartGraphQL(GET_CHECKOUT_QUERY, { checkoutId }, options);
1750
2026
  return json?.data?.Checkout?.GetCheckout;
1751
2027
  }
1752
- var GET_CHECKOUT_ORDER_NUMBER_QUERY = `
2028
+ var BUYER_REF_FIELD = "buyer_ref";
2029
+ function gatewayHasBuyerRef() {
2030
+ return gatewayHas("buyer_ref");
2031
+ }
2032
+ function withBuyerRef(request) {
2033
+ return withGatewayFeature("buyer_ref", request);
2034
+ }
2035
+ var orderNumberQuery = (withRef) => `
1753
2036
  query GetCheckoutOrderNumber($checkoutId: String!) {
1754
2037
  Checkout {
1755
2038
  GetCheckout(checkout_id: $checkoutId) {
1756
2039
  id
1757
- order_id
2040
+ order_id${withRef ? `
2041
+ ${BUYER_REF_FIELD}` : ""}
1758
2042
  }
1759
2043
  }
1760
2044
  }
1761
2045
  `;
1762
- async function getCheckoutOrderNumber(checkoutId, options) {
1763
- const json = await executeCartGraphQL(GET_CHECKOUT_ORDER_NUMBER_QUERY, { checkoutId }, options);
1764
- const n = Number(json?.data?.Checkout?.GetCheckout?.order_id);
1765
- return Number.isInteger(n) && n > 0 ? n : null;
2046
+ var GET_CHECKOUT_ORDER_NUMBER_QUERY = orderNumberQuery(true);
2047
+ var GET_CHECKOUT_ORDER_NUMBER_QUERY_LEGACY = orderNumberQuery(false);
2048
+ async function getCheckoutReference(checkoutId, options) {
2049
+ const json = await withBuyerRef(
2050
+ (withRef) => executeCartGraphQL(
2051
+ withRef ? GET_CHECKOUT_ORDER_NUMBER_QUERY : GET_CHECKOUT_ORDER_NUMBER_QUERY_LEGACY,
2052
+ { checkoutId },
2053
+ options
2054
+ )
2055
+ );
2056
+ const checkout = json?.data?.Checkout?.GetCheckout;
2057
+ const n = Number(checkout?.order_id);
2058
+ return {
2059
+ orderNumber: Number.isInteger(n) && n > 0 ? n : null,
2060
+ buyerRef: normalizeBuyerRef(checkout?.[BUYER_REF_FIELD]) ?? null
2061
+ };
1766
2062
  }
1767
2063
  async function getAvailablePaymentMethods(options) {
1768
2064
  const json = await executeCartGraphQL(GET_AVAILABLE_PAYMENT_METHODS_QUERY, {}, options);
@@ -1796,18 +2092,27 @@ async function createPaymentApplePay(variables, options) {
1796
2092
  const json = await executeCartGraphQL(CREATE_PAYMENT_APPLE_PAY_MUTATION, variables, options);
1797
2093
  return json?.data?.Payment?.CreatePaymentApplePay;
1798
2094
  }
1799
- var CONFIRM_PAYMENT_APPLE_PAY_MUTATION = `
2095
+ var confirmApplePayMutation = (withRef) => `
1800
2096
  mutation ConfirmPaymentApplePay($checkoutId: String!, $applePayToken: String!, $email: String, $shippingAddress: ApplePayAddressInput) {
1801
2097
  Payment {
1802
2098
  ConfirmPaymentApplePay(checkout_id: $checkoutId, apple_pay_token: $applePayToken, email: $email, shipping_address: $shippingAddress) {
1803
2099
  order_id
1804
- status
2100
+ status${withRef ? `
2101
+ ${BUYER_REF_FIELD}` : ""}
1805
2102
  }
1806
2103
  }
1807
2104
  }
1808
2105
  `;
2106
+ var CONFIRM_PAYMENT_APPLE_PAY_MUTATION = confirmApplePayMutation(true);
2107
+ var CONFIRM_PAYMENT_APPLE_PAY_MUTATION_LEGACY = confirmApplePayMutation(false);
1809
2108
  async function confirmPaymentApplePay(variables, options) {
1810
- const json = await executeCartGraphQL(CONFIRM_PAYMENT_APPLE_PAY_MUTATION, variables, options);
2109
+ const json = await withBuyerRef(
2110
+ (withRef) => executeCartGraphQL(
2111
+ withRef ? CONFIRM_PAYMENT_APPLE_PAY_MUTATION : CONFIRM_PAYMENT_APPLE_PAY_MUTATION_LEGACY,
2112
+ variables,
2113
+ options
2114
+ )
2115
+ );
1811
2116
  return json?.data?.Payment?.ConfirmPaymentApplePay;
1812
2117
  }
1813
2118
 
@@ -1845,6 +2150,33 @@ async function getVippsStatus(variables, options) {
1845
2150
 
1846
2151
  // src/core/cart/cart-manager.ts
1847
2152
  var STORAGE_KEY = "vio.cart.v1";
2153
+ function finalTarget(sync) {
2154
+ return sync.steps.reduce((quantity, step) => quantity + step.delta, sync.sent);
2155
+ }
2156
+ function queueStep(sync, target, origin) {
2157
+ const delta = target - finalTarget(sync);
2158
+ if (delta === 0) return;
2159
+ if (delta > 0) {
2160
+ const last = sync.steps[sync.steps.length - 1];
2161
+ if (last && last.delta > 0 && sameOrigin(last.origin ?? {}, origin ?? {})) last.delta += delta;
2162
+ else sync.steps.push({ delta, origin });
2163
+ return;
2164
+ }
2165
+ let left = -delta;
2166
+ while (left > 0 && sync.steps.length > 0) {
2167
+ const last = sync.steps[sync.steps.length - 1];
2168
+ if (last.delta <= 0) break;
2169
+ const take = Math.min(last.delta, left);
2170
+ last.delta -= take;
2171
+ left -= take;
2172
+ if (last.delta === 0) sync.steps.pop();
2173
+ }
2174
+ if (left > 0) {
2175
+ const last = sync.steps[sync.steps.length - 1];
2176
+ if (last && last.delta < 0) last.delta -= left;
2177
+ else sync.steps.push({ delta: -left });
2178
+ }
2179
+ }
1848
2180
  function lineId() {
1849
2181
  return "li_" + Math.random().toString(36).slice(2, 10) + Date.now().toString(36);
1850
2182
  }
@@ -1936,7 +2268,7 @@ var CartManager = class extends EventTarget {
1936
2268
  * undefined when no quantity sync is active for it. */
1937
2269
  intendedQuantity(sponsorId, cartItemId) {
1938
2270
  const sync = this.qtySyncs.get(this.qtySyncKey(sponsorId, cartItemId));
1939
- return sync ? sync.pending ?? sync.sent : void 0;
2271
+ return sync ? finalTarget(sync) : void 0;
1940
2272
  }
1941
2273
  /** Apply a backend response only if no newer response was applied already.
1942
2274
  * Returns false when it was discarded as stale. */
@@ -2099,7 +2431,8 @@ var CartManager = class extends EventTarget {
2099
2431
  currency: li.price?.currency_code || backendCart.currency || matchedLocal?.currency || "NOK",
2100
2432
  imageUrl,
2101
2433
  quantity: intended ?? li.quantity,
2102
- // The server knows nothing about origins: keep the local ones. They
2434
+ // The server keeps its own copy of the origins (per request); the
2435
+ // cart read model does not answer them, so keep the local ones. They
2103
2436
  // are squared with its quantity (a stock clamp) only when no OTHER
2104
2437
  // mutation is in flight for this sponsor — an AddItem answering
2105
2438
  // while another is pending reports a quantity that is already old
@@ -2214,11 +2547,16 @@ var CartManager = class extends EventTarget {
2214
2547
  const cartId = await this.ensureCartId(opts.sponsorId, opts.currency || void 0);
2215
2548
  if (cartId) {
2216
2549
  const gqlOpts = await getCartGraphQLOptions(opts.sponsorId);
2550
+ const origin = opts.origin ? lineOriginWire(normalizeOrigin(opts.origin), {
2551
+ page: currentOriginPage(),
2552
+ privacyMode: await currentPrivacyMode()
2553
+ }) : void 0;
2217
2554
  const lineItemsInput = [
2218
2555
  {
2219
2556
  product_id: Number(opts.productId || 0),
2220
2557
  quantity: qty,
2221
- variant_id: opts.variantId != null ? Number(opts.variantId) : null
2558
+ variant_id: opts.variantId != null ? Number(opts.variantId) : null,
2559
+ ...origin ? { origin } : {}
2222
2560
  }
2223
2561
  ];
2224
2562
  const resCart = await addItem(cartId, lineItemsInput, gqlOpts);
@@ -2271,8 +2609,10 @@ var CartManager = class extends EventTarget {
2271
2609
  if (target === item.quantity) return;
2272
2610
  const targetItemId = item.cartItemId;
2273
2611
  const removedOrigin = latestOrigin(item);
2274
- if (target > item.quantity) addOriginQuantity(item, opts.origin, target - item.quantity);
2612
+ const increase = target > item.quantity;
2613
+ if (increase) addOriginQuantity(item, opts.origin, target - item.quantity);
2275
2614
  else removeOriginQuantity(item, item.quantity - target);
2615
+ const addedOrigin = increase ? latestOrigin(item) : void 0;
2276
2616
  if (target <= 0) {
2277
2617
  cart.items = cart.items.filter((i) => i !== item);
2278
2618
  if (typeof document !== "undefined") {
@@ -2307,37 +2647,47 @@ var CartManager = class extends EventTarget {
2307
2647
  this.pendingQtyIntents.set(this.intentKey(item.productId, item.variantId), target);
2308
2648
  return;
2309
2649
  }
2310
- this.syncQuantity(sponsorId, cart.cartId, targetItemId, target);
2650
+ this.syncQuantity(sponsorId, cart.cartId, targetItemId, target, addedOrigin);
2311
2651
  }
2312
2652
  /** Send a line's target quantity to the backend — ONE request in flight per
2313
- * line. A target that arrives while one is in flight replaces any earlier
2314
- * waiting one and goes out when the current request settles, so the server
2315
- * ends on the last intent and never sees two updates racing each other.
2316
- * While the sync is active the line keeps its optimistic quantity through
2317
- * any backend response (see `updateFromBackendCart`); a rejection drops
2318
- * what was queued and reconciles the cart from the server. */
2319
- syncQuantity(sponsorId, backendCartId, cartItemId, target) {
2653
+ * line. Targets that arrive while one is in flight queue behind it as
2654
+ * steps (`queueStep`): reduced like the line's origins, sent in order once
2655
+ * the current request settles — one request per queued increase from a
2656
+ * different origin, one for a net decrease — so the server ends on the
2657
+ * last intent, credits every increase to its own origin, and never sees two
2658
+ * updates racing each other. While the sync is active the line keeps its
2659
+ * optimistic quantity through any backend response (see
2660
+ * `updateFromBackendCart`); a rejection drops what was queued and
2661
+ * reconciles the cart from the server. */
2662
+ syncQuantity(sponsorId, backendCartId, cartItemId, target, origin) {
2320
2663
  const key = this.qtySyncKey(sponsorId, cartItemId);
2321
2664
  const active = this.qtySyncs.get(key);
2322
2665
  if (active) {
2323
- active.pending = target;
2666
+ queueStep(active, target, origin);
2324
2667
  return;
2325
2668
  }
2326
- const sync = { sent: target };
2669
+ const sync = { sent: target, steps: [] };
2327
2670
  this.qtySyncs.set(key, sync);
2328
2671
  let settling;
2329
2672
  const run = (async () => {
2330
2673
  let next = target;
2674
+ let nextOrigin = origin;
2331
2675
  let removalRetried = false;
2332
2676
  while (next !== void 0) {
2333
- sync.sent = next;
2334
- sync.pending = void 0;
2335
2677
  const seq = this.nextSeq(sponsorId);
2336
2678
  try {
2337
2679
  const gqlOpts = await getCartGraphQLOptions(sponsorId);
2338
- const resCart = next <= 0 ? await deleteItem(backendCartId, cartItemId, gqlOpts) : await updateItem(backendCartId, cartItemId, null, next, gqlOpts);
2339
- next = sync.pending;
2340
- if (next === sync.sent) next = void 0;
2680
+ const originWire = next > 0 && nextOrigin ? lineOriginWire(nextOrigin, { page: currentOriginPage(), privacyMode: await currentPrivacyMode() }) : void 0;
2681
+ const resCart = next <= 0 ? await deleteItem(backendCartId, cartItemId, gqlOpts) : originWire ? await updateItem(backendCartId, cartItemId, null, next, gqlOpts, { origin: originWire }) : await updateItem(backendCartId, cartItemId, null, next, gqlOpts);
2682
+ const step = sync.steps.shift();
2683
+ if (step) {
2684
+ next = sync.sent + step.delta;
2685
+ nextOrigin = step.delta > 0 ? step.origin : void 0;
2686
+ sync.sent = next;
2687
+ } else {
2688
+ next = void 0;
2689
+ nextOrigin = void 0;
2690
+ }
2341
2691
  if (next === void 0) this.qtySyncs.delete(key);
2342
2692
  if (resCart && Array.isArray(resCart.line_items)) {
2343
2693
  const applied = this.applyBackendCart(sponsorId, resCart, seq, settling);
@@ -2346,12 +2696,13 @@ var CartManager = class extends EventTarget {
2346
2696
  }
2347
2697
  }
2348
2698
  } catch (err) {
2349
- const retryRemoval = !removalRetried && sync.pending !== void 0 && sync.pending <= 0;
2699
+ const retryRemoval = !removalRetried && sync.steps.length > 0 && finalTarget(sync) <= 0;
2700
+ sync.steps = [];
2350
2701
  next = void 0;
2702
+ nextOrigin = void 0;
2351
2703
  if (retryRemoval) {
2352
2704
  removalRetried = true;
2353
2705
  sync.sent = 0;
2354
- sync.pending = void 0;
2355
2706
  next = 0;
2356
2707
  } else {
2357
2708
  this.qtySyncs.delete(key);
@@ -2488,10 +2839,17 @@ function normalizeOrigin(origin) {
2488
2839
  if (origin.contentId !== void 0 && origin.contentId !== null && String(origin.contentId).trim()) {
2489
2840
  out.contentId = String(origin.contentId).trim();
2490
2841
  }
2842
+ const kind = componentKindOf(origin.componentKind ?? origin.componentTemplateId);
2843
+ if (kind) out.componentKind = kind;
2844
+ const contentUrl = originAndPath(origin.contentUrl);
2845
+ if (contentUrl) out.contentUrl = contentUrl;
2846
+ if (typeof origin.contentTitle === "string" && origin.contentTitle.trim()) {
2847
+ out.contentTitle = origin.contentTitle.trim().slice(0, CONTENT_TITLE_MAX_LENGTH);
2848
+ }
2491
2849
  return out;
2492
2850
  }
2493
2851
  function sameOrigin(a, b) {
2494
- return a.campaignId === b.campaignId && a.slot === b.slot && a.contentId === b.contentId;
2852
+ return a.campaignId === b.campaignId && a.slot === b.slot && a.contentId === b.contentId && a.componentKind === b.componentKind;
2495
2853
  }
2496
2854
  function latestOrigin(line) {
2497
2855
  const origins = Array.isArray(line.origins) ? line.origins : [];
@@ -2755,7 +3113,7 @@ async function prepareApplePay(config) {
2755
3113
  }
2756
3114
 
2757
3115
  // src/core/version.ts
2758
- var SDK_VERSION = "0.24.0";
3116
+ var SDK_VERSION = "0.25.0";
2759
3117
 
2760
3118
  // src/core/checkout/payments/stripe-embedded.ts
2761
3119
  var CREATE_PAYMENT_INTENT_STRIPE_MUTATION = `
@@ -4063,13 +4421,14 @@ mutation CreatePaymentKlarnaNative($checkoutId: String!, $countryCode: String, $
4063
4421
  }
4064
4422
  }
4065
4423
  `;
4066
- var CONFIRM_PAYMENT_KLARNA_NATIVE_MUTATION = `
4424
+ var confirmPaymentKlarnaNativeMutation = (withRef) => `
4067
4425
  mutation ConfirmPaymentKlarnaNative($checkoutId: String!, $authorizationToken: String!, $autoCapture: Boolean, $billingAddress: KlarnaNativeAddressInput, $shippingAddress: KlarnaNativeAddressInput, $customer: KlarnaNativeCustomerInput) {
4068
4426
  Payment {
4069
4427
  ConfirmPaymentKlarnaNative(checkout_id: $checkoutId, authorization_token: $authorizationToken, auto_capture: $autoCapture, billing_address: $billingAddress, shipping_address: $shippingAddress, customer: $customer) {
4070
4428
  order_id
4071
4429
  checkout_id
4072
- fraud_status
4430
+ fraud_status${withRef ? `
4431
+ ${BUYER_REF_FIELD}` : ""}
4073
4432
  order {
4074
4433
  order_id
4075
4434
  status
@@ -4251,9 +4610,10 @@ async function executeKlarnaGraphQL(query, variables, sponsorId) {
4251
4610
  return executeCartGraphQL(query, variables, await getCartGraphQLOptions(sponsorId));
4252
4611
  }
4253
4612
  var CheckoutManager = class _CheckoutManager extends EventTarget {
4254
- constructor(cartManager) {
4613
+ constructor(cartManager, deps = {}) {
4255
4614
  super();
4256
4615
  this.cartManager = cartManager;
4616
+ this.deps = deps;
4257
4617
  this.state = null;
4258
4618
  /** Counts `open()` calls — see CheckoutState.session. */
4259
4619
  this.sessionSeq = 0;
@@ -4293,6 +4653,8 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
4293
4653
  this.paymentMethodsCache = /* @__PURE__ */ new Map();
4294
4654
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
4295
4655
  this.paymentMethodsPromises = /* @__PURE__ */ new Map();
4656
+ /** In-flight `getOrderReference` per checkout id (see there). */
4657
+ this.referenceInFlight = /* @__PURE__ */ new Map();
4296
4658
  this.kustomController = null;
4297
4659
  preloadStripeJs();
4298
4660
  }
@@ -4320,7 +4682,20 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
4320
4682
  }
4321
4683
  // MARK: - Backend checkout (Vio Commerce GraphQL)
4322
4684
  /* eslint-disable @typescript-eslint/no-explicit-any */
4323
- /** Create the backend checkout for the sponsor's cart (auto-accepts terms). */
4685
+ /**
4686
+ * The attribution of the order this sponsor's cart is about to become
4687
+ * (ADR-0035 phase B), through the facade; `undefined` when there is none
4688
+ * to give or it fails — attribution never costs a checkout.
4689
+ */
4690
+ async attributionFor(sponsorId) {
4691
+ if (!sponsorId || typeof this.deps.orderAttribution !== "function") return void 0;
4692
+ try {
4693
+ return await this.deps.orderAttribution(sponsorId) ?? void 0;
4694
+ } catch {
4695
+ return void 0;
4696
+ }
4697
+ }
4698
+ /** Create the backend checkout for the sponsor's cart (auto-accepts terms); born attributed when the facade knows the attribution. */
4324
4699
  async createCheckout(sponsorId) {
4325
4700
  await this.cartManager.waitForCartMutations(sponsorId);
4326
4701
  const cart = this.cartManager.getCart(sponsorId);
@@ -4332,7 +4707,8 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
4332
4707
  }
4333
4708
  }
4334
4709
  const opts = await getCartGraphQLOptions(sponsorId);
4335
- const checkout = await createCheckout(cartId, opts);
4710
+ const attribution = await this.attributionFor(sponsorId);
4711
+ const checkout = attribution ? await createCheckout(cartId, opts, { attribution }) : await createCheckout(cartId, opts);
4336
4712
  if (checkout?.id) {
4337
4713
  this.state = {
4338
4714
  ...this.state ?? { sponsorId, subtotal: 0, currency: getGlobalCurrency() },
@@ -4363,12 +4739,15 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
4363
4739
  }
4364
4740
  return checkout;
4365
4741
  }
4366
- /** Update the backend checkout (terms are re-asserted unless overridden). */
4742
+ /** Update the backend checkout (terms are re-asserted unless overridden; the attribution rides along unless the caller gives its own). */
4367
4743
  async updateCheckout(variables, sponsorId) {
4368
- const opts = await getCartGraphQLOptions(sponsorId ?? this.state?.sponsorId);
4744
+ const spId = sponsorId ?? this.state?.sponsorId;
4745
+ const opts = await getCartGraphQLOptions(spId);
4746
+ const attribution = "attribution" in variables ? void 0 : await this.attributionFor(spId);
4369
4747
  const fullVars = {
4370
4748
  buyerAcceptsPurchaseConditions: true,
4371
4749
  buyerAcceptsTermsConditions: true,
4750
+ ...attribution ? { attribution } : {},
4372
4751
  ...variables
4373
4752
  };
4374
4753
  const updated = await updateCheckout(fullVars, opts);
@@ -4388,13 +4767,30 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
4388
4767
  * receipt, and a receipt does not fail for it.
4389
4768
  */
4390
4769
  async getOrderNumber(checkoutId, sponsorId) {
4391
- if (!checkoutId) return null;
4392
- try {
4393
- const opts = await getCartGraphQLOptions(sponsorId ?? this.state?.sponsorId);
4394
- return await getCheckoutOrderNumber(checkoutId, opts);
4395
- } catch {
4396
- return null;
4397
- }
4770
+ return (await this.getOrderReference(checkoutId, sponsorId)).orderNumber;
4771
+ }
4772
+ /**
4773
+ * What a paid checkout says of itself: the order number (as above) and the
4774
+ * buyer reference — shopcart's keyed hash of the buyer's email, what the
4775
+ * `vio:payment-success` detail carries as `buyerRef` and analytics sends as
4776
+ * `buyer_id`. Never throws; `null` for whatever the API does not say.
4777
+ */
4778
+ async getOrderReference(checkoutId, sponsorId) {
4779
+ if (!checkoutId) return { orderNumber: null, buyerRef: null };
4780
+ const inFlight = this.referenceInFlight.get(checkoutId);
4781
+ if (inFlight) return inFlight;
4782
+ const request = (async () => {
4783
+ try {
4784
+ const opts = await getCartGraphQLOptions(sponsorId ?? this.state?.sponsorId);
4785
+ return await getCheckoutReference(checkoutId, opts);
4786
+ } catch {
4787
+ return { orderNumber: null, buyerRef: null };
4788
+ } finally {
4789
+ this.referenceInFlight.delete(checkoutId);
4790
+ }
4791
+ })();
4792
+ this.referenceInFlight.set(checkoutId, request);
4793
+ return request;
4398
4794
  }
4399
4795
  /**
4400
4796
  * Payment methods enabled for the sponsor (backend-configured). Memoised per
@@ -4525,9 +4921,10 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
4525
4921
  throw new Error(`[CheckoutManager] Failed to obtain cart_id for sponsor ${spId}`);
4526
4922
  }
4527
4923
  const opts = await getCartGraphQLOptions(spId);
4924
+ const attribution = await this.attributionFor(spId);
4528
4925
  let checkoutId = this.state?.checkoutId;
4529
4926
  if (!checkoutId) {
4530
- const checkoutRes = await createCheckout(cartId, opts);
4927
+ const checkoutRes = attribution ? await createCheckout(cartId, opts, { attribution }) : await createCheckout(cartId, opts);
4531
4928
  checkoutId = checkoutRes?.id;
4532
4929
  }
4533
4930
  if (!checkoutId) {
@@ -4546,6 +4943,7 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
4546
4943
  ...hasEmail ? { email: emailVal } : {},
4547
4944
  buyerAcceptsPurchaseConditions: true,
4548
4945
  buyerAcceptsTermsConditions: true,
4946
+ ...attribution ? { attribution } : {},
4549
4947
  ...extraUpdateVars
4550
4948
  };
4551
4949
  if (addressObj) {
@@ -5541,7 +5939,8 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
5541
5939
  `[VioCheckout] Apple Pay confirmation failed (status: ${confirmRes?.status ?? "none"})`
5542
5940
  );
5543
5941
  }
5544
- return { orderId: confirmRes?.order_id, status: confirmRes?.status };
5942
+ const buyerRef = normalizeBuyerRef(confirmRes?.[BUYER_REF_FIELD]);
5943
+ return { orderId: confirmRes?.order_id, status: confirmRes?.status, ...buyerRef ? { buyerRef } : {} };
5545
5944
  };
5546
5945
  }
5547
5946
  static {
@@ -6044,16 +6443,19 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
6044
6443
  shippingAddress: buildKlarnaAddress(address),
6045
6444
  customer: buildKlarnaCustomer(address)
6046
6445
  };
6047
- const json = await executeKlarnaGraphQL(
6048
- CONFIRM_PAYMENT_KLARNA_NATIVE_MUTATION,
6049
- variables,
6050
- ctx?.sponsorId ?? this.state?.sponsorId
6446
+ const json = await withBuyerRef(
6447
+ (withRef) => executeKlarnaGraphQL(
6448
+ confirmPaymentKlarnaNativeMutation(withRef),
6449
+ variables,
6450
+ ctx?.sponsorId ?? this.state?.sponsorId
6451
+ )
6051
6452
  );
6052
6453
  const confirmRes = json?.data?.Payment?.ConfirmPaymentKlarnaNative;
6053
6454
  if (!confirmRes) {
6054
6455
  throw new Error("Klarna order confirmation failed: missing response data");
6055
6456
  }
6056
6457
  const orderData = confirmRes.order || confirmRes;
6458
+ const buyerRef = normalizeBuyerRef(confirmRes[BUYER_REF_FIELD]);
6057
6459
  if (typeof sessionStorage !== "undefined") {
6058
6460
  sessionStorage.setItem("vio.klarna.confirmed.v1", authorizationToken);
6059
6461
  sessionStorage.removeItem(KLARNA_PENDING_KEY);
@@ -6071,7 +6473,8 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
6071
6473
  result: {
6072
6474
  ...result,
6073
6475
  order: orderData,
6074
- chargedTotal: typeof orderData?.order_amount === "number" ? orderData.order_amount / 100 : ctx.orderAmount / 100
6476
+ chargedTotal: typeof orderData?.order_amount === "number" ? orderData.order_amount / 100 : ctx.orderAmount / 100,
6477
+ ...buyerRef ? { buyerRef } : {}
6075
6478
  }
6076
6479
  }
6077
6480
  })
@@ -6129,57 +6532,173 @@ function toSmallestUnit(amount, currency) {
6129
6532
  return Math.round(amount * 100);
6130
6533
  }
6131
6534
 
6132
- // src/core/content.ts
6133
- var CONTENT_ID_QUERY = "vio_content";
6134
- var CONTENT_TITLE_QUERY = "vio_content_title";
6135
- var CONTENT_ID_MAX_LENGTH = 128;
6136
- var CONTENT_TITLE_MAX_LENGTH = 255;
6137
- function resolveContent(init, doc = typeof document !== "undefined" ? document : null, loc = typeof location !== "undefined" ? location : null) {
6535
+ // src/core/analytics/client-context.ts
6536
+ var UTM_KEYS = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
6537
+ var CLIENT_LANGUAGE_MAX_LENGTH = 16;
6538
+ var CLIENT_REFERRER_MAX_LENGTH = 2048;
6539
+ var CLIENT_UTM_MAX_LENGTH = 128;
6540
+ var CLIENT_TZ_MAX_LENGTH = 64;
6541
+ var CLIENT_SCREEN_MAX = 65535;
6542
+ var CLIENT_LANDING_URL_MAX_LENGTH = 2048;
6543
+ var LANDING_KEY = "vio.landing.v1";
6544
+ function defaultClientEnvironment() {
6545
+ return {
6546
+ navigator: typeof navigator !== "undefined" ? navigator : null,
6547
+ document: typeof document !== "undefined" ? document : null,
6548
+ location: typeof location !== "undefined" ? location : null,
6549
+ screen: typeof screen !== "undefined" ? screen : null,
6550
+ storage: sessionStorageOrNull(),
6551
+ timeZone: () => Intl.DateTimeFormat().resolvedOptions().timeZone
6552
+ };
6553
+ }
6554
+ function resolveLanding(env = defaultClientEnvironment(), persist = true) {
6138
6555
  try {
6139
- const query = parseQuery(loc?.search);
6140
- const id = fits(text(init?.contentId)) ?? fits(text(query.get(CONTENT_ID_QUERY))) ?? fits(text(meta(doc, "name", "vio:content-id"))) ?? fits(stripQueryAndHash(text(attr(doc, 'link[rel="canonical"]', "href")))) ?? fits(stripQueryAndHash(text(meta(doc, "property", "og:url")))) ?? fits(text(loc?.pathname));
6141
- const title = text(init?.contentTitle) ?? text(query.get(CONTENT_TITLE_QUERY)) ?? text(meta(doc, "name", "vio:content-title")) ?? text(meta(doc, "property", "og:title")) ?? text(doc?.title);
6142
- const out = {};
6143
- if (id !== void 0) out.id = id;
6144
- if (title !== void 0) out.title = title.slice(0, CONTENT_TITLE_MAX_LENGTH);
6145
- return out;
6556
+ const utm = utmOf(env.location?.search);
6557
+ const raw = text2(env.document?.referrer);
6558
+ const internal = isSameHost(raw, env.location?.host);
6559
+ const stored = readLanding(env.storage);
6560
+ if ((utm === void 0 || internal) && stored) return stored;
6561
+ const landing = {};
6562
+ const referrer = internal ? void 0 : landingReferrer(raw);
6563
+ if (referrer) landing.referrer = referrer;
6564
+ if (utm) landing.utm = utm;
6565
+ const url = landingUrl(env.location?.href);
6566
+ if (url) landing.url = url;
6567
+ if (persist) writeLanding(env.storage, landing);
6568
+ return landing;
6146
6569
  } catch {
6147
6570
  return {};
6148
6571
  }
6149
6572
  }
6150
- function fits(value) {
6151
- return value !== void 0 && value.length <= CONTENT_ID_MAX_LENGTH ? value : void 0;
6573
+ function landingReferrer(raw) {
6574
+ return originAndPath(raw, CLIENT_REFERRER_MAX_LENGTH);
6152
6575
  }
6153
- function text(value) {
6154
- if (value === void 0 || value === null) return void 0;
6155
- const s = String(value).trim();
6156
- return s ? s : void 0;
6576
+ function landingUrl(raw) {
6577
+ return originAndPath(raw, CLIENT_LANDING_URL_MAX_LENGTH);
6157
6578
  }
6158
- function parseQuery(search) {
6579
+ function storedLanding(storage2 = defaultClientEnvironment().storage) {
6580
+ return readLanding(storage2);
6581
+ }
6582
+ function isSameHost(referrer, host) {
6583
+ const value = text2(referrer);
6584
+ const own = text2(host);
6585
+ if (!value || !own) return false;
6159
6586
  try {
6160
- return new URLSearchParams(typeof search === "string" ? search : "");
6587
+ return new URL(value).host.toLowerCase() === own.toLowerCase();
6161
6588
  } catch {
6162
- return new URLSearchParams();
6589
+ return false;
6163
6590
  }
6164
6591
  }
6165
- function meta(doc, key, value) {
6166
- return attr(doc, `meta[${key}="${value}"]`, "content");
6592
+ function persistLanding(landing, storage2 = defaultClientEnvironment().storage) {
6593
+ writeLanding(storage2, landing);
6167
6594
  }
6168
- function attr(doc, selector, name) {
6595
+ function removeLanding(storage2 = defaultClientEnvironment().storage) {
6169
6596
  try {
6170
- if (!doc || typeof doc.querySelector !== "function") return void 0;
6171
- const el = doc.querySelector(selector);
6172
- const v = el?.getAttribute?.(name);
6173
- return typeof v === "string" ? v : void 0;
6597
+ storage2?.removeItem?.(LANDING_KEY);
6598
+ } catch {
6599
+ }
6600
+ }
6601
+ function resolveClientContext(env = defaultClientEnvironment(), landing = resolveLanding(env)) {
6602
+ const out = {};
6603
+ try {
6604
+ const language = cut2(text2(env.navigator?.language), CLIENT_LANGUAGE_MAX_LENGTH);
6605
+ if (language) out.language = language;
6606
+ const referrer = cut2(landing.referrer, CLIENT_REFERRER_MAX_LENGTH);
6607
+ if (referrer) out.referrer = referrer;
6608
+ for (const key of UTM_KEYS) {
6609
+ const value = cut2(landing.utm?.[key], CLIENT_UTM_MAX_LENGTH);
6610
+ if (value) out[key] = value;
6611
+ }
6612
+ const w = dimension(env.screen?.width);
6613
+ const h = dimension(env.screen?.height);
6614
+ if (w !== void 0) out.screen_w = w;
6615
+ if (h !== void 0) out.screen_h = h;
6616
+ const tz = cut2(text2(safeCall(env.timeZone)), CLIENT_TZ_MAX_LENGTH);
6617
+ if (tz) out.tz = tz;
6618
+ } catch {
6619
+ }
6620
+ return out;
6621
+ }
6622
+ function utmOf(search) {
6623
+ let params;
6624
+ try {
6625
+ params = new URLSearchParams(typeof search === "string" ? search : "");
6174
6626
  } catch {
6175
6627
  return void 0;
6176
6628
  }
6629
+ const out = {};
6630
+ let any = false;
6631
+ for (const key of UTM_KEYS) {
6632
+ const value = cut2(text2(params.get(key)), CLIENT_UTM_MAX_LENGTH);
6633
+ if (value) {
6634
+ out[key] = value;
6635
+ any = true;
6636
+ }
6637
+ }
6638
+ return any ? out : void 0;
6177
6639
  }
6178
- function stripQueryAndHash(url) {
6179
- if (!url) return void 0;
6180
- const cut = url.search(/[?#]/);
6181
- const clean = cut >= 0 ? url.slice(0, cut) : url;
6182
- return clean.trim() || void 0;
6640
+ function readLanding(storage2) {
6641
+ try {
6642
+ const raw = storage2?.getItem(LANDING_KEY);
6643
+ if (!raw) return null;
6644
+ const parsed = JSON.parse(raw);
6645
+ if (!parsed || typeof parsed !== "object") return null;
6646
+ const p = parsed;
6647
+ const landing = {};
6648
+ const referrer = cut2(text2(p.referrer), CLIENT_REFERRER_MAX_LENGTH);
6649
+ if (referrer) landing.referrer = referrer;
6650
+ const url = cut2(text2(p.url), CLIENT_LANDING_URL_MAX_LENGTH);
6651
+ if (url) landing.url = url;
6652
+ if (p.utm && typeof p.utm === "object") {
6653
+ const utm = {};
6654
+ let any = false;
6655
+ for (const key of UTM_KEYS) {
6656
+ const value = cut2(text2(p.utm[key]), CLIENT_UTM_MAX_LENGTH);
6657
+ if (value) {
6658
+ utm[key] = value;
6659
+ any = true;
6660
+ }
6661
+ }
6662
+ if (any) landing.utm = utm;
6663
+ }
6664
+ return landing;
6665
+ } catch {
6666
+ return null;
6667
+ }
6668
+ }
6669
+ function writeLanding(storage2, landing) {
6670
+ try {
6671
+ storage2?.setItem(LANDING_KEY, JSON.stringify(landing));
6672
+ } catch {
6673
+ }
6674
+ }
6675
+ function sessionStorageOrNull() {
6676
+ try {
6677
+ return typeof sessionStorage !== "undefined" && sessionStorage ? sessionStorage : null;
6678
+ } catch {
6679
+ return null;
6680
+ }
6681
+ }
6682
+ function text2(value) {
6683
+ if (value === void 0 || value === null) return void 0;
6684
+ const s = String(value).trim();
6685
+ return s ? s : void 0;
6686
+ }
6687
+ function cut2(value, max) {
6688
+ if (value === void 0) return void 0;
6689
+ return value.length > max ? value.slice(0, max) : value;
6690
+ }
6691
+ function dimension(value) {
6692
+ const n = typeof value === "number" ? value : Number(value);
6693
+ if (!Number.isFinite(n) || n < 0) return void 0;
6694
+ return Math.min(Math.floor(n), CLIENT_SCREEN_MAX);
6695
+ }
6696
+ function safeCall(fn) {
6697
+ try {
6698
+ return fn ? fn() : void 0;
6699
+ } catch {
6700
+ return void 0;
6701
+ }
6183
6702
  }
6184
6703
 
6185
6704
  // src/core/cart/slices.ts
@@ -6280,8 +6799,16 @@ var AnalyticsManager = class extends EventTarget {
6280
6799
  /** Last NON-EMPTY cart snapshot — `purchase` uses it because several flows
6281
6800
  * (Apple Pay) clear the cart BEFORE `vio:payment-success` fires. */
6282
6801
  this.lastCartSnapshot = null;
6802
+ /** The `client` block (language, landing referrer / utm, screen, tz): read once per page load, reused on every event. */
6803
+ this.client = null;
6804
+ /** The landing it was read from — persisted later when consent arrives after the first event. */
6805
+ this.landing = null;
6283
6806
  this.teardown = [];
6284
6807
  }
6808
+ /** The landing this manager resolved on this page load (the first event's), or null before it; read only — for the order's entry context when nothing is stored yet. */
6809
+ get currentLanding() {
6810
+ return this.landing;
6811
+ }
6285
6812
  // MARK: - Public API
6286
6813
  /**
6287
6814
  * Install listeners + begin flushing. Explicit on purpose (no module
@@ -6299,10 +6826,12 @@ var AnalyticsManager = class extends EventTarget {
6299
6826
  if (granted) {
6300
6827
  if (this.anonIdCache) safeStorageSet(ANON_KEY, this.anonIdCache);
6301
6828
  if (this.sessionId) this.persistSession();
6829
+ if (this.landing) persistLanding(this.landing);
6302
6830
  return;
6303
6831
  }
6304
6832
  safeStorageRemove(ANON_KEY);
6305
6833
  safeStorageRemove(SESSION_KEY);
6834
+ removeLanding();
6306
6835
  }
6307
6836
  /** ¿Podemos escribir en el dispositivo? */
6308
6837
  get canPersist() {
@@ -6463,7 +6992,7 @@ var AnalyticsManager = class extends EventTarget {
6463
6992
  // one is usually the tracking tail, not the page. The other host-given
6464
6993
  // fields (broadcastId, componentTemplateId, errorCode, variant, …) are
6465
6994
  // the host's responsibility: the SDK does not touch them.
6466
- content_url: capUrl(ctx.contentUrl ?? (typeof location !== "undefined" ? location.href : void 0)),
6995
+ content_url: capContentUrl(ctx.contentUrl ?? (typeof location !== "undefined" ? location.href : void 0)),
6467
6996
  // The host's own content id — its join key back onto its reporting.
6468
6997
  // Explicit on the event when it fits, else what the SDK resolved for
6469
6998
  // the page (already capped per source), else nothing.
@@ -6491,8 +7020,13 @@ var AnalyticsManager = class extends EventTarget {
6491
7020
  value: com.value,
6492
7021
  currency: com.currency,
6493
7022
  order_id: com.orderId,
6494
- payment_method: com.paymentMethod
7023
+ payment_method: com.paymentMethod,
7024
+ // Only a reference survives — never an email, whatever a host passes —
7025
+ // and only with consent to persist: a visit measured with ephemeral
7026
+ // ids does not carry a stable identifier of a person either.
7027
+ buyer_id: this.canPersist ? normalizeBuyerRef(com.buyerId) : void 0
6495
7028
  }) : void 0;
7029
+ const client = this.clientBlock();
6496
7030
  return {
6497
7031
  event_id: uuid(),
6498
7032
  name,
@@ -6505,9 +7039,22 @@ var AnalyticsManager = class extends EventTarget {
6505
7039
  ...this.externalUserId ? { external_user_id: this.externalUserId } : {},
6506
7040
  ...Object.keys(context).length ? { context } : {},
6507
7041
  ...commerce ? { commerce } : {},
7042
+ ...client ? { client } : {},
6508
7043
  ...opts.props ? { props: opts.props } : {}
6509
7044
  };
6510
7045
  }
7046
+ /**
7047
+ * The `client` block: read once — the landing is remembered for the session
7048
+ * only when we may persist — then the same (frozen) object on every event.
7049
+ * `null` when there is nothing to say (no browser).
7050
+ */
7051
+ clientBlock() {
7052
+ if (!this.client) {
7053
+ this.landing = resolveLanding(void 0, this.canPersist);
7054
+ this.client = Object.freeze(resolveClientContext(void 0, this.landing));
7055
+ }
7056
+ return Object.keys(this.client).length ? this.client : null;
7057
+ }
6511
7058
  /** Guarda la sesión solo si podemos escribir en el dispositivo. */
6512
7059
  persistSession(now = Date.now()) {
6513
7060
  if (!this.canPersist || !this.sessionId) return;
@@ -6621,7 +7168,7 @@ var AnalyticsManager = class extends EventTarget {
6621
7168
  const d = ev.detail ?? {};
6622
7169
  const line = this.findLine(d.productId, d.variantId);
6623
7170
  this.track("add_to_cart", {
6624
- context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0, campaignId: campaignOf(d), slot: slotOf(d), ...contentOf(d) },
7171
+ context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0, campaignId: campaignOf(d), slot: slotOf(d), componentTemplateId: templateOf(d), ...contentOf(d) },
6625
7172
  commerce: {
6626
7173
  items: [
6627
7174
  {
@@ -6671,7 +7218,7 @@ var AnalyticsManager = class extends EventTarget {
6671
7218
  if (d.productId === void 0) return;
6672
7219
  const price = Number(d.price);
6673
7220
  this.track("select_item", {
6674
- context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0, campaignId: campaignOf(d), slot: slotOf(d), ...contentOf(d) },
7221
+ context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0, campaignId: campaignOf(d), slot: slotOf(d), componentTemplateId: templateOf(d), ...contentOf(d) },
6675
7222
  commerce: {
6676
7223
  items: [
6677
7224
  {
@@ -6690,13 +7237,15 @@ var AnalyticsManager = class extends EventTarget {
6690
7237
  });
6691
7238
  on("vio:checkout-open", (ev) => {
6692
7239
  const d = ev.detail ?? {};
6693
- const base = { sponsorId: d.sponsorId, campaignId: campaignOf(d), slot: slotOf(d), ...contentOf(d) };
7240
+ const base = { sponsorId: d.sponsorId, campaignId: campaignOf(d), slot: slotOf(d), componentTemplateId: templateOf(d), ...contentOf(d) };
7241
+ const buyerId = buyerRefOf(d);
7242
+ const buyer = buyerId !== void 0 ? { buyerId } : {};
6694
7243
  const { slices, explicit } = this.cartSlices(d.sponsorId);
6695
7244
  if (slices.length > 1) {
6696
7245
  for (const slice of slices) {
6697
7246
  this.track("begin_checkout", {
6698
7247
  context: this.sliceContext(slice, base),
6699
- commerce: sliceCommerce(slice),
7248
+ commerce: { ...sliceCommerce(slice), ...buyer },
6700
7249
  props: { slice: slice.slice, slices: slice.slices }
6701
7250
  });
6702
7251
  }
@@ -6705,7 +7254,7 @@ var AnalyticsManager = class extends EventTarget {
6705
7254
  const snap = this.cartCommerce(d.sponsorId);
6706
7255
  this.track("begin_checkout", {
6707
7256
  context: this.singleSliceContext(slices[0], explicit, base),
6708
- ...snap ? { commerce: snap } : {}
7257
+ ...snap || buyerId !== void 0 ? { commerce: { ...snap ?? {}, ...buyer } } : {}
6709
7258
  });
6710
7259
  });
6711
7260
  on("vio:payment-success", (ev) => {
@@ -6933,20 +7482,16 @@ function capTitle(value) {
6933
7482
  if (!value) return void 0;
6934
7483
  return value.length > CONTENT_TITLE_MAX_LENGTH ? value.slice(0, CONTENT_TITLE_MAX_LENGTH) : value;
6935
7484
  }
6936
- var CONTENT_URL_MAX_LENGTH = 2048;
6937
- function capUrl(value) {
6938
- if (!value) return void 0;
6939
- if (value.length <= CONTENT_URL_MAX_LENGTH) return value;
6940
- const cut = value.search(/[?#]/);
6941
- const withoutQuery = cut >= 0 ? value.slice(0, cut) : value;
6942
- return withoutQuery.length <= CONTENT_URL_MAX_LENGTH ? withoutQuery : withoutQuery.slice(0, CONTENT_URL_MAX_LENGTH);
6943
- }
6944
7485
  function sliceCommerce(slice) {
6945
7486
  return { items: slice.items, value: slice.value, currency: slice.currency };
6946
7487
  }
6947
7488
  function slotOf(detail) {
6948
7489
  return typeof detail.slot === "string" && detail.slot.trim() ? detail.slot.trim() : void 0;
6949
7490
  }
7491
+ function templateOf(detail) {
7492
+ const value = typeof detail.componentTemplateId === "string" ? detail.componentTemplateId.trim() : "";
7493
+ return fitsId(value || void 0);
7494
+ }
6950
7495
  function contentOf(detail) {
6951
7496
  const out = {};
6952
7497
  if (typeof detail.contentId === "number" && Number.isFinite(detail.contentId)) out.contentId = detail.contentId;
@@ -6954,10 +7499,33 @@ function contentOf(detail) {
6954
7499
  if (typeof detail.contentTitle === "string" && detail.contentTitle.trim()) out.contentTitle = detail.contentTitle.trim();
6955
7500
  return out;
6956
7501
  }
7502
+ function buyerRefOf(d) {
7503
+ if (!d) return void 0;
7504
+ const r = d.result ?? {};
7505
+ const candidates = [
7506
+ d.buyerRef,
7507
+ d.buyer_ref,
7508
+ r.buyerRef,
7509
+ r.buyer_ref,
7510
+ r.confirmation?.buyerRef,
7511
+ r.confirmation?.buyer_ref,
7512
+ r.checkout?.buyerRef,
7513
+ r.checkout?.buyer_ref
7514
+ ];
7515
+ for (const candidate of candidates) {
7516
+ const ref = normalizeBuyerRef(candidate);
7517
+ if (ref) return ref;
7518
+ }
7519
+ return void 0;
7520
+ }
6957
7521
  function purchaseFromPaymentSuccess(d) {
6958
7522
  const r = d.result ?? {};
6959
7523
  const orderId = firstId(d.orderId, r.confirmation?.orderId, d.checkoutId);
6960
- return orderId !== void 0 ? { orderId } : {};
7524
+ const buyerId = buyerRefOf(d);
7525
+ return {
7526
+ ...orderId !== void 0 ? { orderId } : {},
7527
+ ...buyerId !== void 0 ? { buyerId } : {}
7528
+ };
6961
7529
  }
6962
7530
  function firstId(...candidates) {
6963
7531
  for (const c of candidates) {
@@ -7002,8 +7570,87 @@ function pushGa4(event) {
7002
7570
  });
7003
7571
  }
7004
7572
 
7573
+ // src/core/checkout/attribution.ts
7574
+ var CHECKOUT_ATTRIBUTION_CAPS = Object.freeze({
7575
+ surfaceKey: 255,
7576
+ contentUrl: 2048,
7577
+ contentTitle: CONTENT_TITLE_MAX_LENGTH,
7578
+ referrer: CLIENT_REFERRER_MAX_LENGTH,
7579
+ utm: CLIENT_UTM_MAX_LENGTH,
7580
+ landingUrl: CLIENT_LANDING_URL_MAX_LENGTH
7581
+ });
7582
+ function checkoutAttributionOf(input) {
7583
+ const out = {};
7584
+ const surfaceKey = cut3(input.surfaceKey, CHECKOUT_ATTRIBUTION_CAPS.surfaceKey);
7585
+ if (surfaceKey) out.surface_key = surfaceKey;
7586
+ const campaignId = positiveInt(input.pageCampaignId) ?? dominantCampaignOf(input.slices);
7587
+ if (campaignId !== void 0) out.campaign_id = campaignId;
7588
+ if (input.privacyMode === "standard") {
7589
+ const url = originAndPath(input.content?.url, CHECKOUT_ATTRIBUTION_CAPS.contentUrl);
7590
+ if (url) out.content_url = url;
7591
+ const title = cut3(input.content?.title, CHECKOUT_ATTRIBUTION_CAPS.contentTitle);
7592
+ if (title) out.content_title = title;
7593
+ const entry = entryContextOf(input.landing);
7594
+ if (entry) out.entry = entry;
7595
+ }
7596
+ return Object.keys(out).length ? out : void 0;
7597
+ }
7598
+ function entryContextOf(landing) {
7599
+ if (!landing || typeof landing !== "object") return void 0;
7600
+ const out = {};
7601
+ const referrer = cut3(landing.referrer, CHECKOUT_ATTRIBUTION_CAPS.referrer);
7602
+ if (referrer) out.referrer = referrer;
7603
+ const source = cut3(landing.utm?.utm_source, CHECKOUT_ATTRIBUTION_CAPS.utm);
7604
+ if (source) out.utm_source = source;
7605
+ const medium = cut3(landing.utm?.utm_medium, CHECKOUT_ATTRIBUTION_CAPS.utm);
7606
+ if (medium) out.utm_medium = medium;
7607
+ const campaign = cut3(landing.utm?.utm_campaign, CHECKOUT_ATTRIBUTION_CAPS.utm);
7608
+ if (campaign) out.utm_campaign = campaign;
7609
+ const landingUrl2 = cut3(landing.url, CHECKOUT_ATTRIBUTION_CAPS.landingUrl);
7610
+ if (landingUrl2) out.landing_url = landingUrl2;
7611
+ return Object.keys(out).length ? out : void 0;
7612
+ }
7613
+ function dominantCampaignOf(slices) {
7614
+ if (!Array.isArray(slices)) return void 0;
7615
+ let best;
7616
+ for (const slice of slices) {
7617
+ const campaignId = positiveInt(slice?.campaignId);
7618
+ if (campaignId === void 0) continue;
7619
+ const items = Array.isArray(slice.items) ? slice.items : [];
7620
+ const units = items.reduce((sum, item) => sum + (Number(item?.quantity) > 0 ? Number(item.quantity) : 0), 0);
7621
+ const value = Number(slice.value) || 0;
7622
+ if (!best || units > best.units || units === best.units && value > best.value) {
7623
+ best = { campaignId, units, value };
7624
+ }
7625
+ }
7626
+ return best?.campaignId;
7627
+ }
7628
+ function positiveInt(value) {
7629
+ if (value === void 0 || value === null || value === "") return void 0;
7630
+ const n = Number(value);
7631
+ return Number.isInteger(n) && n > 0 && n <= GRAPHQL_INT_MAX ? n : void 0;
7632
+ }
7633
+ function cut3(value, max) {
7634
+ if (typeof value !== "string" && typeof value !== "number") return void 0;
7635
+ const text3 = String(value).trim();
7636
+ if (!text3) return void 0;
7637
+ return text3.length > max ? text3.slice(0, max).trim() || void 0 : text3;
7638
+ }
7639
+
7005
7640
  // src/core/client.ts
7006
7641
  var SLOT_DECLARATION_DEBOUNCE_MS = 1e3;
7642
+ var PRIVACY_MODE_TIMEOUT_MS = 1500;
7643
+ var PRIVACY_UNKNOWN_TTL_MS = 6e4;
7644
+ function privacyModeOf(value) {
7645
+ return value === "minimal" || value === "standard" ? value : void 0;
7646
+ }
7647
+ function privacyModeOfSurface(res) {
7648
+ const surface = res?.surface;
7649
+ if (!surface || typeof surface !== "object") return "unknown";
7650
+ const raw = surface.privacyMode;
7651
+ if (raw === void 0 || raw === null) return "standard";
7652
+ return privacyModeOf(raw) ?? "unknown";
7653
+ }
7007
7654
  var VioFacade = class {
7008
7655
  constructor() {
7009
7656
  this.apiInstance = null;
@@ -7017,7 +7664,7 @@ var VioFacade = class {
7017
7664
  * (another key or environment) is still in flight after a re-init; its answer
7018
7665
  * belongs to that configuration and must not land in this one. */
7019
7666
  this.generation = 0;
7020
- /** Slot ids present on the page (`Vio.registerSlot`): how many elements hold each, its display name and its own campaign. */
7667
+ /** Slot ids present on the page (`Vio.registerSlot`): how many elements hold each, its display name, its own campaign and its kind. */
7021
7668
  this.slotRegistry = /* @__PURE__ */ new Map();
7022
7669
  /** Slots already warned about (`dashboard`: the dashboard's assignment stands; `replaced`: this page moved it). */
7023
7670
  this.warnedDashboardSlots = /* @__PURE__ */ new Set();
@@ -7036,6 +7683,10 @@ var VioFacade = class {
7036
7683
  this.cartManager = null;
7037
7684
  this.checkoutManager = null;
7038
7685
  this.analyticsManager = null;
7686
+ /** What the surface answered of its privacy mode (`GET /v2/mobile/surface`) and when; `unknown` expires after a minute. */
7687
+ this.surfaceMode = null;
7688
+ /** The request in flight for it, if any. */
7689
+ this.surfaceRequest = null;
7039
7690
  }
7040
7691
  init(config) {
7041
7692
  Configuration.init(config);
@@ -7045,6 +7696,9 @@ var VioFacade = class {
7045
7696
  this.cachedBootstrap = null;
7046
7697
  this.bootstrapFailedAt = 0;
7047
7698
  this.generation++;
7699
+ this.surfaceMode = null;
7700
+ this.surfaceRequest = null;
7701
+ this.preloadPrivacyMode();
7048
7702
  this.checkoutManager?.clearPaymentMethodsCache();
7049
7703
  if (this.slotDeclareTimer) {
7050
7704
  clearTimeout(this.slotDeclareTimer);
@@ -7086,9 +7740,13 @@ var VioFacade = class {
7086
7740
  if (!this.cartManager) this.cartManager = new CartManager();
7087
7741
  return this.cartManager;
7088
7742
  }
7089
- /** Checkout flow manager. Subscribes to cart state via the CartManager. */
7743
+ /** Checkout flow manager. Subscribes to cart state via the CartManager; asks here for the order's attribution. */
7090
7744
  get checkout() {
7091
- if (!this.checkoutManager) this.checkoutManager = new CheckoutManager(this.cart);
7745
+ if (!this.checkoutManager) {
7746
+ this.checkoutManager = new CheckoutManager(this.cart, {
7747
+ orderAttribution: (sponsorId) => this.orderAttribution(sponsorId)
7748
+ });
7749
+ }
7092
7750
  return this.checkoutManager;
7093
7751
  }
7094
7752
  /**
@@ -7130,6 +7788,7 @@ var VioFacade = class {
7130
7788
  this.cachedBootstrap = res;
7131
7789
  this.bootstrapFailedAt = 0;
7132
7790
  if (this.slotsDirty) this.scheduleSlotDeclaration();
7791
+ this.preloadPrivacyMode();
7133
7792
  return res;
7134
7793
  },
7135
7794
  (err) => {
@@ -7256,21 +7915,31 @@ var VioFacade = class {
7256
7915
  * the optional display name, and the element's own campaign when it has
7257
7916
  * one: `vio-campaign` or an ancestor's `data-vio-campaign`). The registry is
7258
7917
  * declared ONCE, debounced, after the first successful bootstrap — and again
7259
- * whenever a new id, display name or campaign appears. A `displayName` /
7918
+ * whenever a new id, display name, campaign or kind appears. A `displayName` /
7260
7919
  * `campaignId` given later for an id already registered replaces the one
7261
- * kept; `undefined` leaves it alone. The returned function unregisters;
7920
+ * kept; `undefined` leaves it alone. The third argument is the campaign id
7921
+ * (as before) or `{ campaignId?, kind? }`, and a fourth `{ kind? }` may
7922
+ * follow a positional campaign — the elements call
7923
+ * `registerSlot(id, name, campaignId, { kind })` so a facade from an older
7924
+ * bundle, which reads only three arguments, still gets the campaign. `kind`
7925
+ * is what kind of element holds the slot (`card` | `carousel` | `detail`);
7926
+ * the FIRST kind an id gets stays (the cards a carousel renders share its
7927
+ * id: the slot is the carousel's). The returned function unregisters;
7262
7928
  * removal never re-declares.
7263
7929
  */
7264
- registerSlot(name, displayName, campaignId) {
7930
+ registerSlot(name, displayName, campaignIdOrOptions, options) {
7265
7931
  const id = normalizeSlotId(name);
7266
7932
  if (!id) return () => {
7267
7933
  };
7268
7934
  const label = typeof displayName === "string" && displayName.trim() ? displayName.trim() : void 0;
7269
- const campaign = toCampaignId(campaignId);
7935
+ const positional = campaignIdOrOptions !== null && typeof campaignIdOrOptions === "object" ? campaignIdOrOptions : { campaignId: campaignIdOrOptions };
7936
+ const merged = { ...positional, ...options ?? {} };
7937
+ const campaign = toCampaignId(merged.campaignId);
7938
+ const kind = normalizeSlotKind(merged.kind);
7270
7939
  const entry = this.slotRegistry.get(id);
7271
7940
  let changed = false;
7272
7941
  if (!entry) {
7273
- this.slotRegistry.set(id, { count: 1, displayName: label, campaignId: campaign });
7942
+ this.slotRegistry.set(id, { count: 1, displayName: label, campaignId: campaign, kind });
7274
7943
  changed = true;
7275
7944
  } else {
7276
7945
  entry.count++;
@@ -7282,6 +7951,10 @@ var VioFacade = class {
7282
7951
  entry.campaignId = campaign;
7283
7952
  changed = true;
7284
7953
  }
7954
+ if (kind !== void 0 && entry.kind === void 0) {
7955
+ entry.kind = kind;
7956
+ changed = true;
7957
+ }
7285
7958
  }
7286
7959
  if (changed) {
7287
7960
  this.slotsDirty = true;
@@ -7372,10 +8045,155 @@ var VioFacade = class {
7372
8045
  const content = this.content();
7373
8046
  if (content.id) out.contentId = content.id;
7374
8047
  if (content.title) out.contentTitle = content.title;
8048
+ if (content.url) out.contentUrl = content.url;
7375
8049
  } catch {
7376
8050
  }
7377
8051
  return out;
7378
8052
  }
8053
+ /**
8054
+ * What the surface answered and is still believed: `standard` / `minimal`
8055
+ * for the configuration; `unknown` for a minute after a timeout or a
8056
+ * failure (then it is asked again). `null` = nothing believed.
8057
+ */
8058
+ believedSurfaceMode() {
8059
+ const answer = this.surfaceMode;
8060
+ if (!answer) return null;
8061
+ if (answer.mode !== "unknown") return answer.mode;
8062
+ return Date.now() - answer.at < PRIVACY_UNKNOWN_TTL_MS ? "unknown" : null;
8063
+ }
8064
+ /**
8065
+ * Ask the surface its privacy mode (`GET /v2/mobile/surface`), once —
8066
+ * started at `init()` and again by `bootstrap()`, never by the payment
8067
+ * path, which only reads what is known (`privacyModeNow`). Resolves what
8068
+ * was learnt within 1.5 s: the mode; `standard` for a service that predates
8069
+ * the endpoint (404) or a surface that names none; `unknown` for a word the
8070
+ * SDK does not know, a timeout or any other failure — believed for a
8071
+ * minute, then asked again. An answer that arrives after the cap still
8072
+ * lands for the next step. Never throws.
8073
+ */
8074
+ preloadPrivacyMode() {
8075
+ try {
8076
+ if (this.surfaceRequest) return this.surfaceRequest;
8077
+ const believed = this.believedSurfaceMode();
8078
+ if (believed) return Promise.resolve(believed);
8079
+ if (!Configuration.isInitialized) return Promise.resolve("unknown");
8080
+ const now = Date.now();
8081
+ if (Configuration.get().privacyMode === "minimal") {
8082
+ this.surfaceMode = { mode: "minimal", at: now };
8083
+ return Promise.resolve("minimal");
8084
+ }
8085
+ const generation = this.generation;
8086
+ const learnt = (mode) => {
8087
+ if (generation !== this.generation) return;
8088
+ if (mode === "unknown" && this.surfaceMode && this.surfaceMode.mode !== "unknown") return;
8089
+ this.surfaceMode = { mode, at: Date.now() };
8090
+ };
8091
+ const asked = this.api.surface().then(
8092
+ (res) => privacyModeOfSurface(res),
8093
+ (err) => err instanceof VioApiError && err.status === 404 ? "standard" : "unknown"
8094
+ );
8095
+ let timer;
8096
+ const capped = new Promise((resolve) => {
8097
+ timer = setTimeout(() => resolve("unknown"), PRIVACY_MODE_TIMEOUT_MS);
8098
+ });
8099
+ const request = Promise.race([asked, capped]).then((mode) => {
8100
+ clearTimeout(timer);
8101
+ learnt(mode);
8102
+ if (this.surfaceRequest === request) this.surfaceRequest = null;
8103
+ return mode;
8104
+ });
8105
+ void asked.then((mode) => {
8106
+ clearTimeout(timer);
8107
+ learnt(mode);
8108
+ });
8109
+ this.surfaceRequest = request;
8110
+ return request;
8111
+ } catch {
8112
+ return Promise.resolve("unknown");
8113
+ }
8114
+ }
8115
+ /**
8116
+ * How much of the visit the order attribution may carry (ADR-0035): the
8117
+ * surface's privacy mode, as known RIGHT NOW, without waiting —
8118
+ * `Vio.init({ privacyMode: 'minimal' })` when the host says so, else what
8119
+ * the bootstrap says of the surface, else what the surface answered to the
8120
+ * ask `init()` started (`preloadPrivacyMode`), else the host's `standard`
8121
+ * once the surface could not tell. `unknown` while nothing is known: then
8122
+ * the page, the slot and the entry stay off what goes to Commerce, because
8123
+ * what can reveal which screen someone was on, and why, is only sent when
8124
+ * the surface is known to allow it. The payment path reads this one.
8125
+ */
8126
+ privacyModeNow() {
8127
+ try {
8128
+ const config = Configuration.isInitialized ? Configuration.get() : void 0;
8129
+ if (config?.privacyMode === "minimal") return "minimal";
8130
+ const fromBootstrap = privacyModeOf(this.cachedBootstrap?.surface?.privacyMode);
8131
+ if (fromBootstrap) return fromBootstrap;
8132
+ if (!config) return "unknown";
8133
+ const believed = this.believedSurfaceMode();
8134
+ if (believed === "standard" || believed === "minimal") return believed;
8135
+ if (!believed) this.preloadPrivacyMode();
8136
+ return believed === "unknown" ? config.privacyMode ?? "unknown" : "unknown";
8137
+ } catch {
8138
+ return "unknown";
8139
+ }
8140
+ }
8141
+ /**
8142
+ * `privacyModeNow`, but waiting for the ask in flight when there is one
8143
+ * (at most its 1.5 s cap) — for the cart's lines, which are not the
8144
+ * payment path. Never throws.
8145
+ */
8146
+ async privacyMode() {
8147
+ try {
8148
+ const now = this.privacyModeNow();
8149
+ if (now !== "unknown") return now;
8150
+ if (!this.surfaceRequest) return "unknown";
8151
+ const asked = await this.surfaceRequest;
8152
+ if (asked !== "unknown") return asked;
8153
+ const config = Configuration.isInitialized ? Configuration.get() : void 0;
8154
+ return config?.privacyMode ?? "unknown";
8155
+ } catch {
8156
+ return "unknown";
8157
+ }
8158
+ }
8159
+ /**
8160
+ * The attribution of the order a sponsor's cart is about to become
8161
+ * (ADR-0035 phase B) — what `CreateCheckout` / `UpdateCheckout` send as
8162
+ * `attribution` and shopcart writes on the order: the surface (its SDK
8163
+ * key), the page's campaign (`pageCampaign()`; without one, the campaign
8164
+ * that explains most units of the cart under the same rule `purchase` is
8165
+ * split by — `cartSlices`) and, on a surface KNOWN to be `standard`
8166
+ * (`privacyModeNow`), the page (`content()`'s url as origin + path, and
8167
+ * title) and where the session came in: the landing the session REMEMBERS
8168
+ * (`storedLanding`, else the one analytics resolved on this page) — never
8169
+ * the page the shopper pays on taken for a landing. Synchronous: nothing
8170
+ * on the way to a payment waits for it. snake_case, capped to Commerce's
8171
+ * limits. `undefined` when there is nothing to say; never throws.
8172
+ */
8173
+ orderAttribution(sponsorId) {
8174
+ try {
8175
+ const config = Configuration.isInitialized ? Configuration.get() : void 0;
8176
+ const content = this.content();
8177
+ return checkoutAttributionOf({
8178
+ surfaceKey: config?.apiKey,
8179
+ pageCampaignId: toNumber(this.pageCampaign()?.id),
8180
+ slices: this.cartSlices(sponsorId),
8181
+ content: { url: content.url, title: content.title },
8182
+ landing: this.rememberedLanding(),
8183
+ privacyMode: this.privacyModeNow()
8184
+ });
8185
+ } catch {
8186
+ return void 0;
8187
+ }
8188
+ }
8189
+ /** The landing the session remembers: what the first page stored, else what analytics resolved on this page load (consent pending); null otherwise. */
8190
+ rememberedLanding() {
8191
+ try {
8192
+ return storedLanding() ?? this.analyticsManager?.currentLanding ?? null;
8193
+ } catch {
8194
+ return null;
8195
+ }
8196
+ }
7379
8197
  /**
7380
8198
  * The cart split per campaign — the SAME rule `begin_checkout` / `purchase`
7381
8199
  * use, for hosts with their own analytics sink (Vev). Every unit of every
@@ -7456,10 +8274,11 @@ var VioFacade = class {
7456
8274
  this.slotDeclareTimer = null;
7457
8275
  this.slotsDirty = false;
7458
8276
  void this.declareSlots(
7459
- [...this.slotRegistry.entries()].map(([id, { displayName, campaignId }]) => ({
8277
+ [...this.slotRegistry.entries()].map(([id, { displayName, campaignId, kind }]) => ({
7460
8278
  id,
7461
8279
  ...displayName ? { displayName } : {},
7462
- ...campaignId !== void 0 ? { campaignId } : {}
8280
+ ...campaignId !== void 0 ? { campaignId } : {},
8281
+ ...kind ? { kind } : {}
7463
8282
  }))
7464
8283
  );
7465
8284
  }, SLOT_DECLARATION_DEBOUNCE_MS);
@@ -7539,6 +8358,6 @@ function vippsButtonAttributes(verb = "buy", opts = {}) {
7539
8358
  };
7540
8359
  }
7541
8360
 
7542
- export { ADD_ITEM_MUTATION, ADYEN_RETURN_METHOD, ADYEN_RETURN_QUERY_KEYS, AdyenOriginError, 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, KUSTOM_RETURN_QUERY_KEYS, SDK_VERSION, UPDATE_CHECKOUT_MUTATION, UPDATE_ITEM_MUTATION, UPDATE_SHIPPINGS_BY_SUPPLIER_MUTATION, VIPPS_BUTTON_SCRIPT, VIPPS_BUTTON_TAG, Vio, VioApi, VioApiError, __decorateClass, activeCampaignEntry, addItem, adyenReturnOutcome, adyenReturnParams, autoSlotDisplayName, autoSlotId, campaignCandidates, cartSlicesFrom, checkApplePayAvailability, createCart, createCheckout, createCommerceClient, createPaymentStripe, createPaymentVipps, createVioApi, deleteItem, displayPrice, ensureVippsButton, executeCartGraphQL, formatPrice, getAvailablePaymentMethods, getCart, getCartGraphQLOptions, getCheckout, getCustomerSessionId, getGlobalCurrency, getLineItemsBySupplier, getVippsStatus, kustomReturnOutcome, lineOrigins, nexiReturnPaymentId, normalizeOrigin, normalizeScope, normalizeSlotId, primaryImageUrl, purchaseFromPaymentSuccess, readNexiPending, resolveCampaignId, resolveContent, resolveSlotCampaign, sameOrigin, showApplePaySheet, sponsorCampaignEntry, toCampaignId, updateCheckout, updateItem, updateShippingsBySupplier, vippsButtonAttributes };
7543
- //# sourceMappingURL=chunk-AXJKB2CW.js.map
7544
- //# sourceMappingURL=chunk-AXJKB2CW.js.map
8361
+ export { ADD_ITEM_MUTATION, ADYEN_RETURN_METHOD, ADYEN_RETURN_QUERY_KEYS, AdyenOriginError, AnalyticsManager, BUYER_REF_MAX_LENGTH, CHECKOUT_ATTRIBUTION_CAPS, CLIENT_LANDING_URL_MAX_LENGTH, CONTENT_URL_MAX_LENGTH, CREATE_CART_MUTATION, CREATE_CHECKOUT_MUTATION, CREATE_CHECKOUT_WITH_ATTRIBUTION_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, GRAPHQL_INT_MAX, KUSTOM_RETURN_QUERY_KEYS, LINE_ORIGIN_CAPS, SDK_VERSION, UPDATE_CHECKOUT_MUTATION, UPDATE_CHECKOUT_WITH_ATTRIBUTION_MUTATION, UPDATE_ITEM_MUTATION, UPDATE_ITEM_WITH_ORIGIN_MUTATION, UPDATE_SHIPPINGS_BY_SUPPLIER_MUTATION, VIPPS_BUTTON_SCRIPT, VIPPS_BUTTON_TAG, Vio, VioApi, VioApiError, __decorateClass, activeCampaignEntry, addItem, adyenReturnOutcome, adyenReturnParams, autoSlotDisplayName, autoSlotId, buyerRefOf, campaignCandidates, capContentUrl, cartSlicesFrom, checkApplePayAvailability, checkoutAttributionOf, componentKindOf, createCart, createCheckout, createCommerceClient, createPaymentStripe, createPaymentVipps, createVioApi, deleteItem, displayPrice, dominantCampaignOf, ensureVippsButton, entryContextOf, executeCartGraphQL, formatPrice, gatewayHas, gatewayHasBuyerRef, getAvailablePaymentMethods, getCart, getCartGraphQLOptions, getCheckout, getCheckoutReference, getCustomerSessionId, getGlobalCurrency, getLineItemsBySupplier, getVippsStatus, isUnknownFeatureError, isUnknownTokenError, kustomReturnOutcome, landingUrl, lineOriginWire, lineOrigins, nexiReturnPaymentId, normalizeBuyerRef, normalizeOrigin, normalizeScope, normalizeSlotId, normalizeSlotKind, originAndPath, primaryImageUrl, purchaseFromPaymentSuccess, readNexiPending, resetGatewayFeatures, resolveCampaignId, resolveClientContext, resolveContent, resolveLanding, resolveSlotCampaign, sameOrigin, showApplePaySheet, sponsorCampaignEntry, storedLanding, toCampaignId, updateCheckout, updateItem, updateShippingsBySupplier, vippsButtonAttributes, withGatewayFeature };
8362
+ //# sourceMappingURL=chunk-5KWICUME.js.map
8363
+ //# sourceMappingURL=chunk-5KWICUME.js.map