@adhese/sdk 0.9.0 → 0.11.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.cjs CHANGED
@@ -125,7 +125,7 @@ function createSafeFrame({
125
125
  }
126
126
  const name = "@adhese/sdk";
127
127
  const type = "module";
128
- const version = "0.9.0";
128
+ const version = "0.11.0";
129
129
  const description = "Adhese SDK";
130
130
  const license = "GPL-3.0";
131
131
  const repository = {
@@ -157,7 +157,7 @@ const scripts = {
157
157
  const dependencies = {
158
158
  "@vue/runtime-core": "^3.4.21",
159
159
  remeda: "^1.61.0",
160
- zod: "^3.23.0"
160
+ zod: "^3.23.4"
161
161
  };
162
162
  const packageJson = {
163
163
  name,
@@ -289,6 +289,20 @@ function createSyncHook(name2, {
289
289
  };
290
290
  return [run, (callback) => add(callback, { name: name2, onAdd })];
291
291
  }
292
+ function createPassiveHook(name2, {
293
+ onRun,
294
+ onAdd
295
+ } = {}) {
296
+ hookMap.set(name2, /* @__PURE__ */ new Set());
297
+ function run(arg) {
298
+ Promise.allSettled(Array.from(hookMap.get(name2) ?? []).map((callback) => callback(arg))).catch(console.trace);
299
+ onRun == null ? void 0 : onRun(hookMap.get(name2));
300
+ }
301
+ return [run, (callback) => add(callback, { name: name2, onAdd })];
302
+ }
303
+ function isCallbackAsync(callback) {
304
+ return callback.constructor.name === "AsyncFunction";
305
+ }
292
306
  function add(callback, {
293
307
  name: name2,
294
308
  onAdd
@@ -304,9 +318,6 @@ function add(callback, {
304
318
  (_a = hookMap.get(name2)) == null ? void 0 : _a.delete(callback);
305
319
  };
306
320
  }
307
- function isCallbackAsync(callback) {
308
- return callback.constructor.name === "AsyncFunction";
309
- }
310
321
  let resolveOnInitPromise = () => {
311
322
  };
312
323
  let isInit = false;
@@ -641,6 +652,36 @@ function filterSpecialChars(value) {
641
652
  const specialRegex = /[^\p{L}\p{N}_]/gu;
642
653
  return value.replaceAll(specialRegex, "_");
643
654
  }
655
+ const batch = /* @__PURE__ */ new Map();
656
+ const debouncedRequestAds = remeda.debounce(async (context) => {
657
+ if (batch.size === 0)
658
+ return [];
659
+ const ads = await requestAds({
660
+ slots: Array.from(batch.values()).map(({ options }) => options.slot),
661
+ context
662
+ });
663
+ for (const { options, resolve, reject } of batch.values()) {
664
+ const ad = ads.find(({ slotName }) => runtimeCore.toValue(slotName) === runtimeCore.toValue(options.slot.name));
665
+ if (ad)
666
+ resolve(ad);
667
+ else
668
+ reject(new Error(`Ad: ${runtimeCore.toValue(options.slot.name)} not found`));
669
+ }
670
+ batch.clear();
671
+ return ads;
672
+ }, {
673
+ waitMs: 20,
674
+ timing: "trailing"
675
+ });
676
+ async function requestAd(options) {
677
+ const promise = new Promise(
678
+ (resolve, reject) => {
679
+ batch.set(runtimeCore.toValue(options.slot.name), { options, resolve, reject });
680
+ }
681
+ );
682
+ await debouncedRequestAds.call(options.context);
683
+ return promise;
684
+ }
644
685
  async function requestAds(requestOptions) {
645
686
  var _a, _b, _c, _d, _e;
646
687
  const options = await runOnRequest(requestOptions);
@@ -681,18 +722,9 @@ async function requestAds(requestOptions) {
681
722
  throw error;
682
723
  }
683
724
  }
684
- async function requestAd({
685
- slot,
686
- ...options
687
- }) {
688
- const [ad] = await requestAds({
689
- slots: [slot],
690
- ...options
691
- });
692
- return ad;
693
- }
694
725
  const [runOnRender, onRender] = createAsyncHook("onRender");
695
726
  const [runOnSlotCreate, onSlotCreate] = createSyncHook("onSlotCreate");
727
+ const [runOnViewabilityChanged, onViewabilityChanged] = createPassiveHook("onViewabilityChanged");
696
728
  function useViewabilityObserver({ context, ad, name: name2, element }) {
697
729
  let timeoutId = null;
698
730
  const {
@@ -747,18 +779,11 @@ function useViewabilityObserver({ context, ad, name: name2, element }) {
747
779
  viewabilityObserver.disconnect();
748
780
  }];
749
781
  }
750
- function useRenderIntersectionObserver({ ad, options, element, render }) {
782
+ function useRenderIntersectionObserver({ options, element }) {
751
783
  var _a;
752
784
  const isInViewport = runtimeCore.ref(false);
753
785
  const renderIntersectionObserver = new IntersectionObserver((entries) => {
754
786
  isInViewport.value = entries.some((entry) => entry.isIntersecting);
755
- if (isInViewport.value) {
756
- (async () => {
757
- if (!ad.value && options.lazyLoading)
758
- await render();
759
- await render(ad.value ?? void 0);
760
- })().catch(logger.error);
761
- }
762
787
  }, {
763
788
  rootMargin: ((_a = options.lazyLoadingOptions) == null ? void 0 : _a.rootMargin) ?? "200px",
764
789
  threshold: 0
@@ -835,19 +860,24 @@ function createSlot(slotOptions) {
835
860
  return ((_b = element.value) == null ? void 0 : _b.innerHTML) ? element.value.firstElementChild : null;
836
861
  }
837
862
  const [isInViewport, disposeRenderIntersectionObserver] = useRenderIntersectionObserver({
838
- ad,
839
863
  options,
840
- element,
841
- render
864
+ element
842
865
  });
866
+ const isRendered = runtimeCore.ref(false);
843
867
  runtimeCore.watch([ad, isInViewport], async ([newAd, newIsInViewport], [oldAd]) => {
844
868
  var _a, _b;
845
- if (!newAd || oldAd && remeda.isDeepEqual(newAd, oldAd))
869
+ if ((!newAd || oldAd && remeda.isDeepEqual(newAd, oldAd)) && isRendered.value)
846
870
  return;
847
871
  if (newIsInViewport || context.options.eagerRendering)
848
- await render(newAd);
872
+ await render(newAd ?? void 0);
849
873
  (_b = context.events) == null ? void 0 : _b.changeSlots.dispatch(Array.from(((_a = context.getAll) == null ? void 0 : _a.call(context)) ?? []));
850
874
  });
875
+ runtimeCore.watch(isInViewport, (value) => {
876
+ runOnViewabilityChanged({
877
+ name: name2.value,
878
+ isInViewport: value
879
+ });
880
+ }, { immediate: true });
851
881
  const [
852
882
  isViewabilityTracked,
853
883
  disposeViewabilityObserver
@@ -903,7 +933,7 @@ function createSlot(slotOptions) {
903
933
  });
904
934
  (_b = options.onRender) == null ? void 0 : _b.call(options, element.value);
905
935
  ad.value = renderAd;
906
- disposeRenderIntersectionObserver();
936
+ isRendered.value = true;
907
937
  return element.value;
908
938
  }
909
939
  function cleanElement() {
@@ -992,6 +1022,10 @@ function createSlotManager({
992
1022
  onDispose: onDispose2,
993
1023
  context
994
1024
  });
1025
+ if (slots.has(slot.name.value)) {
1026
+ slot.dispose();
1027
+ throw new Error(`Slot with the name: ${slot.name.value} already exists. Create a new slot with a different format, slot, or the location.`);
1028
+ }
995
1029
  function onDispose2() {
996
1030
  var _a2;
997
1031
  slots.delete(slot.name.value);
@@ -1117,11 +1151,9 @@ function createAdhese(options) {
1117
1151
  location: mergedOptions.location,
1118
1152
  consent: mergedOptions.consent,
1119
1153
  debug: mergedOptions.debug,
1120
- getAll,
1121
- get,
1122
1154
  options: mergedOptions,
1123
1155
  logger,
1124
- addSlot
1156
+ isDisposed: false
1125
1157
  });
1126
1158
  for (const [index, plugin] of mergedOptions.plugins.entries()) {
1127
1159
  plugin(context, {
@@ -1132,7 +1164,8 @@ function createAdhese(options) {
1132
1164
  onRender,
1133
1165
  onRequest,
1134
1166
  onResponse,
1135
- onSlotCreate
1167
+ onSlotCreate,
1168
+ onViewabilityChanged
1136
1169
  });
1137
1170
  }
1138
1171
  context.events = createEventManager();
@@ -1166,12 +1199,16 @@ function createAdhese(options) {
1166
1199
  if (context.parameters)
1167
1200
  (_a = context.events) == null ? void 0 : _a.parametersChange.dispatch(context.parameters);
1168
1201
  }
1202
+ const debouncedFetchAllUnrenderedSlots = remeda.debounce(fetchAllUnrenderedSlots, {
1203
+ waitMs: 100,
1204
+ timing: "both"
1205
+ });
1169
1206
  async function onQueryChange() {
1170
1207
  var _a, _b;
1171
1208
  const query = queryDetector.getQuery();
1172
1209
  (_a = context.parameters) == null ? void 0 : _a.set("dt", query);
1173
1210
  (_b = context.parameters) == null ? void 0 : _b.set("br", query);
1174
- await fetchAndRenderAllSlots();
1211
+ await debouncedFetchAllUnrenderedSlots.call();
1175
1212
  }
1176
1213
  function getConsent() {
1177
1214
  return context.consent;
@@ -1189,22 +1226,25 @@ function createAdhese(options) {
1189
1226
  function getAll() {
1190
1227
  return slotManager.getAll() ?? [];
1191
1228
  }
1229
+ context.getAll = getAll;
1192
1230
  function get(name2) {
1193
1231
  return slotManager.get(name2);
1194
1232
  }
1233
+ context.get = get;
1195
1234
  function addSlot(slotOptions) {
1196
- if (!slotManager)
1197
- throw new Error("Slot manager not initialized");
1198
- return slotManager.add(slotOptions);
1235
+ const newSlot = slotManager.add(slotOptions);
1236
+ debouncedFetchAllUnrenderedSlots.call().catch(logger.error);
1237
+ return newSlot;
1199
1238
  }
1239
+ context.addSlot = addSlot;
1200
1240
  async function findDomSlots2() {
1201
1241
  const domSlots = (await slotManager.findDomSlots() ?? []).filter((slot) => !slot.lazyLoading);
1202
1242
  if (domSlots.length <= 0)
1203
1243
  return [];
1204
- const ads = await requestAds({
1205
- slots: domSlots,
1244
+ const ads = await Promise.all(domSlots.map((slot) => requestAd({
1245
+ slot,
1206
1246
  context
1207
- });
1247
+ })));
1208
1248
  for (const ad of ads) {
1209
1249
  const slot = slotManager.get(ad.slotName);
1210
1250
  if (slot)
@@ -1226,14 +1266,14 @@ function createAdhese(options) {
1226
1266
  }
1227
1267
  return context.debug;
1228
1268
  }
1229
- async function fetchAndRenderAllSlots() {
1230
- const slots = (slotManager.getAll() ?? []).filter((slot) => !slot.lazyLoading);
1269
+ async function fetchAllUnrenderedSlots() {
1270
+ const slots = (slotManager.getAll() ?? []).filter((slot) => !slot.lazyLoading && !slot.ad.value);
1231
1271
  if (slots.length === 0)
1232
1272
  return;
1233
- const ads = await requestAds({
1234
- slots,
1273
+ const ads = await Promise.all(slots.map((slot) => requestAd({
1274
+ slot,
1235
1275
  context
1236
- });
1276
+ })));
1237
1277
  for (const ad of ads) {
1238
1278
  const slot = slotManager.get(ad.slotName);
1239
1279
  if (slot)
@@ -1249,10 +1289,11 @@ function createAdhese(options) {
1249
1289
  });
1250
1290
  (_a = context.parameters) == null ? void 0 : _a.set("xt", data.tcString);
1251
1291
  (_b = context.parameters) == null ? void 0 : _b.delete("tl");
1252
- await fetchAndRenderAllSlots();
1292
+ await debouncedFetchAllUnrenderedSlots.call();
1253
1293
  });
1254
1294
  function dispose() {
1255
1295
  var _a, _b;
1296
+ context.isDisposed = true;
1256
1297
  queryDetector.dispose();
1257
1298
  slotManager.dispose();
1258
1299
  queryDetector.dispose();
@@ -1268,7 +1309,7 @@ function createAdhese(options) {
1268
1309
  onInit(async () => {
1269
1310
  var _a;
1270
1311
  if ((slotManager.getAll().length ?? 0) > 0)
1271
- await fetchAndRenderAllSlots().catch(logger.error);
1312
+ await fetchAllUnrenderedSlots().catch(logger.error);
1272
1313
  if (mergedOptions.findDomSlotsOnLoad)
1273
1314
  await findDomSlots2();
1274
1315
  if (mergedOptions.debug || window.location.search.includes("adhese_debug=true") || isPreviewMode())
@@ -1298,5 +1339,4 @@ function createAdhese(options) {
1298
1339
  exports.createAdhese = createAdhese;
1299
1340
  exports.logger = logger;
1300
1341
  exports.requestAd = requestAd;
1301
- exports.requestAds = requestAds;
1302
1342
  //# sourceMappingURL=index.cjs.map