prebid.js 9.52.0 → 9.53.2

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.
Files changed (235) hide show
  1. package/.circleci/config.yml +27 -4
  2. package/creative/crossDomain.js +4 -2
  3. package/dist/33acrossAnalyticsAdapter.js +1 -1
  4. package/dist/33acrossBidAdapter.js +1 -1
  5. package/dist/33acrossIdSystem.js +1 -1
  6. package/dist/BTBidAdapter.js +1 -1
  7. package/dist/adagioAnalyticsAdapter.js +1 -1
  8. package/dist/adagioBidAdapter.js +1 -1
  9. package/dist/adagioUtils.js +1 -1
  10. package/dist/addefendBidAdapter.js +1 -1
  11. package/dist/adgenerationBidAdapter.js +1 -1
  12. package/dist/adlooxRtdProvider.js +1 -1
  13. package/dist/adqueryBidAdapter.js +1 -1
  14. package/dist/adrelevantisBidAdapter.js +1 -1
  15. package/dist/adstirBidAdapter.js +1 -1
  16. package/dist/adtrgtmeBidAdapter.js +1 -1
  17. package/dist/adxcgAnalyticsAdapter.js +1 -1
  18. package/dist/adxcgBidAdapter.js +1 -1
  19. package/dist/adyoulikeBidAdapter.js +1 -1
  20. package/dist/agmaAnalyticsAdapter.js +1 -1
  21. package/dist/ajaBidAdapter.js +1 -1
  22. package/dist/amxBidAdapter.js +1 -1
  23. package/dist/amxIdSystem.js +1 -1
  24. package/dist/aniviewBidAdapter.js +1 -1
  25. package/dist/appierAnalyticsAdapter.js +1 -1
  26. package/dist/appnexusBidAdapter.js +1 -1
  27. package/dist/asoBidAdapter.js +1 -1
  28. package/dist/axonixBidAdapter.js +1 -1
  29. package/dist/beopBidAdapter.js +1 -1
  30. package/dist/bidglassBidAdapter.js +1 -1
  31. package/dist/big-richmediaBidAdapter.js +1 -1
  32. package/dist/bitmediaBidAdapter.js +1 -1
  33. package/dist/bridBidAdapter.js +1 -1
  34. package/dist/bridgeuppBidAdapter.js +1 -1
  35. package/dist/bridgewellBidAdapter.js +1 -1
  36. package/dist/brightMountainMediaBidAdapter.js +1 -1
  37. package/dist/carodaBidAdapter.js +1 -1
  38. package/dist/chromeAiRtdProvider.js +1 -0
  39. package/dist/chtnwBidAdapter.js +1 -1
  40. package/dist/chunk-core.js +1 -1
  41. package/dist/concertBidAdapter.js +1 -1
  42. package/dist/connectadBidAdapter.js +1 -1
  43. package/dist/consumableBidAdapter.js +1 -1
  44. package/dist/contxtfulBidAdapter.js +1 -1
  45. package/dist/conversantAnalyticsAdapter.js +1 -1
  46. package/dist/conversantBidAdapter.js +1 -1
  47. package/dist/craftBidAdapter.js +1 -1
  48. package/dist/criteoBidAdapter.js +1 -1
  49. package/dist/cwireBidAdapter.js +1 -1
  50. package/dist/dailymotionBidAdapter.js +1 -1
  51. package/dist/debugging-standalone.js +1 -1
  52. package/dist/dependencies.json +10 -2
  53. package/dist/dspxBidAdapter.js +1 -1
  54. package/dist/dxkultureBidAdapter.js +1 -1
  55. package/dist/eplanningBidAdapter.js +1 -1
  56. package/dist/equativBidAdapter.js +1 -1
  57. package/dist/eskimiBidAdapter.js +1 -1
  58. package/dist/euidIdSystem.js +1 -1
  59. package/dist/exadsBidAdapter.js +1 -1
  60. package/dist/excoBidAdapter.js +1 -1
  61. package/dist/fanAdapter.js +1 -1
  62. package/dist/feedadBidAdapter.js +1 -1
  63. package/dist/finativeBidAdapter.js +1 -1
  64. package/dist/freewheel-sspBidAdapter.js +1 -1
  65. package/dist/gmosspBidAdapter.js +1 -1
  66. package/dist/greenbidsAnalyticsAdapter.js +1 -1
  67. package/dist/greenbidsBidAdapter.js +1 -1
  68. package/dist/greenbidsRtdProvider.js +1 -1
  69. package/dist/gridBidAdapter.js +1 -1
  70. package/dist/gumgumBidAdapter.js +1 -1
  71. package/dist/h12mediaBidAdapter.js +1 -1
  72. package/dist/hypelabBidAdapter.js +1 -1
  73. package/dist/id5AnalyticsAdapter.js +1 -1
  74. package/dist/id5IdSystem.js +1 -1
  75. package/dist/imdsBidAdapter.js +1 -1
  76. package/dist/improvedigitalBidAdapter.js +1 -1
  77. package/dist/inmobiBidAdapter.js +1 -1
  78. package/dist/insticatorBidAdapter.js +1 -1
  79. package/dist/intentIqAnalyticsAdapter.js +1 -1
  80. package/dist/ixBidAdapter.js +1 -1
  81. package/dist/jixieBidAdapter.js +1 -1
  82. package/dist/jixieIdSystem.js +1 -0
  83. package/dist/justpremiumBidAdapter.js +1 -1
  84. package/dist/kargoBidAdapter.js +1 -1
  85. package/dist/kimberliteBidAdapter.js +1 -1
  86. package/dist/konduitAnalyticsAdapter.js +1 -1
  87. package/dist/kueezBidAdapter.js +1 -1
  88. package/dist/lassoBidAdapter.js +1 -1
  89. package/dist/lifestreetBidAdapter.js +1 -1
  90. package/dist/liveIntentId.js +1 -1
  91. package/dist/logicadBidAdapter.js +1 -1
  92. package/dist/loglyliftBidAdapter.js +1 -1
  93. package/dist/luceadBidAdapter.js +1 -1
  94. package/dist/mabidderBidAdapter.js +1 -1
  95. package/dist/madsenseBidAdapter.js +1 -1
  96. package/dist/magniteAnalyticsAdapter.js +1 -1
  97. package/dist/malltvAnalyticsAdapter.js +1 -1
  98. package/dist/marsmediaBidAdapter.js +1 -1
  99. package/dist/mediafuseBidAdapter.js +1 -1
  100. package/dist/medianetBidAdapter.js +1 -1
  101. package/dist/medianetUtils.js +1 -1
  102. package/dist/mediasquareBidAdapter.js +1 -1
  103. package/dist/mgidBidAdapter.js +1 -1
  104. package/dist/missenaBidAdapter.js +1 -1
  105. package/dist/mobilefuseBidAdapter.js +1 -1
  106. package/dist/nextMillenniumBidAdapter.js +1 -1
  107. package/dist/nexx360Utils.js +1 -1
  108. package/dist/nobidAnalyticsAdapter.js +1 -1
  109. package/dist/nobidBidAdapter.js +1 -1
  110. package/dist/nodalsAiRtdProvider.js +1 -1
  111. package/dist/not-for-prod/prebid.js +175 -173
  112. package/dist/oguryBidAdapter.js +1 -1
  113. package/dist/onetagBidAdapter.js +1 -1
  114. package/dist/ooloAnalyticsAdapter.js +1 -1
  115. package/dist/openxBidAdapter.js +1 -1
  116. package/dist/optidigitalBidAdapter.js +1 -1
  117. package/dist/orbidderBidAdapter.js +1 -1
  118. package/dist/ortb2.5Translator.js +1 -1
  119. package/dist/outbrainBidAdapter.js +1 -1
  120. package/dist/ozoneBidAdapter.js +1 -1
  121. package/dist/pixfutureBidAdapter.js +1 -1
  122. package/dist/prebidServerBidAdapter.js +1 -1
  123. package/dist/publinkIdSystem.js +1 -1
  124. package/dist/pubmaticAnalyticsAdapter.js +1 -1
  125. package/dist/pubmaticBidAdapter.js +1 -1
  126. package/dist/pubmaticRtdProvider.js +1 -1
  127. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  128. package/dist/pubxaiAnalyticsAdapter.js +1 -1
  129. package/dist/pxyzBidAdapter.js +1 -1
  130. package/dist/quantcastBidAdapter.js +1 -1
  131. package/dist/readpeakBidAdapter.js +1 -1
  132. package/dist/relaidoBidAdapter.js +1 -1
  133. package/dist/relevatehealthBidAdapter.js +1 -1
  134. package/dist/retailspotBidAdapter.js +1 -1
  135. package/dist/rhythmoneBidAdapter.js +1 -1
  136. package/dist/riseUtils.js +1 -1
  137. package/dist/rubiconBidAdapter.js +1 -1
  138. package/dist/seedingAllianceBidAdapter.js +1 -1
  139. package/dist/seedtagBidAdapter.js +1 -1
  140. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  141. package/dist/sharethroughBidAdapter.js +1 -1
  142. package/dist/showheroes-bsBidAdapter.js +1 -1
  143. package/dist/smaatoBidAdapter.js +1 -1
  144. package/dist/smartadserverBidAdapter.js +1 -1
  145. package/dist/smartxBidAdapter.js +1 -1
  146. package/dist/smilewantedBidAdapter.js +1 -1
  147. package/dist/snigelBidAdapter.js +1 -1
  148. package/dist/sonobiBidAdapter.js +1 -1
  149. package/dist/sovrnBidAdapter.js +1 -1
  150. package/dist/sparteoBidAdapter.js +1 -1
  151. package/dist/sspBCBidAdapter.js +1 -1
  152. package/dist/stvBidAdapter.js +1 -1
  153. package/dist/sublimeBidAdapter.js +1 -1
  154. package/dist/taboolaBidAdapter.js +1 -1
  155. package/dist/tappxBidAdapter.js +1 -1
  156. package/dist/targetVideoBidAdapter.js +1 -1
  157. package/dist/teadsBidAdapter.js +1 -1
  158. package/dist/terceptAnalyticsAdapter.js +1 -1
  159. package/dist/themoneytizerBidAdapter.js +1 -1
  160. package/dist/trionBidAdapter.js +1 -1
  161. package/dist/tripleliftBidAdapter.js +1 -1
  162. package/dist/ttdBidAdapter.js +1 -1
  163. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  164. package/dist/uid2IdSystem.js +1 -1
  165. package/dist/underdogmediaBidAdapter.js +1 -1
  166. package/dist/undertoneBidAdapter.js +1 -1
  167. package/dist/unrulyBidAdapter.js +1 -1
  168. package/dist/userId.js +1 -1
  169. package/dist/vidazooUtils.js +1 -1
  170. package/dist/videobyteBidAdapter.js +1 -1
  171. package/dist/visxBidAdapter.js +1 -1
  172. package/dist/vuukleBidAdapter.js +1 -1
  173. package/dist/widespaceBidAdapter.js +1 -1
  174. package/dist/winrBidAdapter.js +1 -1
  175. package/dist/yahooAdsBidAdapter.js +1 -1
  176. package/dist/yandexBidAdapter.js +1 -1
  177. package/dist/yieldmoBidAdapter.js +1 -1
  178. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  179. package/gulpfile.js +12 -6
  180. package/integrationExamples/chromeai/japanese.html +224 -0
  181. package/integrationExamples/gpt/x-domain/creative.html +1 -1
  182. package/karma.conf.maker.js +7 -7
  183. package/karmaRunner.js +3 -3
  184. package/libraries/liveIntentId/shared.js +16 -0
  185. package/modules/.submodules.json +1 -0
  186. package/modules/aniviewBidAdapter.js +32 -23
  187. package/modules/chromeAiRtdProvider.js +421 -0
  188. package/modules/chromeAiRtdProvider.md +230 -0
  189. package/modules/fanAdapter.js +318 -124
  190. package/modules/ixBidAdapter.js +5 -0
  191. package/modules/jixieIdSystem.js +186 -0
  192. package/modules/ozoneBidAdapter.js +214 -336
  193. package/modules/prebidServerBidAdapter/index.js +59 -35
  194. package/modules/pubmaticRtdProvider.js +418 -4
  195. package/modules/pubmaticRtdProvider.md +12 -2
  196. package/modules/relevatehealthBidAdapter.js +20 -130
  197. package/modules/relevatehealthBidAdapter.md +1 -2
  198. package/modules/sovrnBidAdapter.js +4 -4
  199. package/modules/teadsBidAdapter.js +5 -0
  200. package/modules/ttdBidAdapter.js +5 -4
  201. package/modules/userId/index.js +30 -31
  202. package/package.json +5 -4
  203. package/src/adapterManager.js +3 -0
  204. package/test/spec/libraries/cmUtils_spec.js +17 -12
  205. package/test/spec/modules/aniviewBidAdapter_spec.js +15 -2
  206. package/test/spec/modules/chromeAiRtdProvider_spec.js +393 -0
  207. package/test/spec/modules/dgkeywordRtdProvider_spec.js +5 -2
  208. package/test/spec/modules/euidIdSystem_spec.js +9 -3
  209. package/test/spec/modules/fanAdapter_spec.js +264 -268
  210. package/test/spec/modules/id5IdSystem_spec.js +57 -101
  211. package/test/spec/modules/identityLinkIdSystem_spec.js +1 -0
  212. package/test/spec/modules/idxIdSystem_spec.js +2 -75
  213. package/test/spec/modules/instreamTracking_spec.js +15 -18
  214. package/test/spec/modules/ixBidAdapter_spec.js +26 -0
  215. package/test/spec/modules/jixieIdSystem_spec.js +303 -0
  216. package/test/spec/modules/liveIntentExternalIdSystem_spec.js +5 -0
  217. package/test/spec/modules/liveIntentIdMinimalSystem_spec.js +5 -0
  218. package/test/spec/modules/liveIntentIdSystem_spec.js +38 -0
  219. package/test/spec/modules/lmpIdSystem_spec.js +2 -81
  220. package/test/spec/modules/ozoneBidAdapter_spec.js +511 -658
  221. package/test/spec/modules/prebidServerBidAdapter_spec.js +72 -2
  222. package/test/spec/modules/pubmaticBidAdapter_spec.js +411 -126
  223. package/test/spec/modules/pubmaticRtdProvider_spec.js +946 -2
  224. package/test/spec/modules/raynRtdProvider_spec.js +18 -22
  225. package/test/spec/modules/relevatehealthBidAdapter_spec.js +9 -39
  226. package/test/spec/modules/sharedIdSystem_spec.js +3 -3
  227. package/test/spec/modules/sovrnBidAdapter_spec.js +71 -3
  228. package/test/spec/modules/teadsBidAdapter_spec.js +9 -4
  229. package/test/spec/modules/ttdBidAdapter_spec.js +24 -2
  230. package/test/spec/modules/uid2IdSystem_helpers.js +5 -2
  231. package/test/spec/modules/userId_spec.js +142 -387
  232. package/test/spec/modules/yieldoneAnalyticsAdapter_spec.js +13 -12
  233. package/test/spec/modules/zeotapIdPlusIdSystem_spec.js +3 -69
  234. package/wdio.shared.conf.js +2 -2
  235. package/CLAUDE.md +0 -1
@@ -157,31 +157,40 @@ export const spec = {
157
157
  return [];
158
158
  }
159
159
 
160
- return converter.fromORTB({ response: body, request: bidderRequest.data }).bids.map((prebidBid, index) => {
161
- const bid = bids[index];
162
- const replacements = {
163
- auctionPrice: prebidBid.cpm,
164
- auctionId: prebidBid.requestId,
165
- auctionBidId: bid.bidid,
166
- auctionImpId: bid.impid,
167
- auctionSeatId: prebidBid.seatBidId,
168
- auctionAdId: bid.adid,
169
- };
170
-
171
- const bidAdmWithReplacedMacros = replaceMacros(bid.adm, replacements);
172
-
173
- if (isVideoType(prebidBid.mediaType)) {
174
- prebidBid.vastXml = bidAdmWithReplacedMacros;
175
-
176
- if (bid?.nurl) {
177
- prebidBid.vastUrl = replaceMacros(bid.nurl, replacements);
160
+ return converter.fromORTB({ response: body, request: bidderRequest.data }).bids
161
+ .filter((prebidBid, index) => !!bids[index].adm || !!bids[index].nurl)
162
+ .map((prebidBid, index) => {
163
+ const bid = bids[index];
164
+ const replacements = {
165
+ auctionPrice: prebidBid.cpm,
166
+ auctionId: prebidBid.requestId,
167
+ auctionBidId: bid.bidid,
168
+ auctionImpId: bid.impid,
169
+ auctionSeatId: prebidBid.seatBidId,
170
+ auctionAdId: bid.adid,
171
+ };
172
+
173
+ const bidAdmWithReplacedMacros = replaceMacros(bid.adm, replacements);
174
+
175
+ if (isVideoType(prebidBid.mediaType)) {
176
+ if (bidAdmWithReplacedMacros) {
177
+ prebidBid.vastXml = bidAdmWithReplacedMacros;
178
+ }
179
+
180
+ if (bid.nurl) {
181
+ if (!prebidBid.vastXml) {
182
+ prebidBid.vastUrl = replaceMacros(bid.nurl, replacements);
183
+ } else {
184
+ // We do not want to use the vastUrl if we have the vastXml
185
+ delete prebidBid.vastUrl
186
+ }
187
+ }
188
+ } else {
189
+ prebidBid.ad = bidAdmWithReplacedMacros;
178
190
  }
179
- } else {
180
- prebidBid.ad = bidAdmWithReplacedMacros;
181
- }
182
191
 
183
- return prebidBid;
184
- });
192
+ return prebidBid;
193
+ });
185
194
  },
186
195
 
187
196
  getUserSyncs(syncOptions, serverResponses) {
@@ -0,0 +1,421 @@
1
+ import { submodule } from '../src/hook.js';
2
+ import { logError, mergeDeep, logMessage, deepSetValue, deepAccess } from '../src/utils.js';
3
+ import { getCoreStorageManager } from '../src/storageManager.js';
4
+
5
+ /* global LanguageDetector, Summarizer */
6
+ /**
7
+ * @typedef {import('../modules/rtdModule/index.js').RtdSubmodule} RtdSubmodule
8
+ */
9
+
10
+ export const CONSTANTS = Object.freeze({
11
+ SUBMODULE_NAME: 'chromeAi',
12
+ REAL_TIME_MODULE: 'realTimeData',
13
+ LOG_PRE_FIX: 'ChromeAI-Rtd-Provider:',
14
+ STORAGE_KEY: 'chromeAi_detected_data', // Single key for both language and keywords
15
+ MIN_TEXT_LENGTH: 20,
16
+ DEFAULT_CONFIG: {
17
+ languageDetector: {
18
+ enabled: true,
19
+ confidence: 0.8,
20
+ ortb2Path: 'site.content.language' // Default path for language
21
+ },
22
+ summarizer: {
23
+ enabled: false,
24
+ type: 'headline', // 'headline' or 'paragraph'
25
+ format: 'markdown', // 'markdown' or 'plaintext'
26
+ length: 'short', // 'short', 'medium', or 'long'
27
+ ortb2Path: 'site.content.keywords', // Default path for keywords
28
+ cacheInLocalStorage: true // Whether to cache detected keywords in localStorage
29
+ }
30
+ }
31
+ });
32
+
33
+ const storage = getCoreStorageManager(CONSTANTS.SUBMODULE_NAME);
34
+ let moduleConfig = JSON.parse(JSON.stringify(CONSTANTS.DEFAULT_CONFIG));
35
+ let detectedKeywords = null; // To store generated summary/keywords
36
+
37
+ // Helper to initialize Chrome AI API instances (LanguageDetector, Summarizer)
38
+ const _createAiApiInstance = async (ApiConstructor, options) => {
39
+ const apiName = ApiConstructor.name; // e.g., "LanguageDetector" or "Summarizer"
40
+
41
+ try {
42
+ if (!(apiName in self) || typeof self[apiName] !== 'function') { // Also check if it's a function (constructor)
43
+ logError(`${CONSTANTS.LOG_PRE_FIX} ${apiName} API not available or not a constructor in self.`);
44
+ return null;
45
+ }
46
+
47
+ const availability = await ApiConstructor.availability();
48
+ if (availability === 'unavailable') {
49
+ logError(`${CONSTANTS.LOG_PRE_FIX} ${apiName} is unavailable.`);
50
+ return null;
51
+ }
52
+
53
+ let instance;
54
+ if (availability === 'available') {
55
+ instance = await ApiConstructor.create(options);
56
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} ${apiName} instance created (was available).`);
57
+ } else { // Assuming 'after-download' or similar state if not 'available'
58
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} ${apiName} model needs download.`);
59
+
60
+ instance = await ApiConstructor.create(options);
61
+ instance.addEventListener('downloadprogress', (e) => {
62
+ const progress = e.total > 0 ? Math.round(e.loaded / e.total * 100) : (e.loaded > 0 ? 'In progress' : 'Starting');
63
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} ${apiName} model DL: ${progress}${e.total > 0 ? '%' : ''}`);
64
+ });
65
+ await instance.ready;
66
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} ${apiName} model ready after download.`);
67
+ }
68
+ return instance;
69
+ } catch (error) {
70
+ logError(`${CONSTANTS.LOG_PRE_FIX} Error creating ${apiName} instance:`, error);
71
+ return null;
72
+ }
73
+ };
74
+
75
+ const mergeModuleConfig = (config) => {
76
+ // Start with a deep copy of default_config to ensure all keys are present
77
+ let newConfig = JSON.parse(JSON.stringify(CONSTANTS.DEFAULT_CONFIG));
78
+ if (config?.params) {
79
+ mergeDeep(newConfig, config.params);
80
+ }
81
+ moduleConfig = newConfig; // Assign to module-level variable
82
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Module config set:`, moduleConfig);
83
+ return moduleConfig;
84
+ };
85
+
86
+ export const getCurrentUrl = () => window.location.href;
87
+
88
+ export const getPageText = () => {
89
+ const text = document.body.textContent;
90
+ if (!text || text.length < CONSTANTS.MIN_TEXT_LENGTH) {
91
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Not enough text content (length: ${text?.length || 0}) for processing.`);
92
+ return null;
93
+ }
94
+ return text;
95
+ };
96
+
97
+ // --- Chrome AI LocalStorage Helper Functions ---
98
+ export const _getChromeAiDataFromLocalStorage = (url) => {
99
+ if (!storage.hasLocalStorage() || !storage.localStorageIsEnabled()) {
100
+ return null;
101
+ }
102
+ const currentUrl = url || getCurrentUrl();
103
+ const storedJson = storage.getDataFromLocalStorage(CONSTANTS.STORAGE_KEY);
104
+ if (storedJson) {
105
+ try {
106
+ const storedObject = JSON.parse(storedJson);
107
+ return storedObject?.[currentUrl] || null;
108
+ } catch (e) {
109
+ logError(`${CONSTANTS.LOG_PRE_FIX} Error parsing Chrome AI data from localStorage:`, e);
110
+ }
111
+ }
112
+ return null;
113
+ };
114
+
115
+ const _storeChromeAiDataInLocalStorage = (url, data) => {
116
+ try {
117
+ if (!storage.hasLocalStorage() || !storage.localStorageIsEnabled()) {
118
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} localStorage is not available, cannot store Chrome AI data.`);
119
+ return false;
120
+ }
121
+ let overallStorageObject = {};
122
+ const existingStoredJson = storage.getDataFromLocalStorage(CONSTANTS.STORAGE_KEY);
123
+ if (existingStoredJson) {
124
+ try {
125
+ overallStorageObject = JSON.parse(existingStoredJson);
126
+ } catch (e) {
127
+ logError(`${CONSTANTS.LOG_PRE_FIX} Error parsing existing Chrome AI data from localStorage:`, e);
128
+ }
129
+ }
130
+ const currentUrl = url || getCurrentUrl();
131
+ overallStorageObject[currentUrl] = {
132
+ ...overallStorageObject[currentUrl], // Preserve any existing data
133
+ ...data // Overwrite or add new data
134
+ };
135
+ storage.setDataInLocalStorage(CONSTANTS.STORAGE_KEY, JSON.stringify(overallStorageObject));
136
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Chrome AI data stored in localStorage for ${currentUrl}:`, data);
137
+ return true;
138
+ } catch (error) {
139
+ logError(`${CONSTANTS.LOG_PRE_FIX} Error storing Chrome AI data to localStorage:`, error);
140
+ return false;
141
+ }
142
+ };
143
+
144
+ const isLanguageInLocalStorage = (url) => {
145
+ const chromeAiData = _getChromeAiDataFromLocalStorage(url);
146
+ return chromeAiData?.language || null;
147
+ };
148
+
149
+ export const getPrioritizedLanguageData = (reqBidsConfigObj) => {
150
+ // 1. Check auction-specific ORTB2 (passed in reqBidsConfigObj for getBidRequestData)
151
+ // Uses configurable path for language
152
+ if (reqBidsConfigObj && moduleConfig.languageDetector) {
153
+ const langPath = moduleConfig.languageDetector.ortb2Path;
154
+ const lang = deepAccess(reqBidsConfigObj.ortb2Fragments?.global, langPath);
155
+ if (lang) {
156
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Language '${lang}' found in auction-specific ortb2Fragments at path '${langPath}'.`);
157
+ return { language: lang, source: 'auction_ortb2' };
158
+ }
159
+ }
160
+
161
+ // 2. Check localStorage (relevant for both init and getBidRequestData)
162
+ const storedLangData = isLanguageInLocalStorage(getCurrentUrl());
163
+ if (storedLangData) {
164
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Language '${storedLangData.language}' found in localStorage.`);
165
+ return { ...storedLangData, source: 'localStorage' };
166
+ }
167
+
168
+ return null;
169
+ };
170
+
171
+ const getPrioritizedKeywordsData = (reqBidsConfigObj) => {
172
+ // 1. Check auction-specific ORTB2 (passed in reqBidsConfigObj for getBidRequestData)
173
+ if (reqBidsConfigObj && moduleConfig.summarizer) {
174
+ const keywordsPath = moduleConfig.summarizer.ortb2Path;
175
+ const keywords = deepAccess(reqBidsConfigObj.ortb2Fragments?.global, keywordsPath);
176
+ if (keywords && Array.isArray(keywords) && keywords.length > 0) {
177
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Keywords found in auction-specific ortb2Fragments at path '${keywordsPath}'.`, keywords);
178
+ return { keywords: keywords, source: 'auction_ortb2' };
179
+ }
180
+ }
181
+
182
+ // 2. Check localStorage (if enabled)
183
+ if (moduleConfig.summarizer?.cacheInLocalStorage === true) {
184
+ const chromeAiData = _getChromeAiDataFromLocalStorage();
185
+ const storedKeywords = chromeAiData?.keywords;
186
+ if (storedKeywords && Array.isArray(storedKeywords) && storedKeywords.length > 0) {
187
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Keywords found in localStorage.`, storedKeywords);
188
+ return { keywords: storedKeywords, source: 'localStorage' };
189
+ }
190
+ }
191
+ return null;
192
+ };
193
+
194
+ export const storeDetectedLanguage = (language, confidence, url) => {
195
+ if (!language) {
196
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} No valid language to store`);
197
+ return false;
198
+ }
199
+ const dataPayload = { language: language, confidence: confidence };
200
+ return _storeChromeAiDataInLocalStorage(url, { language: dataPayload });
201
+ };
202
+
203
+ export const detectLanguage = async (text) => {
204
+ const detector = await _createAiApiInstance(LanguageDetector);
205
+ if (!detector) {
206
+ return null; // Error already logged by _createAiApiInstance
207
+ }
208
+
209
+ try {
210
+ const results = await detector.detect(text);
211
+ if (!results || results.length === 0) {
212
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} No language results from API.`);
213
+ return null;
214
+ }
215
+ const topResult = results[0];
216
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Detected lang: ${topResult.detectedLanguage} (conf: ${topResult.confidence.toFixed(2)})`);
217
+ if (topResult.confidence < moduleConfig.languageDetector.confidence) {
218
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Lang confidence (${topResult.confidence.toFixed(2)}) < threshold (${moduleConfig.languageDetector.confidence}).`);
219
+ return null;
220
+ }
221
+ return { language: topResult.detectedLanguage, confidence: topResult.confidence };
222
+ } catch (error) {
223
+ logError(`${CONSTANTS.LOG_PRE_FIX} Error during LanguageDetector.detect():`, error);
224
+ return null;
225
+ }
226
+ };
227
+
228
+ export const detectSummary = async (text, config) => {
229
+ const summaryOptions = {
230
+ type: config.type,
231
+ format: config.format,
232
+ length: config.length,
233
+ };
234
+ const summarizer = await _createAiApiInstance(Summarizer, summaryOptions);
235
+ if (!summarizer) {
236
+ return null; // Error already logged by _createAiApiInstance
237
+ }
238
+
239
+ try {
240
+ const summaryResult = await summarizer.summarize(text, summaryOptions);
241
+ if (!summaryResult) {
242
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} No summary result from API.`);
243
+ return null;
244
+ }
245
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Summary generated (type: ${summaryOptions.type}, len: ${summaryOptions.length}):`, summaryResult.substring(0, 100) + '...');
246
+ return summaryResult; // This is a string
247
+ } catch (error) {
248
+ logError(`${CONSTANTS.LOG_PRE_FIX} Error during Summarizer.summarize():`, error);
249
+ return null;
250
+ }
251
+ };
252
+
253
+ const initLanguageDetector = async () => {
254
+ const existingLanguage = getPrioritizedLanguageData(null); // Pass null or undefined for reqBidsConfigObj
255
+ if (existingLanguage && existingLanguage.source === 'localStorage') {
256
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Language detection skipped, language '${existingLanguage.language}' found in localStorage.`);
257
+ return true;
258
+ }
259
+
260
+ const pageText = getPageText();
261
+ if (!pageText) return false;
262
+
263
+ const detectionResult = await detectLanguage(pageText);
264
+ if (!detectionResult) {
265
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Failed to detect language from page content.`);
266
+ return false;
267
+ }
268
+ return storeDetectedLanguage(detectionResult.language, detectionResult.confidence, getCurrentUrl());
269
+ };
270
+
271
+ export const storeDetectedKeywords = (keywords, url) => {
272
+ if (!keywords || !Array.isArray(keywords) || keywords.length === 0) {
273
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} No valid keywords array to store`);
274
+ return false;
275
+ }
276
+ return _storeChromeAiDataInLocalStorage(url, { keywords: keywords });
277
+ };
278
+
279
+ const initSummarizer = async () => {
280
+ // Check for prioritized/cached keywords first (reqBidsConfigObj is null during init)
281
+ const prioritizedData = getPrioritizedKeywordsData(null);
282
+ if (prioritizedData && prioritizedData.source === 'localStorage') {
283
+ detectedKeywords = prioritizedData.keywords;
284
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Summarizer skipped, keywords from localStorage.`, detectedKeywords);
285
+ return true;
286
+ }
287
+ // If auction_ortb2 had data, it would be handled by getBidRequestData directly, init focuses on detection/localStorage
288
+
289
+ const pageText = getPageText();
290
+ if (!pageText) {
291
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Summarizer: No/short text, cannot generate keywords.`);
292
+ return false;
293
+ }
294
+
295
+ if (!moduleConfig.summarizer) {
296
+ logError(`${CONSTANTS.LOG_PRE_FIX} Summarizer config missing during init.`);
297
+ return false;
298
+ }
299
+
300
+ const summaryText = await detectSummary(pageText, moduleConfig.summarizer);
301
+ if (summaryText) {
302
+ // The API returns a single summary string. We treat this string as a single keyword.
303
+ // If multiple keywords were desired from the summary, further processing would be needed here.
304
+ detectedKeywords = [summaryText];
305
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Summary processed and new keywords generated:`, detectedKeywords);
306
+
307
+ if (moduleConfig.summarizer.cacheInLocalStorage === true) {
308
+ storeDetectedKeywords(detectedKeywords, getCurrentUrl());
309
+ }
310
+ return true;
311
+ }
312
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Failed to generate summary, no new keywords.`);
313
+ return false;
314
+ };
315
+
316
+ const init = async (config) => {
317
+ moduleConfig = mergeModuleConfig(config);
318
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Initializing with config:`, moduleConfig);
319
+
320
+ const activeInitializations = [];
321
+
322
+ if (moduleConfig.languageDetector?.enabled !== false) {
323
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Language detection enabled. Initializing...`);
324
+ activeInitializations.push(initLanguageDetector());
325
+ } else {
326
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Language detection disabled by config.`);
327
+ }
328
+
329
+ // Summarizer Initialization
330
+ if (moduleConfig.summarizer?.enabled === true) {
331
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Summarizer enabled. Initializing...`);
332
+ activeInitializations.push(initSummarizer());
333
+ } else {
334
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Summarizer disabled by config.`);
335
+ }
336
+
337
+ if (activeInitializations.length === 0) {
338
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} No features enabled for initialization.`);
339
+ return true; // Module is considered initialized if no features are active/enabled.
340
+ }
341
+
342
+ // Wait for all enabled features to attempt initialization
343
+ try {
344
+ const results = await Promise.all(activeInitializations);
345
+ // Consider init successful if at least one feature init succeeded, or if no features were meant to run.
346
+ const overallSuccess = results.length > 0 ? results.some(result => result === true) : true;
347
+ if (overallSuccess) {
348
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Relevant features initialized.`);
349
+ } else {
350
+ logError(`${CONSTANTS.LOG_PRE_FIX} All enabled features failed to initialize.`);
351
+ }
352
+ return overallSuccess;
353
+ } catch (error) {
354
+ logError(`${CONSTANTS.LOG_PRE_FIX} Error during feature initializations:`, error);
355
+ return false;
356
+ }
357
+ };
358
+
359
+ /**
360
+ * Add language data to bid request
361
+ * @param {Object} reqBidsConfigObj - Request bids configuration object
362
+ * @param {function} callback - Callback function
363
+ */
364
+ const getBidRequestData = (reqBidsConfigObj, callback) => {
365
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} reqBidsConfigObj:`, reqBidsConfigObj);
366
+
367
+ // Ensure ortb2Fragments and global path exist for potential deepSetValue operations
368
+ reqBidsConfigObj.ortb2Fragments = reqBidsConfigObj.ortb2Fragments || {};
369
+ reqBidsConfigObj.ortb2Fragments.global = reqBidsConfigObj.ortb2Fragments.global || {};
370
+
371
+ // Language Data Enrichment
372
+ if (moduleConfig.languageDetector?.enabled !== false) {
373
+ const languageData = getPrioritizedLanguageData(reqBidsConfigObj);
374
+ if (languageData && languageData.source !== 'auction_ortb2') {
375
+ const langPath = moduleConfig.languageDetector.ortb2Path;
376
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Enriching ORTB2 path '${langPath}' with lang '${languageData.language}' from ${languageData.source}.`);
377
+ deepSetValue(reqBidsConfigObj.ortb2Fragments.global, langPath, languageData.language);
378
+ } else if (languageData?.source === 'auction_ortb2') {
379
+ const langPath = moduleConfig.languageDetector.ortb2Path;
380
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Lang already in auction ORTB2 at path '${langPath}', no enrichment needed.`);
381
+ }
382
+ } else {
383
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Language detection disabled, no lang enrichment.`);
384
+ }
385
+
386
+ // Summarizer Data (Keywords) Enrichment
387
+ if (moduleConfig.summarizer?.enabled === true) {
388
+ const keywordsPath = moduleConfig.summarizer.ortb2Path;
389
+ const auctionKeywords = deepAccess(reqBidsConfigObj.ortb2Fragments.global, keywordsPath);
390
+
391
+ if (auctionKeywords && Array.isArray(auctionKeywords) && auctionKeywords.length > 0) {
392
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Keywords already present in auction_ortb2 at path '${keywordsPath}', no enrichment from module.`, auctionKeywords);
393
+ } else {
394
+ // auction_ortb2 path is empty, try to use keywords from initSummarizer (localStorage or fresh detection)
395
+ if (detectedKeywords && detectedKeywords.length > 0) {
396
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Enriching ORTB2 path '${keywordsPath}' with keywords from module (localStorage/detection):`, detectedKeywords);
397
+ deepSetValue(reqBidsConfigObj.ortb2Fragments.global, keywordsPath, detectedKeywords);
398
+ } else {
399
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Summarizer enabled, but no keywords from auction_ortb2, localStorage, or fresh detection for path '${keywordsPath}'.`);
400
+ }
401
+ }
402
+ } else {
403
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Summarizer disabled, no keyword enrichment.`);
404
+ }
405
+
406
+ logMessage(`${CONSTANTS.LOG_PRE_FIX} Final reqBidsConfigObj for auction:`, reqBidsConfigObj);
407
+ callback();
408
+ };
409
+
410
+ /** @type {RtdSubmodule} */
411
+ export const chromeAiSubmodule = {
412
+ name: CONSTANTS.SUBMODULE_NAME,
413
+ init,
414
+ getBidRequestData
415
+ };
416
+
417
+ export const registerSubModule = () => {
418
+ submodule(CONSTANTS.REAL_TIME_MODULE, chromeAiSubmodule);
419
+ };
420
+
421
+ registerSubModule();