@adhese/sdk 1.12.0 → 1.12.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/CHANGELOG.md +6 -0
- package/dist/cjs/package.json.cjs +1 -1
- package/dist/cjs/slot/slot.cjs +13 -0
- package/dist/cjs/slot/slot.cjs.map +1 -1
- package/dist/package.json.js +1 -1
- package/dist/slot/slot.js +13 -0
- package/dist/slot/slot.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const name = "@adhese/sdk";
|
|
4
|
-
const version = "1.12.
|
|
4
|
+
const version = "1.12.1";
|
|
5
5
|
exports.name = name;
|
|
6
6
|
exports.version = version;
|
|
7
7
|
//# sourceMappingURL=package.json.cjs.map
|
package/dist/cjs/slot/slot.cjs
CHANGED
|
@@ -220,7 +220,20 @@ function createSlot(slotOptions) {
|
|
|
220
220
|
if (additionalTrackingPixelElement.value)
|
|
221
221
|
additionalTrackingPixelElement.value.remove();
|
|
222
222
|
});
|
|
223
|
+
let inFlightRequest = null;
|
|
223
224
|
async function request() {
|
|
225
|
+
if ((inFlightRequest == null ? void 0 : inFlightRequest.name) === name.value)
|
|
226
|
+
return inFlightRequest.promise;
|
|
227
|
+
const promise = fetchAd();
|
|
228
|
+
inFlightRequest = { name: name.value, promise };
|
|
229
|
+
try {
|
|
230
|
+
return await promise;
|
|
231
|
+
} finally {
|
|
232
|
+
if ((inFlightRequest == null ? void 0 : inFlightRequest.promise) === promise)
|
|
233
|
+
inFlightRequest = null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async function fetchAd() {
|
|
224
237
|
try {
|
|
225
238
|
if (options.lazyLoading && !isInViewport.value)
|
|
226
239
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slot.cjs","sources":["../../../src/slot/slot.ts"],"sourcesContent":["import type { AdheseAd } from '@adhese/sdk';\nimport type {\n AdheseSlot,\n AdheseSlotContext,\n AdheseSlotOptions,\n RenderMode,\n} from './slot.types';\nimport {\n addTrackingPixel,\n computed,\n doNothing,\n effectScope,\n generateName,\n omit,\n pick,\n reactive,\n type Ref,\n ref,\n renderIframe,\n renderInline,\n type RenderOptions,\n shallowRef,\n uniqueId,\n type UnwrapRef,\n waitForDomLoad,\n watch,\n} from '@adhese/sdk-shared';\nimport { logger } from '../logger/logger';\nimport { useQueryDetector } from '../queryDetector/queryDetector';\nimport { requestAd as extRequestAd } from '../requestAds/requestAds';\nimport {\n useDomLoaded,\n useRenderIntersectionObserver,\n useSlotHooks,\n useViewabilityObserver,\n} from './slot.composables';\n\nconst renderFunctions: Record<\n RenderMode,\n (ad: RenderOptions, element: HTMLElement) => void\n> = {\n iframe: renderIframe,\n inline: renderInline,\n none: doNothing,\n};\n\nconst defaultOptions = {\n renderMode: 'iframe',\n type: 'normal',\n} satisfies Partial<AdheseSlotOptions>;\n\n/**\n * Create a new slot instance. This slot instance can be used to request and render ads.\n *\n * @param slotOptions {AdheseSlotOptions} The options to create the slot with.\n *\n * @return AdheseSlot The created slot instance.\n */\nexport function createSlot(slotOptions: AdheseSlotOptions): AdheseSlot {\n const scope = effectScope();\n\n return scope.run(() => {\n const slotContext = ref<AdheseSlotContext | null>(null);\n const options = slotOptions.context.hooks.runOnSlotCreate({\n ...defaultOptions,\n ...(Object.fromEntries(\n Object.entries(slotOptions).filter(([, value]) => value !== undefined),\n ) as AdheseSlotOptions),\n });\n\n const {\n containingElement,\n slot,\n context,\n pluginOptions,\n initialData = null,\n type = 'normal',\n } = options;\n\n let { renderMode = 'iframe' } = options;\n\n const id = uniqueId();\n const {\n runOnBeforeRender,\n runOnRender,\n runOnBeforeRequest,\n runOnRequest,\n runOnInit,\n runOnDispose,\n runOnEmpty,\n runOnError,\n runOnImpressionTracked,\n runOnViewableTracked,\n ...hooks\n } = useSlotHooks(options, slotContext);\n\n const isDisposed = ref(false);\n const parameters = reactive(\n new Map(Object.entries(options.parameters ?? {})),\n );\n\n const [device, disposeQueryDetector] = useQueryDetector(\n context,\n typeof options.format === 'string'\n ? {\n [options.format]: '(min-width: 0px)',\n }\n : Object.fromEntries(\n options.format.map(item => [item.format, item.query]),\n ),\n );\n\n const format = computed(() =>\n typeof options.format === 'string' ? options.format : device.value,\n );\n\n const data = ref<AdheseAd | null>(null) as Ref<AdheseAd | null>;\n const originalData = ref(data.value) as Ref<AdheseAd | null>;\n const isEmpty = ref(false);\n // Ad stashed by `processOnEmpty(ad)` when `onRequest` identifies an ad's creative as empty. `request()`\n // rescues it into `data`/`originalData` so tracking pixels are still available once the slot renders.\n let emptyAdForTracking: AdheseAd | null = null;\n const name = computed(() =>\n generateName(options.context.location, format.value, options.slot),\n );\n\n const status = ref<UnwrapRef<AdheseSlot>['status']>('initializing');\n\n watch(name, async (newName, oldName) => {\n if (newName === oldName)\n return;\n\n const newAd = await slotContext.value?.request();\n\n if (!newAd)\n return;\n\n slotContext.value?.cleanElement();\n\n data.value = newAd;\n originalData.value = newAd;\n });\n\n const isDomLoaded = useDomLoaded(context);\n\n const element = shallowRef<HTMLElement | null>(null);\n\n function getElement(): HTMLElement | null {\n if (\n !(\n typeof options.containingElement === 'string'\n || !options.containingElement\n )\n ) {\n return options.containingElement;\n }\n\n if (!isDomLoaded.value)\n return null;\n\n return document.querySelector<HTMLElement>(\n `#${options.containingElement}`,\n );\n }\n\n watch(element, async (newElement, oldElement) => {\n if (\n status.value === 'empty'\n || status.value === 'error'\n || status.value === 'loading'\n ) {\n return;\n }\n\n if (newElement === null && data.value) {\n status.value = 'loaded';\n\n return;\n }\n\n if (\n newElement === oldElement\n || (oldElement === null && newElement === null)\n ) {\n return;\n }\n if (!context.options.eagerRendering && !oldElement) {\n return;\n }\n if (status.value === 'rendering' || status.value === 'rendered') {\n return;\n }\n await render();\n });\n\n const domObserver = new MutationObserver(() => {\n element.value = getElement();\n });\n\n domObserver.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n watch(\n isDomLoaded,\n () => {\n element.value = getElement();\n },\n { immediate: true, deep: true },\n );\n\n const isInViewport = useRenderIntersectionObserver({\n options,\n element,\n hooks,\n });\n\n watch(\n isInViewport,\n async (newIsInViewport) => {\n if (newIsInViewport && status.value !== 'rendered' && status.value !== 'rendering')\n await slotContext.value?.render();\n },\n { immediate: true },\n );\n\n hooks.onDispose(() => {\n disposeQueryDetector();\n });\n\n const isViewabilityTracked = useViewabilityObserver({\n context,\n slotContext,\n hooks,\n onTracked(trackingPixel) {\n let viewabilityPixel;\n if (slotContext.value?.data?.origin === undefined) {\n context.logger.warn(\n `Origin not found for ${slotContext.value?.name}`,\n );\n return;\n }\n switch (slotContext.value?.data?.origin) {\n case 'DALE': {\n // @ts-expect-error - Data structure is not typed and very messy to type\n const seatbid = slotContext.value?.data?.originData?.seatbid;\n const bid = seatbid ? seatbid[0]?.bid[0] : undefined;\n viewabilityPixel = bid\n ? bid.ext?.adhese?.viewableImpressionCounter\n : undefined;\n break;\n }\n case 'JERLICIA':\n viewabilityPixel\n = slotContext.value?.data?.viewableImpressionCounter;\n break;\n }\n if (viewabilityPixel) {\n trackingPixel.value = addTrackingPixel(viewabilityPixel);\n context.logger.debug(\n `Viewability tracking pixel fired for ${slotContext.value?.name}`,\n );\n runOnViewableTracked(slotContext.value?.data);\n }\n },\n });\n\n const impressionTrackingPixelElement = ref<HTMLImageElement | null>(null);\n const additionalTrackingPixelElement = ref<HTMLImageElement | null>(null);\n const isImpressionTracked = ref(false);\n const isAdditionalTracked = ref(false);\n\n const impressionTrackedAd = ref<AdheseAd | null>(null);\n const additionalTrackedAd = ref<AdheseAd | null>(null);\n\n watch(\n [status, isInViewport, data],\n ([newStatus, newIsInViewport, newData]) => {\n if (newStatus !== 'rendered' || !newIsInViewport)\n return;\n\n if (\n newData?.impressionCounter\n && impressionTrackedAd.value !== newData\n ) {\n impressionTrackingPixelElement.value?.remove();\n impressionTrackingPixelElement.value = addTrackingPixel(\n newData.impressionCounter,\n );\n impressionTrackedAd.value = newData;\n runOnImpressionTracked(newData);\n isImpressionTracked.value = true;\n context.logger.debug(\n `Impression tracking pixel fired for ${slotContext.value?.name}`,\n );\n }\n if (\n newData?.additionalTracker\n && additionalTrackedAd.value !== newData\n ) {\n additionalTrackingPixelElement.value?.remove();\n additionalTrackingPixelElement.value = addTrackingPixel(\n newData.additionalTracker,\n );\n additionalTrackedAd.value = newData;\n isAdditionalTracked.value = true;\n context.logger.debug(\n `Additional Impression tracking pixel fired for ${slotContext.value?.name}`,\n );\n }\n },\n { immediate: true },\n );\n hooks.onDispose(() => {\n if (impressionTrackingPixelElement.value)\n impressionTrackingPixelElement.value.remove();\n if (additionalTrackingPixelElement.value)\n additionalTrackingPixelElement.value.remove();\n });\n\n async function request(): Promise<AdheseAd | null> {\n try {\n if (options.lazyLoading && !isInViewport.value)\n return null;\n\n status.value = 'loading';\n isEmpty.value = false;\n emptyAdForTracking = null;\n\n let response = await runOnBeforeRequest(null);\n\n if (!response) {\n response = await extRequestAd({\n slot: {\n name: name.value,\n parameters,\n },\n context,\n });\n }\n\n if (response)\n response = await runOnRequest(response);\n\n if (!response && emptyAdForTracking) {\n // `onRequest` identified the ad as empty via `processOnEmpty(ad)`. Keep the original ad around so\n // it still reaches `render()`, where the position is tracked even though no creative is shown.\n response = emptyAdForTracking;\n }\n\n data.value = response;\n\n if (!originalData.value)\n originalData.value = response;\n\n status.value = response ? 'loaded' : 'empty';\n\n if (!response)\n cleanElement();\n\n if (response && context.options.eagerRendering && element.value)\n await render(response);\n\n return response;\n }\n catch (error) {\n processOnError(error as string);\n return null;\n }\n }\n\n async function render(adToRender?: AdheseAd): Promise<HTMLElement | null> {\n if (\n status.value === 'empty'\n || status.value === 'error'\n || status.value === 'initializing'\n ) {\n return null;\n }\n\n try {\n if (options.lazyLoading && !isInViewport.value)\n return null;\n\n status.value = 'rendering';\n await waitForDomLoad();\n element.value = getElement();\n\n if (adToRender) {\n // An explicit ad was passed in, overriding whatever the slot previously fetched. Don't carry over an\n // `isEmpty` flag from an earlier, unrelated ad.\n isEmpty.value = false;\n }\n\n let renderAd = adToRender ?? data.value ?? originalData.value ?? null;\n if (!renderAd) {\n renderAd = await request();\n if ((status.value as UnwrapRef<AdheseSlot>['status']) === 'rendered') {\n return element.value;\n }\n }\n\n const hadAdBeforeBeforeRenderHook = Boolean(renderAd);\n\n renderAd = renderAd && (await runOnBeforeRender(renderAd));\n\n renderMode = renderAd?.renderMode ?? renderMode;\n\n if (!element.value && renderMode !== 'none') {\n logger.debug(\n `Could not render slot for format ${format.value}. No element found.`,\n slotContext.value,\n );\n\n return null;\n }\n\n if (!renderAd) {\n if (hadAdBeforeBeforeRenderHook) {\n // The ad was identified as empty by the `onBeforeRender` hook itself. The position is still\n // tracked as rendered, but no creative is written to the element. Whatever the app already put\n // in the element (e.g. a fallback rendered from `onEmpty`) is left untouched.\n markEmpty();\n // eslint-disable-next-line require-atomic-updates\n status.value = 'rendered';\n logger.debug(`Slot ${name.value} identified as empty by the onBeforeRender hook`, slotContext.value);\n\n return element.value;\n }\n\n return null;\n }\n\n if (isEmpty.value) {\n // The ad was already identified as empty earlier, typically via `processOnEmpty(ad)` from the\n // `onRequest` hook. The position is tracked as rendered, but no creative is written to the element.\n // Whatever the app already put in the element (e.g. a fallback rendered from `onEmpty`) is left\n // untouched.\n // eslint-disable-next-line require-atomic-updates\n status.value = 'rendered';\n\n return element.value;\n }\n\n if (typeof renderAd?.tag !== 'string' && renderMode !== 'none') {\n const error = `Could not render slot for slot ${name.value}. A valid tag doesn't exist or is not HTML string.`;\n throw new Error(error);\n }\n\n if (renderMode !== 'none' && element.value) {\n renderFunctions[renderMode](\n {\n ...renderAd,\n ...pick(options, ['width', 'height']),\n } as RenderOptions,\n element.value,\n );\n }\n\n logger.debug(`Slot rendered ${name.value}`, {\n renderedElement: element,\n location: context.location,\n format,\n containingElement,\n });\n\n // eslint-disable-next-line require-atomic-updates\n status.value = 'rendered';\n\n runOnRender(renderAd);\n\n return element.value;\n }\n catch (error) {\n processOnError(error as string);\n return null;\n }\n }\n\n function markEmpty(): void {\n if (isEmpty.value)\n return;\n\n isEmpty.value = true;\n runOnEmpty();\n }\n\n function processOnEmpty(ad?: AdheseAd): void {\n markEmpty();\n\n if (ad) {\n // An ad was returned, but its creative was identified as empty (e.g. a no-fill/house banner). Stash\n // it so `request()` can keep it alive for tracking once the slot renders, instead of treating this\n // as \"no ad returned at all\".\n emptyAdForTracking = ad;\n return;\n }\n\n status.value = 'empty';\n logger.debug(`No ad to render for slot ${name.value}`);\n }\n\n function processOnError(error: string): void {\n if (status.value !== 'error') {\n status.value = 'error';\n logger.error(error);\n runOnError(new Error(error, {\n cause: error,\n }));\n }\n }\n\n function cleanElement(): void {\n if (!element.value || renderMode === 'none')\n return;\n\n element.value.innerHTML = '';\n element.value.style.position = '';\n }\n\n function dispose(): void {\n cleanElement();\n\n element.value = null;\n\n data.value = null;\n originalData.value = null;\n\n domObserver.disconnect();\n\n runOnDispose();\n\n isDisposed.value = true;\n\n scope.stop();\n }\n\n const state = reactive({\n location: context.location ?? '',\n lazyLoading: options.lazyLoading ?? false,\n type,\n slot,\n parameters,\n format,\n name,\n data,\n isViewabilityTracked,\n isImpressionTracked,\n isEmpty,\n status,\n element,\n isDisposed,\n id,\n pluginOptions,\n isVisible: isInViewport,\n render,\n request,\n dispose,\n processOnEmpty,\n processOnError,\n cleanElement,\n options: omit(options, ['context']),\n ...hooks,\n });\n\n watch(\n state,\n (newState) => {\n slotContext.value = newState;\n },\n {\n deep: true,\n immediate: true,\n },\n );\n\n context.hooks.onInit(async () => {\n await runOnInit();\n\n if (status.value === 'empty' || status.value === 'error') {\n return;\n }\n\n if (initialData) {\n status.value = 'loaded';\n isEmpty.value = false;\n emptyAdForTracking = null;\n\n data.value = initialData;\n\n let response = await runOnRequest(initialData);\n\n if (!response && emptyAdForTracking) {\n // `onRequest` identified the ad as empty via `processOnEmpty(ad)`. Keep the original ad around so\n // it still reaches `render()`, where the position is tracked even though no creative is shown.\n response = emptyAdForTracking;\n }\n\n data.value = response;\n // eslint-disable-next-line require-atomic-updates\n status.value = response ? 'loaded' : 'empty';\n\n return;\n }\n\n status.value = 'initialized';\n\n if (options.lazyLoading) {\n return;\n }\n\n data.value = (await slotContext.value?.request()) ?? null;\n });\n\n return state;\n })!;\n}\n"],"names":["renderIframe","renderInline","doNothing","effectScope","ref","uniqueId","useSlotHooks","reactive","useQueryDetector","computed","generateName","watch","useDomLoaded","shallowRef","useRenderIntersectionObserver","useViewabilityObserver","addTrackingPixel","extRequestAd","waitForDomLoad","logger","pick","omit"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"slot.cjs","sources":["../../../src/slot/slot.ts"],"sourcesContent":["import type { AdheseAd } from '@adhese/sdk';\nimport type {\n AdheseSlot,\n AdheseSlotContext,\n AdheseSlotOptions,\n RenderMode,\n} from './slot.types';\nimport {\n addTrackingPixel,\n computed,\n doNothing,\n effectScope,\n generateName,\n omit,\n pick,\n reactive,\n type Ref,\n ref,\n renderIframe,\n renderInline,\n type RenderOptions,\n shallowRef,\n uniqueId,\n type UnwrapRef,\n waitForDomLoad,\n watch,\n} from '@adhese/sdk-shared';\nimport { logger } from '../logger/logger';\nimport { useQueryDetector } from '../queryDetector/queryDetector';\nimport { requestAd as extRequestAd } from '../requestAds/requestAds';\nimport {\n useDomLoaded,\n useRenderIntersectionObserver,\n useSlotHooks,\n useViewabilityObserver,\n} from './slot.composables';\n\nconst renderFunctions: Record<\n RenderMode,\n (ad: RenderOptions, element: HTMLElement) => void\n> = {\n iframe: renderIframe,\n inline: renderInline,\n none: doNothing,\n};\n\nconst defaultOptions = {\n renderMode: 'iframe',\n type: 'normal',\n} satisfies Partial<AdheseSlotOptions>;\n\n/**\n * Create a new slot instance. This slot instance can be used to request and render ads.\n *\n * @param slotOptions {AdheseSlotOptions} The options to create the slot with.\n *\n * @return AdheseSlot The created slot instance.\n */\nexport function createSlot(slotOptions: AdheseSlotOptions): AdheseSlot {\n const scope = effectScope();\n\n return scope.run(() => {\n const slotContext = ref<AdheseSlotContext | null>(null);\n const options = slotOptions.context.hooks.runOnSlotCreate({\n ...defaultOptions,\n ...(Object.fromEntries(\n Object.entries(slotOptions).filter(([, value]) => value !== undefined),\n ) as AdheseSlotOptions),\n });\n\n const {\n containingElement,\n slot,\n context,\n pluginOptions,\n initialData = null,\n type = 'normal',\n } = options;\n\n let { renderMode = 'iframe' } = options;\n\n const id = uniqueId();\n const {\n runOnBeforeRender,\n runOnRender,\n runOnBeforeRequest,\n runOnRequest,\n runOnInit,\n runOnDispose,\n runOnEmpty,\n runOnError,\n runOnImpressionTracked,\n runOnViewableTracked,\n ...hooks\n } = useSlotHooks(options, slotContext);\n\n const isDisposed = ref(false);\n const parameters = reactive(\n new Map(Object.entries(options.parameters ?? {})),\n );\n\n const [device, disposeQueryDetector] = useQueryDetector(\n context,\n typeof options.format === 'string'\n ? {\n [options.format]: '(min-width: 0px)',\n }\n : Object.fromEntries(\n options.format.map(item => [item.format, item.query]),\n ),\n );\n\n const format = computed(() =>\n typeof options.format === 'string' ? options.format : device.value,\n );\n\n const data = ref<AdheseAd | null>(null) as Ref<AdheseAd | null>;\n const originalData = ref(data.value) as Ref<AdheseAd | null>;\n const isEmpty = ref(false);\n // Ad stashed by `processOnEmpty(ad)` when `onRequest` identifies an ad's creative as empty. `request()`\n // rescues it into `data`/`originalData` so tracking pixels are still available once the slot renders.\n let emptyAdForTracking: AdheseAd | null = null;\n const name = computed(() =>\n generateName(options.context.location, format.value, options.slot),\n );\n\n const status = ref<UnwrapRef<AdheseSlot>['status']>('initializing');\n\n watch(name, async (newName, oldName) => {\n if (newName === oldName)\n return;\n\n const newAd = await slotContext.value?.request();\n\n if (!newAd)\n return;\n\n slotContext.value?.cleanElement();\n\n data.value = newAd;\n originalData.value = newAd;\n });\n\n const isDomLoaded = useDomLoaded(context);\n\n const element = shallowRef<HTMLElement | null>(null);\n\n function getElement(): HTMLElement | null {\n if (\n !(\n typeof options.containingElement === 'string'\n || !options.containingElement\n )\n ) {\n return options.containingElement;\n }\n\n if (!isDomLoaded.value)\n return null;\n\n return document.querySelector<HTMLElement>(\n `#${options.containingElement}`,\n );\n }\n\n watch(element, async (newElement, oldElement) => {\n if (\n status.value === 'empty'\n || status.value === 'error'\n || status.value === 'loading'\n ) {\n return;\n }\n\n if (newElement === null && data.value) {\n status.value = 'loaded';\n\n return;\n }\n\n if (\n newElement === oldElement\n || (oldElement === null && newElement === null)\n ) {\n return;\n }\n if (!context.options.eagerRendering && !oldElement) {\n return;\n }\n if (status.value === 'rendering' || status.value === 'rendered') {\n return;\n }\n await render();\n });\n\n const domObserver = new MutationObserver(() => {\n element.value = getElement();\n });\n\n domObserver.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n watch(\n isDomLoaded,\n () => {\n element.value = getElement();\n },\n { immediate: true, deep: true },\n );\n\n const isInViewport = useRenderIntersectionObserver({\n options,\n element,\n hooks,\n });\n\n watch(\n isInViewport,\n async (newIsInViewport) => {\n if (newIsInViewport && status.value !== 'rendered' && status.value !== 'rendering')\n await slotContext.value?.render();\n },\n { immediate: true },\n );\n\n hooks.onDispose(() => {\n disposeQueryDetector();\n });\n\n const isViewabilityTracked = useViewabilityObserver({\n context,\n slotContext,\n hooks,\n onTracked(trackingPixel) {\n let viewabilityPixel;\n if (slotContext.value?.data?.origin === undefined) {\n context.logger.warn(\n `Origin not found for ${slotContext.value?.name}`,\n );\n return;\n }\n switch (slotContext.value?.data?.origin) {\n case 'DALE': {\n // @ts-expect-error - Data structure is not typed and very messy to type\n const seatbid = slotContext.value?.data?.originData?.seatbid;\n const bid = seatbid ? seatbid[0]?.bid[0] : undefined;\n viewabilityPixel = bid\n ? bid.ext?.adhese?.viewableImpressionCounter\n : undefined;\n break;\n }\n case 'JERLICIA':\n viewabilityPixel\n = slotContext.value?.data?.viewableImpressionCounter;\n break;\n }\n if (viewabilityPixel) {\n trackingPixel.value = addTrackingPixel(viewabilityPixel);\n context.logger.debug(\n `Viewability tracking pixel fired for ${slotContext.value?.name}`,\n );\n runOnViewableTracked(slotContext.value?.data);\n }\n },\n });\n\n const impressionTrackingPixelElement = ref<HTMLImageElement | null>(null);\n const additionalTrackingPixelElement = ref<HTMLImageElement | null>(null);\n const isImpressionTracked = ref(false);\n const isAdditionalTracked = ref(false);\n\n const impressionTrackedAd = ref<AdheseAd | null>(null);\n const additionalTrackedAd = ref<AdheseAd | null>(null);\n\n watch(\n [status, isInViewport, data],\n ([newStatus, newIsInViewport, newData]) => {\n if (newStatus !== 'rendered' || !newIsInViewport)\n return;\n\n if (\n newData?.impressionCounter\n && impressionTrackedAd.value !== newData\n ) {\n impressionTrackingPixelElement.value?.remove();\n impressionTrackingPixelElement.value = addTrackingPixel(\n newData.impressionCounter,\n );\n impressionTrackedAd.value = newData;\n runOnImpressionTracked(newData);\n isImpressionTracked.value = true;\n context.logger.debug(\n `Impression tracking pixel fired for ${slotContext.value?.name}`,\n );\n }\n if (\n newData?.additionalTracker\n && additionalTrackedAd.value !== newData\n ) {\n additionalTrackingPixelElement.value?.remove();\n additionalTrackingPixelElement.value = addTrackingPixel(\n newData.additionalTracker,\n );\n additionalTrackedAd.value = newData;\n isAdditionalTracked.value = true;\n context.logger.debug(\n `Additional Impression tracking pixel fired for ${slotContext.value?.name}`,\n );\n }\n },\n { immediate: true },\n );\n hooks.onDispose(() => {\n if (impressionTrackingPixelElement.value)\n impressionTrackingPixelElement.value.remove();\n if (additionalTrackingPixelElement.value)\n additionalTrackingPixelElement.value.remove();\n });\n\n // The request currently in flight, if any. Several things can ask a slot for its ad at roughly the\n // same moment - the viewport and element watchers, `onInit`, or an app calling `render()` while the\n // initial request is still running - and `render()` starts a request of its own whenever it finds no\n // ad yet. Without this they each fire their own call to the ad server for the same slot. Keyed by\n // name so that a format (and therefore name) change still results in a genuinely new request.\n let inFlightRequest: { name: string; promise: Promise<AdheseAd | null> } | null = null;\n\n async function request(): Promise<AdheseAd | null> {\n if (inFlightRequest?.name === name.value)\n return inFlightRequest.promise;\n\n const promise = fetchAd();\n inFlightRequest = { name: name.value, promise };\n\n try {\n return await promise;\n }\n finally {\n if (inFlightRequest?.promise === promise)\n inFlightRequest = null;\n }\n }\n\n async function fetchAd(): Promise<AdheseAd | null> {\n try {\n if (options.lazyLoading && !isInViewport.value)\n return null;\n\n status.value = 'loading';\n isEmpty.value = false;\n emptyAdForTracking = null;\n\n let response = await runOnBeforeRequest(null);\n\n if (!response) {\n response = await extRequestAd({\n slot: {\n name: name.value,\n parameters,\n },\n context,\n });\n }\n\n if (response)\n response = await runOnRequest(response);\n\n if (!response && emptyAdForTracking) {\n // `onRequest` identified the ad as empty via `processOnEmpty(ad)`. Keep the original ad around so\n // it still reaches `render()`, where the position is tracked even though no creative is shown.\n response = emptyAdForTracking;\n }\n\n data.value = response;\n\n if (!originalData.value)\n originalData.value = response;\n\n status.value = response ? 'loaded' : 'empty';\n\n if (!response)\n cleanElement();\n\n if (response && context.options.eagerRendering && element.value)\n await render(response);\n\n return response;\n }\n catch (error) {\n processOnError(error as string);\n return null;\n }\n }\n\n async function render(adToRender?: AdheseAd): Promise<HTMLElement | null> {\n if (\n status.value === 'empty'\n || status.value === 'error'\n || status.value === 'initializing'\n ) {\n return null;\n }\n\n try {\n if (options.lazyLoading && !isInViewport.value)\n return null;\n\n status.value = 'rendering';\n await waitForDomLoad();\n element.value = getElement();\n\n if (adToRender) {\n // An explicit ad was passed in, overriding whatever the slot previously fetched. Don't carry over an\n // `isEmpty` flag from an earlier, unrelated ad.\n isEmpty.value = false;\n }\n\n let renderAd = adToRender ?? data.value ?? originalData.value ?? null;\n if (!renderAd) {\n renderAd = await request();\n if ((status.value as UnwrapRef<AdheseSlot>['status']) === 'rendered') {\n return element.value;\n }\n }\n\n const hadAdBeforeBeforeRenderHook = Boolean(renderAd);\n\n renderAd = renderAd && (await runOnBeforeRender(renderAd));\n\n renderMode = renderAd?.renderMode ?? renderMode;\n\n if (!element.value && renderMode !== 'none') {\n logger.debug(\n `Could not render slot for format ${format.value}. No element found.`,\n slotContext.value,\n );\n\n return null;\n }\n\n if (!renderAd) {\n if (hadAdBeforeBeforeRenderHook) {\n // The ad was identified as empty by the `onBeforeRender` hook itself. The position is still\n // tracked as rendered, but no creative is written to the element. Whatever the app already put\n // in the element (e.g. a fallback rendered from `onEmpty`) is left untouched.\n markEmpty();\n // eslint-disable-next-line require-atomic-updates\n status.value = 'rendered';\n logger.debug(`Slot ${name.value} identified as empty by the onBeforeRender hook`, slotContext.value);\n\n return element.value;\n }\n\n return null;\n }\n\n if (isEmpty.value) {\n // The ad was already identified as empty earlier, typically via `processOnEmpty(ad)` from the\n // `onRequest` hook. The position is tracked as rendered, but no creative is written to the element.\n // Whatever the app already put in the element (e.g. a fallback rendered from `onEmpty`) is left\n // untouched.\n // eslint-disable-next-line require-atomic-updates\n status.value = 'rendered';\n\n return element.value;\n }\n\n if (typeof renderAd?.tag !== 'string' && renderMode !== 'none') {\n const error = `Could not render slot for slot ${name.value}. A valid tag doesn't exist or is not HTML string.`;\n throw new Error(error);\n }\n\n if (renderMode !== 'none' && element.value) {\n renderFunctions[renderMode](\n {\n ...renderAd,\n ...pick(options, ['width', 'height']),\n } as RenderOptions,\n element.value,\n );\n }\n\n logger.debug(`Slot rendered ${name.value}`, {\n renderedElement: element,\n location: context.location,\n format,\n containingElement,\n });\n\n // eslint-disable-next-line require-atomic-updates\n status.value = 'rendered';\n\n runOnRender(renderAd);\n\n return element.value;\n }\n catch (error) {\n processOnError(error as string);\n return null;\n }\n }\n\n function markEmpty(): void {\n if (isEmpty.value)\n return;\n\n isEmpty.value = true;\n runOnEmpty();\n }\n\n function processOnEmpty(ad?: AdheseAd): void {\n markEmpty();\n\n if (ad) {\n // An ad was returned, but its creative was identified as empty (e.g. a no-fill/house banner). Stash\n // it so `request()` can keep it alive for tracking once the slot renders, instead of treating this\n // as \"no ad returned at all\".\n emptyAdForTracking = ad;\n return;\n }\n\n status.value = 'empty';\n logger.debug(`No ad to render for slot ${name.value}`);\n }\n\n function processOnError(error: string): void {\n if (status.value !== 'error') {\n status.value = 'error';\n logger.error(error);\n runOnError(new Error(error, {\n cause: error,\n }));\n }\n }\n\n function cleanElement(): void {\n if (!element.value || renderMode === 'none')\n return;\n\n element.value.innerHTML = '';\n element.value.style.position = '';\n }\n\n function dispose(): void {\n cleanElement();\n\n element.value = null;\n\n data.value = null;\n originalData.value = null;\n\n domObserver.disconnect();\n\n runOnDispose();\n\n isDisposed.value = true;\n\n scope.stop();\n }\n\n const state = reactive({\n location: context.location ?? '',\n lazyLoading: options.lazyLoading ?? false,\n type,\n slot,\n parameters,\n format,\n name,\n data,\n isViewabilityTracked,\n isImpressionTracked,\n isEmpty,\n status,\n element,\n isDisposed,\n id,\n pluginOptions,\n isVisible: isInViewport,\n render,\n request,\n dispose,\n processOnEmpty,\n processOnError,\n cleanElement,\n options: omit(options, ['context']),\n ...hooks,\n });\n\n watch(\n state,\n (newState) => {\n slotContext.value = newState;\n },\n {\n deep: true,\n immediate: true,\n },\n );\n\n context.hooks.onInit(async () => {\n await runOnInit();\n\n if (status.value === 'empty' || status.value === 'error') {\n return;\n }\n\n if (initialData) {\n status.value = 'loaded';\n isEmpty.value = false;\n emptyAdForTracking = null;\n\n data.value = initialData;\n\n let response = await runOnRequest(initialData);\n\n if (!response && emptyAdForTracking) {\n // `onRequest` identified the ad as empty via `processOnEmpty(ad)`. Keep the original ad around so\n // it still reaches `render()`, where the position is tracked even though no creative is shown.\n response = emptyAdForTracking;\n }\n\n data.value = response;\n // eslint-disable-next-line require-atomic-updates\n status.value = response ? 'loaded' : 'empty';\n\n return;\n }\n\n status.value = 'initialized';\n\n if (options.lazyLoading) {\n return;\n }\n\n data.value = (await slotContext.value?.request()) ?? null;\n });\n\n return state;\n })!;\n}\n"],"names":["renderIframe","renderInline","doNothing","effectScope","ref","uniqueId","useSlotHooks","reactive","useQueryDetector","computed","generateName","watch","useDomLoaded","shallowRef","useRenderIntersectionObserver","useViewabilityObserver","addTrackingPixel","extRequestAd","waitForDomLoad","logger","pick","omit"],"mappings":"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"}
|
package/dist/package.json.js
CHANGED
package/dist/slot/slot.js
CHANGED
|
@@ -218,7 +218,20 @@ function createSlot(slotOptions) {
|
|
|
218
218
|
if (additionalTrackingPixelElement.value)
|
|
219
219
|
additionalTrackingPixelElement.value.remove();
|
|
220
220
|
});
|
|
221
|
+
let inFlightRequest = null;
|
|
221
222
|
async function request() {
|
|
223
|
+
if ((inFlightRequest == null ? void 0 : inFlightRequest.name) === name.value)
|
|
224
|
+
return inFlightRequest.promise;
|
|
225
|
+
const promise = fetchAd();
|
|
226
|
+
inFlightRequest = { name: name.value, promise };
|
|
227
|
+
try {
|
|
228
|
+
return await promise;
|
|
229
|
+
} finally {
|
|
230
|
+
if ((inFlightRequest == null ? void 0 : inFlightRequest.promise) === promise)
|
|
231
|
+
inFlightRequest = null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function fetchAd() {
|
|
222
235
|
try {
|
|
223
236
|
if (options.lazyLoading && !isInViewport.value)
|
|
224
237
|
return null;
|
package/dist/slot/slot.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slot.js","sources":["../../src/slot/slot.ts"],"sourcesContent":["import type { AdheseAd } from '@adhese/sdk';\nimport type {\n AdheseSlot,\n AdheseSlotContext,\n AdheseSlotOptions,\n RenderMode,\n} from './slot.types';\nimport {\n addTrackingPixel,\n computed,\n doNothing,\n effectScope,\n generateName,\n omit,\n pick,\n reactive,\n type Ref,\n ref,\n renderIframe,\n renderInline,\n type RenderOptions,\n shallowRef,\n uniqueId,\n type UnwrapRef,\n waitForDomLoad,\n watch,\n} from '@adhese/sdk-shared';\nimport { logger } from '../logger/logger';\nimport { useQueryDetector } from '../queryDetector/queryDetector';\nimport { requestAd as extRequestAd } from '../requestAds/requestAds';\nimport {\n useDomLoaded,\n useRenderIntersectionObserver,\n useSlotHooks,\n useViewabilityObserver,\n} from './slot.composables';\n\nconst renderFunctions: Record<\n RenderMode,\n (ad: RenderOptions, element: HTMLElement) => void\n> = {\n iframe: renderIframe,\n inline: renderInline,\n none: doNothing,\n};\n\nconst defaultOptions = {\n renderMode: 'iframe',\n type: 'normal',\n} satisfies Partial<AdheseSlotOptions>;\n\n/**\n * Create a new slot instance. This slot instance can be used to request and render ads.\n *\n * @param slotOptions {AdheseSlotOptions} The options to create the slot with.\n *\n * @return AdheseSlot The created slot instance.\n */\nexport function createSlot(slotOptions: AdheseSlotOptions): AdheseSlot {\n const scope = effectScope();\n\n return scope.run(() => {\n const slotContext = ref<AdheseSlotContext | null>(null);\n const options = slotOptions.context.hooks.runOnSlotCreate({\n ...defaultOptions,\n ...(Object.fromEntries(\n Object.entries(slotOptions).filter(([, value]) => value !== undefined),\n ) as AdheseSlotOptions),\n });\n\n const {\n containingElement,\n slot,\n context,\n pluginOptions,\n initialData = null,\n type = 'normal',\n } = options;\n\n let { renderMode = 'iframe' } = options;\n\n const id = uniqueId();\n const {\n runOnBeforeRender,\n runOnRender,\n runOnBeforeRequest,\n runOnRequest,\n runOnInit,\n runOnDispose,\n runOnEmpty,\n runOnError,\n runOnImpressionTracked,\n runOnViewableTracked,\n ...hooks\n } = useSlotHooks(options, slotContext);\n\n const isDisposed = ref(false);\n const parameters = reactive(\n new Map(Object.entries(options.parameters ?? {})),\n );\n\n const [device, disposeQueryDetector] = useQueryDetector(\n context,\n typeof options.format === 'string'\n ? {\n [options.format]: '(min-width: 0px)',\n }\n : Object.fromEntries(\n options.format.map(item => [item.format, item.query]),\n ),\n );\n\n const format = computed(() =>\n typeof options.format === 'string' ? options.format : device.value,\n );\n\n const data = ref<AdheseAd | null>(null) as Ref<AdheseAd | null>;\n const originalData = ref(data.value) as Ref<AdheseAd | null>;\n const isEmpty = ref(false);\n // Ad stashed by `processOnEmpty(ad)` when `onRequest` identifies an ad's creative as empty. `request()`\n // rescues it into `data`/`originalData` so tracking pixels are still available once the slot renders.\n let emptyAdForTracking: AdheseAd | null = null;\n const name = computed(() =>\n generateName(options.context.location, format.value, options.slot),\n );\n\n const status = ref<UnwrapRef<AdheseSlot>['status']>('initializing');\n\n watch(name, async (newName, oldName) => {\n if (newName === oldName)\n return;\n\n const newAd = await slotContext.value?.request();\n\n if (!newAd)\n return;\n\n slotContext.value?.cleanElement();\n\n data.value = newAd;\n originalData.value = newAd;\n });\n\n const isDomLoaded = useDomLoaded(context);\n\n const element = shallowRef<HTMLElement | null>(null);\n\n function getElement(): HTMLElement | null {\n if (\n !(\n typeof options.containingElement === 'string'\n || !options.containingElement\n )\n ) {\n return options.containingElement;\n }\n\n if (!isDomLoaded.value)\n return null;\n\n return document.querySelector<HTMLElement>(\n `#${options.containingElement}`,\n );\n }\n\n watch(element, async (newElement, oldElement) => {\n if (\n status.value === 'empty'\n || status.value === 'error'\n || status.value === 'loading'\n ) {\n return;\n }\n\n if (newElement === null && data.value) {\n status.value = 'loaded';\n\n return;\n }\n\n if (\n newElement === oldElement\n || (oldElement === null && newElement === null)\n ) {\n return;\n }\n if (!context.options.eagerRendering && !oldElement) {\n return;\n }\n if (status.value === 'rendering' || status.value === 'rendered') {\n return;\n }\n await render();\n });\n\n const domObserver = new MutationObserver(() => {\n element.value = getElement();\n });\n\n domObserver.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n watch(\n isDomLoaded,\n () => {\n element.value = getElement();\n },\n { immediate: true, deep: true },\n );\n\n const isInViewport = useRenderIntersectionObserver({\n options,\n element,\n hooks,\n });\n\n watch(\n isInViewport,\n async (newIsInViewport) => {\n if (newIsInViewport && status.value !== 'rendered' && status.value !== 'rendering')\n await slotContext.value?.render();\n },\n { immediate: true },\n );\n\n hooks.onDispose(() => {\n disposeQueryDetector();\n });\n\n const isViewabilityTracked = useViewabilityObserver({\n context,\n slotContext,\n hooks,\n onTracked(trackingPixel) {\n let viewabilityPixel;\n if (slotContext.value?.data?.origin === undefined) {\n context.logger.warn(\n `Origin not found for ${slotContext.value?.name}`,\n );\n return;\n }\n switch (slotContext.value?.data?.origin) {\n case 'DALE': {\n // @ts-expect-error - Data structure is not typed and very messy to type\n const seatbid = slotContext.value?.data?.originData?.seatbid;\n const bid = seatbid ? seatbid[0]?.bid[0] : undefined;\n viewabilityPixel = bid\n ? bid.ext?.adhese?.viewableImpressionCounter\n : undefined;\n break;\n }\n case 'JERLICIA':\n viewabilityPixel\n = slotContext.value?.data?.viewableImpressionCounter;\n break;\n }\n if (viewabilityPixel) {\n trackingPixel.value = addTrackingPixel(viewabilityPixel);\n context.logger.debug(\n `Viewability tracking pixel fired for ${slotContext.value?.name}`,\n );\n runOnViewableTracked(slotContext.value?.data);\n }\n },\n });\n\n const impressionTrackingPixelElement = ref<HTMLImageElement | null>(null);\n const additionalTrackingPixelElement = ref<HTMLImageElement | null>(null);\n const isImpressionTracked = ref(false);\n const isAdditionalTracked = ref(false);\n\n const impressionTrackedAd = ref<AdheseAd | null>(null);\n const additionalTrackedAd = ref<AdheseAd | null>(null);\n\n watch(\n [status, isInViewport, data],\n ([newStatus, newIsInViewport, newData]) => {\n if (newStatus !== 'rendered' || !newIsInViewport)\n return;\n\n if (\n newData?.impressionCounter\n && impressionTrackedAd.value !== newData\n ) {\n impressionTrackingPixelElement.value?.remove();\n impressionTrackingPixelElement.value = addTrackingPixel(\n newData.impressionCounter,\n );\n impressionTrackedAd.value = newData;\n runOnImpressionTracked(newData);\n isImpressionTracked.value = true;\n context.logger.debug(\n `Impression tracking pixel fired for ${slotContext.value?.name}`,\n );\n }\n if (\n newData?.additionalTracker\n && additionalTrackedAd.value !== newData\n ) {\n additionalTrackingPixelElement.value?.remove();\n additionalTrackingPixelElement.value = addTrackingPixel(\n newData.additionalTracker,\n );\n additionalTrackedAd.value = newData;\n isAdditionalTracked.value = true;\n context.logger.debug(\n `Additional Impression tracking pixel fired for ${slotContext.value?.name}`,\n );\n }\n },\n { immediate: true },\n );\n hooks.onDispose(() => {\n if (impressionTrackingPixelElement.value)\n impressionTrackingPixelElement.value.remove();\n if (additionalTrackingPixelElement.value)\n additionalTrackingPixelElement.value.remove();\n });\n\n async function request(): Promise<AdheseAd | null> {\n try {\n if (options.lazyLoading && !isInViewport.value)\n return null;\n\n status.value = 'loading';\n isEmpty.value = false;\n emptyAdForTracking = null;\n\n let response = await runOnBeforeRequest(null);\n\n if (!response) {\n response = await extRequestAd({\n slot: {\n name: name.value,\n parameters,\n },\n context,\n });\n }\n\n if (response)\n response = await runOnRequest(response);\n\n if (!response && emptyAdForTracking) {\n // `onRequest` identified the ad as empty via `processOnEmpty(ad)`. Keep the original ad around so\n // it still reaches `render()`, where the position is tracked even though no creative is shown.\n response = emptyAdForTracking;\n }\n\n data.value = response;\n\n if (!originalData.value)\n originalData.value = response;\n\n status.value = response ? 'loaded' : 'empty';\n\n if (!response)\n cleanElement();\n\n if (response && context.options.eagerRendering && element.value)\n await render(response);\n\n return response;\n }\n catch (error) {\n processOnError(error as string);\n return null;\n }\n }\n\n async function render(adToRender?: AdheseAd): Promise<HTMLElement | null> {\n if (\n status.value === 'empty'\n || status.value === 'error'\n || status.value === 'initializing'\n ) {\n return null;\n }\n\n try {\n if (options.lazyLoading && !isInViewport.value)\n return null;\n\n status.value = 'rendering';\n await waitForDomLoad();\n element.value = getElement();\n\n if (adToRender) {\n // An explicit ad was passed in, overriding whatever the slot previously fetched. Don't carry over an\n // `isEmpty` flag from an earlier, unrelated ad.\n isEmpty.value = false;\n }\n\n let renderAd = adToRender ?? data.value ?? originalData.value ?? null;\n if (!renderAd) {\n renderAd = await request();\n if ((status.value as UnwrapRef<AdheseSlot>['status']) === 'rendered') {\n return element.value;\n }\n }\n\n const hadAdBeforeBeforeRenderHook = Boolean(renderAd);\n\n renderAd = renderAd && (await runOnBeforeRender(renderAd));\n\n renderMode = renderAd?.renderMode ?? renderMode;\n\n if (!element.value && renderMode !== 'none') {\n logger.debug(\n `Could not render slot for format ${format.value}. No element found.`,\n slotContext.value,\n );\n\n return null;\n }\n\n if (!renderAd) {\n if (hadAdBeforeBeforeRenderHook) {\n // The ad was identified as empty by the `onBeforeRender` hook itself. The position is still\n // tracked as rendered, but no creative is written to the element. Whatever the app already put\n // in the element (e.g. a fallback rendered from `onEmpty`) is left untouched.\n markEmpty();\n // eslint-disable-next-line require-atomic-updates\n status.value = 'rendered';\n logger.debug(`Slot ${name.value} identified as empty by the onBeforeRender hook`, slotContext.value);\n\n return element.value;\n }\n\n return null;\n }\n\n if (isEmpty.value) {\n // The ad was already identified as empty earlier, typically via `processOnEmpty(ad)` from the\n // `onRequest` hook. The position is tracked as rendered, but no creative is written to the element.\n // Whatever the app already put in the element (e.g. a fallback rendered from `onEmpty`) is left\n // untouched.\n // eslint-disable-next-line require-atomic-updates\n status.value = 'rendered';\n\n return element.value;\n }\n\n if (typeof renderAd?.tag !== 'string' && renderMode !== 'none') {\n const error = `Could not render slot for slot ${name.value}. A valid tag doesn't exist or is not HTML string.`;\n throw new Error(error);\n }\n\n if (renderMode !== 'none' && element.value) {\n renderFunctions[renderMode](\n {\n ...renderAd,\n ...pick(options, ['width', 'height']),\n } as RenderOptions,\n element.value,\n );\n }\n\n logger.debug(`Slot rendered ${name.value}`, {\n renderedElement: element,\n location: context.location,\n format,\n containingElement,\n });\n\n // eslint-disable-next-line require-atomic-updates\n status.value = 'rendered';\n\n runOnRender(renderAd);\n\n return element.value;\n }\n catch (error) {\n processOnError(error as string);\n return null;\n }\n }\n\n function markEmpty(): void {\n if (isEmpty.value)\n return;\n\n isEmpty.value = true;\n runOnEmpty();\n }\n\n function processOnEmpty(ad?: AdheseAd): void {\n markEmpty();\n\n if (ad) {\n // An ad was returned, but its creative was identified as empty (e.g. a no-fill/house banner). Stash\n // it so `request()` can keep it alive for tracking once the slot renders, instead of treating this\n // as \"no ad returned at all\".\n emptyAdForTracking = ad;\n return;\n }\n\n status.value = 'empty';\n logger.debug(`No ad to render for slot ${name.value}`);\n }\n\n function processOnError(error: string): void {\n if (status.value !== 'error') {\n status.value = 'error';\n logger.error(error);\n runOnError(new Error(error, {\n cause: error,\n }));\n }\n }\n\n function cleanElement(): void {\n if (!element.value || renderMode === 'none')\n return;\n\n element.value.innerHTML = '';\n element.value.style.position = '';\n }\n\n function dispose(): void {\n cleanElement();\n\n element.value = null;\n\n data.value = null;\n originalData.value = null;\n\n domObserver.disconnect();\n\n runOnDispose();\n\n isDisposed.value = true;\n\n scope.stop();\n }\n\n const state = reactive({\n location: context.location ?? '',\n lazyLoading: options.lazyLoading ?? false,\n type,\n slot,\n parameters,\n format,\n name,\n data,\n isViewabilityTracked,\n isImpressionTracked,\n isEmpty,\n status,\n element,\n isDisposed,\n id,\n pluginOptions,\n isVisible: isInViewport,\n render,\n request,\n dispose,\n processOnEmpty,\n processOnError,\n cleanElement,\n options: omit(options, ['context']),\n ...hooks,\n });\n\n watch(\n state,\n (newState) => {\n slotContext.value = newState;\n },\n {\n deep: true,\n immediate: true,\n },\n );\n\n context.hooks.onInit(async () => {\n await runOnInit();\n\n if (status.value === 'empty' || status.value === 'error') {\n return;\n }\n\n if (initialData) {\n status.value = 'loaded';\n isEmpty.value = false;\n emptyAdForTracking = null;\n\n data.value = initialData;\n\n let response = await runOnRequest(initialData);\n\n if (!response && emptyAdForTracking) {\n // `onRequest` identified the ad as empty via `processOnEmpty(ad)`. Keep the original ad around so\n // it still reaches `render()`, where the position is tracked even though no creative is shown.\n response = emptyAdForTracking;\n }\n\n data.value = response;\n // eslint-disable-next-line require-atomic-updates\n status.value = response ? 'loaded' : 'empty';\n\n return;\n }\n\n status.value = 'initialized';\n\n if (options.lazyLoading) {\n return;\n }\n\n data.value = (await slotContext.value?.request()) ?? null;\n });\n\n return state;\n })!;\n}\n"],"names":["extRequestAd"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"slot.js","sources":["../../src/slot/slot.ts"],"sourcesContent":["import type { AdheseAd } from '@adhese/sdk';\nimport type {\n AdheseSlot,\n AdheseSlotContext,\n AdheseSlotOptions,\n RenderMode,\n} from './slot.types';\nimport {\n addTrackingPixel,\n computed,\n doNothing,\n effectScope,\n generateName,\n omit,\n pick,\n reactive,\n type Ref,\n ref,\n renderIframe,\n renderInline,\n type RenderOptions,\n shallowRef,\n uniqueId,\n type UnwrapRef,\n waitForDomLoad,\n watch,\n} from '@adhese/sdk-shared';\nimport { logger } from '../logger/logger';\nimport { useQueryDetector } from '../queryDetector/queryDetector';\nimport { requestAd as extRequestAd } from '../requestAds/requestAds';\nimport {\n useDomLoaded,\n useRenderIntersectionObserver,\n useSlotHooks,\n useViewabilityObserver,\n} from './slot.composables';\n\nconst renderFunctions: Record<\n RenderMode,\n (ad: RenderOptions, element: HTMLElement) => void\n> = {\n iframe: renderIframe,\n inline: renderInline,\n none: doNothing,\n};\n\nconst defaultOptions = {\n renderMode: 'iframe',\n type: 'normal',\n} satisfies Partial<AdheseSlotOptions>;\n\n/**\n * Create a new slot instance. This slot instance can be used to request and render ads.\n *\n * @param slotOptions {AdheseSlotOptions} The options to create the slot with.\n *\n * @return AdheseSlot The created slot instance.\n */\nexport function createSlot(slotOptions: AdheseSlotOptions): AdheseSlot {\n const scope = effectScope();\n\n return scope.run(() => {\n const slotContext = ref<AdheseSlotContext | null>(null);\n const options = slotOptions.context.hooks.runOnSlotCreate({\n ...defaultOptions,\n ...(Object.fromEntries(\n Object.entries(slotOptions).filter(([, value]) => value !== undefined),\n ) as AdheseSlotOptions),\n });\n\n const {\n containingElement,\n slot,\n context,\n pluginOptions,\n initialData = null,\n type = 'normal',\n } = options;\n\n let { renderMode = 'iframe' } = options;\n\n const id = uniqueId();\n const {\n runOnBeforeRender,\n runOnRender,\n runOnBeforeRequest,\n runOnRequest,\n runOnInit,\n runOnDispose,\n runOnEmpty,\n runOnError,\n runOnImpressionTracked,\n runOnViewableTracked,\n ...hooks\n } = useSlotHooks(options, slotContext);\n\n const isDisposed = ref(false);\n const parameters = reactive(\n new Map(Object.entries(options.parameters ?? {})),\n );\n\n const [device, disposeQueryDetector] = useQueryDetector(\n context,\n typeof options.format === 'string'\n ? {\n [options.format]: '(min-width: 0px)',\n }\n : Object.fromEntries(\n options.format.map(item => [item.format, item.query]),\n ),\n );\n\n const format = computed(() =>\n typeof options.format === 'string' ? options.format : device.value,\n );\n\n const data = ref<AdheseAd | null>(null) as Ref<AdheseAd | null>;\n const originalData = ref(data.value) as Ref<AdheseAd | null>;\n const isEmpty = ref(false);\n // Ad stashed by `processOnEmpty(ad)` when `onRequest` identifies an ad's creative as empty. `request()`\n // rescues it into `data`/`originalData` so tracking pixels are still available once the slot renders.\n let emptyAdForTracking: AdheseAd | null = null;\n const name = computed(() =>\n generateName(options.context.location, format.value, options.slot),\n );\n\n const status = ref<UnwrapRef<AdheseSlot>['status']>('initializing');\n\n watch(name, async (newName, oldName) => {\n if (newName === oldName)\n return;\n\n const newAd = await slotContext.value?.request();\n\n if (!newAd)\n return;\n\n slotContext.value?.cleanElement();\n\n data.value = newAd;\n originalData.value = newAd;\n });\n\n const isDomLoaded = useDomLoaded(context);\n\n const element = shallowRef<HTMLElement | null>(null);\n\n function getElement(): HTMLElement | null {\n if (\n !(\n typeof options.containingElement === 'string'\n || !options.containingElement\n )\n ) {\n return options.containingElement;\n }\n\n if (!isDomLoaded.value)\n return null;\n\n return document.querySelector<HTMLElement>(\n `#${options.containingElement}`,\n );\n }\n\n watch(element, async (newElement, oldElement) => {\n if (\n status.value === 'empty'\n || status.value === 'error'\n || status.value === 'loading'\n ) {\n return;\n }\n\n if (newElement === null && data.value) {\n status.value = 'loaded';\n\n return;\n }\n\n if (\n newElement === oldElement\n || (oldElement === null && newElement === null)\n ) {\n return;\n }\n if (!context.options.eagerRendering && !oldElement) {\n return;\n }\n if (status.value === 'rendering' || status.value === 'rendered') {\n return;\n }\n await render();\n });\n\n const domObserver = new MutationObserver(() => {\n element.value = getElement();\n });\n\n domObserver.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n watch(\n isDomLoaded,\n () => {\n element.value = getElement();\n },\n { immediate: true, deep: true },\n );\n\n const isInViewport = useRenderIntersectionObserver({\n options,\n element,\n hooks,\n });\n\n watch(\n isInViewport,\n async (newIsInViewport) => {\n if (newIsInViewport && status.value !== 'rendered' && status.value !== 'rendering')\n await slotContext.value?.render();\n },\n { immediate: true },\n );\n\n hooks.onDispose(() => {\n disposeQueryDetector();\n });\n\n const isViewabilityTracked = useViewabilityObserver({\n context,\n slotContext,\n hooks,\n onTracked(trackingPixel) {\n let viewabilityPixel;\n if (slotContext.value?.data?.origin === undefined) {\n context.logger.warn(\n `Origin not found for ${slotContext.value?.name}`,\n );\n return;\n }\n switch (slotContext.value?.data?.origin) {\n case 'DALE': {\n // @ts-expect-error - Data structure is not typed and very messy to type\n const seatbid = slotContext.value?.data?.originData?.seatbid;\n const bid = seatbid ? seatbid[0]?.bid[0] : undefined;\n viewabilityPixel = bid\n ? bid.ext?.adhese?.viewableImpressionCounter\n : undefined;\n break;\n }\n case 'JERLICIA':\n viewabilityPixel\n = slotContext.value?.data?.viewableImpressionCounter;\n break;\n }\n if (viewabilityPixel) {\n trackingPixel.value = addTrackingPixel(viewabilityPixel);\n context.logger.debug(\n `Viewability tracking pixel fired for ${slotContext.value?.name}`,\n );\n runOnViewableTracked(slotContext.value?.data);\n }\n },\n });\n\n const impressionTrackingPixelElement = ref<HTMLImageElement | null>(null);\n const additionalTrackingPixelElement = ref<HTMLImageElement | null>(null);\n const isImpressionTracked = ref(false);\n const isAdditionalTracked = ref(false);\n\n const impressionTrackedAd = ref<AdheseAd | null>(null);\n const additionalTrackedAd = ref<AdheseAd | null>(null);\n\n watch(\n [status, isInViewport, data],\n ([newStatus, newIsInViewport, newData]) => {\n if (newStatus !== 'rendered' || !newIsInViewport)\n return;\n\n if (\n newData?.impressionCounter\n && impressionTrackedAd.value !== newData\n ) {\n impressionTrackingPixelElement.value?.remove();\n impressionTrackingPixelElement.value = addTrackingPixel(\n newData.impressionCounter,\n );\n impressionTrackedAd.value = newData;\n runOnImpressionTracked(newData);\n isImpressionTracked.value = true;\n context.logger.debug(\n `Impression tracking pixel fired for ${slotContext.value?.name}`,\n );\n }\n if (\n newData?.additionalTracker\n && additionalTrackedAd.value !== newData\n ) {\n additionalTrackingPixelElement.value?.remove();\n additionalTrackingPixelElement.value = addTrackingPixel(\n newData.additionalTracker,\n );\n additionalTrackedAd.value = newData;\n isAdditionalTracked.value = true;\n context.logger.debug(\n `Additional Impression tracking pixel fired for ${slotContext.value?.name}`,\n );\n }\n },\n { immediate: true },\n );\n hooks.onDispose(() => {\n if (impressionTrackingPixelElement.value)\n impressionTrackingPixelElement.value.remove();\n if (additionalTrackingPixelElement.value)\n additionalTrackingPixelElement.value.remove();\n });\n\n // The request currently in flight, if any. Several things can ask a slot for its ad at roughly the\n // same moment - the viewport and element watchers, `onInit`, or an app calling `render()` while the\n // initial request is still running - and `render()` starts a request of its own whenever it finds no\n // ad yet. Without this they each fire their own call to the ad server for the same slot. Keyed by\n // name so that a format (and therefore name) change still results in a genuinely new request.\n let inFlightRequest: { name: string; promise: Promise<AdheseAd | null> } | null = null;\n\n async function request(): Promise<AdheseAd | null> {\n if (inFlightRequest?.name === name.value)\n return inFlightRequest.promise;\n\n const promise = fetchAd();\n inFlightRequest = { name: name.value, promise };\n\n try {\n return await promise;\n }\n finally {\n if (inFlightRequest?.promise === promise)\n inFlightRequest = null;\n }\n }\n\n async function fetchAd(): Promise<AdheseAd | null> {\n try {\n if (options.lazyLoading && !isInViewport.value)\n return null;\n\n status.value = 'loading';\n isEmpty.value = false;\n emptyAdForTracking = null;\n\n let response = await runOnBeforeRequest(null);\n\n if (!response) {\n response = await extRequestAd({\n slot: {\n name: name.value,\n parameters,\n },\n context,\n });\n }\n\n if (response)\n response = await runOnRequest(response);\n\n if (!response && emptyAdForTracking) {\n // `onRequest` identified the ad as empty via `processOnEmpty(ad)`. Keep the original ad around so\n // it still reaches `render()`, where the position is tracked even though no creative is shown.\n response = emptyAdForTracking;\n }\n\n data.value = response;\n\n if (!originalData.value)\n originalData.value = response;\n\n status.value = response ? 'loaded' : 'empty';\n\n if (!response)\n cleanElement();\n\n if (response && context.options.eagerRendering && element.value)\n await render(response);\n\n return response;\n }\n catch (error) {\n processOnError(error as string);\n return null;\n }\n }\n\n async function render(adToRender?: AdheseAd): Promise<HTMLElement | null> {\n if (\n status.value === 'empty'\n || status.value === 'error'\n || status.value === 'initializing'\n ) {\n return null;\n }\n\n try {\n if (options.lazyLoading && !isInViewport.value)\n return null;\n\n status.value = 'rendering';\n await waitForDomLoad();\n element.value = getElement();\n\n if (adToRender) {\n // An explicit ad was passed in, overriding whatever the slot previously fetched. Don't carry over an\n // `isEmpty` flag from an earlier, unrelated ad.\n isEmpty.value = false;\n }\n\n let renderAd = adToRender ?? data.value ?? originalData.value ?? null;\n if (!renderAd) {\n renderAd = await request();\n if ((status.value as UnwrapRef<AdheseSlot>['status']) === 'rendered') {\n return element.value;\n }\n }\n\n const hadAdBeforeBeforeRenderHook = Boolean(renderAd);\n\n renderAd = renderAd && (await runOnBeforeRender(renderAd));\n\n renderMode = renderAd?.renderMode ?? renderMode;\n\n if (!element.value && renderMode !== 'none') {\n logger.debug(\n `Could not render slot for format ${format.value}. No element found.`,\n slotContext.value,\n );\n\n return null;\n }\n\n if (!renderAd) {\n if (hadAdBeforeBeforeRenderHook) {\n // The ad was identified as empty by the `onBeforeRender` hook itself. The position is still\n // tracked as rendered, but no creative is written to the element. Whatever the app already put\n // in the element (e.g. a fallback rendered from `onEmpty`) is left untouched.\n markEmpty();\n // eslint-disable-next-line require-atomic-updates\n status.value = 'rendered';\n logger.debug(`Slot ${name.value} identified as empty by the onBeforeRender hook`, slotContext.value);\n\n return element.value;\n }\n\n return null;\n }\n\n if (isEmpty.value) {\n // The ad was already identified as empty earlier, typically via `processOnEmpty(ad)` from the\n // `onRequest` hook. The position is tracked as rendered, but no creative is written to the element.\n // Whatever the app already put in the element (e.g. a fallback rendered from `onEmpty`) is left\n // untouched.\n // eslint-disable-next-line require-atomic-updates\n status.value = 'rendered';\n\n return element.value;\n }\n\n if (typeof renderAd?.tag !== 'string' && renderMode !== 'none') {\n const error = `Could not render slot for slot ${name.value}. A valid tag doesn't exist or is not HTML string.`;\n throw new Error(error);\n }\n\n if (renderMode !== 'none' && element.value) {\n renderFunctions[renderMode](\n {\n ...renderAd,\n ...pick(options, ['width', 'height']),\n } as RenderOptions,\n element.value,\n );\n }\n\n logger.debug(`Slot rendered ${name.value}`, {\n renderedElement: element,\n location: context.location,\n format,\n containingElement,\n });\n\n // eslint-disable-next-line require-atomic-updates\n status.value = 'rendered';\n\n runOnRender(renderAd);\n\n return element.value;\n }\n catch (error) {\n processOnError(error as string);\n return null;\n }\n }\n\n function markEmpty(): void {\n if (isEmpty.value)\n return;\n\n isEmpty.value = true;\n runOnEmpty();\n }\n\n function processOnEmpty(ad?: AdheseAd): void {\n markEmpty();\n\n if (ad) {\n // An ad was returned, but its creative was identified as empty (e.g. a no-fill/house banner). Stash\n // it so `request()` can keep it alive for tracking once the slot renders, instead of treating this\n // as \"no ad returned at all\".\n emptyAdForTracking = ad;\n return;\n }\n\n status.value = 'empty';\n logger.debug(`No ad to render for slot ${name.value}`);\n }\n\n function processOnError(error: string): void {\n if (status.value !== 'error') {\n status.value = 'error';\n logger.error(error);\n runOnError(new Error(error, {\n cause: error,\n }));\n }\n }\n\n function cleanElement(): void {\n if (!element.value || renderMode === 'none')\n return;\n\n element.value.innerHTML = '';\n element.value.style.position = '';\n }\n\n function dispose(): void {\n cleanElement();\n\n element.value = null;\n\n data.value = null;\n originalData.value = null;\n\n domObserver.disconnect();\n\n runOnDispose();\n\n isDisposed.value = true;\n\n scope.stop();\n }\n\n const state = reactive({\n location: context.location ?? '',\n lazyLoading: options.lazyLoading ?? false,\n type,\n slot,\n parameters,\n format,\n name,\n data,\n isViewabilityTracked,\n isImpressionTracked,\n isEmpty,\n status,\n element,\n isDisposed,\n id,\n pluginOptions,\n isVisible: isInViewport,\n render,\n request,\n dispose,\n processOnEmpty,\n processOnError,\n cleanElement,\n options: omit(options, ['context']),\n ...hooks,\n });\n\n watch(\n state,\n (newState) => {\n slotContext.value = newState;\n },\n {\n deep: true,\n immediate: true,\n },\n );\n\n context.hooks.onInit(async () => {\n await runOnInit();\n\n if (status.value === 'empty' || status.value === 'error') {\n return;\n }\n\n if (initialData) {\n status.value = 'loaded';\n isEmpty.value = false;\n emptyAdForTracking = null;\n\n data.value = initialData;\n\n let response = await runOnRequest(initialData);\n\n if (!response && emptyAdForTracking) {\n // `onRequest` identified the ad as empty via `processOnEmpty(ad)`. Keep the original ad around so\n // it still reaches `render()`, where the position is tracked even though no creative is shown.\n response = emptyAdForTracking;\n }\n\n data.value = response;\n // eslint-disable-next-line require-atomic-updates\n status.value = response ? 'loaded' : 'empty';\n\n return;\n }\n\n status.value = 'initialized';\n\n if (options.lazyLoading) {\n return;\n }\n\n data.value = (await slotContext.value?.request()) ?? null;\n });\n\n return state;\n })!;\n}\n"],"names":["extRequestAd"],"mappings":";;;;;AAqCA,MAAM,kBAGF;AAAA,EACF,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AACR;AAEA,MAAM,iBAAiB;AAAA,EACrB,YAAY;AAAA,EACZ,MAAM;AACR;AASO,SAAS,WAAW,aAA4C;AACrE,QAAM,QAAQ,YAAA;AAEd,SAAO,MAAM,IAAI,MAAM;AACrB,UAAM,cAAc,IAA8B,IAAI;AACtD,UAAM,UAAU,YAAY,QAAQ,MAAM,gBAAgB;AAAA,MACxD,GAAG;AAAA,MACH,GAAI,OAAO;AAAA,QACT,OAAO,QAAQ,WAAW,EAAE,OAAO,CAAC,GAAG,KAAK,MAAM,UAAU,MAAS;AAAA,MAAA;AAAA,IACvE,CACD;AAED,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd,OAAO;AAAA,IAAA,IACL;AAEJ,QAAI,EAAE,aAAa,SAAA,IAAa;AAEhC,UAAM,KAAK,SAAA;AACX,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,IAAA,IACD,aAAa,SAAS,WAAW;AAErC,UAAM,aAAa,IAAI,KAAK;AAC5B,UAAM,aAAa;AAAA,MACjB,IAAI,IAAI,OAAO,QAAQ,QAAQ,cAAc,CAAA,CAAE,CAAC;AAAA,IAAA;AAGlD,UAAM,CAAC,QAAQ,oBAAoB,IAAI;AAAA,MACrC;AAAA,MACA,OAAO,QAAQ,WAAW,WACtB;AAAA,QACE,CAAC,QAAQ,MAAM,GAAG;AAAA,MAAA,IAEpB,OAAO;AAAA,QACL,QAAQ,OAAO,IAAI,CAAA,SAAQ,CAAC,KAAK,QAAQ,KAAK,KAAK,CAAC;AAAA,MAAA;AAAA,IACtD;AAGN,UAAM,SAAS;AAAA,MAAS,MACtB,OAAO,QAAQ,WAAW,WAAW,QAAQ,SAAS,OAAO;AAAA,IAAA;AAG/D,UAAM,OAAO,IAAqB,IAAI;AACtC,UAAM,eAAe,IAAI,KAAK,KAAK;AACnC,UAAM,UAAU,IAAI,KAAK;AAGzB,QAAI,qBAAsC;AAC1C,UAAM,OAAO;AAAA,MAAS,MACpB,aAAa,QAAQ,QAAQ,UAAU,OAAO,OAAO,QAAQ,IAAI;AAAA,IAAA;AAGnE,UAAM,SAAS,IAAqC,cAAc;AAElE,UAAM,MAAM,OAAO,SAAS,YAAY;;AACtC,UAAI,YAAY;AACd;AAEF,YAAM,QAAQ,QAAM,iBAAY,UAAZ,mBAAmB;AAEvC,UAAI,CAAC;AACH;AAEF,wBAAY,UAAZ,mBAAmB;AAEnB,WAAK,QAAQ;AACb,mBAAa,QAAQ;AAAA,IACvB,CAAC;AAED,UAAM,cAAc,aAAa,OAAO;AAExC,UAAM,UAAU,WAA+B,IAAI;AAEnD,aAAS,aAAiC;AACxC,UACE,EACE,OAAO,QAAQ,sBAAsB,YAClC,CAAC,QAAQ,oBAEd;AACA,eAAO,QAAQ;AAAA,MACjB;AAEA,UAAI,CAAC,YAAY;AACf,eAAO;AAET,aAAO,SAAS;AAAA,QACd,IAAI,QAAQ,iBAAiB;AAAA,MAAA;AAAA,IAEjC;AAEA,UAAM,SAAS,OAAO,YAAY,eAAe;AAC/C,UACE,OAAO,UAAU,WACd,OAAO,UAAU,WACjB,OAAO,UAAU,WACpB;AACA;AAAA,MACF;AAEA,UAAI,eAAe,QAAQ,KAAK,OAAO;AACrC,eAAO,QAAQ;AAEf;AAAA,MACF;AAEA,UACE,eAAe,cACX,eAAe,QAAQ,eAAe,MAC1C;AACA;AAAA,MACF;AACA,UAAI,CAAC,QAAQ,QAAQ,kBAAkB,CAAC,YAAY;AAClD;AAAA,MACF;AACA,UAAI,OAAO,UAAU,eAAe,OAAO,UAAU,YAAY;AAC/D;AAAA,MACF;AACA,YAAM,OAAA;AAAA,IACR,CAAC;AAED,UAAM,cAAc,IAAI,iBAAiB,MAAM;AAC7C,cAAQ,QAAQ,WAAA;AAAA,IAClB,CAAC;AAED,gBAAY,QAAQ,SAAS,MAAM;AAAA,MACjC,WAAW;AAAA,MACX,SAAS;AAAA,IAAA,CACV;AAED;AAAA,MACE;AAAA,MACA,MAAM;AACJ,gBAAQ,QAAQ,WAAA;AAAA,MAClB;AAAA,MACA,EAAE,WAAW,MAAM,MAAM,KAAA;AAAA,IAAK;AAGhC,UAAM,eAAe,8BAA8B;AAAA,MACjD;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AAED;AAAA,MACE;AAAA,MACA,OAAO,oBAAoB;;AACzB,YAAI,mBAAmB,OAAO,UAAU,cAAc,OAAO,UAAU;AACrE,kBAAM,iBAAY,UAAZ,mBAAmB;AAAA,MAC7B;AAAA,MACA,EAAE,WAAW,KAAA;AAAA,IAAK;AAGpB,UAAM,UAAU,MAAM;AACpB,2BAAA;AAAA,IACF,CAAC;AAED,UAAM,uBAAuB,uBAAuB;AAAA,MAClD;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,eAAe;;AACvB,YAAI;AACJ,cAAI,uBAAY,UAAZ,mBAAmB,SAAnB,mBAAyB,YAAW,QAAW;AACjD,kBAAQ,OAAO;AAAA,YACb,yBAAwB,iBAAY,UAAZ,mBAAmB,IAAI;AAAA,UAAA;AAEjD;AAAA,QACF;AACA,iBAAQ,uBAAY,UAAZ,mBAAmB,SAAnB,mBAAyB,QAAA;AAAA,UAC/B,KAAK,QAAQ;AAEX,kBAAM,WAAU,6BAAY,UAAZ,mBAAmB,SAAnB,mBAAyB,eAAzB,mBAAqC;AACrD,kBAAM,MAAM,WAAU,aAAQ,CAAC,MAAT,mBAAY,IAAI,KAAK;AAC3C,+BAAmB,OACf,eAAI,QAAJ,mBAAS,WAAT,mBAAiB,4BACjB;AACJ;AAAA,UACF;AAAA,UACA,KAAK;AACH,gCACI,uBAAY,UAAZ,mBAAmB,SAAnB,mBAAyB;AAC7B;AAAA,QAAA;AAEJ,YAAI,kBAAkB;AACpB,wBAAc,QAAQ,iBAAiB,gBAAgB;AACvD,kBAAQ,OAAO;AAAA,YACb,yCAAwC,iBAAY,UAAZ,mBAAmB,IAAI;AAAA,UAAA;AAEjE,gCAAqB,iBAAY,UAAZ,mBAAmB,IAAI;AAAA,QAC9C;AAAA,MACF;AAAA,IAAA,CACD;AAED,UAAM,iCAAiC,IAA6B,IAAI;AACxE,UAAM,iCAAiC,IAA6B,IAAI;AACxE,UAAM,sBAAsB,IAAI,KAAK;AACrC,UAAM,sBAAsB,IAAI,KAAK;AAErC,UAAM,sBAAsB,IAAqB,IAAI;AACrD,UAAM,sBAAsB,IAAqB,IAAI;AAErD;AAAA,MACE,CAAC,QAAQ,cAAc,IAAI;AAAA,MAC3B,CAAC,CAAC,WAAW,iBAAiB,OAAO,MAAM;;AACzC,YAAI,cAAc,cAAc,CAAC;AAC/B;AAEF,aACE,mCAAS,sBACN,oBAAoB,UAAU,SACjC;AACA,+CAA+B,UAA/B,mBAAsC;AACtC,yCAA+B,QAAQ;AAAA,YACrC,QAAQ;AAAA,UAAA;AAEV,8BAAoB,QAAQ;AAC5B,iCAAuB,OAAO;AAC9B,8BAAoB,QAAQ;AAC5B,kBAAQ,OAAO;AAAA,YACb,wCAAuC,iBAAY,UAAZ,mBAAmB,IAAI;AAAA,UAAA;AAAA,QAElE;AACA,aACE,mCAAS,sBACN,oBAAoB,UAAU,SACjC;AACA,+CAA+B,UAA/B,mBAAsC;AACtC,yCAA+B,QAAQ;AAAA,YACrC,QAAQ;AAAA,UAAA;AAEV,8BAAoB,QAAQ;AAC5B,8BAAoB,QAAQ;AAC5B,kBAAQ,OAAO;AAAA,YACb,mDAAkD,iBAAY,UAAZ,mBAAmB,IAAI;AAAA,UAAA;AAAA,QAE7E;AAAA,MACF;AAAA,MACA,EAAE,WAAW,KAAA;AAAA,IAAK;AAEpB,UAAM,UAAU,MAAM;AACpB,UAAI,+BAA+B;AACjC,uCAA+B,MAAM,OAAA;AACvC,UAAI,+BAA+B;AACjC,uCAA+B,MAAM,OAAA;AAAA,IACzC,CAAC;AAOD,QAAI,kBAA8E;AAElF,mBAAe,UAAoC;AACjD,WAAI,mDAAiB,UAAS,KAAK;AACjC,eAAO,gBAAgB;AAEzB,YAAM,UAAU,QAAA;AAChB,wBAAkB,EAAE,MAAM,KAAK,OAAO,QAAA;AAEtC,UAAI;AACF,eAAO,MAAM;AAAA,MACf,UAAA;AAEE,aAAI,mDAAiB,aAAY;AAC/B,4BAAkB;AAAA,MACtB;AAAA,IACF;AAEA,mBAAe,UAAoC;AACjD,UAAI;AACF,YAAI,QAAQ,eAAe,CAAC,aAAa;AACvC,iBAAO;AAET,eAAO,QAAQ;AACf,gBAAQ,QAAQ;AAChB,6BAAqB;AAErB,YAAI,WAAW,MAAM,mBAAmB,IAAI;AAE5C,YAAI,CAAC,UAAU;AACb,qBAAW,MAAMA,UAAa;AAAA,YAC5B,MAAM;AAAA,cACJ,MAAM,KAAK;AAAA,cACX;AAAA,YAAA;AAAA,YAEF;AAAA,UAAA,CACD;AAAA,QACH;AAEA,YAAI;AACF,qBAAW,MAAM,aAAa,QAAQ;AAExC,YAAI,CAAC,YAAY,oBAAoB;AAGnC,qBAAW;AAAA,QACb;AAEA,aAAK,QAAQ;AAEb,YAAI,CAAC,aAAa;AAChB,uBAAa,QAAQ;AAEvB,eAAO,QAAQ,WAAW,WAAW;AAErC,YAAI,CAAC;AACH,uBAAA;AAEF,YAAI,YAAY,QAAQ,QAAQ,kBAAkB,QAAQ;AACxD,gBAAM,OAAO,QAAQ;AAEvB,eAAO;AAAA,MACT,SACO,OAAO;AACZ,uBAAe,KAAe;AAC9B,eAAO;AAAA,MACT;AAAA,IACF;AAEA,mBAAe,OAAO,YAAoD;AACxE,UACE,OAAO,UAAU,WACd,OAAO,UAAU,WACjB,OAAO,UAAU,gBACpB;AACA,eAAO;AAAA,MACT;AAEA,UAAI;AACF,YAAI,QAAQ,eAAe,CAAC,aAAa;AACvC,iBAAO;AAET,eAAO,QAAQ;AACf,cAAM,eAAA;AACN,gBAAQ,QAAQ,WAAA;AAEhB,YAAI,YAAY;AAGd,kBAAQ,QAAQ;AAAA,QAClB;AAEA,YAAI,WAAW,cAAc,KAAK,SAAS,aAAa,SAAS;AACjE,YAAI,CAAC,UAAU;AACb,qBAAW,MAAM,QAAA;AACjB,cAAK,OAAO,UAA8C,YAAY;AACpE,mBAAO,QAAQ;AAAA,UACjB;AAAA,QACF;AAEA,cAAM,8BAA8B,QAAQ,QAAQ;AAEpD,mBAAW,YAAa,MAAM,kBAAkB,QAAQ;AAExD,sBAAa,qCAAU,eAAc;AAErC,YAAI,CAAC,QAAQ,SAAS,eAAe,QAAQ;AAC3C,iBAAO;AAAA,YACL,oCAAoC,OAAO,KAAK;AAAA,YAChD,YAAY;AAAA,UAAA;AAGd,iBAAO;AAAA,QACT;AAEA,YAAI,CAAC,UAAU;AACb,cAAI,6BAA6B;AAI/B,sBAAA;AAEA,mBAAO,QAAQ;AACf,mBAAO,MAAM,QAAQ,KAAK,KAAK,mDAAmD,YAAY,KAAK;AAEnG,mBAAO,QAAQ;AAAA,UACjB;AAEA,iBAAO;AAAA,QACT;AAEA,YAAI,QAAQ,OAAO;AAMjB,iBAAO,QAAQ;AAEf,iBAAO,QAAQ;AAAA,QACjB;AAEA,YAAI,QAAO,qCAAU,SAAQ,YAAY,eAAe,QAAQ;AAC9D,gBAAM,QAAQ,kCAAkC,KAAK,KAAK;AAC1D,gBAAM,IAAI,MAAM,KAAK;AAAA,QACvB;AAEA,YAAI,eAAe,UAAU,QAAQ,OAAO;AAC1C,0BAAgB,UAAU;AAAA,YACxB;AAAA,cACE,GAAG;AAAA,cACH,GAAG,KAAK,SAAS,CAAC,SAAS,QAAQ,CAAC;AAAA,YAAA;AAAA,YAEtC,QAAQ;AAAA,UAAA;AAAA,QAEZ;AAEA,eAAO,MAAM,iBAAiB,KAAK,KAAK,IAAI;AAAA,UAC1C,iBAAiB;AAAA,UACjB,UAAU,QAAQ;AAAA,UAClB;AAAA,UACA;AAAA,QAAA,CACD;AAGD,eAAO,QAAQ;AAEf,oBAAY,QAAQ;AAEpB,eAAO,QAAQ;AAAA,MACjB,SACO,OAAO;AACZ,uBAAe,KAAe;AAC9B,eAAO;AAAA,MACT;AAAA,IACF;AAEA,aAAS,YAAkB;AACzB,UAAI,QAAQ;AACV;AAEF,cAAQ,QAAQ;AAChB,iBAAA;AAAA,IACF;AAEA,aAAS,eAAe,IAAqB;AAC3C,gBAAA;AAEA,UAAI,IAAI;AAIN,6BAAqB;AACrB;AAAA,MACF;AAEA,aAAO,QAAQ;AACf,aAAO,MAAM,4BAA4B,KAAK,KAAK,EAAE;AAAA,IACvD;AAEA,aAAS,eAAe,OAAqB;AAC3C,UAAI,OAAO,UAAU,SAAS;AAC5B,eAAO,QAAQ;AACf,eAAO,MAAM,KAAK;AAClB,mBAAW,IAAI,MAAM,OAAO;AAAA,UAC1B,OAAO;AAAA,QAAA,CACR,CAAC;AAAA,MACJ;AAAA,IACF;AAEA,aAAS,eAAqB;AAC5B,UAAI,CAAC,QAAQ,SAAS,eAAe;AACnC;AAEF,cAAQ,MAAM,YAAY;AAC1B,cAAQ,MAAM,MAAM,WAAW;AAAA,IACjC;AAEA,aAAS,UAAgB;AACvB,mBAAA;AAEA,cAAQ,QAAQ;AAEhB,WAAK,QAAQ;AACb,mBAAa,QAAQ;AAErB,kBAAY,WAAA;AAEZ,mBAAA;AAEA,iBAAW,QAAQ;AAEnB,YAAM,KAAA;AAAA,IACR;AAEA,UAAM,QAAQ,SAAS;AAAA,MACrB,UAAU,QAAQ,YAAY;AAAA,MAC9B,aAAa,QAAQ,eAAe;AAAA,MACpC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,KAAK,SAAS,CAAC,SAAS,CAAC;AAAA,MAClC,GAAG;AAAA,IAAA,CACJ;AAED;AAAA,MACE;AAAA,MACA,CAAC,aAAa;AACZ,oBAAY,QAAQ;AAAA,MACtB;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,WAAW;AAAA,MAAA;AAAA,IACb;AAGF,YAAQ,MAAM,OAAO,YAAY;;AAC/B,YAAM,UAAA;AAEN,UAAI,OAAO,UAAU,WAAW,OAAO,UAAU,SAAS;AACxD;AAAA,MACF;AAEA,UAAI,aAAa;AACf,eAAO,QAAQ;AACf,gBAAQ,QAAQ;AAChB,6BAAqB;AAErB,aAAK,QAAQ;AAEb,YAAI,WAAW,MAAM,aAAa,WAAW;AAE7C,YAAI,CAAC,YAAY,oBAAoB;AAGnC,qBAAW;AAAA,QACb;AAEA,aAAK,QAAQ;AAEb,eAAO,QAAQ,WAAW,WAAW;AAErC;AAAA,MACF;AAEA,aAAO,QAAQ;AAEf,UAAI,QAAQ,aAAa;AACvB;AAAA,MACF;AAEA,WAAK,QAAS,QAAM,iBAAY,UAAZ,mBAAmB,cAAc;AAAA,IACvD,CAAC;AAED,WAAO;AAAA,EACT,CAAC;AACH;"}
|