@adhese/sdk 0.7.0 → 0.8.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.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { effectScope, shallowReactive, watchEffect, watch, reactive, ref, computed, toValue } from "@vue/runtime-core";
1
+ import { toValue, ref, computed, watch, effectScope, reactive, shallowReactive, watchEffect } from "@vue/runtime-core";
2
2
  import { debounce, round, isDeepEqual } from "remeda";
3
3
  import { union, coerce, literal, number, string, ZodIssueCode, NEVER, object, unknown, lazy } from "zod";
4
4
  async function waitForDomLoad() {
@@ -150,109 +150,16 @@ function renderInline(ad, element) {
150
150
  function generateName(location, format, slot) {
151
151
  return `${location}${slot ? `${slot}` : ""}-${format}`;
152
152
  }
153
- async function findDomSlots(context) {
154
- await waitForDomLoad();
155
- return Array.from(document.querySelectorAll(".adunit")).filter((element) => {
156
- var _a;
157
- if (!element.dataset.format)
158
- return false;
159
- const name = generateName(
160
- context.location,
161
- element.dataset.format,
162
- element.dataset.slot
163
- );
164
- return !((_a = context.getAll) == null ? void 0 : _a.call(context).some((activeSlot) => activeSlot.name.value === name));
165
- }).map((element) => createSlot({
166
- format: element.dataset.format,
167
- containingElement: element,
168
- slot: element.dataset.slot,
169
- context
170
- })).filter((slot) => {
171
- var _a;
172
- return !((_a = context.getAll) == null ? void 0 : _a.call(context).some((activeSlot) => activeSlot.name.value === slot.name.value));
173
- });
174
- }
175
- function createSlotManager({
176
- initialSlots = [],
177
- context
178
- }) {
179
- const scope = effectScope();
180
- return scope.run(() => {
181
- const slots = shallowReactive(/* @__PURE__ */ new Map());
182
- watchEffect(() => {
183
- var _a;
184
- (_a = context.events) == null ? void 0 : _a.changeSlots.dispatch(Array.from(slots.values()));
185
- });
186
- function getAll() {
187
- return Array.from(slots).map(([, slot]) => slot);
188
- }
189
- function add(options) {
190
- var _a;
191
- const slot = createSlot({
192
- ...options,
193
- onDispose: onDispose2,
194
- context
195
- });
196
- function onDispose2() {
197
- var _a2;
198
- slots.delete(slot.name.value);
199
- logger.debug("Slot removed", {
200
- slot,
201
- slots: Array.from(slots)
202
- });
203
- (_a2 = context.events) == null ? void 0 : _a2.removeSlot.dispatch(slot);
204
- }
205
- slots.set(slot.name.value, slot);
206
- watch(slot.name, (newName, previousName) => {
207
- slots.set(newName, slot);
208
- slots.delete(previousName);
209
- });
210
- logger.debug("Slot added", {
211
- slot,
212
- slots: Array.from(slots.values())
213
- });
214
- (_a = context.events) == null ? void 0 : _a.addSlot.dispatch(slot);
215
- return slot;
216
- }
217
- async function findDomSlots$1() {
218
- const domSlots = await findDomSlots(
219
- context
220
- );
221
- for (const slot of domSlots)
222
- slots.set(slot.name.value, slot);
223
- return domSlots;
224
- }
225
- function get(name) {
226
- return slots.get(name);
227
- }
228
- function dispose() {
229
- for (const slot of slots.values())
230
- slot.dispose();
231
- slots.clear();
232
- scope.stop();
233
- }
234
- for (const options of initialSlots) {
235
- add({
236
- ...options,
237
- lazyLoading: false
238
- });
239
- }
240
- return {
241
- getAll,
242
- add,
243
- findDomSlots: findDomSlots$1,
244
- get,
245
- dispose
246
- };
247
- });
248
- }
249
- function onTcfConsentChange(callback) {
250
- var _a;
251
- (_a = window.__tcfapi) == null ? void 0 : _a.call(window, "addEventListener", 2, callback);
252
- return () => {
253
- var _a2;
254
- return (_a2 = window.__tcfapi) == null ? void 0 : _a2.call(window, "removeEventListener", 2, callback);
255
- };
153
+ function addTrackingPixel(url) {
154
+ const img = document.createElement("img");
155
+ img.src = url.toString();
156
+ img.style.height = "1px";
157
+ img.style.width = "1px";
158
+ img.style.margin = "-1px";
159
+ img.style.border = "0";
160
+ img.style.position = "absolute";
161
+ img.style.top = "0";
162
+ return document.body.appendChild(img);
256
163
  }
257
164
  function createQueryDetector({
258
165
  onChange,
@@ -365,54 +272,35 @@ function createLogger({
365
272
  const logger = createLogger({
366
273
  scope: "Adhese SDK"
367
274
  });
368
- function createParameters(options, queryDetector) {
369
- const parameters = /* @__PURE__ */ new Map();
370
- if (options.logReferrer)
371
- parameters.set("re", btoa(document.referrer));
372
- if (options.logUrl)
373
- parameters.set("ur", btoa(window.location.href));
374
- for (const [key, value] of Object.entries({
375
- ...options.parameters ?? {},
376
- tl: options.consent ? "all" : "none",
377
- dt: queryDetector.getQuery(),
378
- br: queryDetector.getQuery(),
379
- rn: Math.round(Math.random() * 1e4).toString()
380
- }))
381
- parameters.set(key, value);
382
- return parameters;
275
+ const hookMap = /* @__PURE__ */ new Map();
276
+ function clearAllHooks() {
277
+ hookMap.clear();
383
278
  }
384
- function setupLogging(mergedOptions) {
385
- if (mergedOptions.debug || window.location.search.includes("adhese_debug=true")) {
386
- logger.setMinLogLevelThreshold("debug");
387
- logger.debug("Debug logging enabled");
388
- }
389
- logger.debug("Created Adhese SDK instance", {
390
- mergedOptions
391
- });
392
- }
393
- function isPreviewMode() {
394
- return window.location.search.includes("adhesePreviewCreativeId");
395
- }
396
- function createHook({
279
+ function createHook(name, {
397
280
  onRun,
398
- onAdd,
399
- onDispose: onDispose2
400
- }) {
401
- const callbacks = /* @__PURE__ */ new Set();
402
- function run() {
403
- for (const callback of callbacks)
404
- callback();
405
- onRun == null ? void 0 : onRun(callbacks);
406
- }
281
+ onAdd
282
+ } = {}) {
283
+ hookMap.set(name, /* @__PURE__ */ new Set());
284
+ const run = async (arg) => {
285
+ let latestResult = arg;
286
+ for (const callback of hookMap.get(name) ?? [])
287
+ latestResult = await callback(latestResult) ?? latestResult;
288
+ onRun == null ? void 0 : onRun(hookMap.get(name));
289
+ return latestResult;
290
+ };
407
291
  function add(callback) {
408
- callbacks.add(callback);
409
- onAdd == null ? void 0 : onAdd(callbacks);
410
- }
411
- function dispose() {
412
- onDispose2 == null ? void 0 : onDispose2(callbacks);
413
- callbacks.clear();
292
+ const hookSet = hookMap.get(name);
293
+ if (hookSet)
294
+ hookSet.add(callback);
295
+ else
296
+ hookMap.set(name, /* @__PURE__ */ new Set([callback]));
297
+ onAdd == null ? void 0 : onAdd(hookSet);
298
+ return () => {
299
+ var _a;
300
+ (_a = hookMap.get(name)) == null ? void 0 : _a.delete(callback);
301
+ };
414
302
  }
415
- return [run, add, dispose];
303
+ return [run, add];
416
304
  }
417
305
  let resolveOnInitPromise = () => {
418
306
  };
@@ -420,246 +308,313 @@ let isInit = false;
420
308
  const waitOnInit = new Promise((resolve) => {
421
309
  resolveOnInitPromise = resolve;
422
310
  });
423
- const [runOnInit, onInit, disposeOnInit] = createHook({
311
+ const [runOnInit, onInit] = createHook("onInit", {
424
312
  onRun(callbacks) {
425
313
  isInit = true;
426
314
  resolveOnInitPromise();
427
315
  logger.debug("Initialization completed");
428
- callbacks.clear();
316
+ callbacks == null ? void 0 : callbacks.clear();
429
317
  },
430
318
  onAdd() {
431
319
  if (isInit)
432
- runOnInit();
320
+ runOnInit().catch(logger.error);
433
321
  }
434
322
  });
435
- let resolveOnDisposePromise = () => {
436
- };
437
- let isDisposed = false;
438
- new Promise((resolve) => {
439
- resolveOnDisposePromise = resolve;
440
- });
441
- const [runOnDispose, onDispose, disposeOnDispose] = createHook({
442
- onRun(callbacks) {
443
- isDisposed = true;
444
- resolveOnDisposePromise();
445
- logger.debug("Disposal completed");
446
- callbacks.clear();
447
- },
448
- onAdd() {
449
- if (isDisposed)
450
- runOnDispose();
323
+ const [runOnRequest, onRequest] = createHook("onRequest");
324
+ const [runOnResponse, onResponse] = createHook("onResponse");
325
+ const numberLike = union([coerce.string().regex(/^\d+$/), literal("")]).transform((value) => value === "" ? void 0 : Number(value));
326
+ const booleanLike = union([coerce.boolean(), literal("")]);
327
+ const urlLike = union([coerce.string(), literal("")]).transform((value) => {
328
+ try {
329
+ return new URL(value);
330
+ } catch {
331
+ return void 0;
451
332
  }
452
333
  });
453
- function createAdhese(options) {
454
- const scope = effectScope();
455
- return scope.run(() => {
456
- const mergedOptions = {
457
- host: `https://ads-${options.account}.adhese.com`,
458
- poolHost: `https://pool-${options.account}.adhese.com`,
459
- location: "homepage",
460
- requestType: "POST",
461
- debug: false,
462
- initialSlots: [],
463
- findDomSlotsOnLoad: false,
464
- consent: false,
465
- logReferrer: true,
466
- logUrl: true,
467
- safeFrame: false,
468
- eagerRendering: false,
469
- viewabilityTracking: true,
470
- plugins: [],
471
- ...options
472
- };
473
- setupLogging(mergedOptions);
474
- const context = reactive({
475
- location: mergedOptions.location,
476
- consent: mergedOptions.consent,
477
- debug: mergedOptions.debug,
478
- getAll,
479
- get,
480
- options: mergedOptions,
481
- logger
334
+ const dateLike = union([coerce.string(), literal("")]).transform((value) => {
335
+ if (value === "")
336
+ return void 0;
337
+ const date = new Date(numberLike.safeParse(value).success ? Number(value) : value);
338
+ if (Number.isNaN(date.getTime()))
339
+ return void 0;
340
+ return date;
341
+ });
342
+ const cssValueLike = union([coerce.string(), literal(""), number()]).transform((value) => {
343
+ if (value === "" || value === 0 || value === "0")
344
+ return void 0;
345
+ if (numberLike.parse(value))
346
+ return `${numberLike.parse(value)}px`;
347
+ return String(value);
348
+ });
349
+ const isJson = string().transform((value, { addIssue }) => {
350
+ try {
351
+ return JSON.parse(value.replaceAll("'", '"'));
352
+ } catch (error) {
353
+ addIssue({
354
+ code: ZodIssueCode.custom,
355
+ message: `Invalid JSON: ${error.message}`
482
356
  });
483
- context.events = createEventManager();
484
- context.safeFrame = options.safeFrame ? createSafeFrame({
485
- renderFile: `${mergedOptions.poolHost}/sf/r.html`,
486
- context
487
- }) : void 0;
488
- function getLocation() {
489
- return context.location;
490
- }
491
- function setLocation(newLocation) {
492
- var _a;
493
- context.location = newLocation;
494
- (_a = context.events) == null ? void 0 : _a.locationChange.dispatch(newLocation);
495
- }
496
- const queryDetector = createQueryDetector({
497
- onChange: onQueryChange,
498
- queries: mergedOptions.queries
357
+ return NEVER;
358
+ }
359
+ });
360
+ const isHtmlString = string().transform((value, { addIssue }) => {
361
+ var _a;
362
+ const htmlParser = new DOMParser();
363
+ try {
364
+ const html = htmlParser.parseFromString(value, "text/html");
365
+ if (((_a = html.body) == null ? void 0 : _a.children.length) === 0)
366
+ throw new Error("Invalid HTML");
367
+ return value;
368
+ } catch (error) {
369
+ addIssue({
370
+ code: ZodIssueCode.custom,
371
+ message: error.message
499
372
  });
500
- context.parameters = createParameters(mergedOptions, queryDetector);
501
- watch(
502
- context.parameters,
503
- onParametersChange,
504
- {
505
- deep: true,
506
- immediate: true
507
- }
508
- );
509
- function onParametersChange() {
510
- var _a;
511
- if (context.parameters)
512
- (_a = context.events) == null ? void 0 : _a.parametersChange.dispatch(context.parameters);
513
- }
514
- async function onQueryChange() {
515
- var _a, _b;
516
- const query = queryDetector.getQuery();
517
- (_a = context.parameters) == null ? void 0 : _a.set("dt", query);
518
- (_b = context.parameters) == null ? void 0 : _b.set("br", query);
519
- await fetchAndRenderAllSlots();
520
- }
521
- function getConsent() {
522
- return context.consent;
523
- }
524
- function setConsent(newConsent) {
525
- var _a, _b;
526
- (_a = context.parameters) == null ? void 0 : _a.set("tl", newConsent ? "all" : "none");
527
- context.consent = newConsent;
528
- (_b = context.events) == null ? void 0 : _b.consentChange.dispatch(newConsent);
529
- }
530
- const slotManager = createSlotManager({
531
- initialSlots: mergedOptions.initialSlots,
532
- context
533
- });
534
- function getAll() {
535
- return slotManager.getAll() ?? [];
536
- }
537
- function get(name) {
538
- return slotManager.get(name);
539
- }
540
- async function addSlot(slotOptions) {
541
- if (!slotManager)
542
- throw new Error("Slot manager not initialized");
543
- const slot = slotManager.add(slotOptions);
544
- if (!slot.lazyLoading) {
545
- slot.ad.value = await requestAd({
546
- slot,
547
- context
548
- });
373
+ return NEVER;
374
+ }
375
+ });
376
+ const isJsonOrHtmlString = union([isJson, isHtmlString]);
377
+ const isJsonOrHtmlOptionalString = union([coerce.string(), isJsonOrHtmlString]).transform((value) => {
378
+ if (value === "")
379
+ return void 0;
380
+ return value;
381
+ }).optional();
382
+ const baseSchema = object({
383
+ adDuration: numberLike.optional(),
384
+ adFormat: string().optional(),
385
+ adType: string(),
386
+ additionalCreativeTracker: urlLike.optional(),
387
+ additionalViewableTracker: string().optional(),
388
+ adspaceEnd: dateLike.optional(),
389
+ adspaceId: string().optional(),
390
+ adspaceKey: string().optional(),
391
+ adspaceStart: dateLike.optional(),
392
+ advertiserId: string().optional(),
393
+ altText: string().optional(),
394
+ auctionable: booleanLike.optional(),
395
+ body: isJsonOrHtmlOptionalString,
396
+ clickTag: urlLike.optional(),
397
+ comment: string().optional(),
398
+ creativeName: string().optional(),
399
+ deliveryGroupId: string().optional(),
400
+ deliveryMultiples: string().optional(),
401
+ ext: string().optional(),
402
+ extension: object({
403
+ mediaType: string(),
404
+ prebid: unknown().optional()
405
+ }).optional(),
406
+ height: numberLike.optional(),
407
+ id: string(),
408
+ impressionCounter: urlLike.optional(),
409
+ libId: string().optional(),
410
+ orderId: string().optional(),
411
+ orderName: string().optional(),
412
+ orderProperty: string().optional(),
413
+ origin: union([literal("JERLICIA"), literal("DALE")]),
414
+ originData: unknown().optional(),
415
+ originInstance: string().optional(),
416
+ poolPath: urlLike.optional(),
417
+ preview: booleanLike.optional(),
418
+ priority: numberLike.optional(),
419
+ sfSrc: urlLike.optional(),
420
+ share: string().optional(),
421
+ // eslint-disable-next-line ts/naming-convention
422
+ slotID: string(),
423
+ slotName: string(),
424
+ swfSrc: urlLike.optional(),
425
+ tag: isJsonOrHtmlOptionalString,
426
+ tagUrl: urlLike.optional(),
427
+ timeStamp: dateLike.optional(),
428
+ trackedImpressionCounter: urlLike.optional(),
429
+ tracker: urlLike.optional(),
430
+ trackingUrl: urlLike.optional(),
431
+ url: urlLike.optional(),
432
+ viewableImpressionCounter: urlLike.optional(),
433
+ width: numberLike.optional(),
434
+ widthLarge: cssValueLike.optional()
435
+ });
436
+ const jerliciaSchema = object({
437
+ origin: literal("JERLICIA"),
438
+ tag: isJsonOrHtmlString
439
+ }).passthrough();
440
+ const daleSchema = object({
441
+ origin: literal("DALE"),
442
+ body: isJsonOrHtmlString
443
+ }).passthrough().transform(({ body, ...data }) => ({
444
+ ...data,
445
+ tag: body
446
+ }));
447
+ const adResponseSchema = baseSchema.extend({
448
+ additionalCreatives: lazy(() => union([adResponseSchema.array(), string()]).optional())
449
+ });
450
+ const adSchema = adResponseSchema.transform(({
451
+ additionalCreatives,
452
+ ...data
453
+ }) => {
454
+ const filteredValue = Object.fromEntries(
455
+ Object.entries(data).filter(([, value]) => Boolean(value) && JSON.stringify(value) !== "{}" && JSON.stringify(value) !== "[]")
456
+ );
457
+ return {
458
+ ...filteredValue,
459
+ additionalCreatives: Array.isArray(additionalCreatives) ? additionalCreatives.map((creative) => adSchema.parse(creative)) : additionalCreatives
460
+ };
461
+ });
462
+ function parseResponse(response) {
463
+ const schemaMap = {
464
+ /* eslint-disable ts/naming-convention */
465
+ JERLICIA: jerliciaSchema,
466
+ DALE: daleSchema
467
+ /* eslint-enable ts/naming-convention */
468
+ };
469
+ const preParsed = adResponseSchema.array().parse(response);
470
+ return preParsed.map((item) => {
471
+ const schema = schemaMap[item.origin];
472
+ if (!schema)
473
+ return adSchema.parse(item);
474
+ return schema.parse(item);
475
+ });
476
+ }
477
+ async function requestPreviews(account) {
478
+ const previewObjects = getPreviewObjects();
479
+ const list = (await Promise.allSettled(previewObjects.filter((previewObject) => "adhesePreviewCreativeId" in previewObject).map(async (previewObject) => {
480
+ const endpoint = new URL(`https://${account}-preview.adhese.org/creatives/preview/json/tag.do`);
481
+ endpoint.searchParams.set(
482
+ "id",
483
+ previewObject.adhesePreviewCreativeId
484
+ );
485
+ const response = await fetch(endpoint.href, {
486
+ method: "GET",
487
+ headers: {
488
+ accept: "application/json"
549
489
  }
550
- return slot;
490
+ });
491
+ if (!response.ok)
492
+ return Promise.reject(new Error(`Failed to request preview ad with ID: ${previewObject.adhesePreviewCreativeId}`));
493
+ return await response.json();
494
+ }))).filter((response) => {
495
+ if (response.status === "rejected") {
496
+ logger.error(response.reason);
497
+ return false;
551
498
  }
552
- async function findDomSlots2() {
553
- const domSlots = (await slotManager.findDomSlots() ?? []).filter((slot) => !slot.lazyLoading);
554
- if (domSlots.length <= 0)
555
- return [];
556
- const ads = await requestAds({
557
- slots: domSlots,
558
- context
559
- });
560
- for (const ad of ads) {
561
- const slot = slotManager.get(ad.slotName);
562
- if (slot)
563
- slot.ad.value = ad;
564
- }
565
- return domSlots;
499
+ return response.status === "fulfilled";
500
+ }).map((response) => response.value.map((item) => ({
501
+ ...item,
502
+ preview: true
503
+ })));
504
+ return adSchema.array().parse(list.flat());
505
+ }
506
+ function getPreviewObjects() {
507
+ const currentUrl = new URL(window.location.href);
508
+ const previewObjects = [];
509
+ let currentObject = {};
510
+ for (const [key, value] of currentUrl.searchParams.entries()) {
511
+ if (key === "adhesePreviewCreativeId" && Object.keys(currentObject).length > 0) {
512
+ previewObjects.push(currentObject);
513
+ currentObject = {};
566
514
  }
567
- async function toggleDebug() {
568
- var _a, _b;
569
- context.debug = !context.debug;
570
- if (context.debug) {
571
- logger.setMinLogLevelThreshold("debug");
572
- logger.debug("Debug mode enabled");
573
- (_a = context.events) == null ? void 0 : _a.debugChange.dispatch(true);
574
- } else {
575
- logger.debug("Debug mode disabled");
576
- logger.setMinLogLevelThreshold("info");
577
- (_b = context.events) == null ? void 0 : _b.debugChange.dispatch(false);
578
- }
579
- return context.debug;
515
+ currentObject[key] = value;
516
+ }
517
+ if (Object.keys(currentObject).length > 0)
518
+ previewObjects.push(currentObject);
519
+ return previewObjects;
520
+ }
521
+ function requestWithPost({
522
+ context: { options: { host }, parameters },
523
+ ...options
524
+ }) {
525
+ const payload = {
526
+ ...options,
527
+ slots: options.slots.map((slot) => ({
528
+ slotname: toValue(slot.name),
529
+ parameters: parseParameters(slot.parameters)
530
+ })),
531
+ parameters: parameters && parseParameters(parameters)
532
+ };
533
+ return fetch(`${new URL(host).href}json`, {
534
+ method: "POST",
535
+ body: JSON.stringify(payload),
536
+ headers: {
537
+ // eslint-disable-next-line ts/naming-convention
538
+ "Content-Type": "application/json"
580
539
  }
581
- async function fetchAndRenderAllSlots() {
582
- const slots = (slotManager.getAll() ?? []).filter((slot) => !slot.lazyLoading);
583
- if (slots.length === 0)
584
- return;
585
- const ads = await requestAds({
586
- slots,
587
- context
588
- });
589
- for (const ad of ads) {
590
- const slot = slotManager.get(ad.slotName);
591
- if (slot)
592
- slot.ad.value = ad;
593
- }
540
+ });
541
+ }
542
+ async function requestWithGet({ context, slots }) {
543
+ return fetch(new URL(`${context.options.host}/json/sl${slots.map((slot) => toValue(slot.name)).join("/sl")}`), {
544
+ method: "GET",
545
+ headers: {
546
+ // eslint-disable-next-line ts/naming-convention
547
+ "Content-Type": "application/json"
594
548
  }
595
- const disposeOnTcfConsentChange = onTcfConsentChange(async (data) => {
596
- var _a, _b;
597
- if (!data.tcString)
598
- return;
599
- logger.debug("TCF v2 consent data received", {
600
- data
601
- });
602
- (_a = context.parameters) == null ? void 0 : _a.set("xt", data.tcString);
603
- (_b = context.parameters) == null ? void 0 : _b.delete("tl");
604
- await fetchAndRenderAllSlots();
549
+ });
550
+ }
551
+ function parseParameters(parameters) {
552
+ return Object.fromEntries(Array.from(parameters.entries()).filter(([key]) => {
553
+ if (key.length === 2)
554
+ return true;
555
+ logger.warn(`Invalid parameter key: ${key}. Key should be exactly 2 characters long. Key will be ignored.`);
556
+ return false;
557
+ }).map(([key, value]) => {
558
+ if (typeof value === "string")
559
+ return [key, filterSpecialChars(value)];
560
+ return [key, value.map(filterSpecialChars)];
561
+ }));
562
+ }
563
+ function filterSpecialChars(value) {
564
+ const specialRegex = /[^\p{L}\p{N}_]/gu;
565
+ return value.replaceAll(specialRegex, "_");
566
+ }
567
+ async function requestAds(requestOptions) {
568
+ var _a, _b, _c, _d, _e;
569
+ const options = await runOnRequest(requestOptions);
570
+ const { context } = options;
571
+ try {
572
+ (_a = context.events) == null ? void 0 : _a.requestAd.dispatch({
573
+ ...options,
574
+ context
605
575
  });
606
- function dispose() {
607
- var _a, _b;
608
- queryDetector.dispose();
609
- slotManager.dispose();
610
- queryDetector.dispose();
611
- disposeOnTcfConsentChange();
612
- (_a = context.parameters) == null ? void 0 : _a.clear();
613
- logger.resetLogs();
614
- (_b = context.events) == null ? void 0 : _b.dispose();
615
- logger.info("Adhese instance disposed");
616
- runOnDispose();
617
- disposeOnInit();
618
- disposeOnDispose();
619
- scope.stop();
620
- }
621
- for (const plugin of mergedOptions.plugins)
622
- plugin(context);
623
- onInit(async () => {
624
- var _a;
625
- if ((slotManager.getAll().length ?? 0) > 0)
626
- await fetchAndRenderAllSlots().catch(logger.error);
627
- if (mergedOptions.findDomSlotsOnLoad)
628
- await findDomSlots2();
629
- if (mergedOptions.debug || window.location.search.includes("adhese_debug=true") || isPreviewMode())
630
- (_a = context.events) == null ? void 0 : _a.debugChange.dispatch(true);
631
- if (!scope.active)
632
- dispose();
576
+ const [response, previews] = await Promise.all([((_b = context.options.requestType) == null ? void 0 : _b.toUpperCase()) === "POST" ? requestWithPost(options) : requestWithGet(options), requestPreviews(context.options.account)]);
577
+ logger.debug("Received response", response);
578
+ if (!response.ok)
579
+ throw new Error(`Failed to request ad: ${response.status} ${response.statusText}`);
580
+ const result = parseResponse(await response.json());
581
+ logger.debug("Parsed ad", result);
582
+ if (previews.length > 0)
583
+ logger.info(`Found ${previews.length} ${previews.length === 1 ? "preview" : "previews"}. Replacing ads in response with preview items`, previews);
584
+ const matchedPreviews = previews.map(({ slotName, ...preview }) => {
585
+ const partnerAd = result.find((ad) => ad.libId === preview.libId);
586
+ return {
587
+ slotName: `${(partnerAd == null ? void 0 : partnerAd.slotName) ?? slotName}`,
588
+ ...preview
589
+ };
633
590
  });
634
- runOnInit();
635
- return {
636
- ...mergedOptions,
637
- ...slotManager,
638
- parameters: context.parameters,
639
- events: context.events,
640
- getLocation,
641
- setLocation,
642
- getConsent,
643
- setConsent,
644
- addSlot,
645
- findDomSlots: findDomSlots2,
646
- dispose,
647
- toggleDebug,
648
- context
649
- };
650
- });
591
+ if (matchedPreviews.length > 0)
592
+ (_c = context.events) == null ? void 0 : _c.previewReceived.dispatch(matchedPreviews);
593
+ const mergedResult = await runOnResponse([
594
+ ...result.filter((ad) => !previews.some((preview) => preview.libId === ad.libId)),
595
+ ...matchedPreviews
596
+ ]);
597
+ if (mergedResult.length === 0)
598
+ throw new Error("No ads found");
599
+ (_d = context.events) == null ? void 0 : _d.responseReceived.dispatch(mergedResult);
600
+ return mergedResult;
601
+ } catch (error) {
602
+ logger.error(String(error));
603
+ (_e = context.events) == null ? void 0 : _e.requestError.dispatch(error);
604
+ throw error;
605
+ }
651
606
  }
652
- function addTrackingPixel(url) {
653
- const img = document.createElement("img");
654
- img.src = url.toString();
655
- img.style.height = "1px";
656
- img.style.width = "1px";
657
- img.style.margin = "-1px";
658
- img.style.border = "0";
659
- img.style.position = "absolute";
660
- img.style.top = "0";
661
- return document.body.appendChild(img);
607
+ async function requestAd({
608
+ slot,
609
+ ...options
610
+ }) {
611
+ const [ad] = await requestAds({
612
+ slots: [slot],
613
+ ...options
614
+ });
615
+ return ad;
662
616
  }
617
+ const [runOnRender, onRender] = createHook("onRender");
663
618
  function useViewabilityObserver({ context, ad, name, element }) {
664
619
  let timeoutId = null;
665
620
  const {
@@ -840,10 +795,10 @@ function createSlot(options) {
840
795
  var _a, _b;
841
796
  await waitForDomLoad();
842
797
  await waitOnInit;
843
- const renderAd = adToRender ?? ad.value ?? await requestAd$1();
844
- if (originalAd.value) {
845
- ad.value = ((_a = options.onBeforeRender) == null ? void 0 : _a.call(options, adToRender ?? originalAd.value)) ?? renderAd;
846
- }
798
+ let renderAd = adToRender ?? ad.value ?? originalAd.value ?? await requestAd$1();
799
+ if (renderAd)
800
+ renderAd = ((_a = options.onBeforeRender) == null ? void 0 : _a.call(options, renderAd)) ?? renderAd;
801
+ renderAd = await runOnRender(renderAd);
847
802
  if (!element.value) {
848
803
  const error = `Could not create slot for format ${format.value}. No element found.`;
849
804
  logger.error(error, options);
@@ -851,8 +806,8 @@ function createSlot(options) {
851
806
  }
852
807
  if (context.debug)
853
808
  element.value.style.position = "relative";
854
- if (context.safeFrame && ad.value && renderMode === "iframe") {
855
- const position = context.safeFrame.addPosition(ad.value, element.value);
809
+ if (context.safeFrame && renderAd && renderMode === "iframe") {
810
+ const position = context.safeFrame.addPosition(renderAd, element.value);
856
811
  await context.safeFrame.render(position);
857
812
  } else {
858
813
  renderFunctions[renderMode](renderAd, element.value);
@@ -891,323 +846,378 @@ function createSlot(options) {
891
846
  scope.stop();
892
847
  }
893
848
  return {
894
- location: context.location,
895
- lazyLoading: options.lazyLoading ?? false,
896
- slot,
897
- parameters,
898
- format,
899
- name,
900
- ad,
901
- isViewabilityTracked,
902
- isImpressionTracked,
903
- render,
904
- getElement,
849
+ location: context.location,
850
+ lazyLoading: options.lazyLoading ?? false,
851
+ slot,
852
+ parameters,
853
+ format,
854
+ name,
855
+ ad,
856
+ isViewabilityTracked,
857
+ isImpressionTracked,
858
+ render,
859
+ getElement,
860
+ dispose
861
+ };
862
+ });
863
+ }
864
+ function useDomLoaded() {
865
+ const isDomLoaded = ref(false);
866
+ onInit(async () => {
867
+ await waitForDomLoad();
868
+ isDomLoaded.value = true;
869
+ });
870
+ return isDomLoaded;
871
+ }
872
+ async function findDomSlots(context) {
873
+ await waitForDomLoad();
874
+ return Array.from(document.querySelectorAll(".adunit")).filter((element) => {
875
+ var _a;
876
+ if (!element.dataset.format)
877
+ return false;
878
+ const name = generateName(
879
+ context.location,
880
+ element.dataset.format,
881
+ element.dataset.slot
882
+ );
883
+ return !((_a = context.getAll) == null ? void 0 : _a.call(context).some((activeSlot) => activeSlot.name.value === name));
884
+ }).map((element) => createSlot({
885
+ format: element.dataset.format,
886
+ containingElement: element,
887
+ slot: element.dataset.slot,
888
+ context
889
+ })).filter((slot) => {
890
+ var _a;
891
+ return !((_a = context.getAll) == null ? void 0 : _a.call(context).some((activeSlot) => activeSlot.name.value === slot.name.value));
892
+ });
893
+ }
894
+ function createSlotManager({
895
+ initialSlots = [],
896
+ context
897
+ }) {
898
+ const scope = effectScope();
899
+ return scope.run(() => {
900
+ const slots = shallowReactive(/* @__PURE__ */ new Map());
901
+ watchEffect(() => {
902
+ var _a;
903
+ (_a = context.events) == null ? void 0 : _a.changeSlots.dispatch(Array.from(slots.values()));
904
+ });
905
+ function getAll() {
906
+ return Array.from(slots).map(([, slot]) => slot);
907
+ }
908
+ function add(options) {
909
+ var _a;
910
+ const slot = createSlot({
911
+ ...options,
912
+ onDispose: onDispose2,
913
+ context
914
+ });
915
+ function onDispose2() {
916
+ var _a2;
917
+ slots.delete(slot.name.value);
918
+ logger.debug("Slot removed", {
919
+ slot,
920
+ slots: Array.from(slots)
921
+ });
922
+ (_a2 = context.events) == null ? void 0 : _a2.removeSlot.dispatch(slot);
923
+ }
924
+ slots.set(slot.name.value, slot);
925
+ watch(slot.name, (newName, previousName) => {
926
+ slots.set(newName, slot);
927
+ slots.delete(previousName);
928
+ });
929
+ logger.debug("Slot added", {
930
+ slot,
931
+ slots: Array.from(slots.values())
932
+ });
933
+ (_a = context.events) == null ? void 0 : _a.addSlot.dispatch(slot);
934
+ return slot;
935
+ }
936
+ async function findDomSlots$1() {
937
+ const domSlots = await findDomSlots(
938
+ context
939
+ );
940
+ for (const slot of domSlots)
941
+ slots.set(slot.name.value, slot);
942
+ return domSlots;
943
+ }
944
+ function get(name) {
945
+ return slots.get(name);
946
+ }
947
+ function dispose() {
948
+ for (const slot of slots.values())
949
+ slot.dispose();
950
+ slots.clear();
951
+ scope.stop();
952
+ }
953
+ for (const options of initialSlots) {
954
+ add({
955
+ ...options,
956
+ lazyLoading: false
957
+ });
958
+ }
959
+ return {
960
+ getAll,
961
+ add,
962
+ findDomSlots: findDomSlots$1,
963
+ get,
905
964
  dispose
906
965
  };
907
966
  });
908
967
  }
909
- function useDomLoaded() {
910
- const isDomLoaded = ref(false);
911
- onInit(async () => {
912
- await waitForDomLoad();
913
- isDomLoaded.value = true;
914
- });
915
- return isDomLoaded;
968
+ function onTcfConsentChange(callback) {
969
+ var _a;
970
+ (_a = window.__tcfapi) == null ? void 0 : _a.call(window, "addEventListener", 2, callback);
971
+ return () => {
972
+ var _a2;
973
+ return (_a2 = window.__tcfapi) == null ? void 0 : _a2.call(window, "removeEventListener", 2, callback);
974
+ };
916
975
  }
917
- const numberLike = union([coerce.string().regex(/^\d+$/), literal("")]).transform((value) => value === "" ? void 0 : Number(value));
918
- const booleanLike = union([coerce.boolean(), literal("")]);
919
- const urlLike = union([coerce.string(), literal("")]).transform((value) => {
920
- try {
921
- return new URL(value);
922
- } catch {
923
- return void 0;
976
+ function createParameters(options, queryDetector) {
977
+ const parameters = /* @__PURE__ */ new Map();
978
+ if (options.logReferrer)
979
+ parameters.set("re", btoa(document.referrer));
980
+ if (options.logUrl)
981
+ parameters.set("ur", btoa(window.location.href));
982
+ for (const [key, value] of Object.entries({
983
+ ...options.parameters ?? {},
984
+ tl: options.consent ? "all" : "none",
985
+ dt: queryDetector.getQuery(),
986
+ br: queryDetector.getQuery(),
987
+ rn: Math.round(Math.random() * 1e4).toString()
988
+ }))
989
+ parameters.set(key, value);
990
+ return parameters;
991
+ }
992
+ function setupLogging(mergedOptions) {
993
+ if (mergedOptions.debug || window.location.search.includes("adhese_debug=true")) {
994
+ logger.setMinLogLevelThreshold("debug");
995
+ logger.debug("Debug logging enabled");
924
996
  }
925
- });
926
- const dateLike = union([coerce.string(), literal("")]).transform((value) => {
927
- if (value === "")
928
- return void 0;
929
- const date = new Date(numberLike.safeParse(value).success ? Number(value) : value);
930
- if (Number.isNaN(date.getTime()))
931
- return void 0;
932
- return date;
933
- });
934
- const cssValueLike = union([coerce.string(), literal(""), number()]).transform((value) => {
935
- if (value === "" || value === 0 || value === "0")
936
- return void 0;
937
- if (numberLike.parse(value))
938
- return `${numberLike.parse(value)}px`;
939
- return String(value);
940
- });
941
- const isJson = string().transform((value, { addIssue }) => {
942
- try {
943
- return JSON.parse(value.replaceAll("'", '"'));
944
- } catch (error) {
945
- addIssue({
946
- code: ZodIssueCode.custom,
947
- message: `Invalid JSON: ${error.message}`
948
- });
949
- return NEVER;
997
+ logger.debug("Created Adhese SDK instance", {
998
+ mergedOptions
999
+ });
1000
+ }
1001
+ function isPreviewMode() {
1002
+ return window.location.search.includes("adhesePreviewCreativeId");
1003
+ }
1004
+ let isDisposed = false;
1005
+ const [runOnDispose, onDispose] = createHook("onDispose", {
1006
+ onRun(callbacks) {
1007
+ isDisposed = true;
1008
+ logger.debug("Disposal completed");
1009
+ callbacks == null ? void 0 : callbacks.clear();
1010
+ },
1011
+ onAdd() {
1012
+ if (isDisposed)
1013
+ runOnDispose().catch(logger.error);
950
1014
  }
951
1015
  });
952
- const isHtmlString = string().transform((value, { addIssue }) => {
953
- var _a;
954
- const htmlParser = new DOMParser();
955
- try {
956
- const html = htmlParser.parseFromString(value, "text/html");
957
- if (((_a = html.body) == null ? void 0 : _a.children.length) === 0)
958
- throw new Error("Invalid HTML");
959
- return value;
960
- } catch (error) {
961
- addIssue({
962
- code: ZodIssueCode.custom,
963
- message: error.message
1016
+ function createAdhese(options) {
1017
+ const scope = effectScope();
1018
+ return scope.run(() => {
1019
+ const mergedOptions = {
1020
+ host: `https://ads-${options.account}.adhese.com`,
1021
+ poolHost: `https://pool-${options.account}.adhese.com`,
1022
+ location: "homepage",
1023
+ requestType: "POST",
1024
+ debug: false,
1025
+ initialSlots: [],
1026
+ findDomSlotsOnLoad: false,
1027
+ consent: false,
1028
+ logReferrer: true,
1029
+ logUrl: true,
1030
+ safeFrame: false,
1031
+ eagerRendering: false,
1032
+ viewabilityTracking: true,
1033
+ plugins: [],
1034
+ ...options
1035
+ };
1036
+ setupLogging(mergedOptions);
1037
+ const context = reactive({
1038
+ location: mergedOptions.location,
1039
+ consent: mergedOptions.consent,
1040
+ debug: mergedOptions.debug,
1041
+ getAll,
1042
+ get,
1043
+ options: mergedOptions,
1044
+ logger,
1045
+ addSlot
964
1046
  });
965
- return NEVER;
966
- }
967
- });
968
- const isJsonOrHtmlString = union([isJson, isHtmlString]);
969
- const isJsonOrHtmlOptionalString = union([coerce.string(), isJsonOrHtmlString]).transform((value) => {
970
- if (value === "")
971
- return void 0;
972
- return value;
973
- }).optional();
974
- const baseSchema = object({
975
- adDuration: numberLike.optional(),
976
- adFormat: string().optional(),
977
- adType: string(),
978
- additionalCreativeTracker: urlLike.optional(),
979
- additionalViewableTracker: string().optional(),
980
- adspaceEnd: dateLike.optional(),
981
- adspaceId: string().optional(),
982
- adspaceKey: string().optional(),
983
- adspaceStart: dateLike.optional(),
984
- advertiserId: string().optional(),
985
- altText: string().optional(),
986
- auctionable: booleanLike.optional(),
987
- body: isJsonOrHtmlOptionalString,
988
- clickTag: urlLike.optional(),
989
- comment: string().optional(),
990
- creativeName: string().optional(),
991
- deliveryGroupId: string().optional(),
992
- deliveryMultiples: string().optional(),
993
- ext: string().optional(),
994
- extension: object({
995
- mediaType: string(),
996
- prebid: unknown().optional()
997
- }).optional(),
998
- height: numberLike.optional(),
999
- id: string(),
1000
- impressionCounter: urlLike.optional(),
1001
- libId: string().optional(),
1002
- orderId: string().optional(),
1003
- orderName: string().optional(),
1004
- orderProperty: string().optional(),
1005
- origin: union([literal("JERLICIA"), literal("DALE")]),
1006
- originData: unknown().optional(),
1007
- originInstance: string().optional(),
1008
- poolPath: urlLike.optional(),
1009
- preview: booleanLike.optional(),
1010
- priority: numberLike.optional(),
1011
- sfSrc: urlLike.optional(),
1012
- share: string().optional(),
1013
- // eslint-disable-next-line ts/naming-convention
1014
- slotID: string(),
1015
- slotName: string(),
1016
- swfSrc: urlLike.optional(),
1017
- tag: isJsonOrHtmlOptionalString,
1018
- tagUrl: urlLike.optional(),
1019
- timeStamp: dateLike.optional(),
1020
- trackedImpressionCounter: urlLike.optional(),
1021
- tracker: urlLike.optional(),
1022
- trackingUrl: urlLike.optional(),
1023
- url: urlLike.optional(),
1024
- viewableImpressionCounter: urlLike.optional(),
1025
- width: numberLike.optional(),
1026
- widthLarge: cssValueLike.optional()
1027
- });
1028
- const jerliciaSchema = object({
1029
- origin: literal("JERLICIA"),
1030
- tag: isJsonOrHtmlString
1031
- }).passthrough();
1032
- const daleSchema = object({
1033
- origin: literal("DALE"),
1034
- body: isJsonOrHtmlString
1035
- }).passthrough().transform(({ body, ...data }) => ({
1036
- ...data,
1037
- tag: body
1038
- }));
1039
- const adResponseSchema = baseSchema.extend({
1040
- additionalCreatives: lazy(() => union([adResponseSchema.array(), string()]).optional())
1041
- });
1042
- const adSchema = adResponseSchema.transform(({
1043
- additionalCreatives,
1044
- ...data
1045
- }) => {
1046
- const filteredValue = Object.fromEntries(
1047
- Object.entries(data).filter(([, value]) => Boolean(value) && JSON.stringify(value) !== "{}" && JSON.stringify(value) !== "[]")
1048
- );
1049
- return {
1050
- ...filteredValue,
1051
- additionalCreatives: Array.isArray(additionalCreatives) ? additionalCreatives.map((creative) => adSchema.parse(creative)) : additionalCreatives
1052
- };
1053
- });
1054
- function parseResponse(response) {
1055
- const schemaMap = {
1056
- /* eslint-disable ts/naming-convention */
1057
- JERLICIA: jerliciaSchema,
1058
- DALE: daleSchema
1059
- /* eslint-enable ts/naming-convention */
1060
- };
1061
- const preParsed = adResponseSchema.array().parse(response);
1062
- return preParsed.map((item) => {
1063
- const schema = schemaMap[item.origin];
1064
- if (!schema)
1065
- return adSchema.parse(item);
1066
- return schema.parse(item);
1067
- });
1068
- }
1069
- async function requestPreviews(account) {
1070
- const previewObjects = getPreviewObjects();
1071
- const list = (await Promise.allSettled(previewObjects.filter((previewObject) => "adhesePreviewCreativeId" in previewObject).map(async (previewObject) => {
1072
- const endpoint = new URL(`https://${account}-preview.adhese.org/creatives/preview/json/tag.do`);
1073
- endpoint.searchParams.set(
1074
- "id",
1075
- previewObject.adhesePreviewCreativeId
1076
- );
1077
- const response = await fetch(endpoint.href, {
1078
- method: "GET",
1079
- headers: {
1080
- accept: "application/json"
1081
- }
1047
+ for (const [index, plugin] of mergedOptions.plugins.entries()) {
1048
+ plugin(context, {
1049
+ index
1050
+ });
1051
+ }
1052
+ context.events = createEventManager();
1053
+ context.safeFrame = options.safeFrame ? createSafeFrame({
1054
+ renderFile: `${mergedOptions.poolHost}/sf/r.html`,
1055
+ context
1056
+ }) : void 0;
1057
+ function getLocation() {
1058
+ return context.location;
1059
+ }
1060
+ function setLocation(newLocation) {
1061
+ var _a;
1062
+ context.location = newLocation;
1063
+ (_a = context.events) == null ? void 0 : _a.locationChange.dispatch(newLocation);
1064
+ }
1065
+ const queryDetector = createQueryDetector({
1066
+ onChange: onQueryChange,
1067
+ queries: mergedOptions.queries
1082
1068
  });
1083
- if (!response.ok)
1084
- return Promise.reject(new Error(`Failed to request preview ad with ID: ${previewObject.adhesePreviewCreativeId}`));
1085
- return await response.json();
1086
- }))).filter((response) => {
1087
- if (response.status === "rejected") {
1088
- logger.error(response.reason);
1089
- return false;
1069
+ context.parameters = createParameters(mergedOptions, queryDetector);
1070
+ watch(
1071
+ context.parameters,
1072
+ onParametersChange,
1073
+ {
1074
+ deep: true,
1075
+ immediate: true
1076
+ }
1077
+ );
1078
+ function onParametersChange() {
1079
+ var _a;
1080
+ if (context.parameters)
1081
+ (_a = context.events) == null ? void 0 : _a.parametersChange.dispatch(context.parameters);
1090
1082
  }
1091
- return response.status === "fulfilled";
1092
- }).map((response) => response.value.map((item) => ({
1093
- ...item,
1094
- preview: true
1095
- })));
1096
- return adSchema.array().parse(list.flat());
1097
- }
1098
- function getPreviewObjects() {
1099
- const currentUrl = new URL(window.location.href);
1100
- const previewObjects = [];
1101
- let currentObject = {};
1102
- for (const [key, value] of currentUrl.searchParams.entries()) {
1103
- if (key === "adhesePreviewCreativeId" && Object.keys(currentObject).length > 0) {
1104
- previewObjects.push(currentObject);
1105
- currentObject = {};
1083
+ async function onQueryChange() {
1084
+ var _a, _b;
1085
+ const query = queryDetector.getQuery();
1086
+ (_a = context.parameters) == null ? void 0 : _a.set("dt", query);
1087
+ (_b = context.parameters) == null ? void 0 : _b.set("br", query);
1088
+ await fetchAndRenderAllSlots();
1106
1089
  }
1107
- currentObject[key] = value;
1108
- }
1109
- if (Object.keys(currentObject).length > 0)
1110
- previewObjects.push(currentObject);
1111
- return previewObjects;
1112
- }
1113
- function requestWithPost({
1114
- context: { options: { host }, parameters },
1115
- ...options
1116
- }) {
1117
- const payload = {
1118
- ...options,
1119
- slots: options.slots.map((slot) => ({
1120
- slotname: toValue(slot.name),
1121
- parameters: parseParameters(slot.parameters)
1122
- })),
1123
- parameters: parameters && parseParameters(parameters)
1124
- };
1125
- return fetch(`${new URL(host).href}json`, {
1126
- method: "POST",
1127
- body: JSON.stringify(payload),
1128
- headers: {
1129
- // eslint-disable-next-line ts/naming-convention
1130
- "Content-Type": "application/json"
1090
+ function getConsent() {
1091
+ return context.consent;
1131
1092
  }
1132
- });
1133
- }
1134
- async function requestWithGet({ context, slots }) {
1135
- return fetch(new URL(`${context.options.host}/json/sl${slots.map((slot) => toValue(slot.name)).join("/sl")}`), {
1136
- method: "GET",
1137
- headers: {
1138
- // eslint-disable-next-line ts/naming-convention
1139
- "Content-Type": "application/json"
1093
+ function setConsent(newConsent) {
1094
+ var _a, _b;
1095
+ (_a = context.parameters) == null ? void 0 : _a.set("tl", newConsent ? "all" : "none");
1096
+ context.consent = newConsent;
1097
+ (_b = context.events) == null ? void 0 : _b.consentChange.dispatch(newConsent);
1140
1098
  }
1141
- });
1142
- }
1143
- function parseParameters(parameters) {
1144
- return Object.fromEntries(Array.from(parameters.entries()).filter(([key]) => {
1145
- if (key.length === 2)
1146
- return true;
1147
- logger.warn(`Invalid parameter key: ${key}. Key should be exactly 2 characters long. Key will be ignored.`);
1148
- return false;
1149
- }).map(([key, value]) => {
1150
- if (typeof value === "string")
1151
- return [key, filterSpecialChars(value)];
1152
- return [key, value.map(filterSpecialChars)];
1153
- }));
1154
- }
1155
- function filterSpecialChars(value) {
1156
- const specialRegex = /[^\p{L}\p{N}_]/gu;
1157
- return value.replaceAll(specialRegex, "_");
1158
- }
1159
- async function requestAds(options) {
1160
- var _a, _b, _c, _d, _e;
1161
- const { context } = options;
1162
- try {
1163
- (_a = context.events) == null ? void 0 : _a.requestAd.dispatch({
1164
- ...options,
1099
+ const slotManager = createSlotManager({
1100
+ initialSlots: mergedOptions.initialSlots,
1165
1101
  context
1166
1102
  });
1167
- const [response, previews] = await Promise.all([((_b = context.options.requestType) == null ? void 0 : _b.toUpperCase()) === "POST" ? requestWithPost(options) : requestWithGet(options), requestPreviews(context.options.account)]);
1168
- logger.debug("Received response", response);
1169
- if (!response.ok)
1170
- throw new Error(`Failed to request ad: ${response.status} ${response.statusText}`);
1171
- const result = parseResponse(await response.json());
1172
- logger.debug("Parsed ad", result);
1173
- if (previews.length > 0)
1174
- logger.info(`Found ${previews.length} ${previews.length === 1 ? "preview" : "previews"}. Replacing ads in response with preview items`, previews);
1175
- const matchedPreviews = previews.map(({ slotName, ...preview }) => {
1176
- const partnerAd = result.find((ad) => ad.libId === preview.libId);
1177
- return {
1178
- slotName: `${(partnerAd == null ? void 0 : partnerAd.slotName) ?? slotName}`,
1179
- ...preview
1180
- };
1103
+ function getAll() {
1104
+ return slotManager.getAll() ?? [];
1105
+ }
1106
+ function get(name) {
1107
+ return slotManager.get(name);
1108
+ }
1109
+ function addSlot(slotOptions) {
1110
+ if (!slotManager)
1111
+ throw new Error("Slot manager not initialized");
1112
+ return slotManager.add(slotOptions);
1113
+ }
1114
+ async function findDomSlots2() {
1115
+ const domSlots = (await slotManager.findDomSlots() ?? []).filter((slot) => !slot.lazyLoading);
1116
+ if (domSlots.length <= 0)
1117
+ return [];
1118
+ const ads = await requestAds({
1119
+ slots: domSlots,
1120
+ context
1121
+ });
1122
+ for (const ad of ads) {
1123
+ const slot = slotManager.get(ad.slotName);
1124
+ if (slot)
1125
+ slot.ad.value = ad;
1126
+ }
1127
+ return domSlots;
1128
+ }
1129
+ async function toggleDebug() {
1130
+ var _a, _b;
1131
+ context.debug = !context.debug;
1132
+ if (context.debug) {
1133
+ logger.setMinLogLevelThreshold("debug");
1134
+ logger.debug("Debug mode enabled");
1135
+ (_a = context.events) == null ? void 0 : _a.debugChange.dispatch(true);
1136
+ } else {
1137
+ logger.debug("Debug mode disabled");
1138
+ logger.setMinLogLevelThreshold("info");
1139
+ (_b = context.events) == null ? void 0 : _b.debugChange.dispatch(false);
1140
+ }
1141
+ return context.debug;
1142
+ }
1143
+ async function fetchAndRenderAllSlots() {
1144
+ const slots = (slotManager.getAll() ?? []).filter((slot) => !slot.lazyLoading);
1145
+ if (slots.length === 0)
1146
+ return;
1147
+ const ads = await requestAds({
1148
+ slots,
1149
+ context
1150
+ });
1151
+ for (const ad of ads) {
1152
+ const slot = slotManager.get(ad.slotName);
1153
+ if (slot)
1154
+ slot.ad.value = ad;
1155
+ }
1156
+ }
1157
+ const disposeOnTcfConsentChange = onTcfConsentChange(async (data) => {
1158
+ var _a, _b;
1159
+ if (!data.tcString)
1160
+ return;
1161
+ logger.debug("TCF v2 consent data received", {
1162
+ data
1163
+ });
1164
+ (_a = context.parameters) == null ? void 0 : _a.set("xt", data.tcString);
1165
+ (_b = context.parameters) == null ? void 0 : _b.delete("tl");
1166
+ await fetchAndRenderAllSlots();
1181
1167
  });
1182
- if (matchedPreviews.length > 0)
1183
- (_c = context.events) == null ? void 0 : _c.previewReceived.dispatch(matchedPreviews);
1184
- const mergedResult = [
1185
- ...result.filter((ad) => !previews.some((preview) => preview.libId === ad.libId)),
1186
- ...matchedPreviews
1187
- ];
1188
- if (mergedResult.length === 0)
1189
- throw new Error("No ads found");
1190
- (_d = context.events) == null ? void 0 : _d.responseReceived.dispatch(mergedResult);
1191
- return mergedResult;
1192
- } catch (error) {
1193
- logger.error(String(error));
1194
- (_e = context.events) == null ? void 0 : _e.requestError.dispatch(error);
1195
- throw error;
1196
- }
1197
- }
1198
- async function requestAd({
1199
- slot,
1200
- ...options
1201
- }) {
1202
- const [ad] = await requestAds({
1203
- slots: [slot],
1204
- ...options
1168
+ function dispose() {
1169
+ var _a, _b;
1170
+ queryDetector.dispose();
1171
+ slotManager.dispose();
1172
+ queryDetector.dispose();
1173
+ disposeOnTcfConsentChange();
1174
+ (_a = context.parameters) == null ? void 0 : _a.clear();
1175
+ logger.resetLogs();
1176
+ (_b = context.events) == null ? void 0 : _b.dispose();
1177
+ logger.info("Adhese instance disposed");
1178
+ runOnDispose().catch(logger.error);
1179
+ clearAllHooks();
1180
+ scope.stop();
1181
+ }
1182
+ onInit(async () => {
1183
+ var _a;
1184
+ if ((slotManager.getAll().length ?? 0) > 0)
1185
+ await fetchAndRenderAllSlots().catch(logger.error);
1186
+ if (mergedOptions.findDomSlotsOnLoad)
1187
+ await findDomSlots2();
1188
+ if (mergedOptions.debug || window.location.search.includes("adhese_debug=true") || isPreviewMode())
1189
+ (_a = context.events) == null ? void 0 : _a.debugChange.dispatch(true);
1190
+ if (!scope.active)
1191
+ dispose();
1192
+ });
1193
+ runOnInit().catch(logger.error);
1194
+ return {
1195
+ parameters: context.parameters,
1196
+ events: context.events,
1197
+ getLocation,
1198
+ setLocation,
1199
+ getConsent,
1200
+ setConsent,
1201
+ addSlot,
1202
+ findDomSlots: findDomSlots2,
1203
+ dispose,
1204
+ toggleDebug,
1205
+ get: slotManager.get,
1206
+ getAll: slotManager.getAll,
1207
+ context,
1208
+ options: mergedOptions
1209
+ };
1205
1210
  });
1206
- return ad;
1207
1211
  }
1208
1212
  export {
1209
1213
  createAdhese,
1214
+ logger,
1210
1215
  onDispose,
1211
- onInit
1216
+ onInit,
1217
+ onRender,
1218
+ onRequest,
1219
+ onResponse,
1220
+ requestAd,
1221
+ requestAds
1212
1222
  };
1213
1223
  //# sourceMappingURL=index.js.map