@vio-live/web-sdk 0.23.0 → 0.23.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.
@@ -1659,6 +1659,18 @@ var CartManager = class extends EventTarget {
1659
1659
  await Promise.allSettled([...set]);
1660
1660
  }
1661
1661
  }
1662
+ /**
1663
+ * True while an AddItem/UpdateItem/DeleteItem of this sponsor OTHER than
1664
+ * `except` has not settled. A mutation applying its own answer passes
1665
+ * itself: its response is current for what IT sent — only the others make
1666
+ * it stale.
1667
+ */
1668
+ hasMutationsInFlight(sponsorId, except) {
1669
+ const set = this.inFlightMutations.get(sponsorId);
1670
+ if (!set || set.size === 0) return false;
1671
+ for (const m of set) if (m !== except) return true;
1672
+ return false;
1673
+ }
1662
1674
  /** Register a backend mutation so `waitForCartMutations` can await it. */
1663
1675
  trackMutation(sponsorId, mutation) {
1664
1676
  let set = this.inFlightMutations.get(sponsorId);
@@ -1687,10 +1699,10 @@ var CartManager = class extends EventTarget {
1687
1699
  }
1688
1700
  /** Apply a backend response only if no newer response was applied already.
1689
1701
  * Returns false when it was discarded as stale. */
1690
- applyBackendCart(sponsorId, backendCart, seq) {
1702
+ applyBackendCart(sponsorId, backendCart, seq, settling) {
1691
1703
  if (seq < (this.appliedSeq.get(sponsorId) ?? 0)) return false;
1692
1704
  this.appliedSeq.set(sponsorId, seq);
1693
- this.updateFromBackendCart(sponsorId, backendCart);
1705
+ this.updateFromBackendCart(sponsorId, backendCart, { settling });
1694
1706
  return true;
1695
1707
  }
1696
1708
  /** The final answer of a line's sync lost the per-sponsor seq race (another
@@ -1698,7 +1710,7 @@ var CartManager = class extends EventTarget {
1698
1710
  * is the latest word. Copy its quantity (and price) so a server-side clamp
1699
1711
  * — stock — doesn't leave the optimistic value behind for good, with the
1700
1712
  * checkout / Apple Pay total built on it. */
1701
- reconcileLineFromResponse(sponsorId, cartItemId, resCart) {
1713
+ reconcileLineFromResponse(sponsorId, cartItemId, resCart, settling) {
1702
1714
  const cart = this.cartsBySponsor.get(sponsorId);
1703
1715
  const line = cart?.items.find((i) => i.cartItemId === cartItemId || i.id === cartItemId);
1704
1716
  const server = resCart.line_items?.find((li) => li.id === cartItemId);
@@ -1707,7 +1719,9 @@ var CartManager = class extends EventTarget {
1707
1719
  let changed = false;
1708
1720
  if (server.quantity !== line.quantity) {
1709
1721
  line.quantity = server.quantity;
1710
- line.origins = reconciledOrigins(line.origins, server.quantity);
1722
+ if (!this.hasMutationsInFlight(sponsorId, settling)) {
1723
+ line.origins = reconciledOrigins(line.origins, server.quantity);
1724
+ }
1711
1725
  changed = true;
1712
1726
  }
1713
1727
  if (price != null && price !== line.unitPrice) {
@@ -1783,8 +1797,12 @@ var CartManager = class extends EventTarget {
1783
1797
  this.cartIdPromises.set(sponsorId, creation);
1784
1798
  return creation;
1785
1799
  }
1786
- /** Overwrite local cart state with the backend cart response (source of truth). */
1787
- updateFromBackendCart(sponsorId, backendCart) {
1800
+ /**
1801
+ * Overwrite local cart state with the backend cart response (source of
1802
+ * truth). `opts.settling` is the mutation whose answer this is: it does not
1803
+ * count as "in flight" for the origins (see `hasMutationsInFlight`).
1804
+ */
1805
+ updateFromBackendCart(sponsorId, backendCart, opts = {}) {
1788
1806
  const cart = this.cartsBySponsor.get(sponsorId);
1789
1807
  if (!cart) return;
1790
1808
  if (backendCart.cart_id) cart.cartId = backendCart.cart_id;
@@ -1840,9 +1858,12 @@ var CartManager = class extends EventTarget {
1840
1858
  currency: li.price?.currency_code || backendCart.currency || matchedLocal?.currency || "NOK",
1841
1859
  imageUrl,
1842
1860
  quantity: intended ?? li.quantity,
1843
- // The server knows nothing about origins: keep the local ones,
1844
- // squared with the quantity it answered.
1845
- origins: matchedLocal ? reconciledOrigins(matchedLocal.origins, intended ?? li.quantity) : void 0,
1861
+ // The server knows nothing about origins: keep the local ones. They
1862
+ // are squared with its quantity (a stock clamp) only when no OTHER
1863
+ // mutation is in flight for this sponsor — an AddItem answering
1864
+ // while another is pending reports a quantity that is already old
1865
+ // (see `lineOrigins`).
1866
+ origins: matchedLocal ? this.hasMutationsInFlight(sponsorId, opts.settling) ? matchedLocal.origins : reconciledOrigins(matchedLocal.origins, intended ?? li.quantity) : void 0,
1846
1867
  shipping: li.shipping,
1847
1868
  availableShippings: li.available_shippings
1848
1869
  };
@@ -1946,6 +1967,7 @@ var CartManager = class extends EventTarget {
1946
1967
  const addedItem = existing ?? cart.items[cart.items.length - 1];
1947
1968
  const addedLineId = addedItem.id;
1948
1969
  const seq = this.nextSeq(opts.sponsorId);
1970
+ let settling;
1949
1971
  const mutation = (async () => {
1950
1972
  try {
1951
1973
  const cartId = await this.ensureCartId(opts.sponsorId, opts.currency || void 0);
@@ -1960,7 +1982,7 @@ var CartManager = class extends EventTarget {
1960
1982
  ];
1961
1983
  const resCart = await addItem(cartId, lineItemsInput, gqlOpts);
1962
1984
  if (resCart && Array.isArray(resCart.line_items)) {
1963
- this.applyBackendCart(opts.sponsorId, resCart, seq);
1985
+ this.applyBackendCart(opts.sponsorId, resCart, seq, settling);
1964
1986
  }
1965
1987
  }
1966
1988
  } catch (err) {
@@ -1968,7 +1990,7 @@ var CartManager = class extends EventTarget {
1968
1990
  const line = c?.items.find((i) => i.id === addedLineId);
1969
1991
  if (c && line) {
1970
1992
  line.quantity -= qty;
1971
- removeOriginQuantity(line, qty);
1993
+ removeFromOrigin(line, opts.origin, qty);
1972
1994
  if (line.quantity <= 0) c.items = c.items.filter((i) => i !== line);
1973
1995
  if (c.items.length === 0) this.cartsBySponsor.delete(opts.sponsorId);
1974
1996
  this.persist();
@@ -1977,6 +1999,7 @@ var CartManager = class extends EventTarget {
1977
1999
  this.emitError("AddItem rejected by the backend \u2014 item removed from the cart", err);
1978
2000
  }
1979
2001
  })();
2002
+ settling = mutation;
1980
2003
  this.trackMutation(opts.sponsorId, mutation);
1981
2004
  return addedItem;
1982
2005
  }
@@ -2061,6 +2084,7 @@ var CartManager = class extends EventTarget {
2061
2084
  }
2062
2085
  const sync = { sent: target };
2063
2086
  this.qtySyncs.set(key, sync);
2087
+ let settling;
2064
2088
  const run = (async () => {
2065
2089
  let next = target;
2066
2090
  let removalRetried = false;
@@ -2075,9 +2099,9 @@ var CartManager = class extends EventTarget {
2075
2099
  if (next === sync.sent) next = void 0;
2076
2100
  if (next === void 0) this.qtySyncs.delete(key);
2077
2101
  if (resCart && Array.isArray(resCart.line_items)) {
2078
- const applied = this.applyBackendCart(sponsorId, resCart, seq);
2102
+ const applied = this.applyBackendCart(sponsorId, resCart, seq, settling);
2079
2103
  if (!applied && next === void 0) {
2080
- this.reconcileLineFromResponse(sponsorId, cartItemId, resCart);
2104
+ this.reconcileLineFromResponse(sponsorId, cartItemId, resCart, settling);
2081
2105
  }
2082
2106
  }
2083
2107
  } catch (err) {
@@ -2100,6 +2124,7 @@ var CartManager = class extends EventTarget {
2100
2124
  }
2101
2125
  }
2102
2126
  })();
2127
+ settling = run;
2103
2128
  this.trackMutation(sponsorId, run);
2104
2129
  }
2105
2130
  removeItem(lineItemId, sponsorId) {
@@ -2245,6 +2270,22 @@ function addOriginQuantity(line, origin, qty) {
2245
2270
  }
2246
2271
  line.origins = origins;
2247
2272
  }
2273
+ function removeFromOrigin(line, origin, qty) {
2274
+ if (!(qty > 0) || !Array.isArray(line.origins)) return;
2275
+ let left = qty;
2276
+ if (origin) {
2277
+ const target = normalizeOrigin(origin);
2278
+ const index = line.origins.findIndex((o) => sameOrigin(o, target));
2279
+ if (index >= 0) {
2280
+ const found = line.origins[index];
2281
+ const take = Math.min(found.quantity, left);
2282
+ found.quantity -= take;
2283
+ left -= take;
2284
+ if (found.quantity <= 0) line.origins.splice(index, 1);
2285
+ }
2286
+ }
2287
+ if (left > 0) removeOriginQuantity(line, left);
2288
+ }
2248
2289
  function removeOriginQuantity(line, qty) {
2249
2290
  if (!(qty > 0) || !Array.isArray(line.origins)) return;
2250
2291
  let left = qty;
@@ -2270,8 +2311,11 @@ function lineOrigins(line) {
2270
2311
  const known = (Array.isArray(line.origins) ? line.origins : []).filter((o) => o && o.quantity > 0);
2271
2312
  const total = known.reduce((sum, o) => sum + o.quantity, 0);
2272
2313
  const quantity = line.quantity > 0 ? line.quantity : 0;
2273
- if (total >= quantity) return known.map((o) => ({ ...o }));
2274
- return [...known.map((o) => ({ ...o })), { quantity: quantity - total }];
2314
+ if (total === quantity) return known.map((o) => ({ ...o }));
2315
+ if (total < quantity) return [...known.map((o) => ({ ...o })), { quantity: quantity - total }];
2316
+ const copy = { origins: known.map((o) => ({ ...o })) };
2317
+ removeOriginQuantity(copy, total - quantity);
2318
+ return copy.origins;
2275
2319
  }
2276
2320
 
2277
2321
  // src/core/checkout/payments/apple-pay.ts
@@ -2470,7 +2514,7 @@ async function prepareApplePay(config) {
2470
2514
  }
2471
2515
 
2472
2516
  // src/core/version.ts
2473
- var SDK_VERSION = "0.23.0";
2517
+ var SDK_VERSION = "0.23.1";
2474
2518
 
2475
2519
  // src/core/checkout/payments/stripe-embedded.ts
2476
2520
  var CREATE_PAYMENT_INTENT_STRIPE_MUTATION = `
@@ -5847,19 +5891,24 @@ function toSmallestUnit(amount, currency) {
5847
5891
  // src/core/content.ts
5848
5892
  var CONTENT_ID_QUERY = "vio_content";
5849
5893
  var CONTENT_TITLE_QUERY = "vio_content_title";
5894
+ var CONTENT_ID_MAX_LENGTH = 128;
5895
+ var CONTENT_TITLE_MAX_LENGTH = 255;
5850
5896
  function resolveContent(init, doc = typeof document !== "undefined" ? document : null, loc = typeof location !== "undefined" ? location : null) {
5851
5897
  try {
5852
5898
  const query = parseQuery(loc?.search);
5853
- const id = text(init?.contentId) ?? text(query.get(CONTENT_ID_QUERY)) ?? text(meta(doc, "name", "vio:content-id")) ?? stripQueryAndHash(text(attr(doc, 'link[rel="canonical"]', "href")) ?? text(meta(doc, "property", "og:url"))) ?? text(loc?.pathname);
5899
+ 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));
5854
5900
  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);
5855
5901
  const out = {};
5856
5902
  if (id !== void 0) out.id = id;
5857
- if (title !== void 0) out.title = title.slice(0, 255);
5903
+ if (title !== void 0) out.title = title.slice(0, CONTENT_TITLE_MAX_LENGTH);
5858
5904
  return out;
5859
5905
  } catch {
5860
5906
  return {};
5861
5907
  }
5862
5908
  }
5909
+ function fits(value) {
5910
+ return value !== void 0 && value.length <= CONTENT_ID_MAX_LENGTH ? value : void 0;
5911
+ }
5863
5912
  function text(value) {
5864
5913
  if (value === void 0 || value === null) return void 0;
5865
5914
  const s = String(value).trim();
@@ -5895,6 +5944,9 @@ function stripQueryAndHash(url) {
5895
5944
  // src/core/slots.ts
5896
5945
  var SLOT_ID_MAX_LENGTH = 100;
5897
5946
  var SLOT_DECLARATION_TTL_MS = 24 * 60 * 60 * 1e3;
5947
+ var SLOT_DECLARATION_FAILURE_TTL_MS = 60 * 60 * 1e3;
5948
+ var MAX_LOCATIONS_PER_DECLARATION = 100;
5949
+ var MAX_DECLARATIONS_PER_LOAD = 3;
5898
5950
  function resolveSlotCampaign(boot, slot, now = Date.now(), platform) {
5899
5951
  if (!boot) return null;
5900
5952
  return findSlotAssignment(boot, slot, now, platform) ?? defaultCampaignEntry(boot);
@@ -5962,7 +6014,7 @@ function toNumber(value) {
5962
6014
  }
5963
6015
  function normalizeSlotId(value) {
5964
6016
  if (typeof value !== "string") return "";
5965
- return value.trim().slice(0, SLOT_ID_MAX_LENGTH);
6017
+ return value.trim().slice(0, SLOT_ID_MAX_LENGTH).trim();
5966
6018
  }
5967
6019
  function normalizeSlotLocations(slots) {
5968
6020
  const out = [];
@@ -5978,8 +6030,11 @@ function normalizeSlotLocations(slots) {
5978
6030
  return out;
5979
6031
  }
5980
6032
  function autoSlotId(kind, ordinal, scope) {
5981
- const prefix = normalizeScope(scope);
5982
- return `${prefix ? `${prefix}-` : ""}${kind}-${ordinal + 1}`;
6033
+ const tail = `${kind}-${ordinal + 1}`;
6034
+ let prefix = normalizeScope(scope);
6035
+ const room = SLOT_ID_MAX_LENGTH - tail.length - 1;
6036
+ if (prefix.length > room) prefix = prefix.slice(0, Math.max(0, room)).replace(/-+$/, "");
6037
+ return `${prefix ? `${prefix}-` : ""}${tail}`;
5983
6038
  }
5984
6039
  function autoSlotDisplayName(kind, ordinal, scopeName) {
5985
6040
  const label = `${kind.charAt(0).toUpperCase()}${kind.slice(1)} ${ordinal + 1}`;
@@ -5990,8 +6045,24 @@ function normalizeScope(scope) {
5990
6045
  if (typeof scope !== "string") return "";
5991
6046
  return scope.trim().replace(/\s+/g, "-");
5992
6047
  }
5993
- function slotDeclarationKeys(locations) {
5994
- return locations.map((l) => l.displayName ? `${l.id}${l.displayName}` : l.id).sort();
6048
+ var SLOT_DECLARATION_MEMORY_CAP = 500;
6049
+ function slotDeclarationKey(location2) {
6050
+ return location2.displayName ? `${location2.id}${location2.displayName}` : location2.id;
6051
+ }
6052
+ function pendingSlotKeys(memory, keys, now = Date.now(), ttl = SLOT_DECLARATION_TTL_MS) {
6053
+ return keys.filter((key) => {
6054
+ const at = memory?.[key];
6055
+ if (typeof at !== "number" || !Number.isFinite(at)) return true;
6056
+ return now - at >= ttl || now < at;
6057
+ });
6058
+ }
6059
+ function rememberSlotKeys(memory, keys, at, cap = SLOT_DECLARATION_MEMORY_CAP) {
6060
+ const next = { ...memory ?? {} };
6061
+ for (const key of keys) next[key] = at;
6062
+ const entries = Object.entries(next);
6063
+ if (entries.length <= cap) return next;
6064
+ entries.sort((a, b) => b[1] - a[1]);
6065
+ return Object.fromEntries(entries.slice(0, cap));
5995
6066
  }
5996
6067
  function slotDeclarationStorageKey(platform, apiKey) {
5997
6068
  return `vio.slots.${platform}.${fnv1a(apiKey)}`;
@@ -6005,31 +6076,95 @@ function fnv1a(input) {
6005
6076
  return hash.toString(16).padStart(8, "0");
6006
6077
  }
6007
6078
  function readSlotDeclaration(storageKey) {
6008
- if (typeof localStorage === "undefined") return null;
6009
6079
  try {
6080
+ if (typeof localStorage === "undefined") return {};
6010
6081
  const raw = localStorage.getItem(storageKey);
6011
- if (!raw) return null;
6082
+ if (!raw) return {};
6012
6083
  const parsed = JSON.parse(raw);
6013
- if (!parsed || !Array.isArray(parsed.keys) || typeof parsed.at !== "number") return null;
6014
- return { keys: parsed.keys.filter((x) => typeof x === "string"), at: parsed.at };
6084
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
6085
+ if (Array.isArray(parsed.keys)) return {};
6086
+ const out = {};
6087
+ for (const [key, at] of Object.entries(parsed)) {
6088
+ if (typeof at === "number" && Number.isFinite(at)) out[key] = at;
6089
+ }
6090
+ return out;
6015
6091
  } catch {
6016
- return null;
6092
+ return {};
6017
6093
  }
6018
6094
  }
6019
6095
  function writeSlotDeclaration(storageKey, memory) {
6020
- if (typeof localStorage === "undefined") return;
6021
6096
  try {
6097
+ if (typeof localStorage === "undefined") return;
6022
6098
  localStorage.setItem(storageKey, JSON.stringify(memory));
6023
6099
  } catch {
6024
6100
  }
6025
6101
  }
6026
- function isSlotDeclarationFresh(memory, keys, now = Date.now()) {
6027
- if (!memory) return false;
6028
- if (now - memory.at >= SLOT_DECLARATION_TTL_MS || now < memory.at) return false;
6029
- if (memory.keys.length !== keys.length) return false;
6030
- const remembered = [...memory.keys].sort();
6031
- const wanted = [...keys].sort();
6032
- return remembered.every((key, i) => key === wanted[i]);
6102
+
6103
+ // src/core/cart/slices.ts
6104
+ function cartSlicesFrom(lines, resolveCampaign, fallbackCurrency) {
6105
+ if (!Array.isArray(lines) || lines.length === 0) return [];
6106
+ const slices = /* @__PURE__ */ new Map();
6107
+ for (const line of lines) {
6108
+ if (!line || typeof line !== "object") continue;
6109
+ const sponsorId = Number(line.sponsorId);
6110
+ const unitPrice = Number(line.unitPrice) || 0;
6111
+ for (const origin of lineOrigins(line)) {
6112
+ let campaignId = origin.campaignId;
6113
+ if (campaignId === void 0) {
6114
+ try {
6115
+ campaignId = resolveCampaign(origin.slot, sponsorId);
6116
+ } catch {
6117
+ campaignId = void 0;
6118
+ }
6119
+ }
6120
+ const key = campaignId === void 0 ? "default" : String(campaignId);
6121
+ let slice = slices.get(key);
6122
+ if (!slice) {
6123
+ slice = {
6124
+ campaignId,
6125
+ sponsors: /* @__PURE__ */ new Set(),
6126
+ slots: /* @__PURE__ */ new Set(),
6127
+ contents: /* @__PURE__ */ new Set(),
6128
+ items: /* @__PURE__ */ new Map(),
6129
+ value: 0,
6130
+ currency: line.currency || fallbackCurrency
6131
+ };
6132
+ slices.set(key, slice);
6133
+ }
6134
+ if (Number.isFinite(sponsorId)) slice.sponsors.add(sponsorId);
6135
+ if (origin.slot) slice.slots.add(origin.slot);
6136
+ if (origin.contentId) slice.contents.add(origin.contentId);
6137
+ const itemKey = `${line.productId}::${line.variantId ?? ""}`;
6138
+ const item = slice.items.get(itemKey);
6139
+ if (item) item.quantity = (item.quantity ?? 0) + origin.quantity;
6140
+ else {
6141
+ slice.items.set(itemKey, {
6142
+ productId: line.productId,
6143
+ name: line.name,
6144
+ brand: line.brand,
6145
+ variantId: line.variantId,
6146
+ price: unitPrice,
6147
+ quantity: origin.quantity
6148
+ });
6149
+ }
6150
+ slice.value += unitPrice * origin.quantity;
6151
+ }
6152
+ }
6153
+ const list = [...slices.values()];
6154
+ return list.map((s, i) => ({
6155
+ campaignId: s.campaignId,
6156
+ sponsorId: s.sponsors.size === 1 ? [...s.sponsors][0] : void 0,
6157
+ slot: s.slots.size === 1 ? [...s.slots][0] : void 0,
6158
+ contentId: s.contents.size === 1 ? [...s.contents][0] : void 0,
6159
+ items: [...s.items.values()],
6160
+ value: Math.round(s.value * 100) / 100,
6161
+ currency: s.currency,
6162
+ slice: i + 1,
6163
+ slices: list.length
6164
+ }));
6165
+ }
6166
+ function hasExplicitCampaign(lines) {
6167
+ return lines.some((line) => lineOrigins(line).some((o) => o.campaignId !== void 0));
6033
6168
  }
6034
6169
 
6035
6170
  // src/core/analytics/analytics-manager.ts
@@ -6229,25 +6364,34 @@ var AnalyticsManager = class extends EventTarget {
6229
6364
  if (!anonId) return null;
6230
6365
  const boot = vioBootstrapCache();
6231
6366
  const ctx = opts.context ?? {};
6232
- const content = ctx.contentId === void 0 || ctx.contentTitle === void 0 ? vioContent() : {};
6367
+ const explicitContentId = fitsId(ctx.contentId);
6368
+ const content = explicitContentId === void 0 || ctx.contentTitle === void 0 ? vioContent() : {};
6233
6369
  const context = compact({
6234
6370
  campaign_id: resolveCampaignId(ctx, boot),
6235
6371
  broadcast_id: ctx.broadcastId,
6236
6372
  campaign_component_id: ctx.campaignComponentId,
6237
6373
  app_placement_id: ctx.appPlacementId,
6238
- // A slot is a location id to the campaigns service (ADR-0027).
6239
- location_id: ctx.locationId ?? ctx.slot,
6374
+ // A slot is a location id to the campaigns service (ADR-0027). An
6375
+ // explicit location id that does not fit gives way to the slot.
6376
+ location_id: fitsId(ctx.locationId) ?? fitsId(ctx.slot),
6240
6377
  component_template_id: ctx.componentTemplateId,
6241
6378
  sponsor_id: ctx.sponsorId,
6242
6379
  activation_id: ctx.activationId,
6243
- content_url: ctx.contentUrl ?? (typeof location !== "undefined" ? location.href : void 0),
6380
+ // Cut to the collector's 2 048 — the query string first, since a long
6381
+ // one is usually the tracking tail, not the page. The other host-given
6382
+ // fields (broadcastId, componentTemplateId, errorCode, variant, …) are
6383
+ // the host's responsibility: the SDK does not touch them.
6384
+ content_url: capUrl(ctx.contentUrl ?? (typeof location !== "undefined" ? location.href : void 0)),
6244
6385
  // The host's own content id — its join key back onto its reporting.
6245
- // Explicit on the event, else what the SDK resolved for the page.
6246
- content_id: ctx.contentId ?? content.id,
6386
+ // Explicit on the event when it fits, else what the SDK resolved for
6387
+ // the page (already capped per source), else nothing.
6388
+ content_id: explicitContentId ?? fitsId(content.id),
6247
6389
  // Deletion-proof reporting: sources have no trash bin — the title
6248
6390
  // snapshotted at event time keeps reports legible if content dies.
6249
6391
  error_code: ctx.errorCode,
6250
- content_title: ctx.contentTitle ?? content.title ?? (typeof document !== "undefined" && document.title ? document.title.slice(0, 255) : void 0),
6392
+ content_title: capTitle(
6393
+ ctx.contentTitle ?? content.title ?? (typeof document !== "undefined" && document.title ? document.title : void 0)
6394
+ ),
6251
6395
  variant: ctx.variant
6252
6396
  });
6253
6397
  const com = opts.commerce;
@@ -6395,7 +6539,7 @@ var AnalyticsManager = class extends EventTarget {
6395
6539
  const d = ev.detail ?? {};
6396
6540
  const line = this.findLine(d.productId, d.variantId);
6397
6541
  this.track("add_to_cart", {
6398
- context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0, slot: slotOf(d), ...contentOf(d) },
6542
+ context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0, campaignId: campaignOf(d), slot: slotOf(d), ...contentOf(d) },
6399
6543
  commerce: {
6400
6544
  items: [
6401
6545
  {
@@ -6416,7 +6560,7 @@ var AnalyticsManager = class extends EventTarget {
6416
6560
  const d = ev.detail ?? {};
6417
6561
  if (d.productId === void 0) return;
6418
6562
  this.track("remove_from_cart", {
6419
- context: { sponsorId: d.sponsorId, slot: slotOf(d), ...contentOf(d) },
6563
+ context: { sponsorId: d.sponsorId, campaignId: campaignOf(d), slot: slotOf(d), ...contentOf(d) },
6420
6564
  commerce: {
6421
6565
  items: [
6422
6566
  {
@@ -6445,7 +6589,7 @@ var AnalyticsManager = class extends EventTarget {
6445
6589
  if (d.productId === void 0) return;
6446
6590
  const price = Number(d.price);
6447
6591
  this.track("select_item", {
6448
- context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0, slot: slotOf(d), ...contentOf(d) },
6592
+ context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0, campaignId: campaignOf(d), slot: slotOf(d), ...contentOf(d) },
6449
6593
  commerce: {
6450
6594
  items: [
6451
6595
  {
@@ -6464,38 +6608,42 @@ var AnalyticsManager = class extends EventTarget {
6464
6608
  });
6465
6609
  on("vio:checkout-open", (ev) => {
6466
6610
  const d = ev.detail ?? {};
6467
- const slices = this.cartSlices(d.sponsorId);
6611
+ const base = { sponsorId: d.sponsorId, campaignId: campaignOf(d), slot: slotOf(d), ...contentOf(d) };
6612
+ const { slices, explicit } = this.cartSlices(d.sponsorId);
6468
6613
  if (slices.length > 1) {
6469
6614
  for (const slice of slices) {
6470
6615
  this.track("begin_checkout", {
6471
- context: this.sliceContext(slice, { sponsorId: d.sponsorId, slot: slotOf(d), ...contentOf(d) }),
6472
- commerce: slice.commerce
6616
+ context: this.sliceContext(slice, base),
6617
+ commerce: sliceCommerce(slice),
6618
+ props: { slice: slice.slice, slices: slice.slices }
6473
6619
  });
6474
6620
  }
6475
6621
  return;
6476
6622
  }
6477
6623
  const snap = this.cartCommerce(d.sponsorId);
6478
6624
  this.track("begin_checkout", {
6479
- context: { sponsorId: d.sponsorId, slot: slotOf(d), ...contentOf(d) },
6625
+ context: this.singleSliceContext(slices[0], explicit, base),
6480
6626
  ...snap ? { commerce: snap } : {}
6481
6627
  });
6482
6628
  });
6483
6629
  on("vio:payment-success", (ev) => {
6484
6630
  const d = ev.detail ?? {};
6485
6631
  const sponsorId = d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0;
6486
- const slices = this.cartSlices(sponsorId);
6632
+ const base = { sponsorId, campaignId: campaignOf(d), slot: slotOf(d), ...contentOf(d) };
6633
+ const { slices, explicit } = this.cartSlices(sponsorId);
6487
6634
  if (slices.length > 1) {
6488
6635
  for (const slice of slices) {
6489
6636
  this.track("purchase", {
6490
- context: this.sliceContext(slice, { sponsorId, slot: slotOf(d), ...contentOf(d) }),
6491
- commerce: { ...slice.commerce, ...purchaseFromPaymentSuccess(d), paymentMethod: d.method }
6637
+ context: this.sliceContext(slice, base),
6638
+ commerce: { ...sliceCommerce(slice), ...purchaseFromPaymentSuccess(d), paymentMethod: d.method },
6639
+ props: { slice: slice.slice, slices: slice.slices }
6492
6640
  });
6493
6641
  }
6494
6642
  return;
6495
6643
  }
6496
6644
  const snap = this.cartCommerce(sponsorId);
6497
6645
  this.track("purchase", {
6498
- context: { sponsorId, slot: slotOf(d), ...contentOf(d) },
6646
+ context: this.singleSliceContext(slices[0], explicit, base),
6499
6647
  commerce: {
6500
6648
  // value / currency / items: the cart snapshot, on every path.
6501
6649
  ...snap ?? {},
@@ -6523,67 +6671,50 @@ var AnalyticsManager = class extends EventTarget {
6523
6671
  return items;
6524
6672
  }
6525
6673
  /**
6526
- * The cart split per campaign (ADR-0034): every unit of every line goes to
6527
- * the campaign of its origin — the one the place showed when it was added
6528
- * — and units without one to the campaign the current rule resolves for
6529
- * the line's sponsor. One slice per campaign: its items (quantities from
6530
- * the origins), its value, and the slot / content when they are the same
6531
- * for the whole slice. A cart with one campaign is one slice.
6674
+ * The cart split per campaign — the shared rule of cart/slices.ts over the
6675
+ * current cart (else the pre-clear snapshot), plus whether any unit named
6676
+ * its campaign itself (a cart without origins is reported as before).
6532
6677
  */
6533
6678
  cartSlices(sponsorId) {
6534
6679
  const lines = this.cartLines(sponsorId);
6535
- if (lines.length === 0) return [];
6680
+ if (lines.length === 0) return { slices: [], explicit: false };
6536
6681
  const boot = vioBootstrapCache();
6537
- const slices = /* @__PURE__ */ new Map();
6538
- for (const line of lines) {
6539
- for (const origin of lineOrigins(line)) {
6540
- const campaignId = origin.campaignId ?? resolveCampaignId({ slot: origin.slot, sponsorId: line.sponsorId }, boot);
6541
- const key = campaignId === void 0 ? "default" : String(campaignId);
6542
- let slice = slices.get(key);
6543
- if (!slice) {
6544
- slice = {
6545
- campaignId,
6546
- commerce: { items: [], value: 0, currency: line.currency ?? this.lastCartSnapshot?.currency },
6547
- slots: /* @__PURE__ */ new Set(),
6548
- contents: /* @__PURE__ */ new Set(),
6549
- lines: /* @__PURE__ */ new Map()
6550
- };
6551
- slices.set(key, slice);
6552
- }
6553
- if (origin.slot) slice.slots.add(origin.slot);
6554
- if (origin.contentId) slice.contents.add(origin.contentId);
6555
- const itemKey = `${line.productId}::${line.variantId ?? ""}`;
6556
- const item = slice.lines.get(itemKey);
6557
- if (item) item.quantity = (item.quantity ?? 0) + origin.quantity;
6558
- else {
6559
- const fresh = {
6560
- productId: line.productId,
6561
- name: line.name,
6562
- brand: line.brand,
6563
- variantId: line.variantId,
6564
- price: line.unitPrice,
6565
- quantity: origin.quantity
6566
- };
6567
- slice.lines.set(itemKey, fresh);
6568
- slice.commerce.items.push(fresh);
6569
- }
6570
- slice.commerce.value = (slice.commerce.value ?? 0) + line.unitPrice * origin.quantity;
6571
- }
6572
- }
6573
- return [...slices.values()].map((s) => ({
6574
- campaignId: s.campaignId,
6575
- slot: s.slots.size === 1 ? [...s.slots][0] : void 0,
6576
- contentId: s.contents.size === 1 ? [...s.contents][0] : void 0,
6577
- commerce: s.commerce
6578
- }));
6682
+ const slices = cartSlicesFrom(
6683
+ lines,
6684
+ (slot, lineSponsorId) => resolveCampaignId({ slot, sponsorId: lineSponsorId }, boot),
6685
+ this.lastCartSnapshot?.currency
6686
+ );
6687
+ return { slices, explicit: hasExplicitCampaign(lines) };
6579
6688
  }
6580
- /** The context of a per-campaign event: the slice's campaign, slot and content, over the event's own. */
6689
+ /**
6690
+ * The context of one of several per-campaign events: the slice's campaign,
6691
+ * slot and content — only its own; when the slice spans several slots or
6692
+ * articles the event names none rather than the one the click came from.
6693
+ */
6581
6694
  sliceContext(slice, base) {
6582
6695
  return compact({
6583
6696
  ...base,
6584
- campaignId: slice.campaignId ?? base.campaignId,
6585
- slot: slice.slot ?? base.slot,
6586
- contentId: slice.contentId ?? base.contentId
6697
+ sponsorId: slice.sponsorId ?? base.sponsorId,
6698
+ campaignId: slice.campaignId,
6699
+ slot: slice.slot,
6700
+ contentId: slice.contentId,
6701
+ // The click's title only describes the click's content: when the slice
6702
+ // is another article (or several), the page's own title applies instead.
6703
+ contentTitle: slice.contentId !== void 0 && slice.contentId === base.contentId ? base.contentTitle : void 0
6704
+ });
6705
+ }
6706
+ /**
6707
+ * The context of a single-campaign checkout/purchase: when the cart's origins
6708
+ * say which campaign they came from, the event says it too (plus the slot /
6709
+ * content when unique) — a cart without origins is exactly as before.
6710
+ */
6711
+ singleSliceContext(slice, explicit, base) {
6712
+ if (!slice || !explicit) return base;
6713
+ return compact({
6714
+ ...base,
6715
+ campaignId: base.campaignId ?? slice.campaignId,
6716
+ slot: base.slot ?? slice.slot,
6717
+ contentId: base.contentId ?? slice.contentId
6587
6718
  });
6588
6719
  }
6589
6720
  /** Current cart → commerce payload; falls back to the pre-clear snapshot. */
@@ -6691,10 +6822,10 @@ function resolveCampaignId(ctx, boot) {
6691
6822
  const defaultId = boot?.campaign?.id;
6692
6823
  if (ctx.slot) {
6693
6824
  const platform = Configuration.isInitialized ? Configuration.get().platform : void 0;
6694
- const assigned = toNumber2(findSlotAssignment(boot, ctx.slot, Date.now(), platform)?.id);
6825
+ const assigned = toNumber(findSlotAssignment(boot, ctx.slot, Date.now(), platform)?.id);
6695
6826
  if (assigned !== void 0) return assigned;
6696
6827
  }
6697
- const sponsorId = toNumber2(ctx.sponsorId);
6828
+ const sponsorId = toNumber(ctx.sponsorId);
6698
6829
  if (sponsorId === void 0) return defaultId;
6699
6830
  try {
6700
6831
  const campaigns = Array.isArray(boot?.campaigns) ? boot.campaigns : [];
@@ -6702,12 +6833,35 @@ function resolveCampaignId(ctx, boot) {
6702
6833
  (c) => !!c && typeof c === "object" && c.status === "active" && campaignSponsorIds(c).includes(sponsorId)
6703
6834
  );
6704
6835
  const chosen = matches.find((c) => c.id === defaultId) ?? matches[0];
6705
- const chosenId = toNumber2(chosen?.id);
6836
+ const chosenId = toNumber(chosen?.id);
6706
6837
  if (chosenId !== void 0) return chosenId;
6707
6838
  } catch {
6708
6839
  }
6709
6840
  return defaultId;
6710
6841
  }
6842
+ function campaignOf(detail) {
6843
+ return toNumber(detail.campaignId);
6844
+ }
6845
+ function fitsId(value) {
6846
+ if (value === void 0 || value === null) return void 0;
6847
+ const s = String(value);
6848
+ return s && s.length <= CONTENT_ID_MAX_LENGTH ? value : void 0;
6849
+ }
6850
+ function capTitle(value) {
6851
+ if (!value) return void 0;
6852
+ return value.length > CONTENT_TITLE_MAX_LENGTH ? value.slice(0, CONTENT_TITLE_MAX_LENGTH) : value;
6853
+ }
6854
+ var CONTENT_URL_MAX_LENGTH = 2048;
6855
+ function capUrl(value) {
6856
+ if (!value) return void 0;
6857
+ if (value.length <= CONTENT_URL_MAX_LENGTH) return value;
6858
+ const cut = value.search(/[?#]/);
6859
+ const withoutQuery = cut >= 0 ? value.slice(0, cut) : value;
6860
+ return withoutQuery.length <= CONTENT_URL_MAX_LENGTH ? withoutQuery : withoutQuery.slice(0, CONTENT_URL_MAX_LENGTH);
6861
+ }
6862
+ function sliceCommerce(slice) {
6863
+ return { items: slice.items, value: slice.value, currency: slice.currency };
6864
+ }
6711
6865
  function slotOf(detail) {
6712
6866
  return typeof detail.slot === "string" && detail.slot.trim() ? detail.slot.trim() : void 0;
6713
6867
  }
@@ -6720,11 +6874,11 @@ function contentOf(detail) {
6720
6874
  }
6721
6875
  function campaignSponsorIds(campaign) {
6722
6876
  const ids = [];
6723
- const primary = toNumber2(campaign.primarySponsor?.id);
6877
+ const primary = toNumber(campaign.primarySponsor?.id);
6724
6878
  if (primary !== void 0) ids.push(primary);
6725
6879
  const secondaries = Array.isArray(campaign.secondarySponsors) ? campaign.secondarySponsors : [];
6726
6880
  for (const s of secondaries) {
6727
- const id = toNumber2(s?.id);
6881
+ const id = toNumber(s?.id);
6728
6882
  if (id !== void 0) ids.push(id);
6729
6883
  }
6730
6884
  return ids;
@@ -6742,11 +6896,6 @@ function firstId(...candidates) {
6742
6896
  }
6743
6897
  return void 0;
6744
6898
  }
6745
- function toNumber2(value) {
6746
- if (value === void 0 || value === null || value === "") return void 0;
6747
- const n = Number(value);
6748
- return Number.isFinite(n) ? n : void 0;
6749
- }
6750
6899
  function pushGa4(event) {
6751
6900
  const commerceNames = /* @__PURE__ */ new Set([
6752
6901
  "view_item_list",
@@ -6803,8 +6952,11 @@ var VioFacade = class {
6803
6952
  this.slotDeclareTimer = null;
6804
6953
  /** A name appeared (or the key changed) since the registry was last declared. */
6805
6954
  this.slotsDirty = false;
6806
- /** `Vio.content()` resolved once per page; forgotten on every init(). */
6807
- this.contentCache = null;
6955
+ /** Manifests POSTed by this page load (abuse / rollout guard, see declareSlots). */
6956
+ this.declarationsThisLoad = 0;
6957
+ /** Each cap warns once per page load. */
6958
+ this.warnedLocationCap = false;
6959
+ this.warnedDeclarationCap = false;
6808
6960
  // Client-side managers — created lazily on first access. They don't
6809
6961
  // need Vio.init() to work (state is purely local).
6810
6962
  this.cartManager = null;
@@ -6825,26 +6977,26 @@ var VioFacade = class {
6825
6977
  this.slotDeclareTimer = null;
6826
6978
  }
6827
6979
  this.slotsDirty = this.slotRegistry.size > 0;
6828
- this.contentCache = null;
6980
+ this.declarationsThisLoad = 0;
6981
+ this.warnedLocationCap = false;
6982
+ this.warnedDeclarationCap = false;
6829
6983
  }
6830
6984
  /**
6831
6985
  * The content (article, page) this SDK instance is rendered in — what every
6832
6986
  * analytics event carries as `content_id` / `content_title` when the event
6833
- * has no explicit one. Resolved once per page (init option → URL query →
6834
- * `vio:content-*` metas → canonical/og:url → pathname; see
6835
- * `resolveContent`), forgotten on every `init()`. Never throws; `{}` without
6836
- * a document.
6987
+ * has no explicit one: init option → URL query → `vio:content-*` metas →
6988
+ * canonical/og:url → pathname (see `resolveContent`). Resolved LIVE on each
6989
+ * call — a few querySelectors — so an SPA route change, a new canonical or
6990
+ * a new title are seen without any re-init. Never throws; `{}` without a
6991
+ * document.
6837
6992
  */
6838
6993
  content() {
6839
- if (this.contentCache) return this.contentCache;
6840
6994
  let init;
6841
6995
  if (Configuration.isInitialized) {
6842
6996
  const cfg = Configuration.get();
6843
6997
  init = { contentId: cfg.contentId, contentTitle: cfg.contentTitle };
6844
6998
  }
6845
- const resolved = resolveContent(init);
6846
- if (typeof document !== "undefined") this.contentCache = resolved;
6847
- return resolved;
6999
+ return resolveContent(init);
6848
7000
  }
6849
7001
  get isInitialized() {
6850
7002
  return Configuration.isInitialized;
@@ -6952,34 +7104,61 @@ var VioFacade = class {
6952
7104
  * trimmed, cut at 100 chars, deduped; empties are dropped.
6953
7105
  */
6954
7106
  async declareSlots(slots, opts = {}) {
7107
+ let storageKey;
7108
+ let keys = [];
7109
+ let now = Date.now();
6955
7110
  try {
6956
- const locations = normalizeSlotLocations(slots);
6957
- if (locations.length === 0) return null;
7111
+ const all = normalizeSlotLocations(slots);
7112
+ if (all.length === 0) return null;
6958
7113
  if (!Configuration.isInitialized) {
6959
7114
  warn("declareSlots() before Vio.init() \u2014 nothing declared");
6960
7115
  return null;
6961
7116
  }
6962
7117
  const cfg = Configuration.get();
6963
- const storageKey = slotDeclarationStorageKey(cfg.platform, cfg.apiKey);
6964
- const keys = slotDeclarationKeys(locations);
6965
- const now = Date.now();
6966
- if (!opts.force && isSlotDeclarationFresh(readSlotDeclaration(storageKey), keys, now)) {
7118
+ storageKey = slotDeclarationStorageKey(cfg.platform, cfg.apiKey);
7119
+ now = Date.now();
7120
+ const memory = readSlotDeclaration(storageKey);
7121
+ const pending2 = new Set(pendingSlotKeys(memory, all.map(slotDeclarationKey), now));
7122
+ let locations = opts.force ? all : all.filter((l) => pending2.has(slotDeclarationKey(l)));
7123
+ if (locations.length === 0) return null;
7124
+ if (locations.length > MAX_LOCATIONS_PER_DECLARATION) {
7125
+ if (!this.warnedLocationCap) {
7126
+ this.warnedLocationCap = true;
7127
+ warn(
7128
+ `declareSlots(): ${locations.length} slots on one page \u2014 only the first ${MAX_LOCATIONS_PER_DECLARATION} are declared`
7129
+ );
7130
+ }
7131
+ locations = locations.slice(0, MAX_LOCATIONS_PER_DECLARATION);
7132
+ }
7133
+ keys = locations.map(slotDeclarationKey);
7134
+ if (!opts.force && this.declarationsThisLoad >= MAX_DECLARATIONS_PER_LOAD) {
7135
+ if (!this.warnedDeclarationCap) {
7136
+ this.warnedDeclarationCap = true;
7137
+ warn(
7138
+ `declareSlots(): already ${MAX_DECLARATIONS_PER_LOAD} manifests this page load \u2014 further ones are skipped (force: true to override)`
7139
+ );
7140
+ }
6967
7141
  return null;
6968
7142
  }
7143
+ this.declarationsThisLoad++;
6969
7144
  const result = await this.api.declareManifest({
6970
7145
  platform: cfg.platform,
6971
7146
  mode: "merge",
6972
7147
  locations
6973
7148
  });
6974
7149
  if (result?.mode === "merge") {
6975
- writeSlotDeclaration(storageKey, { keys, at: now });
7150
+ writeSlotDeclaration(storageKey, rememberSlotKeys(memory, keys, now));
6976
7151
  } else {
7152
+ writeSlotDeclaration(storageKey, rememberSlotKeys(memory, keys, failedAt(now)));
6977
7153
  warn(
6978
7154
  "slots declared but the server ignored mode=merge (old server); not caching, will retry next load"
6979
7155
  );
6980
7156
  }
6981
7157
  return result;
6982
7158
  } catch (err) {
7159
+ if (storageKey && keys.length > 0) {
7160
+ writeSlotDeclaration(storageKey, rememberSlotKeys(readSlotDeclaration(storageKey), keys, failedAt(now)));
7161
+ }
6983
7162
  warn("declareSlots() failed \u2014", err);
6984
7163
  return null;
6985
7164
  }
@@ -7049,10 +7228,10 @@ var VioFacade = class {
7049
7228
  attribution(slot, hint) {
7050
7229
  const out = {};
7051
7230
  try {
7052
- const name = typeof slot === "string" ? slot.trim() : "";
7231
+ const name = normalizeSlotId(slot);
7053
7232
  if (name) out.slot = name;
7054
7233
  const boot = this.cachedBootstrap;
7055
- const hinted = toNumber3(hint?.sponsorId);
7234
+ const hinted = toNumber(hint?.sponsorId);
7056
7235
  if (boot) {
7057
7236
  if (hinted !== void 0) {
7058
7237
  const campaignId = resolveCampaignId({ slot: name || void 0, sponsorId: hinted }, boot);
@@ -7060,9 +7239,9 @@ var VioFacade = class {
7060
7239
  out.sponsorId = hinted;
7061
7240
  } else {
7062
7241
  const campaign = this.campaignForSlot(name || null);
7063
- const campaignId = toNumber3(campaign?.id);
7242
+ const campaignId = toNumber(campaign?.id);
7064
7243
  if (campaignId !== void 0) out.campaignId = campaignId;
7065
- const sponsorId = toNumber3(campaign?.primarySponsor?.id);
7244
+ const sponsorId = toNumber(campaign?.primarySponsor?.id);
7066
7245
  if (sponsorId !== void 0) out.sponsorId = sponsorId;
7067
7246
  }
7068
7247
  } else if (hinted !== void 0) {
@@ -7075,6 +7254,35 @@ var VioFacade = class {
7075
7254
  }
7076
7255
  return out;
7077
7256
  }
7257
+ /**
7258
+ * The cart split per campaign — the SAME rule `begin_checkout` / `purchase`
7259
+ * use, for hosts with their own analytics sink (Vev). Every unit of every
7260
+ * line goes to the campaign of its origin; units without one to the campaign
7261
+ * the current rule resolves for the line's sponsor (slot assignment → the
7262
+ * sponsor's active campaign → default); `slot` / `contentId` / `sponsorId`
7263
+ * only when they are the same for the whole slice; `value` with two
7264
+ * decimals; `slice` / `slices` number the parts. Pure over the current cart
7265
+ * (or over `opts.items`); `[]` when there is nothing.
7266
+ */
7267
+ cartSlices(sponsorId, opts = {}) {
7268
+ try {
7269
+ let lines = [];
7270
+ if (Array.isArray(opts.items)) {
7271
+ lines = opts.items.filter((i) => sponsorId === void 0 || Number(i?.sponsorId) === Number(sponsorId));
7272
+ } else {
7273
+ this.cart.getAllCarts().forEach((state, id) => {
7274
+ if (sponsorId === void 0 || id === Number(sponsorId)) lines.push(...state.items);
7275
+ });
7276
+ }
7277
+ const boot = this.cachedBootstrap;
7278
+ return cartSlicesFrom(
7279
+ lines,
7280
+ (slot, lineSponsorId) => resolveCampaignId({ slot, sponsorId: lineSponsorId }, boot)
7281
+ );
7282
+ } catch {
7283
+ return [];
7284
+ }
7285
+ }
7078
7286
  scheduleSlotDeclaration() {
7079
7287
  if (this.slotDeclareTimer) clearTimeout(this.slotDeclareTimer);
7080
7288
  this.slotDeclareTimer = setTimeout(() => {
@@ -7107,10 +7315,8 @@ var VioFacade = class {
7107
7315
  function warn(...args) {
7108
7316
  if (typeof console !== "undefined") console.warn("[Vio]", ...args);
7109
7317
  }
7110
- function toNumber3(value) {
7111
- if (value === void 0 || value === null || value === "") return void 0;
7112
- const n = Number(value);
7113
- return Number.isFinite(n) ? n : void 0;
7318
+ function failedAt(now) {
7319
+ return now - (SLOT_DECLARATION_TTL_MS - SLOT_DECLARATION_FAILURE_TTL_MS);
7114
7320
  }
7115
7321
  var _vioGlobal = globalThis;
7116
7322
  var Vio = _vioGlobal.__VIO_FACADE__ ?? (_vioGlobal.__VIO_FACADE__ = new VioFacade());
@@ -7164,6 +7370,6 @@ function vippsButtonAttributes(verb = "buy", opts = {}) {
7164
7370
  };
7165
7371
  }
7166
7372
 
7167
- 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, addItem, adyenReturnOutcome, adyenReturnParams, autoSlotDisplayName, autoSlotId, 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, primaryImageUrl, purchaseFromPaymentSuccess, readNexiPending, resolveCampaignId, resolveContent, resolveSlotCampaign, sameOrigin, showApplePaySheet, updateCheckout, updateItem, updateShippingsBySupplier, vippsButtonAttributes };
7168
- //# sourceMappingURL=chunk-AMDI7KH3.js.map
7169
- //# sourceMappingURL=chunk-AMDI7KH3.js.map
7373
+ 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, addItem, adyenReturnOutcome, adyenReturnParams, autoSlotDisplayName, autoSlotId, 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, updateCheckout, updateItem, updateShippingsBySupplier, vippsButtonAttributes };
7374
+ //# sourceMappingURL=chunk-BRJMT7P4.js.map
7375
+ //# sourceMappingURL=chunk-BRJMT7P4.js.map