@adhese/sdk 0.15.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,36 +1,7 @@
1
- import { createLogger, toValue, ref, computed, watch, effectScope, uniqueId, reactive, waitForDomLoad, createEventManager, awaitTimeout } from "@adhese/sdk-shared";
1
+ import { createLogger, toValue, ref, computed, waitForDomLoad, watch, effectScope, uniqueId, reactive, createEventManager, awaitTimeout } from "@adhese/sdk-shared";
2
2
  import { debounce, round, isDeepEqual, doNothing } from "remeda";
3
3
  const name = "@adhese/sdk";
4
- const version = "0.15.0";
5
- function renderIframe(ad, element) {
6
- const iframe = document.createElement("iframe");
7
- iframe.srcdoc = `
8
- <!DOCTYPE html>
9
- <html>
10
- <head>
11
- <style>
12
- body {
13
- margin: 0;
14
- padding: 0;
15
- overflow: hidden;
16
- }
17
- </style>
18
- </head>
19
- <body>
20
- ${String(ad.tag)}
21
- </body>
22
- `.replaceAll(/\s+/g, " ").trim();
23
- iframe.style.border = "none";
24
- iframe.style.width = ad.width ? `${ad.width}px` : "auto";
25
- iframe.style.height = ad.height ? `${ad.height}px` : "auto";
26
- element.replaceChildren(iframe);
27
- }
28
- function renderInline(ad, element) {
29
- element.innerHTML = String(ad.tag);
30
- }
31
- function generateName(location, format, slot) {
32
- return `${location}${slot ? `${slot}` : ""}-${format}`;
33
- }
4
+ const version = "0.16.1";
34
5
  function addTrackingPixel(url) {
35
6
  const img = document.createElement("img");
36
7
  img.src = url.toString();
@@ -42,47 +13,6 @@ function addTrackingPixel(url) {
42
13
  img.style.top = "0";
43
14
  return document.body.appendChild(img);
44
15
  }
45
- const logger = createLogger({
46
- scope: `${name}@${version}`
47
- });
48
- function createQueryDetector({
49
- onChange,
50
- queries = {
51
- mobile: "(max-width: 768px) and (pointer: coarse)",
52
- tablet: "(min-width: 769px) and (max-width: 1024px) and (pointer: coarse)",
53
- desktop: "(min-width: 1025px) and (pointer: fine)"
54
- }
55
- } = {}) {
56
- const mediaMap = new Map(
57
- Object.entries(queries).map(([key, query]) => [key, window.matchMedia(query)])
58
- );
59
- function getQuery() {
60
- for (const [device, query] of Object.entries(queries)) {
61
- if (window.matchMedia(query).matches)
62
- return device;
63
- }
64
- return "unknown";
65
- }
66
- const handleOnChange = debounce(() => {
67
- void (onChange == null ? void 0 : onChange(getQuery()));
68
- logger.debug(`Change device ${getQuery()}`);
69
- }, {
70
- waitMs: 50
71
- });
72
- if (onChange) {
73
- for (const query of mediaMap.values())
74
- query.addEventListener("change", handleOnChange.call);
75
- }
76
- function dispose() {
77
- for (const query of mediaMap.values())
78
- query.removeEventListener("change", handleOnChange.call);
79
- }
80
- return {
81
- queries: mediaMap,
82
- getQuery,
83
- dispose
84
- };
85
- }
86
16
  const hookMap = /* @__PURE__ */ new Map();
87
17
  function clearAllHooks() {
88
18
  hookMap.clear();
@@ -176,6 +106,9 @@ const [runOnInit, onInit] = createSyncHook("onInit", {
176
106
  runOnInit();
177
107
  }
178
108
  });
109
+ const logger = createLogger({
110
+ scope: `${name}@${version}`
111
+ });
179
112
  const [runOnRequest, onRequest] = createAsyncHook("onRequest");
180
113
  const [runOnResponse, onResponse] = createAsyncHook("onResponse");
181
114
  async function requestPreviews(account) {
@@ -197,7 +130,7 @@ async function requestPreviews(account) {
197
130
  return Promise.reject(new Error(`Failed to request preview ad with ID: ${previewObject.adhesePreviewCreativeId}`));
198
131
  return await response.json();
199
132
  })),
200
- import("./requestAds.schema-Cb8_FVMb.js").then((module) => module.adSchema)
133
+ import("./requestAds.schema-DbGNhzE_.js").then((module) => module.adSchema)
201
134
  ]);
202
135
  return adSchema.array().parse(list.filter((response) => {
203
136
  if (response.status === "rejected") {
@@ -313,7 +246,7 @@ async function requestAds(requestOptions) {
313
246
  const [response, previews, parseResponse] = await Promise.all([
314
247
  ((_b = context.options.requestType) == null ? void 0 : _b.toUpperCase()) === "POST" ? requestWithPost(options) : requestWithGet(options),
315
248
  requestPreviews(context.options.account),
316
- import("./requestAds.schema-Cb8_FVMb.js").then((module) => module.parseResponse)
249
+ import("./requestAds.schema-DbGNhzE_.js").then((module) => module.parseResponse)
317
250
  ]);
318
251
  logger.debug("Received response", response);
319
252
  if (!response.ok)
@@ -335,8 +268,6 @@ async function requestAds(requestOptions) {
335
268
  ...result.filter((ad) => !previews.some((preview) => preview.libId === ad.libId)),
336
269
  ...matchedPreviews
337
270
  ]);
338
- if (mergedResult.length === 0)
339
- throw new Error("No ads found");
340
271
  (_d = context.events) == null ? void 0 : _d.responseReceived.dispatch(mergedResult);
341
272
  return mergedResult;
342
273
  } catch (error) {
@@ -346,7 +277,113 @@ async function requestAds(requestOptions) {
346
277
  }
347
278
  }
348
279
  const [runOnSlotCreate, onSlotCreate] = createSyncHook("onSlotCreate");
349
- function useViewabilityObserver({ context, ad, name: name2, element }) {
280
+ let isDisposed = false;
281
+ const [runOnDispose, onDispose] = createSyncHook("onDispose", {
282
+ onRun(callbacks) {
283
+ isDisposed = true;
284
+ callbacks == null ? void 0 : callbacks.clear();
285
+ },
286
+ onAdd() {
287
+ if (isDisposed)
288
+ runOnDispose();
289
+ }
290
+ });
291
+ function useQueryDetector(queries = {
292
+ mobile: "(max-width: 768px)",
293
+ tablet: "(min-width: 769px) and (max-width: 1024px)",
294
+ desktop: "(min-width: 1025px)"
295
+ }) {
296
+ const entries = Object.entries(queries);
297
+ const active = ref(getQuery(entries));
298
+ const queryList = entries.map(([, query]) => window.matchMedia(query));
299
+ const handleOnChange = debounce(() => {
300
+ active.value = getQuery(entries);
301
+ }, {
302
+ waitMs: 50
303
+ });
304
+ onInit(() => {
305
+ for (const query of queryList)
306
+ query.addEventListener("change", handleOnChange.call);
307
+ handleOnChange.call();
308
+ });
309
+ function dispose() {
310
+ for (const query of queryList)
311
+ query.removeEventListener("change", handleOnChange.call);
312
+ }
313
+ onDispose(dispose);
314
+ return [computed(() => active.value), dispose];
315
+ }
316
+ function getQuery(entries) {
317
+ for (const [device, query] of entries) {
318
+ if (window.matchMedia(query).matches)
319
+ return device;
320
+ }
321
+ return "unknown";
322
+ }
323
+ function renderIframe(ad, element) {
324
+ const iframe = document.createElement("iframe");
325
+ iframe.srcdoc = `
326
+ <!DOCTYPE html>
327
+ <html>
328
+ <head>
329
+ <style>
330
+ body {
331
+ margin: 0;
332
+ padding: 0;
333
+ overflow: hidden;
334
+ }
335
+ </style>
336
+ </head>
337
+ <body>
338
+ ${String(ad.tag)}
339
+ </body>
340
+ `.replaceAll(/\s+/g, " ").trim();
341
+ iframe.style.border = "none";
342
+ iframe.style.width = ad.width ? `${ad.width}px` : "auto";
343
+ iframe.style.height = ad.height ? `${ad.height}px` : "auto";
344
+ element.replaceChildren(iframe);
345
+ }
346
+ function renderInline(ad, element) {
347
+ element.innerHTML = String(ad.tag);
348
+ }
349
+ function generateName(location, format, slot) {
350
+ return `${location}${slot ? `${slot}` : ""}-${format}`;
351
+ }
352
+ function useDomLoaded() {
353
+ const isDomLoaded = ref(false);
354
+ onInit(async () => {
355
+ await waitForDomLoad();
356
+ isDomLoaded.value = true;
357
+ });
358
+ return isDomLoaded;
359
+ }
360
+ function useRenderIntersectionObserver({ options, element, hooks }) {
361
+ var _a;
362
+ const isInViewport = ref(false);
363
+ const renderIntersectionObserver = new IntersectionObserver((entries) => {
364
+ isInViewport.value = entries.some((entry) => entry.isIntersecting);
365
+ }, {
366
+ rootMargin: ((_a = options.lazyLoadingOptions) == null ? void 0 : _a.rootMargin) ?? "200px",
367
+ threshold: 0
368
+ });
369
+ function observe(newElement, oldElement) {
370
+ if (oldElement)
371
+ renderIntersectionObserver.unobserve(oldElement);
372
+ if (newElement)
373
+ renderIntersectionObserver.observe(newElement);
374
+ return () => {
375
+ if (newElement)
376
+ renderIntersectionObserver.unobserve(newElement);
377
+ };
378
+ }
379
+ watch(element, observe);
380
+ observe(element.value);
381
+ hooks.onDispose(() => {
382
+ renderIntersectionObserver.disconnect();
383
+ });
384
+ return isInViewport;
385
+ }
386
+ function useViewabilityObserver({ context, slotContext, hooks, onTracked }) {
350
387
  let timeoutId = null;
351
388
  const {
352
389
  threshold,
@@ -361,16 +398,12 @@ function useViewabilityObserver({ context, ad, name: name2, element }) {
361
398
  const trackingPixel = ref(null);
362
399
  const isTracked = computed(() => Boolean(trackingPixel.value));
363
400
  const viewabilityObserver = new IntersectionObserver(([entry]) => {
364
- if (context.options.viewabilityTracking && !trackingPixel.value && ad) {
401
+ if (context.options.viewabilityTracking && !trackingPixel.value) {
365
402
  const ratio = round(entry.intersectionRatio, 1);
366
403
  if (ratio >= threshold && !timeoutId) {
367
404
  timeoutId = setTimeout(() => {
368
- var _a;
369
405
  timeoutId = null;
370
- if ((_a = ad.value) == null ? void 0 : _a.viewableImpressionCounter) {
371
- trackingPixel.value = addTrackingPixel(ad.value.viewableImpressionCounter);
372
- logger.debug(`Viewability tracking pixel fired for ${name2.value}`);
373
- }
406
+ onTracked == null ? void 0 : onTracked(trackingPixel);
374
407
  }, duration);
375
408
  } else if (ratio < threshold && timeoutId) {
376
409
  clearTimeout(timeoutId);
@@ -382,8 +415,6 @@ function useViewabilityObserver({ context, ad, name: name2, element }) {
382
415
  threshold: Array.from({ length: 11 }, (_, i) => i * 0.1)
383
416
  });
384
417
  function observe(newElement, oldElement) {
385
- if (oldElement)
386
- viewabilityObserver.unobserve(oldElement);
387
418
  if (newElement && context.options.viewabilityTracking)
388
419
  viewabilityObserver.observe(newElement);
389
420
  return () => {
@@ -391,168 +422,183 @@ function useViewabilityObserver({ context, ad, name: name2, element }) {
391
422
  viewabilityObserver.unobserve(newElement);
392
423
  };
393
424
  }
394
- watch(element, observe);
395
- observe(element.value);
396
- return [isTracked, () => {
425
+ watch(() => {
426
+ var _a;
427
+ return (_a = slotContext.value) == null ? void 0 : _a.element;
428
+ }, (element) => {
429
+ if (element)
430
+ observe(element);
431
+ }, { immediate: true });
432
+ watch(slotContext, (slot) => {
433
+ if (slot == null ? void 0 : slot.element)
434
+ viewabilityObserver.observe(slot.element);
435
+ }, { once: true });
436
+ hooks.onDispose(() => {
397
437
  var _a;
398
438
  (_a = trackingPixel.value) == null ? void 0 : _a.remove();
399
439
  viewabilityObserver.disconnect();
400
- }];
401
- }
402
- function useRenderIntersectionObserver({ options, element }) {
403
- var _a;
404
- const isInViewport = ref(false);
405
- const renderIntersectionObserver = new IntersectionObserver((entries) => {
406
- isInViewport.value = entries.some((entry) => entry.isIntersecting);
407
- }, {
408
- rootMargin: ((_a = options.lazyLoadingOptions) == null ? void 0 : _a.rootMargin) ?? "200px",
409
- threshold: 0
410
440
  });
411
- function observe(newElement, oldElement) {
412
- if (oldElement)
413
- renderIntersectionObserver.unobserve(oldElement);
414
- if (newElement)
415
- renderIntersectionObserver.observe(newElement);
416
- return () => {
417
- if (newElement)
418
- renderIntersectionObserver.unobserve(newElement);
419
- };
420
- }
421
- watch(element, observe);
422
- observe(element.value);
423
- return [isInViewport, () => {
424
- renderIntersectionObserver.disconnect();
425
- }];
441
+ return isTracked;
426
442
  }
427
443
  function useSlotHooks({ setup }, slotContext, id) {
428
- const [runOnSlotRender, onRender, disposeOnRender] = createAsyncHook(`onRender:${id}`);
444
+ const [runOnBeforeRender, onBeforeRender, disposeOnBeforeRender] = createAsyncHook(`onBeforeRender:${id}`);
445
+ const [runOnRender, onRender, disposeOnRender] = createAsyncHook(`onRender:${id}`);
446
+ const [runOnBeforeRequest, onBeforeRequest, disposeOnBeforeRequest] = createAsyncHook(`onBeforeRequest:${id}`);
429
447
  const [runOnRequest2, onRequest2, disposeOnRequest] = createAsyncHook(`onRequest:${id}`);
430
448
  const [runOnDispose2, onDispose2, disposeOnDispose] = createPassiveHook(`onDispose:${id}`);
431
449
  setup == null ? void 0 : setup(slotContext, {
450
+ onBeforeRender,
432
451
  onRender,
452
+ onBeforeRequest,
433
453
  onDispose: onDispose2,
434
454
  onRequest: onRequest2
435
455
  });
436
456
  onDispose2(() => {
457
+ disposeOnBeforeRender();
437
458
  disposeOnRender();
459
+ disposeOnBeforeRequest();
438
460
  disposeOnRequest();
439
461
  disposeOnDispose();
440
462
  });
441
- return { runOnSlotRender, runOnRequest: runOnRequest2, runOnDispose: runOnDispose2 };
463
+ return { runOnBeforeRender, runOnRender, runOnBeforeRequest, runOnRequest: runOnRequest2, runOnDispose: runOnDispose2, onDispose: onDispose2, onBeforeRequest, onRequest: onRequest2, onBeforeRender, onRender };
442
464
  }
443
465
  const renderFunctions = {
444
466
  iframe: renderIframe,
445
467
  inline: renderInline,
446
468
  none: doNothing
447
469
  };
470
+ const defaultOptions = {
471
+ renderMode: "iframe",
472
+ type: "normal"
473
+ };
448
474
  function createSlot(slotOptions) {
449
475
  const scope = effectScope();
450
- const mergedOptions = {
451
- renderMode: "iframe",
452
- ...slotOptions
453
- };
454
476
  return scope.run(() => {
455
477
  const slotContext = ref(null);
456
- const options = runOnSlotCreate(mergedOptions);
457
- const id = uniqueId();
458
- const { runOnSlotRender, runOnDispose: runOnDispose2, runOnRequest: runOnRequest2 } = useSlotHooks(options, slotContext, id);
478
+ const options = runOnSlotCreate({
479
+ ...defaultOptions,
480
+ ...slotOptions
481
+ });
459
482
  const {
460
483
  containingElement,
461
484
  slot,
462
485
  context,
463
- renderMode
464
- } = mergedOptions;
486
+ renderMode = "iframe",
487
+ type = "normal"
488
+ } = options;
489
+ const id = uniqueId();
490
+ const {
491
+ runOnBeforeRender,
492
+ runOnRender,
493
+ runOnBeforeRequest,
494
+ runOnRequest: runOnRequest2,
495
+ runOnDispose: runOnDispose2,
496
+ ...hooks
497
+ } = useSlotHooks(options, slotContext, id);
498
+ const isDisposed2 = ref(false);
465
499
  const parameters = reactive(new Map(Object.entries(options.parameters ?? {})));
466
- let queryDetector = null;
467
- if (typeof options.format !== "string") {
468
- queryDetector = createQueryDetector({
469
- onChange: onQueryChange,
470
- queries: Object.fromEntries(options.format.map((item) => [item.format, item.query]))
471
- });
472
- }
473
- const format = ref(queryDetector ? queryDetector.getQuery() : options.format);
474
- function onQueryChange(newFormat) {
475
- format.value = newFormat;
476
- }
477
- const ad = ref(null);
478
- const originalAd = ref(ad.value);
479
- const name2 = computed(() => generateName(context.location, format.value, slot));
500
+ const [device, disposeQueryDetector] = useQueryDetector(typeof options.format === "string" ? {
501
+ [options.format]: "(min-width: 0px)"
502
+ } : Object.fromEntries(options.format.map((item) => [item.format, item.query])));
503
+ const format = computed(() => typeof options.format === "string" ? options.format : device.value);
504
+ const data = ref(null);
505
+ const originalData = ref(data.value);
506
+ const name2 = computed(() => generateName(options.context.location, format.value, options.slot));
480
507
  watch(name2, async (newName, oldName) => {
508
+ var _a, _b;
481
509
  if (newName === oldName)
482
510
  return;
483
- const newAd = await requestAd$1();
484
- cleanElement();
485
- ad.value = newAd;
486
- originalAd.value = newAd;
511
+ const newAd = await ((_a = slotContext.value) == null ? void 0 : _a.request());
512
+ if (!newAd)
513
+ return;
514
+ (_b = slotContext.value) == null ? void 0 : _b.cleanElement();
515
+ data.value = newAd;
516
+ originalData.value = newAd;
487
517
  });
488
518
  const isDomLoaded = useDomLoaded();
489
- const isDisposed2 = ref(false);
490
519
  const element = computed(
491
520
  () => {
492
- if (!(typeof containingElement === "string" || !containingElement))
493
- return containingElement;
494
- if (!isDomLoaded.value || isDisposed2.value)
521
+ var _a;
522
+ if (!(typeof options.containingElement === "string" || !options.containingElement))
523
+ return options.containingElement;
524
+ if (!isDomLoaded.value || ((_a = slotContext.value) == null ? void 0 : _a.isDisposed))
495
525
  return null;
496
- return document.querySelector(`.adunit[data-format="${format.value}"]#${containingElement}${slot ? `[data-slot="${slot}"]` : ""}`);
526
+ return document.querySelector(`.adunit[data-format="${format.value}"]#${options.containingElement}${options.slot ? `[data-slot="${options.slot}"]` : ""}`);
497
527
  }
498
528
  );
499
- const [isInViewport, disposeRenderIntersectionObserver] = useRenderIntersectionObserver({
529
+ const isInViewport = useRenderIntersectionObserver({
500
530
  options,
501
- element
531
+ element,
532
+ hooks
502
533
  });
503
534
  const status = ref("initializing");
504
- watch([ad, isInViewport], async ([newAd, newIsInViewport], [oldAd]) => {
505
- if ((!newAd || oldAd && isDeepEqual(newAd, oldAd)) && status.value === "rendered")
535
+ watch([data, isInViewport], async ([newData, newIsInViewport], [oldData]) => {
536
+ var _a;
537
+ if ((!newData || oldData && isDeepEqual(newData, oldData)) && status.value === "rendered")
506
538
  return;
507
539
  if (newIsInViewport)
508
- await render(newAd ?? void 0);
540
+ await ((_a = slotContext.value) == null ? void 0 : _a.render(newData ?? void 0));
509
541
  });
510
- watch(isInViewport, (value) => {
542
+ hooks.onDispose(() => {
543
+ disposeQueryDetector();
544
+ });
545
+ onInit(async () => {
511
546
  var _a;
512
- (_a = options.onViewabilityChanged) == null ? void 0 : _a.call(options, value);
547
+ status.value = "initialized";
548
+ if (options.lazyLoading)
549
+ return;
550
+ data.value = await ((_a = slotContext.value) == null ? void 0 : _a.request()) ?? null;
513
551
  });
514
- const [
515
- isViewabilityTracked,
516
- disposeViewabilityObserver
517
- ] = useViewabilityObserver({
552
+ const isViewabilityTracked = useViewabilityObserver({
518
553
  context,
519
- ad,
520
- name: name2,
521
- element
554
+ slotContext,
555
+ hooks,
556
+ onTracked(trackingPixel) {
557
+ var _a, _b, _c, _d, _e;
558
+ if ((_b = (_a = slotContext.value) == null ? void 0 : _a.data) == null ? void 0 : _b.viewableImpressionCounter) {
559
+ trackingPixel.value = addTrackingPixel((_d = (_c = slotContext.value) == null ? void 0 : _c.data) == null ? void 0 : _d.viewableImpressionCounter);
560
+ context.logger.debug(`Viewability tracking pixel fired for ${(_e = slotContext.value) == null ? void 0 : _e.name}`);
561
+ }
562
+ }
522
563
  });
523
564
  const impressionTrackingPixelElement = ref(null);
524
565
  const isImpressionTracked = computed(() => Boolean(impressionTrackingPixelElement.value));
525
- async function requestAd$1() {
566
+ async function request() {
567
+ if (options.lazyLoading && !isInViewport.value)
568
+ return null;
526
569
  status.value = "loading";
527
- await runOnRequest2();
528
- const response = await requestAd({
529
- slot: {
530
- name: name2.value,
531
- parameters
532
- },
533
- context
534
- });
535
- if (response) {
536
- ad.value = response;
537
- if (!originalAd.value)
538
- originalAd.value = response;
539
- status.value = response ? "loaded" : "empty";
570
+ let response = await runOnBeforeRequest(null);
571
+ if (!response) {
572
+ response = await requestAd({
573
+ slot: {
574
+ name: name2.value,
575
+ parameters
576
+ },
577
+ context
578
+ });
540
579
  }
580
+ if (response)
581
+ response = await runOnRequest2(response);
582
+ data.value = response;
583
+ if (!originalData.value)
584
+ originalData.value = response;
585
+ status.value = response ? "loaded" : "empty";
586
+ if (!response)
587
+ cleanElement();
541
588
  return response;
542
589
  }
543
590
  async function render(adToRender) {
544
- var _a, _b;
545
591
  status.value = "rendering";
546
592
  await waitForDomLoad();
547
593
  await waitOnInit;
548
- let renderAd = adToRender ?? ad.value ?? originalAd.value ?? await requestAd$1();
594
+ let renderAd = adToRender ?? data.value ?? originalData.value ?? await request();
595
+ renderAd = renderAd && await runOnBeforeRender(renderAd);
549
596
  if (!renderAd) {
550
597
  status.value = "empty";
551
598
  logger.debug(`No ad to render for slot ${name2.value}`);
552
599
  return null;
553
600
  }
554
- renderAd = ((_a = options.onBeforeRender) == null ? void 0 : _a.call(options, renderAd)) ?? renderAd;
555
- renderAd = await runOnSlotRender(renderAd);
601
+ renderAd = await runOnRender(renderAd);
556
602
  if (!element.value) {
557
603
  const error = `Could not create slot for format ${format.value}. No element found.`;
558
604
  logger.error(error, options);
@@ -560,6 +606,12 @@ function createSlot(slotOptions) {
560
606
  }
561
607
  if (context.debug)
562
608
  element.value.style.position = "relative";
609
+ if (typeof (renderAd == null ? void 0 : renderAd.tag) !== "string") {
610
+ const error = `Could not render slot for slot ${name2.value}. A valid tag doesn't exist or is not HTML string.`;
611
+ logger.error(error, options);
612
+ status.value = "error";
613
+ throw new Error(error);
614
+ }
563
615
  renderFunctions[renderMode](renderAd, element.value);
564
616
  if (renderAd.impressionCounter && !impressionTrackingPixelElement.value) {
565
617
  impressionTrackingPixelElement.value = addTrackingPixel(renderAd.impressionCounter);
@@ -571,8 +623,7 @@ function createSlot(slotOptions) {
571
623
  format,
572
624
  containingElement
573
625
  });
574
- (_b = options.onRender) == null ? void 0 : _b.call(options, element.value);
575
- ad.value = renderAd;
626
+ data.value = renderAd;
576
627
  status.value = "rendered";
577
628
  return element.value;
578
629
  }
@@ -583,34 +634,27 @@ function createSlot(slotOptions) {
583
634
  element.value.style.position = "";
584
635
  element.value.style.width = "";
585
636
  element.value.style.height = "";
637
+ data.value = null;
638
+ originalData.value = null;
586
639
  }
587
640
  function dispose() {
588
- var _a, _b;
641
+ var _a;
589
642
  cleanElement();
590
643
  (_a = impressionTrackingPixelElement.value) == null ? void 0 : _a.remove();
591
- ad.value = null;
592
- disposeRenderIntersectionObserver();
593
- disposeViewabilityObserver();
594
- (_b = options.onDispose) == null ? void 0 : _b.call(options);
595
- queryDetector == null ? void 0 : queryDetector.dispose();
644
+ data.value = null;
596
645
  runOnDispose2();
597
646
  isDisposed2.value = true;
598
647
  scope.stop();
599
648
  }
600
- onInit(async () => {
601
- status.value = "initialized";
602
- if (options.lazyLoading)
603
- return;
604
- ad.value = await requestAd$1();
605
- });
606
649
  const state = reactive({
607
650
  location: context.location ?? "",
608
651
  lazyLoading: options.lazyLoading ?? false,
652
+ type,
609
653
  slot,
610
654
  parameters,
611
655
  format,
612
656
  name: name2,
613
- ad,
657
+ data,
614
658
  isViewabilityTracked,
615
659
  isImpressionTracked,
616
660
  status,
@@ -618,8 +662,10 @@ function createSlot(slotOptions) {
618
662
  isDisposed: isDisposed2,
619
663
  id,
620
664
  render,
621
- request: requestAd$1,
622
- dispose
665
+ request,
666
+ dispose,
667
+ cleanElement,
668
+ ...hooks
623
669
  });
624
670
  watch(state, (newState) => {
625
671
  slotContext.value = newState;
@@ -630,14 +676,6 @@ function createSlot(slotOptions) {
630
676
  return state;
631
677
  });
632
678
  }
633
- function useDomLoaded() {
634
- const isDomLoaded = ref(false);
635
- onInit(async () => {
636
- await waitForDomLoad();
637
- isDomLoaded.value = true;
638
- });
639
- return isDomLoaded;
640
- }
641
679
  async function findDomSlots(context) {
642
680
  await waitForDomLoad();
643
681
  return Array.from(document.querySelectorAll(".adunit")).filter((element) => {
@@ -672,21 +710,24 @@ function createSlotManager({
672
710
  var _a;
673
711
  const slot = createSlot({
674
712
  ...options,
675
- onDispose: onDispose2,
676
- context
713
+ context,
714
+ setup(slotContext, slotHooks) {
715
+ var _a2;
716
+ (_a2 = options.setup) == null ? void 0 : _a2.call(options, slotContext, slotHooks);
717
+ slotHooks.onDispose(() => {
718
+ var _a3;
719
+ context.slots.delete(slot.id);
720
+ logger.debug("Slot removed", {
721
+ slot
722
+ });
723
+ (_a3 = context.events) == null ? void 0 : _a3.removeSlot.dispatch(slot);
724
+ });
725
+ }
677
726
  });
678
727
  if (get(slot.name)) {
679
728
  slot.dispose();
680
729
  throw new Error(`Slot with the name: ${slot.name} already exists. Create a new slot with a different format, slot, or the location.`);
681
730
  }
682
- function onDispose2() {
683
- var _a2;
684
- context.slots.delete(slot.id);
685
- logger.debug("Slot removed", {
686
- slot
687
- });
688
- (_a2 = context.events) == null ? void 0 : _a2.removeSlot.dispatch(slot);
689
- }
690
731
  context.slots.set(slot.id, slot);
691
732
  logger.debug("Slot added", {
692
733
  slot,
@@ -725,29 +766,34 @@ function createSlotManager({
725
766
  dispose
726
767
  };
727
768
  }
728
- function onTcfConsentChange(callback) {
729
- var _a;
730
- (_a = window.__tcfapi) == null ? void 0 : _a.call(window, "addEventListener", 2, callback);
731
- return () => {
732
- var _a2;
733
- return (_a2 = window.__tcfapi) == null ? void 0 : _a2.call(window, "removeEventListener", 2, callback);
734
- };
735
- }
736
- function createParameters(options, queryDetector) {
737
- const parameters = /* @__PURE__ */ new Map();
738
- if (options.logReferrer)
739
- parameters.set("re", btoa(document.referrer));
740
- if (options.logUrl)
741
- parameters.set("ur", btoa(window.location.href));
742
- for (const [key, value] of Object.entries({
743
- ...options.parameters ?? {},
744
- tl: options.consent ? "all" : "none",
745
- dt: queryDetector.getQuery(),
746
- br: queryDetector.getQuery(),
747
- rn: Math.round(Math.random() * 1e4).toString()
748
- }))
749
- parameters.set(key, value);
750
- return parameters;
769
+ function useConsent(context) {
770
+ const consent = ref("none");
771
+ const consentType = computed(() => consent.value === "none" || consent.value === "all" ? "binary" : "tcf");
772
+ function onTcfConsentChange(data) {
773
+ if (data.tcString)
774
+ consent.value = data.tcString;
775
+ }
776
+ onInit(() => {
777
+ var _a;
778
+ (_a = window.__tcfapi) == null ? void 0 : _a.call(window, "addEventListener", 2, onTcfConsentChange);
779
+ });
780
+ onDispose(() => {
781
+ var _a;
782
+ (_a = window.__tcfapi) == null ? void 0 : _a.call(window, "removeEventListener", 2, onTcfConsentChange);
783
+ });
784
+ watch(() => context.consent, (newConsent) => {
785
+ consent.value = newConsent ? "all" : "none";
786
+ }, { immediate: true });
787
+ watch([consent, consentType], ([newConsent, newConsentType]) => {
788
+ if (newConsentType === "binary") {
789
+ context.parameters.set("tl", newConsent);
790
+ context.parameters.delete("xt");
791
+ } else {
792
+ context.parameters.set("xt", newConsent);
793
+ context.parameters.delete("tl");
794
+ }
795
+ }, { immediate: true });
796
+ return [consent, consentType];
751
797
  }
752
798
  function setupLogging(mergedOptions) {
753
799
  if (mergedOptions.debug || window.location.search.includes("adhese_debug=true")) {
@@ -761,17 +807,65 @@ function setupLogging(mergedOptions) {
761
807
  function isPreviewMode() {
762
808
  return window.location.search.includes("adhesePreviewCreativeId");
763
809
  }
764
- let isDisposed = false;
765
- const [runOnDispose, onDispose] = createSyncHook("onDispose", {
766
- onRun(callbacks) {
767
- isDisposed = true;
768
- callbacks == null ? void 0 : callbacks.clear();
769
- },
770
- onAdd() {
771
- if (isDisposed)
772
- runOnDispose();
773
- }
774
- });
810
+ async function fetchAllUnrenderedSlots(slots) {
811
+ const filteredSlots = slots.filter((slot) => !slot.lazyLoading && !slot.data);
812
+ if (filteredSlots.length === 0)
813
+ return;
814
+ await Promise.allSettled(filteredSlots.map((slot) => slot.request()));
815
+ }
816
+ function useMainQueryDetector(mergedOptions, context) {
817
+ const [device] = useQueryDetector(mergedOptions.queries);
818
+ watch(device, async (newDevice) => {
819
+ var _a, _b;
820
+ context.device = newDevice;
821
+ (_a = context.parameters) == null ? void 0 : _a.set("dt", newDevice);
822
+ (_b = context.parameters) == null ? void 0 : _b.set("br", newDevice);
823
+ await Promise.allSettled(context.getAll().map((slot) => slot.request()));
824
+ }, { immediate: true });
825
+ }
826
+ function useMainDebugMode(context) {
827
+ watch(() => context.debug, async (newDebug) => {
828
+ var _a, _b;
829
+ if (newDebug) {
830
+ context.logger.setMinLogLevelThreshold("debug");
831
+ context.logger.debug("Debug mode enabled");
832
+ (_a = context.events) == null ? void 0 : _a.debugChange.dispatch(true);
833
+ } else {
834
+ context.logger.debug("Debug mode disabled");
835
+ context.logger.setMinLogLevelThreshold("info");
836
+ (_b = context.events) == null ? void 0 : _b.debugChange.dispatch(false);
837
+ }
838
+ }, {
839
+ immediate: true
840
+ });
841
+ onDispose(() => {
842
+ context.logger.resetLogs();
843
+ context.logger.info("Adhese instance disposed");
844
+ });
845
+ }
846
+ function useMainParameters(context, options) {
847
+ const parameters = /* @__PURE__ */ new Map();
848
+ if (options.logReferrer)
849
+ parameters.set("re", btoa(document.referrer));
850
+ if (options.logUrl)
851
+ parameters.set("ur", btoa(window.location.href));
852
+ for (const [key, value] of Object.entries({
853
+ ...options.parameters ?? {},
854
+ rn: Math.round(Math.random() * 1e4).toString()
855
+ }))
856
+ parameters.set(key, value);
857
+ context.parameters = parameters;
858
+ watch(
859
+ () => context.parameters,
860
+ (newParameters) => {
861
+ var _a;
862
+ (_a = context.events) == null ? void 0 : _a.parametersChange.dispatch(newParameters);
863
+ },
864
+ {
865
+ deep: true
866
+ }
867
+ );
868
+ }
775
869
  function createAdhese(options) {
776
870
  const scope = effectScope();
777
871
  return scope.run(() => {
@@ -802,6 +896,7 @@ function createAdhese(options) {
802
896
  parameters: /* @__PURE__ */ new Map(),
803
897
  events: createEventManager(),
804
898
  slots: /* @__PURE__ */ new Map(),
899
+ device: "unknown",
805
900
  dispose,
806
901
  findDomSlots: findDomSlots2,
807
902
  getAll,
@@ -819,40 +914,11 @@ function createAdhese(options) {
819
914
  onSlotCreate
820
915
  });
821
916
  }
822
- context.events = createEventManager();
823
917
  watch(() => context.location, (newLocation) => {
824
918
  var _a;
825
919
  (_a = context.events) == null ? void 0 : _a.locationChange.dispatch(newLocation);
826
920
  });
827
- const queryDetector = createQueryDetector({
828
- onChange: onQueryChange,
829
- queries: mergedOptions.queries
830
- });
831
- context.parameters = createParameters(mergedOptions, queryDetector);
832
- watch(
833
- () => context.parameters,
834
- (newParameters) => {
835
- var _a;
836
- (_a = context.events) == null ? void 0 : _a.parametersChange.dispatch(newParameters);
837
- },
838
- {
839
- deep: true
840
- }
841
- );
842
- async function onQueryChange() {
843
- var _a, _b;
844
- const query = queryDetector.getQuery();
845
- (_a = context.parameters) == null ? void 0 : _a.set("dt", query);
846
- (_b = context.parameters) == null ? void 0 : _b.set("br", query);
847
- await fetchAllUnrenderedSlots();
848
- }
849
- watch(() => context.consent, (newConsent) => {
850
- var _a, _b;
851
- (_a = context.parameters) == null ? void 0 : _a.set("tl", newConsent ? "all" : "none");
852
- (_b = context.events) == null ? void 0 : _b.consentChange.dispatch(newConsent);
853
- }, {
854
- immediate: true
855
- });
921
+ useMainParameters(context, mergedOptions);
856
922
  const slotManager = createSlotManager({
857
923
  initialSlots: mergedOptions.initialSlots,
858
924
  context
@@ -873,52 +939,19 @@ function createAdhese(options) {
873
939
  const domSlots = (await slotManager.findDomSlots() ?? []).filter((slot) => !slot.lazyLoading);
874
940
  if (domSlots.length <= 0)
875
941
  return [];
876
- await fetchAllUnrenderedSlots();
942
+ await fetchAllUnrenderedSlots(context.getAll());
877
943
  return domSlots;
878
944
  }
879
945
  context.findDomSlots = findDomSlots2;
880
- watch(() => context.debug, async (newDebug) => {
881
- var _a, _b;
882
- if (newDebug) {
883
- logger.setMinLogLevelThreshold("debug");
884
- logger.debug("Debug mode enabled");
885
- (_a = context.events) == null ? void 0 : _a.debugChange.dispatch(true);
886
- } else {
887
- logger.debug("Debug mode disabled");
888
- logger.setMinLogLevelThreshold("info");
889
- (_b = context.events) == null ? void 0 : _b.debugChange.dispatch(false);
890
- }
891
- }, {
892
- immediate: true
893
- });
894
- async function fetchAllUnrenderedSlots() {
895
- const slots = (slotManager.getAll() ?? []).filter((slot) => !slot.lazyLoading && !slot.ad);
896
- if (slots.length === 0)
897
- return;
898
- await Promise.allSettled(slots.map((slot) => slot.request()));
899
- }
900
- const disposeOnTcfConsentChange = onTcfConsentChange(async (data) => {
901
- var _a, _b;
902
- if (!data.tcString)
903
- return;
904
- logger.debug("TCF v2 consent data received", {
905
- data
906
- });
907
- (_a = context.parameters) == null ? void 0 : _a.set("xt", data.tcString);
908
- (_b = context.parameters) == null ? void 0 : _b.delete("tl");
909
- await fetchAllUnrenderedSlots();
910
- });
946
+ useMainDebugMode(context);
947
+ useMainQueryDetector(mergedOptions, context);
948
+ useConsent(context);
911
949
  function dispose() {
912
950
  var _a, _b;
913
951
  context.isDisposed = true;
914
- queryDetector.dispose();
915
952
  slotManager.dispose();
916
- queryDetector.dispose();
917
- disposeOnTcfConsentChange();
918
953
  (_a = context.parameters) == null ? void 0 : _a.clear();
919
- logger.resetLogs();
920
954
  (_b = context.events) == null ? void 0 : _b.dispose();
921
- logger.info("Adhese instance disposed");
922
955
  runOnDispose();
923
956
  clearAllHooks();
924
957
  scope.stop();
@@ -928,7 +961,7 @@ function createAdhese(options) {
928
961
  var _a;
929
962
  await awaitTimeout(0);
930
963
  if ((slotManager.getAll().length ?? 0) > 0)
931
- await fetchAllUnrenderedSlots().catch(logger.error);
964
+ await fetchAllUnrenderedSlots(context.getAll()).catch(logger.error);
932
965
  if (mergedOptions.findDomSlotsOnLoad)
933
966
  await (context == null ? void 0 : context.findDomSlots());
934
967
  if (mergedOptions.debug || window.location.search.includes("adhese_debug=true") || isPreviewMode())