@adhese/sdk 0.8.1 → 0.10.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.8.1";
128
+ const version = "0.10.0";
129
129
  const description = "Adhese SDK";
130
130
  const license = "GPL-3.0";
131
131
  const repository = {
@@ -255,7 +255,7 @@ const hookMap = /* @__PURE__ */ new Map();
255
255
  function clearAllHooks() {
256
256
  hookMap.clear();
257
257
  }
258
- function createHook(name2, {
258
+ function createAsyncHook(name2, {
259
259
  onRun,
260
260
  onAdd
261
261
  } = {}) {
@@ -267,19 +267,56 @@ function createHook(name2, {
267
267
  onRun == null ? void 0 : onRun(hookMap.get(name2));
268
268
  return latestResult;
269
269
  };
270
- function add(callback) {
271
- const hookSet = hookMap.get(name2);
272
- if (hookSet)
273
- hookSet.add(callback);
274
- else
275
- hookMap.set(name2, /* @__PURE__ */ new Set([callback]));
276
- onAdd == null ? void 0 : onAdd(hookSet);
277
- return () => {
278
- var _a;
279
- (_a = hookMap.get(name2)) == null ? void 0 : _a.delete(callback);
280
- };
270
+ return [run, (callback) => add(callback, { name: name2, onAdd })];
271
+ }
272
+ function createSyncHook(name2, {
273
+ onRun,
274
+ onAdd
275
+ } = {}) {
276
+ hookMap.set(name2, /* @__PURE__ */ new Set());
277
+ const run = (arg) => {
278
+ let latestResult = arg;
279
+ const promisedCallbacks = [];
280
+ for (const callback of hookMap.get(name2) ?? []) {
281
+ if (isCallbackAsync(callback))
282
+ promisedCallbacks.push(callback);
283
+ else
284
+ latestResult = callback(latestResult) ?? latestResult;
285
+ }
286
+ Promise.allSettled(promisedCallbacks.map((callback) => callback(latestResult))).catch(console.trace);
287
+ onRun == null ? void 0 : onRun(hookMap.get(name2));
288
+ return latestResult;
289
+ };
290
+ return [run, (callback) => add(callback, { name: name2, onAdd })];
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));
281
300
  }
282
- return [run, add];
301
+ return [run, (callback) => add(callback, { name: name2, onAdd })];
302
+ }
303
+ function isCallbackAsync(callback) {
304
+ return callback.constructor.name === "AsyncFunction";
305
+ }
306
+ function add(callback, {
307
+ name: name2,
308
+ onAdd
309
+ }) {
310
+ const hookSet = hookMap.get(name2);
311
+ if (hookSet)
312
+ hookSet.add(callback);
313
+ else
314
+ hookMap.set(name2, /* @__PURE__ */ new Set([callback]));
315
+ onAdd == null ? void 0 : onAdd(hookSet);
316
+ return () => {
317
+ var _a;
318
+ (_a = hookMap.get(name2)) == null ? void 0 : _a.delete(callback);
319
+ };
283
320
  }
284
321
  let resolveOnInitPromise = () => {
285
322
  };
@@ -287,7 +324,7 @@ let isInit = false;
287
324
  const waitOnInit = new Promise((resolve) => {
288
325
  resolveOnInitPromise = resolve;
289
326
  });
290
- const [runOnInit, onInit] = createHook("onInit", {
327
+ const [runOnInit, onInit] = createSyncHook("onInit", {
291
328
  onRun(callbacks) {
292
329
  isInit = true;
293
330
  resolveOnInitPromise();
@@ -295,7 +332,7 @@ const [runOnInit, onInit] = createHook("onInit", {
295
332
  },
296
333
  onAdd() {
297
334
  if (isInit)
298
- runOnInit().catch(console.error);
335
+ runOnInit();
299
336
  }
300
337
  });
301
338
  const defaultLogLevels = ["trace", "debug", "info", "warn", "error"];
@@ -371,8 +408,8 @@ function createLogger({
371
408
  const logger = createLogger({
372
409
  scope: "Adhese SDK"
373
410
  });
374
- const [runOnRequest, onRequest] = createHook("onRequest");
375
- const [runOnResponse, onResponse] = createHook("onResponse");
411
+ const [runOnRequest, onRequest] = createAsyncHook("onRequest");
412
+ const [runOnResponse, onResponse] = createAsyncHook("onResponse");
376
413
  const numberLike = zod.union([zod.coerce.string().regex(/^\d+$/), zod.literal("")]).transform((value) => value === "" ? void 0 : Number(value));
377
414
  const booleanLike = zod.union([zod.coerce.boolean(), zod.literal("")]);
378
415
  const urlLike = zod.union([zod.coerce.string(), zod.literal("")]).transform((value) => {
@@ -665,7 +702,9 @@ async function requestAd({
665
702
  });
666
703
  return ad;
667
704
  }
668
- const [runOnRender, onRender] = createHook("onRender");
705
+ const [runOnRender, onRender] = createAsyncHook("onRender");
706
+ const [runOnSlotCreate, onSlotCreate] = createSyncHook("onSlotCreate");
707
+ const [runOnViewabilityChanged, onViewabilityChanged] = createPassiveHook("onViewabilityChanged");
669
708
  function useViewabilityObserver({ context, ad, name: name2, element }) {
670
709
  let timeoutId = null;
671
710
  const {
@@ -720,18 +759,11 @@ function useViewabilityObserver({ context, ad, name: name2, element }) {
720
759
  viewabilityObserver.disconnect();
721
760
  }];
722
761
  }
723
- function useRenderIntersectionObserver({ ad, options, element, render }) {
762
+ function useRenderIntersectionObserver({ options, element }) {
724
763
  var _a;
725
764
  const isInViewport = runtimeCore.ref(false);
726
765
  const renderIntersectionObserver = new IntersectionObserver((entries) => {
727
766
  isInViewport.value = entries.some((entry) => entry.isIntersecting);
728
- if (isInViewport.value) {
729
- (async () => {
730
- if (!ad.value && options.lazyLoading)
731
- await render();
732
- await render(ad.value ?? void 0);
733
- })().catch(logger.error);
734
- }
735
767
  }, {
736
768
  rootMargin: ((_a = options.lazyLoadingOptions) == null ? void 0 : _a.rootMargin) ?? "200px",
737
769
  threshold: 0
@@ -756,9 +788,10 @@ const renderFunctions = {
756
788
  iframe: renderIframe,
757
789
  inline: renderInline
758
790
  };
759
- function createSlot(options) {
791
+ function createSlot(slotOptions) {
760
792
  const scope = runtimeCore.effectScope();
761
793
  return scope.run(() => {
794
+ const options = runOnSlotCreate(slotOptions);
762
795
  const {
763
796
  containingElement,
764
797
  slot,
@@ -807,19 +840,24 @@ function createSlot(options) {
807
840
  return ((_b = element.value) == null ? void 0 : _b.innerHTML) ? element.value.firstElementChild : null;
808
841
  }
809
842
  const [isInViewport, disposeRenderIntersectionObserver] = useRenderIntersectionObserver({
810
- ad,
811
843
  options,
812
- element,
813
- render
844
+ element
814
845
  });
846
+ const isRendered = runtimeCore.ref(false);
815
847
  runtimeCore.watch([ad, isInViewport], async ([newAd, newIsInViewport], [oldAd]) => {
816
848
  var _a, _b;
817
- if (!newAd || oldAd && remeda.isDeepEqual(newAd, oldAd))
849
+ if ((!newAd || oldAd && remeda.isDeepEqual(newAd, oldAd)) && isRendered.value)
818
850
  return;
819
851
  if (newIsInViewport || context.options.eagerRendering)
820
- await render(newAd);
852
+ await render(newAd ?? void 0);
821
853
  (_b = context.events) == null ? void 0 : _b.changeSlots.dispatch(Array.from(((_a = context.getAll) == null ? void 0 : _a.call(context)) ?? []));
822
854
  });
855
+ runtimeCore.watch(isInViewport, (value) => {
856
+ runOnViewabilityChanged({
857
+ name: name2.value,
858
+ isInViewport: value
859
+ });
860
+ }, { immediate: true });
823
861
  const [
824
862
  isViewabilityTracked,
825
863
  disposeViewabilityObserver
@@ -874,7 +912,8 @@ function createSlot(options) {
874
912
  containingElement
875
913
  });
876
914
  (_b = options.onRender) == null ? void 0 : _b.call(options, element.value);
877
- disposeRenderIntersectionObserver();
915
+ ad.value = renderAd;
916
+ isRendered.value = true;
878
917
  return element.value;
879
918
  }
880
919
  function cleanElement() {
@@ -956,7 +995,7 @@ function createSlotManager({
956
995
  function getAll() {
957
996
  return Array.from(slots).map(([, slot]) => slot);
958
997
  }
959
- function add(options) {
998
+ function add2(options) {
960
999
  var _a;
961
1000
  const slot = createSlot({
962
1001
  ...options,
@@ -1002,14 +1041,14 @@ function createSlotManager({
1002
1041
  scope.stop();
1003
1042
  }
1004
1043
  for (const options of initialSlots) {
1005
- add({
1044
+ add2({
1006
1045
  ...options,
1007
1046
  lazyLoading: false
1008
1047
  });
1009
1048
  }
1010
1049
  return {
1011
1050
  getAll,
1012
- add,
1051
+ add: add2,
1013
1052
  findDomSlots: findDomSlots$1,
1014
1053
  get,
1015
1054
  dispose
@@ -1053,14 +1092,14 @@ function isPreviewMode() {
1053
1092
  return window.location.search.includes("adhesePreviewCreativeId");
1054
1093
  }
1055
1094
  let isDisposed = false;
1056
- const [runOnDispose, onDispose] = createHook("onDispose", {
1095
+ const [runOnDispose, onDispose] = createSyncHook("onDispose", {
1057
1096
  onRun(callbacks) {
1058
1097
  isDisposed = true;
1059
1098
  callbacks == null ? void 0 : callbacks.clear();
1060
1099
  },
1061
1100
  onAdd() {
1062
1101
  if (isDisposed)
1063
- runOnDispose().catch(console.error);
1102
+ runOnDispose();
1064
1103
  }
1065
1104
  });
1066
1105
  function createAdhese(options) {
@@ -1102,7 +1141,9 @@ function createAdhese(options) {
1102
1141
  onDispose,
1103
1142
  onRender,
1104
1143
  onRequest,
1105
- onResponse
1144
+ onResponse,
1145
+ onSlotCreate,
1146
+ onViewabilityChanged
1106
1147
  });
1107
1148
  }
1108
1149
  context.events = createEventManager();
@@ -1136,12 +1177,16 @@ function createAdhese(options) {
1136
1177
  if (context.parameters)
1137
1178
  (_a = context.events) == null ? void 0 : _a.parametersChange.dispatch(context.parameters);
1138
1179
  }
1180
+ const debouncedFetchAllUnrenderedSlots = remeda.debounce(fetchAllUnrenderedSlots, {
1181
+ waitMs: 100,
1182
+ timing: "both"
1183
+ });
1139
1184
  async function onQueryChange() {
1140
1185
  var _a, _b;
1141
1186
  const query = queryDetector.getQuery();
1142
1187
  (_a = context.parameters) == null ? void 0 : _a.set("dt", query);
1143
1188
  (_b = context.parameters) == null ? void 0 : _b.set("br", query);
1144
- await fetchAndRenderAllSlots();
1189
+ await debouncedFetchAllUnrenderedSlots.call();
1145
1190
  }
1146
1191
  function getConsent() {
1147
1192
  return context.consent;
@@ -1163,9 +1208,9 @@ function createAdhese(options) {
1163
1208
  return slotManager.get(name2);
1164
1209
  }
1165
1210
  function addSlot(slotOptions) {
1166
- if (!slotManager)
1167
- throw new Error("Slot manager not initialized");
1168
- return slotManager.add(slotOptions);
1211
+ const newSlot = slotManager.add(slotOptions);
1212
+ debouncedFetchAllUnrenderedSlots.call().catch(logger.error);
1213
+ return newSlot;
1169
1214
  }
1170
1215
  async function findDomSlots2() {
1171
1216
  const domSlots = (await slotManager.findDomSlots() ?? []).filter((slot) => !slot.lazyLoading);
@@ -1196,8 +1241,8 @@ function createAdhese(options) {
1196
1241
  }
1197
1242
  return context.debug;
1198
1243
  }
1199
- async function fetchAndRenderAllSlots() {
1200
- const slots = (slotManager.getAll() ?? []).filter((slot) => !slot.lazyLoading);
1244
+ async function fetchAllUnrenderedSlots() {
1245
+ const slots = (slotManager.getAll() ?? []).filter((slot) => !slot.lazyLoading && !slot.ad.value);
1201
1246
  if (slots.length === 0)
1202
1247
  return;
1203
1248
  const ads = await requestAds({
@@ -1219,7 +1264,7 @@ function createAdhese(options) {
1219
1264
  });
1220
1265
  (_a = context.parameters) == null ? void 0 : _a.set("xt", data.tcString);
1221
1266
  (_b = context.parameters) == null ? void 0 : _b.delete("tl");
1222
- await fetchAndRenderAllSlots();
1267
+ await debouncedFetchAllUnrenderedSlots.call();
1223
1268
  });
1224
1269
  function dispose() {
1225
1270
  var _a, _b;
@@ -1231,14 +1276,14 @@ function createAdhese(options) {
1231
1276
  logger.resetLogs();
1232
1277
  (_b = context.events) == null ? void 0 : _b.dispose();
1233
1278
  logger.info("Adhese instance disposed");
1234
- runOnDispose().catch(logger.error);
1279
+ runOnDispose();
1235
1280
  clearAllHooks();
1236
1281
  scope.stop();
1237
1282
  }
1238
1283
  onInit(async () => {
1239
1284
  var _a;
1240
1285
  if ((slotManager.getAll().length ?? 0) > 0)
1241
- await fetchAndRenderAllSlots().catch(logger.error);
1286
+ await fetchAllUnrenderedSlots().catch(logger.error);
1242
1287
  if (mergedOptions.findDomSlotsOnLoad)
1243
1288
  await findDomSlots2();
1244
1289
  if (mergedOptions.debug || window.location.search.includes("adhese_debug=true") || isPreviewMode())
@@ -1246,7 +1291,7 @@ function createAdhese(options) {
1246
1291
  if (!scope.active)
1247
1292
  dispose();
1248
1293
  });
1249
- runOnInit().catch(logger.error);
1294
+ runOnInit();
1250
1295
  return {
1251
1296
  parameters: context.parameters,
1252
1297
  events: context.events,