@liquidcommercedev/rmn-sdk 1.5.0-beta.15 → 1.5.0-beta.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -80,574 +80,6 @@ exports.RMN_ENV = void 0;
80
80
  RMN_ENV["PRODUCTION"] = "production";
81
81
  })(exports.RMN_ENV || (exports.RMN_ENV = {}));
82
82
 
83
- const SPOT_EVENTS_EXAMPLE = [
84
- {
85
- event: exports.RMN_SPOT_EVENT.CLICK,
86
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwidXIiOm51bGx9&s=hWz37kbxi_u95EVNn2aoQhc5Aas',
87
- },
88
- {
89
- event: exports.RMN_SPOT_EVENT.IMPRESSION,
90
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiYmEiOjEsImZxIjowfQ&s=djoysjCimurf-5T11AlNAwwLSS8',
91
- },
92
- {
93
- event: exports.RMN_SPOT_EVENT.PURCHASE,
94
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjU5fQ&s=AAPAw-3SfZ0JMzjEGFSwt9L-2S4',
95
- },
96
- {
97
- event: exports.RMN_SPOT_EVENT.ADD_TO_CART,
98
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYwfQ&s=uzQFcjgL7m9XqUG8FvTPVN5YkZY',
99
- },
100
- {
101
- event: exports.RMN_SPOT_EVENT.ADD_TO_WISHLIST,
102
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYzfQ&s=m3ISU_iIy-OFtXrTKpI6cJAEC0k',
103
- },
104
- {
105
- event: exports.RMN_SPOT_EVENT.BUY_NOW,
106
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjY5fQ&s=l6MOscQC-q-FkC2Ksd7w6jjySCQ',
107
- },
108
- ];
109
- const RB_SPOTS_SELECTION_EXAMPLE = {
110
- rbHomepageHero: [
111
- {
112
- id: 'abc123',
113
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
114
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
115
- width: 1140,
116
- height: 640,
117
- header: 'Premium Wine Collection',
118
- description: 'Discover our exclusive selection of vintage wines',
119
- ctaText: 'Shop Wines',
120
- textColor: '#ffffff',
121
- ctaTextColor: '#ffffff',
122
- primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Collection',
123
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Wine',
124
- events: SPOT_EVENTS_EXAMPLE,
125
- productIds: [1, 2, 3],
126
- },
127
- {
128
- id: 'jkl012',
129
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
130
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
131
- width: 1140,
132
- height: 640,
133
- header: 'Whiskey Collection',
134
- description: 'Premium whiskeys from around the world',
135
- ctaText: 'Shop Whiskeys',
136
- textColor: '#ffffff',
137
- backgroundColor: '#2c1810',
138
- ctaTextColor: '#2c1810',
139
- primaryImage: 'https://placehold.co/1140x640/png?text=Whiskey',
140
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Whiskey',
141
- events: SPOT_EVENTS_EXAMPLE,
142
- productIds: [10, 11],
143
- },
144
- {
145
- id: 'stu901',
146
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
147
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
148
- width: 1140,
149
- height: 640,
150
- header: 'Summer Cocktails',
151
- description: 'Essential spirits for summer mixing',
152
- ctaText: 'Mix It Up',
153
- textColor: '#ffffff',
154
- backgroundColor: '#4d3a0a',
155
- ctaTextColor: '#4d3a0a',
156
- primaryImage: 'https://placehold.co/1140x640/png?text=Cocktails',
157
- secondaryImage: 'https://placehold.co/1140x640/png?text=Mixers',
158
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Cocktails',
159
- mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Mixers',
160
- events: SPOT_EVENTS_EXAMPLE,
161
- productIds: [16, 17, 18],
162
- },
163
- {
164
- id: 'def456',
165
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
166
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
167
- width: 1140,
168
- height: 640,
169
- header: 'Craft Beer Festival',
170
- description: 'Local breweries and exclusive releases',
171
- ctaText: 'Explore Beers',
172
- textColor: '#ffffff',
173
- ctaTextColor: '#ffffff',
174
- primaryImage: 'https://placehold.co/1140x640/png?text=Beer+Festival',
175
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Beer',
176
- events: SPOT_EVENTS_EXAMPLE,
177
- productIds: [4, 5, 6],
178
- },
179
- {
180
- id: 'mno345',
181
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
182
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
183
- width: 1140,
184
- height: 640,
185
- header: 'Champagne Selection',
186
- description: 'Finest champagnes for every occasion',
187
- ctaText: 'View Champagnes',
188
- textColor: '#ffffff',
189
- backgroundColor: '#1a1a1a',
190
- ctaTextColor: '#1a1a1a',
191
- primaryImage: 'https://placehold.co/1140x640/png?text=Champagne',
192
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Champagne',
193
- events: SPOT_EVENTS_EXAMPLE,
194
- productIds: [12, 13],
195
- },
196
- {
197
- id: 'vwx234',
198
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
199
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
200
- width: 1140,
201
- height: 640,
202
- header: 'Wine Regions',
203
- description: 'Explore wines from top regions',
204
- ctaText: 'Tour Regions',
205
- textColor: '#ffffff',
206
- backgroundColor: '#4d0a0a',
207
- ctaTextColor: '#4d0a0a',
208
- primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Regions',
209
- secondaryImage: 'https://placehold.co/1140x640/png?text=Vineyards',
210
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Regions',
211
- mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Vineyards',
212
- events: SPOT_EVENTS_EXAMPLE,
213
- productIds: [19, 20, 21],
214
- },
215
- {
216
- id: 'ghi789',
217
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
218
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
219
- width: 1140,
220
- height: 640,
221
- header: 'Rare Spirits',
222
- description: 'Limited edition spirits collection',
223
- ctaText: 'View Collection',
224
- textColor: '#ffffff',
225
- ctaTextColor: '#ffffff',
226
- primaryImage: 'https://placehold.co/1140x640/png?text=Rare+Spirits',
227
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Spirits',
228
- events: SPOT_EVENTS_EXAMPLE,
229
- productIds: [7, 8, 9],
230
- },
231
- {
232
- id: 'pqr678',
233
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
234
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
235
- width: 1140,
236
- height: 640,
237
- header: 'Gin Collection',
238
- description: 'Artisanal gins and botanicals',
239
- ctaText: 'Explore Gins',
240
- textColor: '#ffffff',
241
- backgroundColor: '#0a4d4d',
242
- ctaTextColor: '#0a4d4d',
243
- primaryImage: 'https://placehold.co/1140x640/png?text=Gin',
244
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gin',
245
- events: SPOT_EVENTS_EXAMPLE,
246
- productIds: [14, 15],
247
- },
248
- {
249
- id: 'yz5678',
250
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
251
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
252
- width: 1140,
253
- height: 640,
254
- header: 'Tequila Collection',
255
- description: 'Premium tequilas and mezcals',
256
- ctaText: 'Shop Tequila',
257
- textColor: '#ffffff',
258
- backgroundColor: '#0a4d2b',
259
- ctaTextColor: '#0a4d2b',
260
- primaryImage: 'https://placehold.co/1140x640/png?text=Tequila',
261
- secondaryImage: 'https://placehold.co/1140x640/png?text=Mezcal',
262
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Tequila',
263
- mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Mezcal',
264
- events: SPOT_EVENTS_EXAMPLE,
265
- productIds: [22, 23, 24],
266
- },
267
- ],
268
- rbHomepageHeroFullImage: [
269
- {
270
- id: 'hjk567',
271
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
272
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
273
- width: 1140,
274
- height: 640,
275
- header: 'Holiday Gift Guide',
276
- description: 'Perfect spirits for every occasion',
277
- ctaText: 'Shop Gifts',
278
- textColor: '#ffffff',
279
- ctaTextColor: '#ffffff',
280
- primaryImage: 'https://placehold.co/1140x640/png?text=Gift+Guide',
281
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gifts',
282
- events: SPOT_EVENTS_EXAMPLE,
283
- productIds: [25, 26],
284
- },
285
- {
286
- id: 'qwe890',
287
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
288
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
289
- width: 1140,
290
- height: 640,
291
- header: 'Summer Wine Festival',
292
- description: 'Refreshing wines for summer',
293
- ctaText: 'Shop Festival',
294
- textColor: '#ffffff',
295
- ctaTextColor: '#ffffff',
296
- primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Festival',
297
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Festival',
298
- events: SPOT_EVENTS_EXAMPLE,
299
- productIds: [27, 28],
300
- },
301
- ],
302
- rbHomepageHeroTwoTile: [
303
- {
304
- id: 'iop987',
305
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
306
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
307
- width: 1140,
308
- height: 640,
309
- header: 'Bourbon Selection',
310
- description: "Kentucky's finest bourbons",
311
- ctaText: 'Shop Bourbon',
312
- textColor: '#ffffff',
313
- backgroundColor: '#2c1810',
314
- ctaTextColor: '#2c1810',
315
- primaryImage: 'https://placehold.co/1140x640/png?text=Bourbon',
316
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Bourbon',
317
- events: SPOT_EVENTS_EXAMPLE,
318
- productIds: [29, 30],
319
- },
320
- {
321
- id: 'lkj012',
322
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
323
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
324
- width: 1140,
325
- height: 640,
326
- header: 'Vodka Collection',
327
- description: 'Premium vodkas from around the world',
328
- ctaText: 'Shop Vodka',
329
- textColor: '#ffffff',
330
- backgroundColor: '#1a1a1a',
331
- ctaTextColor: '#1a1a1a',
332
- primaryImage: 'https://placehold.co/1140x640/png?text=Vodka',
333
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Vodka',
334
- events: SPOT_EVENTS_EXAMPLE,
335
- productIds: [31, 32],
336
- },
337
- ],
338
- rbHomepageHeroThreeTile: [
339
- {
340
- id: 'bnm345',
341
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
342
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
343
- width: 1140,
344
- height: 640,
345
- header: 'Rum Collection',
346
- description: 'Caribbean and craft rums',
347
- ctaText: 'Shop Rum',
348
- textColor: '#ffffff',
349
- backgroundColor: '#4d3a0a',
350
- ctaTextColor: '#4d3a0a',
351
- primaryImage: 'https://placehold.co/1140x640/png?text=Rum',
352
- secondaryImage: 'https://placehold.co/1140x640/png?text=Craft+Rum',
353
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Rum',
354
- mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Craft',
355
- events: SPOT_EVENTS_EXAMPLE,
356
- productIds: [33, 34],
357
- },
358
- {
359
- id: 'fgh678',
360
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
361
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
362
- width: 1140,
363
- height: 640,
364
- header: 'Scotch Selection',
365
- description: 'Single malts and blends',
366
- ctaText: 'Shop Scotch',
367
- textColor: '#ffffff',
368
- backgroundColor: '#4d0a0a',
369
- ctaTextColor: '#4d0a0a',
370
- primaryImage: 'https://placehold.co/1140x640/png?text=Scotch',
371
- secondaryImage: 'https://placehold.co/1140x640/png?text=Single+Malts',
372
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Scotch',
373
- mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Malts',
374
- events: SPOT_EVENTS_EXAMPLE,
375
- productIds: [35, 36],
376
- },
377
- ],
378
- rbLargeCategoryImageTout: [
379
- {
380
- id: 'cde567',
381
- spot: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
382
- variant: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
383
- width: 468,
384
- height: 410,
385
- header: 'Rare & Limited Edition',
386
- description: 'Discover our collection of hard-to-find and limited release spirits.',
387
- textColor: '#ffffff',
388
- ctaTextColor: '#ffffff',
389
- primaryImage: 'https://placehold.co/468x410/png?text=Rare+Spirits',
390
- mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Rare+Spirits',
391
- ctaText: 'Shop Rare Spirits',
392
- events: SPOT_EVENTS_EXAMPLE,
393
- productIds: [37, 38, 39, 40, 41],
394
- },
395
- {
396
- id: 'efg789',
397
- spot: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
398
- variant: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
399
- width: 468,
400
- height: 410,
401
- header: 'Vintage Champagnes',
402
- description: 'Explore our prestigious collection of aged champagnes.',
403
- textColor: '#ffffff',
404
- ctaTextColor: '#ffffff',
405
- primaryImage: 'https://placehold.co/468x410/png?text=Vintage+Champagne',
406
- mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Champagne',
407
- ctaText: 'View Collection',
408
- events: SPOT_EVENTS_EXAMPLE,
409
- productIds: [42, 43, 44, 45, 46],
410
- },
411
- {
412
- id: 'ghi012',
413
- spot: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
414
- variant: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
415
- width: 468,
416
- height: 410,
417
- header: 'Small Batch Bourbon',
418
- description: 'Hand-selected premium bourbon from craft distilleries.',
419
- textColor: '#ffffff',
420
- ctaTextColor: '#ffffff',
421
- primaryImage: 'https://placehold.co/468x410/png?text=Craft+Bourbon',
422
- mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Bourbon',
423
- ctaText: 'Explore Bourbon',
424
- events: SPOT_EVENTS_EXAMPLE,
425
- productIds: [47, 48, 49, 50, 51],
426
- },
427
- ],
428
- rbSmallDiscoverTout: [
429
- {
430
- id: 'jkl345',
431
- spot: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
432
- variant: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
433
- width: 224,
434
- height: 378,
435
- header: 'Château Margaux 2015 Bordeaux',
436
- textColor: '#ffffff',
437
- primaryImage: 'https://placehold.co/224x378/png?text=Château+Margaux',
438
- mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Château+Margaux',
439
- events: SPOT_EVENTS_EXAMPLE,
440
- productIds: [52, 53, 54, 55, 56],
441
- },
442
- {
443
- id: 'lmn678',
444
- spot: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
445
- variant: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
446
- width: 224,
447
- height: 378,
448
- header: 'Macallan 25 Year',
449
- textColor: '#ffffff',
450
- primaryImage: 'https://placehold.co/224x378/png?text=Macallan+25',
451
- mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Macallan',
452
- events: SPOT_EVENTS_EXAMPLE,
453
- productIds: [57, 58, 59, 60, 61],
454
- },
455
- {
456
- id: 'nop901',
457
- spot: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
458
- variant: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
459
- width: 224,
460
- height: 378,
461
- header: 'Louis XIII Cognac',
462
- textColor: '#ffffff',
463
- primaryImage: 'https://placehold.co/224x378/png?text=Louis+XIII',
464
- mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Louis+XIII',
465
- events: SPOT_EVENTS_EXAMPLE,
466
- productIds: [62, 63, 64, 65, 66],
467
- },
468
- ],
469
- rbSmallCategoryImageTout: [
470
- {
471
- id: 'qrs234',
472
- spot: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
473
- variant: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
474
- width: 224,
475
- height: 410,
476
- header: 'Japanese Sake',
477
- textColor: '#ffffff',
478
- primaryImage: 'https://placehold.co/224x410/png?text=Japanese+Sake',
479
- mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Japanese+Sake',
480
- events: SPOT_EVENTS_EXAMPLE,
481
- productIds: [67, 68, 69, 70, 71],
482
- },
483
- {
484
- id: 'stu567',
485
- spot: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
486
- variant: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
487
- width: 224,
488
- height: 410,
489
- header: 'Craft Vermouth',
490
- textColor: '#ffffff',
491
- primaryImage: 'https://placehold.co/224x410/png?text=Craft+Vermouth',
492
- mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Vermouth',
493
- events: SPOT_EVENTS_EXAMPLE,
494
- productIds: [72, 73, 74, 75, 76],
495
- },
496
- {
497
- id: 'vwx890',
498
- spot: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
499
- variant: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
500
- width: 224,
501
- height: 410,
502
- header: 'Premium Mezcal',
503
- textColor: '#ffffff',
504
- primaryImage: 'https://placehold.co/224x410/png?text=Premium+Mezcal',
505
- mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Mezcal',
506
- events: SPOT_EVENTS_EXAMPLE,
507
- productIds: [77, 78, 79, 80, 81],
508
- },
509
- ],
510
- rbCollectionBannerWithoutTextBlock: [
511
- {
512
- id: 'yz1234',
513
- spot: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
514
- variant: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
515
- width: 887,
516
- height: 344,
517
- primaryImage: 'https://placehold.co/887x344/png?text=Summer+Cocktails',
518
- mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Summer+Cocktails',
519
- events: SPOT_EVENTS_EXAMPLE,
520
- productIds: [82, 83, 84, 85, 86],
521
- },
522
- {
523
- id: 'abc567',
524
- spot: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
525
- variant: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
526
- width: 887,
527
- height: 344,
528
- primaryImage: 'https://placehold.co/887x344/png?text=Holiday+Spirits',
529
- mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Holiday+Spirits',
530
- events: SPOT_EVENTS_EXAMPLE,
531
- productIds: [87, 88, 89, 90, 91],
532
- },
533
- {
534
- id: 'def901',
535
- spot: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
536
- variant: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
537
- width: 887,
538
- height: 344,
539
- primaryImage: 'https://placehold.co/887x344/png?text=Wine+Essentials',
540
- mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Wine+Essentials',
541
- events: SPOT_EVENTS_EXAMPLE,
542
- productIds: [92, 93, 94, 95, 96],
543
- },
544
- ],
545
- rbNavigationBanner: [
546
- {
547
- id: 'ghi234',
548
- spot: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
549
- variant: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
550
- width: 440,
551
- height: 220,
552
- header: 'Explore Tequilas',
553
- textColor: '#ffffff',
554
- primaryImage: 'https://placehold.co/440x220/png?text=Tequila+Collection',
555
- mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Tequila+Collection',
556
- events: SPOT_EVENTS_EXAMPLE,
557
- productIds: [97, 98, 99, 100, 101],
558
- },
559
- {
560
- id: 'jkl678',
561
- spot: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
562
- variant: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
563
- width: 440,
564
- height: 220,
565
- header: 'Craft Gin Selection',
566
- textColor: '#ffffff',
567
- primaryImage: 'https://placehold.co/440x220/png?text=Gin+Selection',
568
- mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Gin+Selection',
569
- events: SPOT_EVENTS_EXAMPLE,
570
- productIds: [102, 103, 104, 105, 106],
571
- },
572
- {
573
- id: 'mno012',
574
- spot: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
575
- variant: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
576
- width: 440,
577
- height: 220,
578
- header: 'Premium Vodka',
579
- textColor: '#ffffff',
580
- primaryImage: 'https://placehold.co/440x220/png?text=Vodka+Premium',
581
- mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Vodka+Premium',
582
- events: SPOT_EVENTS_EXAMPLE,
583
- productIds: [107, 108, 109, 110, 111],
584
- },
585
- ],
586
- };
587
- const IAB_SPOTS_SELECTION_EXAMPLE = {
588
- banner: [],
589
- billboard: [
590
- {
591
- id: 'kol567',
592
- spot: exports.RMN_SPOT_TYPE.BILLBOARD,
593
- variant: `${exports.RMN_SPOT_TYPE.BILLBOARD}V2`,
594
- width: 1140,
595
- height: 640,
596
- header: 'Holiday Gift Guide',
597
- description: 'Perfect spirits for every occasion',
598
- ctaText: 'Shop Gifts',
599
- textColor: '#ffffff',
600
- ctaTextColor: '#ffffff',
601
- primaryImage: 'https://placehold.co/1140x640/png?text=Gift+Guide',
602
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gifts',
603
- events: SPOT_EVENTS_EXAMPLE,
604
- productIds: [25, 26],
605
- },
606
- {
607
- id: 'hpm390',
608
- spot: exports.RMN_SPOT_TYPE.BILLBOARD,
609
- variant: `${exports.RMN_SPOT_TYPE.BILLBOARD}V2`,
610
- width: 1140,
611
- height: 640,
612
- header: 'Summer Wine Festival',
613
- description: 'Refreshing wines for summer',
614
- ctaText: 'Shop Festival',
615
- textColor: '#ffffff',
616
- ctaTextColor: '#ffffff',
617
- primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Festival',
618
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Festival',
619
- events: SPOT_EVENTS_EXAMPLE,
620
- productIds: [27, 28],
621
- },
622
- ],
623
- button2: [],
624
- featurePhoneLargeBanner: [],
625
- featurePhoneMediumBanner: [],
626
- featurePhoneSmallBanner: [],
627
- halfPage: [],
628
- inText: [],
629
- largeLeaderboard: [],
630
- largeRectangle: [],
631
- leaderboard: [],
632
- mediumRectangle: [],
633
- microBar: [],
634
- mobilePhoneInterstitial1: [],
635
- mobilePhoneInterstitial2: [],
636
- mobilePhoneInterstitial3: [],
637
- popUp: [],
638
- portrait: [],
639
- rbProductUpcs: [],
640
- skyscraper: [],
641
- smallRectangle: [],
642
- smallSquare: [],
643
- smartphoneBanner1: [],
644
- smartphoneBanner2: [],
645
- square: [],
646
- verticalBanner: [],
647
- verticalRectangle: [],
648
- wideSkyscraper: [],
649
- };
650
-
651
83
  const REQUEST_CLOUD_PARTNER_SITE = 'X-Liquid-Partner-Site';
652
84
  const REQUEST_CLOUD_PROTECTED_KEY = 'X-Liquid-Protected';
653
85
  const REQUEST_CLOUD_PROTECTED_TIMESTAMP = 'X-Liquid-Timestamp';
@@ -6694,8 +6126,122 @@ function getEventTypeFromRawEvent(event) {
6694
6126
  if (matchesKeywordPattern(words, required, optional)) {
6695
6127
  return eventType;
6696
6128
  }
6697
- }
6698
- return null;
6129
+ }
6130
+ return null;
6131
+ }
6132
+
6133
+ // Configuration object with target field names
6134
+ const extractorConfig = {
6135
+ ids: [
6136
+ // Universal product identifiers
6137
+ 'gtin',
6138
+ 'gtin8',
6139
+ 'gtin12',
6140
+ 'gtin13',
6141
+ 'gtin14',
6142
+ 'mpn',
6143
+ 'sku',
6144
+ 'upc',
6145
+ 'ean',
6146
+ 'isbn',
6147
+ 'isbn10',
6148
+ 'isbn13',
6149
+ 'asin',
6150
+ // Product codes and references
6151
+ 'coupon',
6152
+ 'barcode',
6153
+ 'product_code',
6154
+ 'part_number',
6155
+ 'model_number',
6156
+ 'item_variant',
6157
+ 'item_number',
6158
+ 'article_number',
6159
+ 'reference',
6160
+ 'groupingId',
6161
+ ],
6162
+ price: [
6163
+ 'price',
6164
+ 'unitPrice',
6165
+ 'cost',
6166
+ 'current_price',
6167
+ 'sale_price',
6168
+ 'price_value',
6169
+ 'sale_price_value',
6170
+ 'regular_price',
6171
+ 'discount_price',
6172
+ 'unit_price',
6173
+ 'original_price',
6174
+ 'final_price',
6175
+ 'retail_price',
6176
+ ],
6177
+ };
6178
+ /**
6179
+ * Extracts deep values from an object based on specified target type
6180
+ * @param data - The source data object to extract values from
6181
+ * @param target - The type of values to extract ('ids' or 'price')
6182
+ * @param options - Optional configuration for the extraction process
6183
+ * @returns Array of extracted values or a single value if onlyFirst is true
6184
+ */
6185
+ function extractDeepValues(data, target, options = {}) {
6186
+ const {
6187
+ // eslint-disable-next-line @typescript-eslint/naming-convention
6188
+ onlyFirst = false, shouldIncludeZero = false, } = options;
6189
+ const values = [];
6190
+ const targetProperties = new Set(extractorConfig[target].map((name) => name.toLowerCase()));
6191
+ /**
6192
+ * Checks if a property name matches the target criteria
6193
+ */
6194
+ const isTargetField = (key) => {
6195
+ const normalizedKey = key.toLowerCase();
6196
+ const hasTarget = targetProperties.has(normalizedKey);
6197
+ if (target === 'ids') {
6198
+ return normalizedKey.endsWith('id') || normalizedKey.endsWith('ids') || hasTarget;
6199
+ }
6200
+ return hasTarget;
6201
+ };
6202
+ /**
6203
+ * Validates and normalizes extracted values
6204
+ */
6205
+ const validateValue = (value) => {
6206
+ if (typeof value === 'string') {
6207
+ return value.trim().length > 0;
6208
+ }
6209
+ if (typeof value === 'number') {
6210
+ return !isNaN(value) && (shouldIncludeZero || value !== 0);
6211
+ }
6212
+ return false;
6213
+ };
6214
+ /**
6215
+ * Processes a value and extracts matching fields
6216
+ */
6217
+ const processValue = (value, currentKey) => {
6218
+ // Early exit conditions
6219
+ if (value == null || (onlyFirst && values.length > 0))
6220
+ return;
6221
+ // Process current value if it matches target criteria
6222
+ if (currentKey && isTargetField(currentKey)) {
6223
+ if (Array.isArray(value)) {
6224
+ const validValues = value.filter(validateValue);
6225
+ values.push(...validValues);
6226
+ }
6227
+ else if (validateValue(value)) {
6228
+ values.push(value);
6229
+ }
6230
+ return;
6231
+ }
6232
+ // Recursive processing for nested structures
6233
+ if (Array.isArray(value)) {
6234
+ value.forEach((item) => processValue(item));
6235
+ }
6236
+ else if (typeof value === 'object') {
6237
+ Object.entries(value).forEach(([key, val]) => processValue(val, key));
6238
+ }
6239
+ };
6240
+ processValue(data);
6241
+ // Return based on options
6242
+ if (values.length === 0)
6243
+ return undefined;
6244
+ return onlyFirst ? values[0] : values;
6699
6245
  }
6700
6246
 
6701
6247
  class SingletonManager {
@@ -6863,97 +6409,6 @@ class ObjectHelper {
6863
6409
  }
6864
6410
  }
6865
6411
 
6866
- /**
6867
- * Recursively extracts ID values from a nested data structure.
6868
- * Searches for specified property names and collects their primitive values (strings/numbers).
6869
- * Captures properties ending with 'id' and any additional specified property names.
6870
- *
6871
- * @param data - The data structure to search through (can be nested objects/arrays)
6872
- * @param propertyNames - Array of additional property names to look for (optional)
6873
- * @returns Array of extracted ID values (strings/numbers only)
6874
- *
6875
- * @example
6876
- * const data = {
6877
- * id: [1, 2, 3],
6878
- * nested: { id: 'abc', userId: 123 },
6879
- * items: [{ id: 456, productId: '789', sku: 'ABC123' }]
6880
- * };
6881
- * extractDeepIds(data); // Returns [1, 2, 3, 'abc', 123, 456, '789', 'ABC123']
6882
- */
6883
- function extractDeepIds(data, propertyNames) {
6884
- const ids = [];
6885
- const defaultPropertyNames = [
6886
- // Universal product identifiers
6887
- 'gtin', // Global Trade Item Number
6888
- 'gtin8', // 8-digit GTIN
6889
- 'gtin12', // 12-digit GTIN (UPC)
6890
- 'gtin13', // 13-digit GTIN (EAN)
6891
- 'gtin14', // 14-digit GTIN
6892
- 'mpn', // Manufacturer Part Number
6893
- 'sku', // Stock Keeping Unit
6894
- 'upc', // Universal Product Code
6895
- 'ean', // European Article Number
6896
- 'isbn', // International Standard Book Number
6897
- 'isbn10', // 10-digit ISBN
6898
- 'isbn13', // 13-digit ISBN
6899
- 'asin', // Amazon Standard Identification Number
6900
- // Product codes and references
6901
- 'coupon',
6902
- 'barcode',
6903
- 'product_code',
6904
- 'part_number',
6905
- 'model_number',
6906
- 'item_variant',
6907
- 'item_number',
6908
- 'article_number',
6909
- 'reference',
6910
- 'groupingId',
6911
- ];
6912
- // Convert property names to lowercase for consistent comparison
6913
- const additionalProperties = new Set((defaultPropertyNames).map((name) => name.toLowerCase()));
6914
- /**
6915
- * Checks if a property name is an ID field
6916
- * @param key - The property name to check
6917
- * @returns boolean indicating if the key is an ID field
6918
- */
6919
- const isIdField = (key) => {
6920
- const lowercaseKey = key.toLowerCase();
6921
- return lowercaseKey.endsWith('id') || additionalProperties.has(lowercaseKey);
6922
- };
6923
- /**
6924
- * Processes a value and extracts IDs if it matches criteria
6925
- * @param value - The value to process
6926
- * @param currentKey - The property name of the current value
6927
- */
6928
- const processValue = (value, currentKey) => {
6929
- // Early exit for null/undefined values
6930
- if (value == null)
6931
- return;
6932
- // If current key matches our target properties
6933
- if (currentKey && isIdField(currentKey)) {
6934
- if (Array.isArray(value)) {
6935
- // Filter and push valid array values in one pass
6936
- ids.push(...value.filter((item) => typeof item === 'string' || typeof item === 'number'));
6937
- }
6938
- else if (typeof value === 'string' || typeof value === 'number') {
6939
- ids.push(value);
6940
- }
6941
- return; // Stop processing this branch after handling the ID
6942
- }
6943
- // Recursively process nested structures
6944
- if (Array.isArray(value)) {
6945
- value.forEach((item) => processValue(item));
6946
- }
6947
- else if (typeof value === 'object') {
6948
- // Process all enumerable properties
6949
- for (const [key, val] of Object.entries(value)) {
6950
- processValue(val, key);
6951
- }
6952
- }
6953
- };
6954
- processValue(data);
6955
- return ids;
6956
- }
6957
6412
  // Fallback method using fetch if sendBeacon isn't available
6958
6413
  async function fallbackEventFire(url) {
6959
6414
  try {
@@ -6979,22 +6434,49 @@ async function fallbackEventFire(url) {
6979
6434
  return false;
6980
6435
  }
6981
6436
  }
6437
+ /**
6438
+ * Helper function to decode base64 string and parse JSON
6439
+ *
6440
+ * @param {string} base64String - The base64 encoded JSON string
6441
+ * @returns {T | null} - Decoded and parsed object or null if invalid
6442
+ */
6443
+ function decodeBase64Json(base64String) {
6444
+ try {
6445
+ return JSON.parse(atob(base64String));
6446
+ }
6447
+ catch (_a) {
6448
+ return null;
6449
+ }
6450
+ }
6982
6451
  /**
6983
6452
  * Extracts and decodes a URL from a base64-encoded query parameter.
6984
6453
  *
6985
6454
  * @param {string} url - The URL containing the base64-encoded query parameter.
6986
6455
  * @returns {string | null} - The decoded URL or null if not found or invalid.
6456
+ * @throws {Error} - If URL is malformed or payload is invalid.
6987
6457
  */
6988
6458
  function getRedirectUrlFromPayload(url) {
6459
+ if (!url)
6460
+ return null;
6989
6461
  try {
6990
- const base64String = new URL(url).searchParams.get('e');
6991
- if (!base64String) {
6462
+ // Extract initial payload
6463
+ const payload = new URL(url).searchParams.get('p');
6464
+ if (!payload)
6992
6465
  return null;
6993
- }
6994
- const data = JSON.parse(atob(base64String));
6995
- return data.ur || null;
6466
+ // Decode first layer
6467
+ const decodedData = decodeBase64Json(payload);
6468
+ if (!(decodedData === null || decodedData === void 0 ? void 0 : decodedData.u))
6469
+ return null;
6470
+ // Extract URL from nested query
6471
+ const eventPayload = new URLSearchParams(decodedData.u).get('e');
6472
+ if (!eventPayload)
6473
+ return null;
6474
+ // Decode second layer
6475
+ const eventData = decodeBase64Json(eventPayload);
6476
+ return (eventData === null || eventData === void 0 ? void 0 : eventData.ur) || null;
6996
6477
  }
6997
6478
  catch (_a) {
6479
+ console.warn('RmnSdk: Failed to extract redirect URL from payload.');
6998
6480
  return null;
6999
6481
  }
7000
6482
  }
@@ -7054,6 +6536,23 @@ function calculateScaleFactor(elementScale) {
7054
6536
  // Math.max ensures the value isn't less than minScale
7055
6537
  return Math.max(minScale, Math.min(maxScale, scaleFactor));
7056
6538
  }
6539
+ /**
6540
+ * Converts an object to a query string.
6541
+ *
6542
+ * @param {Record<string, string|number|undefined|null>} obj - The object to be converted to a query string.
6543
+ * @returns {string} - The query string.
6544
+ */
6545
+ function objectToQueryParams(obj) {
6546
+ return Object.entries(obj !== null && obj !== void 0 ? obj : {})
6547
+ .map(([key, value]) => {
6548
+ if (value == null) {
6549
+ return '';
6550
+ }
6551
+ return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
6552
+ })
6553
+ .filter(Boolean)
6554
+ .join('&');
6555
+ }
7057
6556
 
7058
6557
  class UniqueIdGenerator {
7059
6558
  /**
@@ -16262,6 +15761,7 @@ class LocalStorageService {
16262
15761
  console.warn('Local storage is not supported in this environment');
16263
15762
  return;
16264
15763
  }
15764
+ this.setUserId();
16265
15765
  this.spots = new Map();
16266
15766
  // Sync local storage with the current state
16267
15767
  this.syncLocalStorage();
@@ -16348,6 +15848,49 @@ class LocalStorageService {
16348
15848
  });
16349
15849
  this.updateLocalStorage();
16350
15850
  }
15851
+ // ======================== Utility functions ======================== //
15852
+ getUserId() {
15853
+ const key = LocalStorageService.localStorageKey;
15854
+ if (!key) {
15855
+ this.setUserId();
15856
+ }
15857
+ return key.replace(`${LocalStorageService.localStorageKeyPrefix}_`, '');
15858
+ }
15859
+ /**
15860
+ * Sets the user ID in the local storage.
15861
+ * If no existing key is found,
15862
+ * it generates a new unique ID and sets it as the local storage key.
15863
+ */
15864
+ setUserId() {
15865
+ const prefix = LocalStorageService.localStorageKeyPrefix;
15866
+ const existingKeys = Object.keys(window.localStorage).filter((key) => key.startsWith(prefix));
15867
+ const setNewKey = () => {
15868
+ const uniqueId = UniqueIdGenerator.generate();
15869
+ const newLocalStorageKey = `${prefix}_${uniqueId}`;
15870
+ window.localStorage.setItem(newLocalStorageKey, '');
15871
+ LocalStorageService.localStorageKey = newLocalStorageKey;
15872
+ };
15873
+ if (existingKeys.length === 0) {
15874
+ setNewKey();
15875
+ }
15876
+ else {
15877
+ const validKey = existingKeys.find((key) => UniqueIdGenerator.isValid(key.replace(`${prefix}_`, '')));
15878
+ // Delete all other keys except the valid one
15879
+ existingKeys.forEach((key) => {
15880
+ if (key !== validKey) {
15881
+ window.localStorage.removeItem(key);
15882
+ }
15883
+ });
15884
+ if (validKey) {
15885
+ // Found a valid key, assign it to localStorageKey
15886
+ LocalStorageService.localStorageKey = validKey;
15887
+ }
15888
+ else {
15889
+ // No valid key found, generate a new key
15890
+ setNewKey();
15891
+ }
15892
+ }
15893
+ }
16351
15894
  mapToObject(map) {
16352
15895
  return Object.fromEntries(map);
16353
15896
  }
@@ -16401,7 +15944,8 @@ class LocalStorageService {
16401
15944
  return atob(data);
16402
15945
  }
16403
15946
  }
16404
- LocalStorageService.localStorageKey = 'lc_rmn';
15947
+ LocalStorageService.localStorageKeyPrefix = 'lc_rmn';
15948
+ LocalStorageService.localStorageKey = '';
16405
15949
  LocalStorageService.spotExpirationTime = 1000 * 60 * 60 * 24 * 7; // 7 days
16406
15950
  LocalStorageService.encryptData = true;
16407
15951
 
@@ -18840,651 +18384,1357 @@ const STYLES$3 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderCo
18840
18384
  font-size: 28px;
18841
18385
  }
18842
18386
 
18843
- .${prefix}__description {
18844
- font-size: 14px;
18845
- }
18387
+ .${prefix}__description {
18388
+ font-size: 14px;
18389
+ }
18390
+
18391
+ .${prefix}__cta-button {
18392
+ font-size: 13px;
18393
+ }
18394
+ }
18395
+
18396
+ @container (min-width: 1280px) {
18397
+ .${prefix}__cta-button {
18398
+ font-size: 14px;
18399
+ }
18400
+ }
18401
+ </style>
18402
+ `;
18403
+ };
18404
+ function rbLargeCategoryImageToutTemplate(spot, config) {
18405
+ const { prefix = '' } = config;
18406
+ return `
18407
+ ${GFONT_PRECONNECT}
18408
+ ${GFONT_SOURCE_SANS_3}
18409
+ ${GFONT_CORMORANT}
18410
+ ${STYLES$3(spot, config)}
18411
+ <div class="${prefix}">
18412
+ <div class="${prefix}__text">
18413
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18414
+ ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
18415
+ ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
18416
+ </div>
18417
+ </div>
18418
+ `;
18419
+ }
18420
+
18421
+ const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
18422
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
18423
+ return `
18424
+ <style>
18425
+ .${prefix} {
18426
+ width: 100%;
18427
+ height: 100%;
18428
+ display: flex;
18429
+ flex-direction: column;
18430
+ justify-content: flex-end;
18431
+ border-radius: 5px;
18432
+ overflow: hidden;
18433
+ cursor: pointer;
18434
+ background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
18435
+ background-size: cover;
18436
+ background-position: center;
18437
+ background-repeat: no-repeat;
18438
+ position: relative;
18439
+ }
18440
+
18441
+ .${prefix}__header {
18442
+ font-size: 16px;
18443
+ color: ${textColor};
18444
+ line-height: 20px;
18445
+ font-weight: 400;
18446
+ font-family: "Source Sans 3", system-ui;
18447
+ font-style: normal;
18448
+ margin: 0;
18449
+ padding: 15px 10%;
18450
+ }
18451
+
18452
+ @container (min-width: 640px) {
18453
+ .${prefix} {
18454
+ background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
18455
+ }
18456
+ }
18457
+
18458
+ @container (min-width: 768px) {
18459
+ .${prefix}__header {
18460
+ font-size: 22px;
18461
+ }
18462
+ }
18463
+
18464
+ @container (min-width: 1024px) {
18465
+ .${prefix}__header {
18466
+ font-size: 24px;
18467
+ }
18468
+ }
18469
+
18470
+ @container (min-width: 1280px) {
18471
+ .${prefix}__header {
18472
+ font-size: 28px;
18473
+ }
18474
+ }
18475
+ </style>
18476
+ `;
18477
+ };
18478
+ function rbNavigationBannerTemplate(spot, config) {
18479
+ const { prefix = '' } = config;
18480
+ return `
18481
+ ${GFONT_PRECONNECT}
18482
+ ${GFONT_SOURCE_SANS_3}
18483
+ ${STYLES$2(spot, config)}
18484
+ <div class="${prefix}">
18485
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18486
+ </div>
18487
+ `;
18488
+ }
18489
+
18490
+ const STYLES$1 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
18491
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
18492
+ return `
18493
+ <style>
18494
+ .${prefix} {
18495
+ width: 100%;
18496
+ height: 100%;
18497
+ display: flex;
18498
+ flex-direction: column;
18499
+ justify-content: flex-end;
18500
+ background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
18501
+ background-size: cover;
18502
+ background-repeat: no-repeat;
18503
+ background-position: center;
18504
+ border-radius: 5px;
18505
+ overflow: hidden;
18506
+ cursor: pointer;
18507
+ position: relative;
18508
+ }
18509
+
18510
+ .${prefix}__header {
18511
+ font-size: 12px;
18512
+ color: ${textColor};
18513
+ line-height: 16px;
18514
+ font-family: "Source Sans 3", system-ui;
18515
+ font-style: normal;
18516
+ font-weight: 400;
18517
+ margin: 0;
18518
+ padding: 10px;
18519
+ }
18520
+
18521
+ @container (min-width: 640px) {
18522
+ .${prefix} {
18523
+ background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
18524
+ }
18525
+ }
18526
+ </style>
18527
+ `;
18528
+ };
18529
+ function rbSmallCategoryImageToutTemplate(spot, config) {
18530
+ const { prefix = '' } = config;
18531
+ return `
18532
+ ${GFONT_PRECONNECT}
18533
+ ${GFONT_CORMORANT}
18534
+ ${STYLES$1(spot, config)}
18535
+ <div class="${prefix}">
18536
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18537
+ </div>
18538
+ `;
18539
+ }
18540
+
18541
+ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primaryImage, mobilePrimaryImage = primaryImage, }, { prefix }) => `
18542
+ <style>
18543
+ .${prefix} {
18544
+ width: 100%;
18545
+ height: 100%;
18546
+ background-color: ${backgroundColor};
18547
+ cursor: pointer;
18548
+ display: flex;
18549
+ flex-direction: column;
18550
+ border-radius: 5px;
18551
+ }
18552
+
18553
+ .${prefix}__image {
18554
+ width: 100%;
18555
+ height: 100%;
18556
+ background-image: url("${mobilePrimaryImage}");
18557
+ background-size: cover;
18558
+ background-repeat: no-repeat;
18559
+ background-position: center;
18560
+ border-radius: 5px;
18561
+ }
18562
+
18563
+ .${prefix}__text {
18564
+ text-align: left;
18565
+ display: flex;
18566
+ flex-direction: row;
18567
+ align-items: flex-start;
18568
+ justify-content: flex-start;
18569
+ width: 100%;
18570
+ height: fit-content;
18571
+ position: relative;
18572
+ }
18846
18573
 
18847
- .${prefix}__cta-button {
18848
- font-size: 13px;
18849
- }
18850
- }
18574
+ .${prefix}__header {
18575
+ font-size: 12px;
18576
+ color: ${textColor};
18577
+ padding-top: 5px;
18578
+ line-height: 16px;
18579
+ font-family: "Source Sans 3", system-ui;
18580
+ font-style: normal;
18581
+ font-weight: 400;
18582
+ margin: 0;
18583
+ }
18851
18584
 
18852
- @container (min-width: 1280px) {
18853
- .${prefix}__cta-button {
18854
- font-size: 14px;
18855
- }
18585
+ @container (min-width: 640px) {
18586
+ .${prefix}__image {
18587
+ background-image: url("${primaryImage}");
18856
18588
  }
18857
- </style>
18858
- `;
18859
- };
18860
- function rbLargeCategoryImageToutTemplate(spot, config) {
18589
+ }
18590
+ </style>
18591
+ `;
18592
+ function rbSmallDiscoverToutTemplate(spot, config) {
18861
18593
  const { prefix = '' } = config;
18862
18594
  return `
18863
18595
  ${GFONT_PRECONNECT}
18864
- ${GFONT_SOURCE_SANS_3}
18865
18596
  ${GFONT_CORMORANT}
18866
- ${STYLES$3(spot, config)}
18597
+ ${STYLES(spot, config)}
18867
18598
  <div class="${prefix}">
18599
+ <div class="${prefix}__image"></div>
18868
18600
  <div class="${prefix}__text">
18869
18601
  ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18870
- ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
18871
- ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
18872
18602
  </div>
18873
18603
  </div>
18874
18604
  `;
18875
18605
  }
18876
18606
 
18877
- const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
18878
- const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
18879
- return `
18880
- <style>
18881
- .${prefix} {
18882
- width: 100%;
18883
- height: 100%;
18884
- display: flex;
18885
- flex-direction: column;
18886
- justify-content: flex-end;
18887
- border-radius: 5px;
18888
- overflow: hidden;
18889
- cursor: pointer;
18890
- background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
18891
- background-size: cover;
18892
- background-position: center;
18893
- background-repeat: no-repeat;
18894
- position: relative;
18607
+ /**
18608
+ * Returns the HTML element for the given spot.
18609
+ *
18610
+ * @param {ISpot} spot - The spot object.
18611
+ * @param {ISpotTemplateConfig} config - The spot template configuration.
18612
+ *
18613
+ * @return {HTMLElement | null} - The HTML element for the given spot or null if the spot template is not found.
18614
+ */
18615
+ const SPOT_TEMPLATE_HTML_ELEMENT = (spot, config) => {
18616
+ const templates = {
18617
+ // Reserve Bar Spot Templates
18618
+ [exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE]: {
18619
+ rbHomepageHeroThreeTile: rbHomepageHeroThreeTileTemplate,
18620
+ },
18621
+ [exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE]: {
18622
+ rbHomepageHeroTwoTile: rbHomepageHeroTwoTileTemplate,
18623
+ },
18624
+ [exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE]: {
18625
+ rbHomepageHeroFullImage: rbHomepageHeroFullImageTemplate,
18626
+ },
18627
+ [exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT]: {
18628
+ rbLargeCategoryImageTout: rbLargeCategoryImageToutTemplate,
18629
+ },
18630
+ [exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT]: {
18631
+ rbSmallDiscoverTout: rbSmallDiscoverToutTemplate,
18632
+ },
18633
+ [exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT]: {
18634
+ rbSmallCategoryImageTout: rbSmallCategoryImageToutTemplate,
18635
+ },
18636
+ [exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK]: {
18637
+ rbCollectionBannerWithoutTextBlock: rbCollectionBannerWithoutTextBlockTemplate,
18638
+ },
18639
+ [exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER]: {
18640
+ rbNavigationBanner: rbNavigationBannerTemplate,
18641
+ },
18642
+ [exports.RMN_SPOT_TYPE.RB_PRODUCT_UPCS]: {
18643
+ rbProductUpcs: () => '', // No template for this spot type, it will be handled by ReserveBar App.
18644
+ },
18645
+ // IAB Standard Spot Templates
18646
+ [exports.RMN_SPOT_TYPE.BILLBOARD]: {
18647
+ billboardV1: billboardV1Template,
18648
+ billboardV2: billboardV2Template,
18649
+ billboardV3: billboardV3Template,
18650
+ },
18651
+ [exports.RMN_SPOT_TYPE.LARGE_RECTANGLE]: {
18652
+ largeRectangleV1: largeRectangleV1Template,
18653
+ },
18654
+ [exports.RMN_SPOT_TYPE.VERTICAL_RECTANGLE]: {
18655
+ verticalRectangleV1: verticalRectangleV1Template,
18656
+ },
18657
+ [exports.RMN_SPOT_TYPE.SQUARE]: {
18658
+ squareV1: squareV1Template,
18659
+ squareV2: squareV2Template,
18660
+ },
18661
+ [exports.RMN_SPOT_TYPE.LARGE_LEADERBOARD]: {
18662
+ largeLeaderboardV1: largeLeaderboardV1Template,
18663
+ largeLeaderboardV2: largeLeaderboardV2Template,
18664
+ },
18665
+ [exports.RMN_SPOT_TYPE.WIDE_SKYSCRAPER]: {
18666
+ wideSkyscraperV1: wideSkyscraperV1Template,
18667
+ },
18668
+ [exports.RMN_SPOT_TYPE.IN_TEXT]: {
18669
+ inTextV1: inTextV1Template,
18670
+ },
18671
+ };
18672
+ const spotVariants = templates[spot.spot];
18673
+ if (!spotVariants) {
18674
+ return null;
18675
+ }
18676
+ const variantTemplate = spotVariants[spot.variant];
18677
+ if (!variantTemplate) {
18678
+ return null;
18679
+ }
18680
+ // Generate a highly unique prefix to avoid conflicts with other elements.
18681
+ const prefix = 's' + UniqueIdGenerator.generate().toLowerCase();
18682
+ const spotHtmlString = variantTemplate(spot, { ...config, prefix });
18683
+ return spotHtmlStringToElement(spotHtmlString);
18684
+ };
18685
+
18686
+ // For the moment, we will only focus on sites that use Google Analytics,
18687
+ // but we will add support for other analytics tools in the future.
18688
+ var AnalyticsTool;
18689
+ (function (AnalyticsTool) {
18690
+ AnalyticsTool["GoogleAnalytics"] = "google-analytics";
18691
+ AnalyticsTool["Other"] = "Other";
18692
+ })(AnalyticsTool || (AnalyticsTool = {}));
18693
+
18694
+ class DataLayerMonitor {
18695
+ constructor() {
18696
+ if (!window.dataLayer) {
18697
+ return;
18698
+ }
18699
+ this.originalPush = window.dataLayer.push;
18700
+ }
18701
+ static getInstance() {
18702
+ if (!DataLayerMonitor.instance) {
18703
+ DataLayerMonitor.instance = new DataLayerMonitor();
18704
+ }
18705
+ return DataLayerMonitor.instance;
18706
+ }
18707
+ setListener(listener) {
18708
+ this.listener = listener;
18709
+ }
18710
+ start() {
18711
+ window.dataLayer.push = (...args) => {
18712
+ const result = this.originalPush.apply(window.dataLayer, args);
18713
+ const pushedEvent = args[0];
18714
+ if (this.listener) {
18715
+ const normalizedData = this.cleanEventData(pushedEvent);
18716
+ if (normalizedData) {
18717
+ this.listener(normalizedData);
18718
+ }
18719
+ }
18720
+ return result;
18721
+ };
18722
+ }
18723
+ cleanEventData(data) {
18724
+ const eventName = getEventTypeFromRawEvent(data.event);
18725
+ if (!eventName) {
18726
+ return null;
18727
+ }
18728
+ const productIds = extractDeepValues(data, 'ids', {
18729
+ onlyFirst: false,
18730
+ shouldIncludeZero: true,
18731
+ });
18732
+ if (Array.isArray(productIds) && productIds.length === 0) {
18733
+ return null;
18734
+ }
18735
+ const normalizedData = {
18736
+ event: eventName,
18737
+ productIds,
18738
+ };
18739
+ if (eventName === exports.RMN_SPOT_EVENT.PURCHASE) {
18740
+ const productPrice = extractDeepValues(data, 'price', {
18741
+ onlyFirst: true,
18742
+ shouldIncludeZero: true,
18743
+ });
18744
+ if (productPrice) {
18745
+ normalizedData.productPrice = productPrice;
18746
+ }
18747
+ }
18748
+ return normalizedData;
18749
+ }
18750
+ stop() {
18751
+ if (this.originalPush) {
18752
+ window.dataLayer.push = this.originalPush;
18753
+ }
18754
+ this.listener = undefined;
18755
+ }
18756
+ }
18757
+
18758
+ // @TODO: Add support for user to push events to our own data layer, if they don't use any analytics tool.
18759
+ // window.rmnDataLayer = window.rmnDataLayer || [];
18760
+ class MonitorService {
18761
+ constructor() {
18762
+ const analyticsTool = this.detectAnalyticsTool();
18763
+ switch (analyticsTool) {
18764
+ case AnalyticsTool.GoogleAnalytics:
18765
+ this.implementedMonitor = DataLayerMonitor.getInstance();
18766
+ break;
18767
+ case AnalyticsTool.Other:
18768
+ default:
18769
+ console.warn('This site uses an unsupported analytics tool.');
18770
+ break;
18771
+ }
18772
+ if (analyticsTool === AnalyticsTool.Other) {
18773
+ return;
18774
+ }
18775
+ this.pubSubService = PubsubService.getInstance();
18776
+ this.localStorageService = LocalStorageService.getInstance();
18777
+ }
18778
+ static getInstance() {
18779
+ if (!MonitorService.instance) {
18780
+ MonitorService.instance = new MonitorService();
18781
+ }
18782
+ return MonitorService.instance;
18783
+ }
18784
+ start() {
18785
+ if (!this.implementedMonitor)
18786
+ return;
18787
+ this.implementedMonitor.setListener(async (eventData) => {
18788
+ var _a;
18789
+ await this.matchAndFireEvent(eventData, (_a = this.localStorageService) === null || _a === void 0 ? void 0 : _a.getSpots());
18790
+ });
18791
+ this.implementedMonitor.start();
18792
+ }
18793
+ async matchAndFireEvent(eventData, spots) {
18794
+ var _a, _b;
18795
+ if (!spots)
18796
+ return;
18797
+ const eventProductIds = new Set(eventData.productIds.map((productId) => String(productId)));
18798
+ for (const spot of Object.values(spots)) {
18799
+ if (!spot.productIds.length)
18800
+ continue;
18801
+ const hasCommonProductIds = spot.productIds.find((productId) => eventProductIds.has(String(productId)));
18802
+ if (!hasCommonProductIds || !Object.values(exports.RMN_SPOT_EVENT).includes(eventData.event)) {
18803
+ continue;
18804
+ }
18805
+ const eventUrl = (_b = (_a = spot.events.find((event) => event.event === eventData.event)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '';
18806
+ const additionalQueryParams = objectToQueryParams({
18807
+ override: eventData.productPrice,
18808
+ });
18809
+ await this.fireAndPublishSpotEvent({
18810
+ spotEvent: eventData.event,
18811
+ eventUrl: `${eventUrl}${additionalQueryParams ? `&${additionalQueryParams}` : ''}`,
18812
+ placementId: spot.placementId,
18813
+ spotId: spot.spotId,
18814
+ });
18815
+ }
18816
+ }
18817
+ async fireAndPublishSpotEvent({ spotEvent, eventUrl, placementId, spotId, }) {
18818
+ await fireEvent({
18819
+ event: spotEvent,
18820
+ eventUrl,
18821
+ });
18822
+ if (!this.pubSubService)
18823
+ return;
18824
+ this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
18825
+ eventType: spotEvent,
18826
+ placementId,
18827
+ spotId,
18828
+ });
18829
+ }
18830
+ detectAnalyticsTool() {
18831
+ let analyticsTool = AnalyticsTool.Other;
18832
+ // Check for Google Analytics
18833
+ if (typeof window.ga !== 'undefined') {
18834
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18835
+ }
18836
+ // Check for Google Analytics 4
18837
+ if (typeof window.gtag !== 'undefined') {
18838
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18839
+ }
18840
+ // Check for Google Tag Manager
18841
+ if (typeof window.google_tag_manager !== 'undefined') {
18842
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18843
+ }
18844
+ // @TODO: Add support for other analytics tools
18845
+ // Check for Heap Analytics
18846
+ // Check for Mixpanel
18847
+ // Check for Woopra
18848
+ // Check for Segment
18849
+ // Check for Amplitude
18850
+ return analyticsTool;
18851
+ }
18852
+ }
18853
+
18854
+ class EventService {
18855
+ constructor() {
18856
+ this.pubSubService = PubsubService.getInstance();
18857
+ this.localStorageService = LocalStorageService.getInstance();
18858
+ this.activeSpots = new Map();
18859
+ this.spotStates = new Map();
18860
+ this.intersectionObserver = new IntersectionObserverService();
18861
+ // Start the user monitor, which will track and check user interactions
18862
+ MonitorService.getInstance().start();
18895
18863
  }
18896
-
18897
- .${prefix}__header {
18898
- font-size: 16px;
18899
- color: ${textColor};
18900
- line-height: 20px;
18901
- font-weight: 400;
18902
- font-family: "Source Sans 3", system-ui;
18903
- font-style: normal;
18904
- margin: 0;
18905
- padding: 15px 10%;
18864
+ static getInstance() {
18865
+ if (!EventService.instance) {
18866
+ EventService.instance = new EventService();
18867
+ }
18868
+ return EventService.instance;
18906
18869
  }
18907
-
18908
- @container (min-width: 640px) {
18909
- .${prefix} {
18910
- background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
18911
- }
18870
+ subscribe(eventType, callback) {
18871
+ return this.pubSubService.subscribe(eventType, callback);
18912
18872
  }
18913
-
18914
- @container (min-width: 768px) {
18915
- .${prefix}__header {
18916
- font-size: 22px;
18917
- }
18873
+ publish(eventType, data) {
18874
+ this.pubSubService.publish(eventType, data);
18918
18875
  }
18919
-
18920
- @container (min-width: 1024px) {
18921
- .${prefix}__header {
18922
- font-size: 24px;
18923
- }
18876
+ registerSpot(params) {
18877
+ const { placementId, spot, spotElement } = params;
18878
+ this.activeSpots.set(placementId, { spotElement });
18879
+ // Fire impression event
18880
+ this.fireImpressionEvent(placementId, spot);
18881
+ // Handle intersection observer
18882
+ this.handleIntersectionObserver(placementId, spot, spotElement);
18883
+ // Attach click event listener
18884
+ spotElement.addEventListener('click', async () => await this.handleClick(params));
18924
18885
  }
18925
-
18926
- @container (min-width: 1280px) {
18927
- .${prefix}__header {
18928
- font-size: 28px;
18929
- }
18886
+ unregisterSpot(placementId) {
18887
+ const placementIdClean = placementId.replace('#', '');
18888
+ const spotData = this.activeSpots.get(placementIdClean);
18889
+ if (!spotData) {
18890
+ this.handleSpotState(placementIdClean, {
18891
+ state: {
18892
+ error: `Active spot with placementId ${placementIdClean} not found.`,
18893
+ },
18894
+ });
18895
+ return;
18896
+ }
18897
+ this.intersectionObserver.unobserve(spotData.spotElement);
18898
+ this.handleSpotState(placementIdClean, {
18899
+ dom: {
18900
+ spotElement: undefined,
18901
+ visibleOnViewport: false,
18902
+ },
18903
+ state: {
18904
+ unmounted: true,
18905
+ mounted: false,
18906
+ },
18907
+ });
18908
+ this.activeSpots.delete(placementIdClean);
18909
+ const placementElement = document.getElementById(placementIdClean);
18910
+ if (!placementElement) {
18911
+ this.handleSpotState(placementIdClean, {
18912
+ state: {
18913
+ error: `Placement element with id ${placementIdClean} not found.`,
18914
+ },
18915
+ });
18916
+ return;
18917
+ }
18918
+ placementElement.innerHTML = '';
18930
18919
  }
18931
- </style>
18932
- `;
18933
- };
18934
- function rbNavigationBannerTemplate(spot, config) {
18935
- const { prefix = '' } = config;
18936
- return `
18937
- ${GFONT_PRECONNECT}
18938
- ${GFONT_SOURCE_SANS_3}
18939
- ${STYLES$2(spot, config)}
18940
- <div class="${prefix}">
18941
- ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18942
- </div>
18943
- `;
18944
- }
18945
-
18946
- const STYLES$1 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
18947
- const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
18948
- return `
18949
- <style>
18950
- .${prefix} {
18951
- width: 100%;
18952
- height: 100%;
18953
- display: flex;
18954
- flex-direction: column;
18955
- justify-content: flex-end;
18956
- background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
18957
- background-size: cover;
18958
- background-repeat: no-repeat;
18959
- background-position: center;
18960
- border-radius: 5px;
18961
- overflow: hidden;
18962
- cursor: pointer;
18963
- position: relative;
18964
- }
18965
-
18966
- .${prefix}__header {
18967
- font-size: 12px;
18968
- color: ${textColor};
18969
- line-height: 16px;
18970
- font-family: "Source Sans 3", system-ui;
18971
- font-style: normal;
18972
- font-weight: 400;
18973
- margin: 0;
18974
- padding: 10px;
18975
- }
18976
-
18977
- @container (min-width: 640px) {
18978
- .${prefix} {
18979
- background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
18920
+ /**
18921
+ * Updates the state of a spot.
18922
+ *
18923
+ * @param {string} placementId - The placement ID of the spot.
18924
+ * @param {Partial<ILifecycleState>} updates - The updates to apply to the spot state.
18925
+ * @param {boolean} publish - Whether to publish the updated state.
18926
+ *
18927
+ * @returns {void}
18928
+ */
18929
+ handleSpotState(placementId, updates, publish = true) {
18930
+ let currentState = this.spotStates.get(placementId);
18931
+ if (!currentState) {
18932
+ currentState = {
18933
+ identifier: {
18934
+ placementId,
18935
+ spotId: '',
18936
+ spotType: '',
18937
+ },
18938
+ dom: {
18939
+ spotElement: undefined,
18940
+ visibleOnViewport: false,
18941
+ },
18942
+ state: {
18943
+ mounted: false,
18944
+ unmounted: false,
18945
+ loading: false,
18946
+ error: undefined,
18947
+ },
18948
+ displayConfig: {
18949
+ isCarousel: false,
18950
+ isCarouselItem: false,
18951
+ isSingleItem: false,
18952
+ },
18953
+ };
18980
18954
  }
18981
- }
18982
- </style>
18983
- `;
18984
- };
18985
- function rbSmallCategoryImageToutTemplate(spot, config) {
18986
- const { prefix = '' } = config;
18987
- return `
18988
- ${GFONT_PRECONNECT}
18989
- ${GFONT_CORMORANT}
18990
- ${STYLES$1(spot, config)}
18991
- <div class="${prefix}">
18992
- ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18993
- </div>
18994
- `;
18955
+ const merged = this.deepMerge(currentState, updates);
18956
+ this.spotStates.set(placementId, merged);
18957
+ if (publish) {
18958
+ this.pubSubService.publish(exports.RMN_EVENT.LIFECYCLE_STATE, this.spotStates.get(placementId));
18959
+ }
18960
+ }
18961
+ deepMerge(current, updates) {
18962
+ return {
18963
+ identifier: updates.identifier
18964
+ ? { ...current.identifier, ...updates.identifier }
18965
+ : current.identifier,
18966
+ dom: updates.dom ? { ...current.dom, ...updates.dom } : current.dom,
18967
+ state: updates.state ? { ...current.state, ...updates.state } : current.state,
18968
+ displayConfig: updates.displayConfig
18969
+ ? { ...current.displayConfig, ...updates.displayConfig }
18970
+ : current.displayConfig,
18971
+ };
18972
+ }
18973
+ async handleClick({ placementId, spot }) {
18974
+ var _a, _b, _c;
18975
+ this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
18976
+ eventType: exports.RMN_SPOT_EVENT.CLICK,
18977
+ placementId,
18978
+ spotId: spot.id,
18979
+ });
18980
+ await fireEvent({
18981
+ event: exports.RMN_SPOT_EVENT.CLICK,
18982
+ eventUrl: (_b = (_a = spot.events.find((event) => event.event === exports.RMN_SPOT_EVENT.CLICK)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18983
+ });
18984
+ // Save spot to local storage for event tracking
18985
+ this.localStorageService.setSpot(spot.id, {
18986
+ placementId,
18987
+ spotId: spot.id,
18988
+ spotType: spot.spot,
18989
+ events: spot.events,
18990
+ productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [1, 2, 3],
18991
+ });
18992
+ }
18993
+ handleIntersectionObserver(placementId, _spot, spotElement) {
18994
+ const spotIsVisibleCallback = async () => {
18995
+ this.intersectionObserver.unobserve(spotElement);
18996
+ this.handleSpotState(placementId, {
18997
+ dom: {
18998
+ spotElement,
18999
+ visibleOnViewport: true,
19000
+ },
19001
+ });
19002
+ };
19003
+ this.intersectionObserver.observe(spotElement, spotIsVisibleCallback);
19004
+ }
19005
+ fireImpressionEvent(placementId, spot) {
19006
+ this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
19007
+ eventType: exports.RMN_SPOT_EVENT.IMPRESSION,
19008
+ placementId,
19009
+ spotId: spot.id,
19010
+ });
19011
+ (async () => {
19012
+ var _a, _b;
19013
+ await fireEvent({
19014
+ event: exports.RMN_SPOT_EVENT.IMPRESSION,
19015
+ eventUrl: (_b = (_a = spot.events.find((event) => event.event === exports.RMN_SPOT_EVENT.IMPRESSION)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
19016
+ });
19017
+ })();
19018
+ }
18995
19019
  }
18996
19020
 
18997
- const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primaryImage, mobilePrimaryImage = primaryImage, }, { prefix }) => `
18998
- <style>
18999
- .${prefix} {
19000
- width: 100%;
19001
- height: 100%;
19002
- background-color: ${backgroundColor};
19003
- cursor: pointer;
19004
- display: flex;
19005
- flex-direction: column;
19006
- border-radius: 5px;
19007
- }
19021
+ const SELECTION_API_PATH = '/spots/selection';
19008
19022
 
19009
- .${prefix}__image {
19010
- width: 100%;
19011
- height: 100%;
19012
- background-image: url("${mobilePrimaryImage}");
19013
- background-size: cover;
19014
- background-repeat: no-repeat;
19015
- background-position: center;
19016
- border-radius: 5px;
19023
+ class SelectionService extends BaseApi {
19024
+ constructor(auth) {
19025
+ super(auth);
19017
19026
  }
19018
-
19019
- .${prefix}__text {
19020
- text-align: left;
19021
- display: flex;
19022
- flex-direction: row;
19023
- align-items: flex-start;
19024
- justify-content: flex-start;
19025
- width: 100%;
19026
- height: fit-content;
19027
- position: relative;
19027
+ static getInstance(auth) {
19028
+ return SingletonManager.getInstance('SelectionService', () => new SelectionService(auth));
19028
19029
  }
19029
-
19030
- .${prefix}__header {
19031
- font-size: 12px;
19032
- color: ${textColor};
19033
- padding-top: 5px;
19034
- line-height: 16px;
19035
- font-family: "Source Sans 3", system-ui;
19036
- font-style: normal;
19037
- font-weight: 400;
19038
- margin: 0;
19030
+ /**
19031
+ * Makes a selection request on our server based on the provided data.
19032
+ *
19033
+ * @param {ISpotSelectionParams} data - Spots selection parameters.
19034
+ *
19035
+ * @return {Promise<ISpots | { error: string }>} - The spots response object.
19036
+ */
19037
+ async spotSelection(data) {
19038
+ if (data.userId === undefined) {
19039
+ data.userId = this.getUserId();
19040
+ }
19041
+ const { isOk, val, isErr } = await this.post(SELECTION_API_PATH, data, {});
19042
+ if (isErr) {
19043
+ return { error: `There was an error during spot selection: (${isErr === null || isErr === void 0 ? void 0 : isErr.errorMessage})` };
19044
+ }
19045
+ if (isOk && val && val.data && (val === null || val === void 0 ? void 0 : val.refresh.token)) {
19046
+ this.authInfo.authenticated = true;
19047
+ this.authInfo.token = val.refresh.token;
19048
+ return val.data.spots;
19049
+ }
19050
+ return { error: 'Spot selection response was not successful' };
19039
19051
  }
19040
-
19041
- @container (min-width: 640px) {
19042
- .${prefix}__image {
19043
- background-image: url("${primaryImage}");
19044
- }
19052
+ getUserId() {
19053
+ const isWeb = typeof window !== 'undefined';
19054
+ if (isWeb) {
19055
+ const localStorageService = LocalStorageService.getInstance();
19056
+ return localStorageService.getUserId();
19057
+ }
19058
+ return undefined;
19045
19059
  }
19046
- </style>
19047
- `;
19048
- function rbSmallDiscoverToutTemplate(spot, config) {
19049
- const { prefix = '' } = config;
19050
- return `
19051
- ${GFONT_PRECONNECT}
19052
- ${GFONT_CORMORANT}
19053
- ${STYLES(spot, config)}
19054
- <div class="${prefix}">
19055
- <div class="${prefix}__image"></div>
19056
- <div class="${prefix}__text">
19057
- ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
19058
- </div>
19059
- </div>
19060
- `;
19061
19060
  }
19062
19061
 
19063
- /**
19064
- * Returns the HTML element for the given spot.
19065
- *
19066
- * @param {ISpot} spot - The spot object.
19067
- * @param {ISpotTemplateConfig} config - The spot template configuration.
19068
- *
19069
- * @return {HTMLElement | null} - The HTML element for the given spot or null if the spot template is not found.
19070
- */
19071
- const SPOT_TEMPLATE_HTML_ELEMENT = (spot, config) => {
19072
- const templates = {
19073
- // Reserve Bar Spot Templates
19074
- [exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE]: {
19075
- rbHomepageHeroThreeTile: rbHomepageHeroThreeTileTemplate,
19062
+ const SPOT_EVENTS_EXAMPLE = [
19063
+ {
19064
+ event: exports.RMN_SPOT_EVENT.CLICK,
19065
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwidXIiOm51bGx9&s=hWz37kbxi_u95EVNn2aoQhc5Aas',
19066
+ },
19067
+ {
19068
+ event: exports.RMN_SPOT_EVENT.IMPRESSION,
19069
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiYmEiOjEsImZxIjowfQ&s=djoysjCimurf-5T11AlNAwwLSS8',
19070
+ },
19071
+ {
19072
+ event: exports.RMN_SPOT_EVENT.PURCHASE,
19073
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjU5fQ&s=AAPAw-3SfZ0JMzjEGFSwt9L-2S4',
19074
+ },
19075
+ {
19076
+ event: exports.RMN_SPOT_EVENT.ADD_TO_CART,
19077
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYwfQ&s=uzQFcjgL7m9XqUG8FvTPVN5YkZY',
19078
+ },
19079
+ {
19080
+ event: exports.RMN_SPOT_EVENT.ADD_TO_WISHLIST,
19081
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYzfQ&s=m3ISU_iIy-OFtXrTKpI6cJAEC0k',
19082
+ },
19083
+ {
19084
+ event: exports.RMN_SPOT_EVENT.BUY_NOW,
19085
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjY5fQ&s=l6MOscQC-q-FkC2Ksd7w6jjySCQ',
19086
+ },
19087
+ ];
19088
+ ({
19089
+ rbHomepageHero: [
19090
+ {
19091
+ id: 'abc123',
19092
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19093
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19094
+ width: 1140,
19095
+ height: 640,
19096
+ header: 'Premium Wine Collection',
19097
+ description: 'Discover our exclusive selection of vintage wines',
19098
+ ctaText: 'Shop Wines',
19099
+ textColor: '#ffffff',
19100
+ ctaTextColor: '#ffffff',
19101
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Collection',
19102
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Wine',
19103
+ events: SPOT_EVENTS_EXAMPLE,
19104
+ productIds: [1, 2, 3],
19105
+ },
19106
+ {
19107
+ id: 'jkl012',
19108
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19109
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19110
+ width: 1140,
19111
+ height: 640,
19112
+ header: 'Whiskey Collection',
19113
+ description: 'Premium whiskeys from around the world',
19114
+ ctaText: 'Shop Whiskeys',
19115
+ textColor: '#ffffff',
19116
+ backgroundColor: '#2c1810',
19117
+ ctaTextColor: '#2c1810',
19118
+ primaryImage: 'https://placehold.co/1140x640/png?text=Whiskey',
19119
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Whiskey',
19120
+ events: SPOT_EVENTS_EXAMPLE,
19121
+ productIds: [10, 11],
19122
+ },
19123
+ {
19124
+ id: 'stu901',
19125
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19126
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19127
+ width: 1140,
19128
+ height: 640,
19129
+ header: 'Summer Cocktails',
19130
+ description: 'Essential spirits for summer mixing',
19131
+ ctaText: 'Mix It Up',
19132
+ textColor: '#ffffff',
19133
+ backgroundColor: '#4d3a0a',
19134
+ ctaTextColor: '#4d3a0a',
19135
+ primaryImage: 'https://placehold.co/1140x640/png?text=Cocktails',
19136
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Mixers',
19137
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Cocktails',
19138
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Mixers',
19139
+ events: SPOT_EVENTS_EXAMPLE,
19140
+ productIds: [16, 17, 18],
19141
+ },
19142
+ {
19143
+ id: 'def456',
19144
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19145
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19146
+ width: 1140,
19147
+ height: 640,
19148
+ header: 'Craft Beer Festival',
19149
+ description: 'Local breweries and exclusive releases',
19150
+ ctaText: 'Explore Beers',
19151
+ textColor: '#ffffff',
19152
+ ctaTextColor: '#ffffff',
19153
+ primaryImage: 'https://placehold.co/1140x640/png?text=Beer+Festival',
19154
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Beer',
19155
+ events: SPOT_EVENTS_EXAMPLE,
19156
+ productIds: [4, 5, 6],
19157
+ },
19158
+ {
19159
+ id: 'mno345',
19160
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19161
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19162
+ width: 1140,
19163
+ height: 640,
19164
+ header: 'Champagne Selection',
19165
+ description: 'Finest champagnes for every occasion',
19166
+ ctaText: 'View Champagnes',
19167
+ textColor: '#ffffff',
19168
+ backgroundColor: '#1a1a1a',
19169
+ ctaTextColor: '#1a1a1a',
19170
+ primaryImage: 'https://placehold.co/1140x640/png?text=Champagne',
19171
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Champagne',
19172
+ events: SPOT_EVENTS_EXAMPLE,
19173
+ productIds: [12, 13],
19174
+ },
19175
+ {
19176
+ id: 'vwx234',
19177
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19178
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19179
+ width: 1140,
19180
+ height: 640,
19181
+ header: 'Wine Regions',
19182
+ description: 'Explore wines from top regions',
19183
+ ctaText: 'Tour Regions',
19184
+ textColor: '#ffffff',
19185
+ backgroundColor: '#4d0a0a',
19186
+ ctaTextColor: '#4d0a0a',
19187
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Regions',
19188
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Vineyards',
19189
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Regions',
19190
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Vineyards',
19191
+ events: SPOT_EVENTS_EXAMPLE,
19192
+ productIds: [19, 20, 21],
19076
19193
  },
19077
- [exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE]: {
19078
- rbHomepageHeroTwoTile: rbHomepageHeroTwoTileTemplate,
19194
+ {
19195
+ id: 'ghi789',
19196
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19197
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19198
+ width: 1140,
19199
+ height: 640,
19200
+ header: 'Rare Spirits',
19201
+ description: 'Limited edition spirits collection',
19202
+ ctaText: 'View Collection',
19203
+ textColor: '#ffffff',
19204
+ ctaTextColor: '#ffffff',
19205
+ primaryImage: 'https://placehold.co/1140x640/png?text=Rare+Spirits',
19206
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Spirits',
19207
+ events: SPOT_EVENTS_EXAMPLE,
19208
+ productIds: [7, 8, 9],
19079
19209
  },
19080
- [exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE]: {
19081
- rbHomepageHeroFullImage: rbHomepageHeroFullImageTemplate,
19210
+ {
19211
+ id: 'pqr678',
19212
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19213
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19214
+ width: 1140,
19215
+ height: 640,
19216
+ header: 'Gin Collection',
19217
+ description: 'Artisanal gins and botanicals',
19218
+ ctaText: 'Explore Gins',
19219
+ textColor: '#ffffff',
19220
+ backgroundColor: '#0a4d4d',
19221
+ ctaTextColor: '#0a4d4d',
19222
+ primaryImage: 'https://placehold.co/1140x640/png?text=Gin',
19223
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gin',
19224
+ events: SPOT_EVENTS_EXAMPLE,
19225
+ productIds: [14, 15],
19082
19226
  },
19083
- [exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT]: {
19084
- rbLargeCategoryImageTout: rbLargeCategoryImageToutTemplate,
19227
+ {
19228
+ id: 'yz5678',
19229
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19230
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19231
+ width: 1140,
19232
+ height: 640,
19233
+ header: 'Tequila Collection',
19234
+ description: 'Premium tequilas and mezcals',
19235
+ ctaText: 'Shop Tequila',
19236
+ textColor: '#ffffff',
19237
+ backgroundColor: '#0a4d2b',
19238
+ ctaTextColor: '#0a4d2b',
19239
+ primaryImage: 'https://placehold.co/1140x640/png?text=Tequila',
19240
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Mezcal',
19241
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Tequila',
19242
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Mezcal',
19243
+ events: SPOT_EVENTS_EXAMPLE,
19244
+ productIds: [22, 23, 24],
19085
19245
  },
19086
- [exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT]: {
19087
- rbSmallDiscoverTout: rbSmallDiscoverToutTemplate,
19246
+ ],
19247
+ rbHomepageHeroFullImage: [
19248
+ {
19249
+ id: 'hjk567',
19250
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19251
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19252
+ width: 1140,
19253
+ height: 640,
19254
+ header: 'Holiday Gift Guide',
19255
+ description: 'Perfect spirits for every occasion',
19256
+ ctaText: 'Shop Gifts',
19257
+ textColor: '#ffffff',
19258
+ ctaTextColor: '#ffffff',
19259
+ primaryImage: 'https://placehold.co/1140x640/png?text=Gift+Guide',
19260
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gifts',
19261
+ events: SPOT_EVENTS_EXAMPLE,
19262
+ productIds: [25, 26],
19263
+ },
19264
+ {
19265
+ id: 'qwe890',
19266
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19267
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19268
+ width: 1140,
19269
+ height: 640,
19270
+ header: 'Summer Wine Festival',
19271
+ description: 'Refreshing wines for summer',
19272
+ ctaText: 'Shop Festival',
19273
+ textColor: '#ffffff',
19274
+ ctaTextColor: '#ffffff',
19275
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Festival',
19276
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Festival',
19277
+ events: SPOT_EVENTS_EXAMPLE,
19278
+ productIds: [27, 28],
19279
+ },
19280
+ ],
19281
+ rbHomepageHeroTwoTile: [
19282
+ {
19283
+ id: 'iop987',
19284
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19285
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19286
+ width: 1140,
19287
+ height: 640,
19288
+ header: 'Bourbon Selection',
19289
+ description: "Kentucky's finest bourbons",
19290
+ ctaText: 'Shop Bourbon',
19291
+ textColor: '#ffffff',
19292
+ backgroundColor: '#2c1810',
19293
+ ctaTextColor: '#2c1810',
19294
+ primaryImage: 'https://placehold.co/1140x640/png?text=Bourbon',
19295
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Bourbon',
19296
+ events: SPOT_EVENTS_EXAMPLE,
19297
+ productIds: [29, 30],
19298
+ },
19299
+ {
19300
+ id: 'lkj012',
19301
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19302
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19303
+ width: 1140,
19304
+ height: 640,
19305
+ header: 'Vodka Collection',
19306
+ description: 'Premium vodkas from around the world',
19307
+ ctaText: 'Shop Vodka',
19308
+ textColor: '#ffffff',
19309
+ backgroundColor: '#1a1a1a',
19310
+ ctaTextColor: '#1a1a1a',
19311
+ primaryImage: 'https://placehold.co/1140x640/png?text=Vodka',
19312
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Vodka',
19313
+ events: SPOT_EVENTS_EXAMPLE,
19314
+ productIds: [31, 32],
19315
+ },
19316
+ ],
19317
+ rbHomepageHeroThreeTile: [
19318
+ {
19319
+ id: 'bnm345',
19320
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19321
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19322
+ width: 1140,
19323
+ height: 640,
19324
+ header: 'Rum Collection',
19325
+ description: 'Caribbean and craft rums',
19326
+ ctaText: 'Shop Rum',
19327
+ textColor: '#ffffff',
19328
+ backgroundColor: '#4d3a0a',
19329
+ ctaTextColor: '#4d3a0a',
19330
+ primaryImage: 'https://placehold.co/1140x640/png?text=Rum',
19331
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Craft+Rum',
19332
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Rum',
19333
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Craft',
19334
+ events: SPOT_EVENTS_EXAMPLE,
19335
+ productIds: [33, 34],
19336
+ },
19337
+ {
19338
+ id: 'fgh678',
19339
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19340
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19341
+ width: 1140,
19342
+ height: 640,
19343
+ header: 'Scotch Selection',
19344
+ description: 'Single malts and blends',
19345
+ ctaText: 'Shop Scotch',
19346
+ textColor: '#ffffff',
19347
+ backgroundColor: '#4d0a0a',
19348
+ ctaTextColor: '#4d0a0a',
19349
+ primaryImage: 'https://placehold.co/1140x640/png?text=Scotch',
19350
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Single+Malts',
19351
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Scotch',
19352
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Malts',
19353
+ events: SPOT_EVENTS_EXAMPLE,
19354
+ productIds: [35, 36],
19355
+ },
19356
+ ],
19357
+ rbLargeCategoryImageTout: [
19358
+ {
19359
+ id: 'cde567',
19360
+ spot: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
19361
+ variant: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
19362
+ width: 468,
19363
+ height: 410,
19364
+ header: 'Rare & Limited Edition',
19365
+ description: 'Discover our collection of hard-to-find and limited release spirits.',
19366
+ textColor: '#ffffff',
19367
+ ctaTextColor: '#ffffff',
19368
+ primaryImage: 'https://placehold.co/468x410/png?text=Rare+Spirits',
19369
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Rare+Spirits',
19370
+ ctaText: 'Shop Rare Spirits',
19371
+ events: SPOT_EVENTS_EXAMPLE,
19372
+ productIds: [37, 38, 39, 40, 41],
19373
+ },
19374
+ {
19375
+ id: 'efg789',
19376
+ spot: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
19377
+ variant: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
19378
+ width: 468,
19379
+ height: 410,
19380
+ header: 'Vintage Champagnes',
19381
+ description: 'Explore our prestigious collection of aged champagnes.',
19382
+ textColor: '#ffffff',
19383
+ ctaTextColor: '#ffffff',
19384
+ primaryImage: 'https://placehold.co/468x410/png?text=Vintage+Champagne',
19385
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Champagne',
19386
+ ctaText: 'View Collection',
19387
+ events: SPOT_EVENTS_EXAMPLE,
19388
+ productIds: [42, 43, 44, 45, 46],
19389
+ },
19390
+ {
19391
+ id: 'ghi012',
19392
+ spot: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
19393
+ variant: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
19394
+ width: 468,
19395
+ height: 410,
19396
+ header: 'Small Batch Bourbon',
19397
+ description: 'Hand-selected premium bourbon from craft distilleries.',
19398
+ textColor: '#ffffff',
19399
+ ctaTextColor: '#ffffff',
19400
+ primaryImage: 'https://placehold.co/468x410/png?text=Craft+Bourbon',
19401
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Bourbon',
19402
+ ctaText: 'Explore Bourbon',
19403
+ events: SPOT_EVENTS_EXAMPLE,
19404
+ productIds: [47, 48, 49, 50, 51],
19405
+ },
19406
+ ],
19407
+ rbSmallDiscoverTout: [
19408
+ {
19409
+ id: 'jkl345',
19410
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
19411
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
19412
+ width: 224,
19413
+ height: 378,
19414
+ header: 'Château Margaux 2015 Bordeaux',
19415
+ textColor: '#ffffff',
19416
+ primaryImage: 'https://placehold.co/224x378/png?text=Château+Margaux',
19417
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Château+Margaux',
19418
+ events: SPOT_EVENTS_EXAMPLE,
19419
+ productIds: [52, 53, 54, 55, 56],
19420
+ },
19421
+ {
19422
+ id: 'lmn678',
19423
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
19424
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
19425
+ width: 224,
19426
+ height: 378,
19427
+ header: 'Macallan 25 Year',
19428
+ textColor: '#ffffff',
19429
+ primaryImage: 'https://placehold.co/224x378/png?text=Macallan+25',
19430
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Macallan',
19431
+ events: SPOT_EVENTS_EXAMPLE,
19432
+ productIds: [57, 58, 59, 60, 61],
19433
+ },
19434
+ {
19435
+ id: 'nop901',
19436
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
19437
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
19438
+ width: 224,
19439
+ height: 378,
19440
+ header: 'Louis XIII Cognac',
19441
+ textColor: '#ffffff',
19442
+ primaryImage: 'https://placehold.co/224x378/png?text=Louis+XIII',
19443
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Louis+XIII',
19444
+ events: SPOT_EVENTS_EXAMPLE,
19445
+ productIds: [62, 63, 64, 65, 66],
19088
19446
  },
19089
- [exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT]: {
19090
- rbSmallCategoryImageTout: rbSmallCategoryImageToutTemplate,
19447
+ ],
19448
+ rbSmallCategoryImageTout: [
19449
+ {
19450
+ id: 'qrs234',
19451
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
19452
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
19453
+ width: 224,
19454
+ height: 410,
19455
+ header: 'Japanese Sake',
19456
+ textColor: '#ffffff',
19457
+ primaryImage: 'https://placehold.co/224x410/png?text=Japanese+Sake',
19458
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Japanese+Sake',
19459
+ events: SPOT_EVENTS_EXAMPLE,
19460
+ productIds: [67, 68, 69, 70, 71],
19091
19461
  },
19092
- [exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK]: {
19093
- rbCollectionBannerWithoutTextBlock: rbCollectionBannerWithoutTextBlockTemplate,
19462
+ {
19463
+ id: 'stu567',
19464
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
19465
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
19466
+ width: 224,
19467
+ height: 410,
19468
+ header: 'Craft Vermouth',
19469
+ textColor: '#ffffff',
19470
+ primaryImage: 'https://placehold.co/224x410/png?text=Craft+Vermouth',
19471
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Vermouth',
19472
+ events: SPOT_EVENTS_EXAMPLE,
19473
+ productIds: [72, 73, 74, 75, 76],
19094
19474
  },
19095
- [exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER]: {
19096
- rbNavigationBanner: rbNavigationBannerTemplate,
19475
+ {
19476
+ id: 'vwx890',
19477
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
19478
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
19479
+ width: 224,
19480
+ height: 410,
19481
+ header: 'Premium Mezcal',
19482
+ textColor: '#ffffff',
19483
+ primaryImage: 'https://placehold.co/224x410/png?text=Premium+Mezcal',
19484
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Mezcal',
19485
+ events: SPOT_EVENTS_EXAMPLE,
19486
+ productIds: [77, 78, 79, 80, 81],
19097
19487
  },
19098
- [exports.RMN_SPOT_TYPE.RB_PRODUCT_UPCS]: {
19099
- rbProductUpcs: () => '', // No template for this spot type, it will be handled by ReserveBar App.
19488
+ ],
19489
+ rbCollectionBannerWithoutTextBlock: [
19490
+ {
19491
+ id: 'yz1234',
19492
+ spot: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
19493
+ variant: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
19494
+ width: 887,
19495
+ height: 344,
19496
+ primaryImage: 'https://placehold.co/887x344/png?text=Summer+Cocktails',
19497
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Summer+Cocktails',
19498
+ events: SPOT_EVENTS_EXAMPLE,
19499
+ productIds: [82, 83, 84, 85, 86],
19100
19500
  },
19101
- // IAB Standard Spot Templates
19102
- [exports.RMN_SPOT_TYPE.BILLBOARD]: {
19103
- billboardV1: billboardV1Template,
19104
- billboardV2: billboardV2Template,
19105
- billboardV3: billboardV3Template,
19501
+ {
19502
+ id: 'abc567',
19503
+ spot: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
19504
+ variant: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
19505
+ width: 887,
19506
+ height: 344,
19507
+ primaryImage: 'https://placehold.co/887x344/png?text=Holiday+Spirits',
19508
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Holiday+Spirits',
19509
+ events: SPOT_EVENTS_EXAMPLE,
19510
+ productIds: [87, 88, 89, 90, 91],
19106
19511
  },
19107
- [exports.RMN_SPOT_TYPE.LARGE_RECTANGLE]: {
19108
- largeRectangleV1: largeRectangleV1Template,
19512
+ {
19513
+ id: 'def901',
19514
+ spot: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
19515
+ variant: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
19516
+ width: 887,
19517
+ height: 344,
19518
+ primaryImage: 'https://placehold.co/887x344/png?text=Wine+Essentials',
19519
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Wine+Essentials',
19520
+ events: SPOT_EVENTS_EXAMPLE,
19521
+ productIds: [92, 93, 94, 95, 96],
19109
19522
  },
19110
- [exports.RMN_SPOT_TYPE.VERTICAL_RECTANGLE]: {
19111
- verticalRectangleV1: verticalRectangleV1Template,
19523
+ ],
19524
+ rbNavigationBanner: [
19525
+ {
19526
+ id: 'ghi234',
19527
+ spot: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
19528
+ variant: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
19529
+ width: 440,
19530
+ height: 220,
19531
+ header: 'Explore Tequilas',
19532
+ textColor: '#ffffff',
19533
+ primaryImage: 'https://placehold.co/440x220/png?text=Tequila+Collection',
19534
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Tequila+Collection',
19535
+ events: SPOT_EVENTS_EXAMPLE,
19536
+ productIds: [97, 98, 99, 100, 101],
19112
19537
  },
19113
- [exports.RMN_SPOT_TYPE.SQUARE]: {
19114
- squareV1: squareV1Template,
19115
- squareV2: squareV2Template,
19538
+ {
19539
+ id: 'jkl678',
19540
+ spot: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
19541
+ variant: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
19542
+ width: 440,
19543
+ height: 220,
19544
+ header: 'Craft Gin Selection',
19545
+ textColor: '#ffffff',
19546
+ primaryImage: 'https://placehold.co/440x220/png?text=Gin+Selection',
19547
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Gin+Selection',
19548
+ events: SPOT_EVENTS_EXAMPLE,
19549
+ productIds: [102, 103, 104, 105, 106],
19116
19550
  },
19117
- [exports.RMN_SPOT_TYPE.LARGE_LEADERBOARD]: {
19118
- largeLeaderboardV1: largeLeaderboardV1Template,
19119
- largeLeaderboardV2: largeLeaderboardV2Template,
19551
+ {
19552
+ id: 'mno012',
19553
+ spot: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
19554
+ variant: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
19555
+ width: 440,
19556
+ height: 220,
19557
+ header: 'Premium Vodka',
19558
+ textColor: '#ffffff',
19559
+ primaryImage: 'https://placehold.co/440x220/png?text=Vodka+Premium',
19560
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Vodka+Premium',
19561
+ events: SPOT_EVENTS_EXAMPLE,
19562
+ productIds: [107, 108, 109, 110, 111],
19120
19563
  },
19121
- [exports.RMN_SPOT_TYPE.WIDE_SKYSCRAPER]: {
19122
- wideSkyscraperV1: wideSkyscraperV1Template,
19564
+ ],
19565
+ });
19566
+ ({
19567
+ banner: [],
19568
+ billboard: [
19569
+ {
19570
+ id: 'kol567',
19571
+ spot: exports.RMN_SPOT_TYPE.BILLBOARD,
19572
+ variant: `${exports.RMN_SPOT_TYPE.BILLBOARD}V2`,
19573
+ width: 1140,
19574
+ height: 640,
19575
+ header: 'Holiday Gift Guide',
19576
+ description: 'Perfect spirits for every occasion',
19577
+ ctaText: 'Shop Gifts',
19578
+ textColor: '#ffffff',
19579
+ ctaTextColor: '#ffffff',
19580
+ primaryImage: 'https://placehold.co/1140x640/png?text=Gift+Guide',
19581
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gifts',
19582
+ events: SPOT_EVENTS_EXAMPLE,
19583
+ productIds: [25, 26],
19123
19584
  },
19124
- [exports.RMN_SPOT_TYPE.IN_TEXT]: {
19125
- inTextV1: inTextV1Template,
19585
+ {
19586
+ id: 'hpm390',
19587
+ spot: exports.RMN_SPOT_TYPE.BILLBOARD,
19588
+ variant: `${exports.RMN_SPOT_TYPE.BILLBOARD}V2`,
19589
+ width: 1140,
19590
+ height: 640,
19591
+ header: 'Summer Wine Festival',
19592
+ description: 'Refreshing wines for summer',
19593
+ ctaText: 'Shop Festival',
19594
+ textColor: '#ffffff',
19595
+ ctaTextColor: '#ffffff',
19596
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Festival',
19597
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Festival',
19598
+ events: SPOT_EVENTS_EXAMPLE,
19599
+ productIds: [27, 28],
19126
19600
  },
19127
- };
19128
- const spotVariants = templates[spot.spot];
19129
- if (!spotVariants) {
19130
- return null;
19131
- }
19132
- const variantTemplate = spotVariants[spot.variant];
19133
- if (!variantTemplate) {
19134
- return null;
19135
- }
19136
- // Generate a highly unique prefix to avoid conflicts with other elements.
19137
- const prefix = 's' + UniqueIdGenerator.generate().toLowerCase();
19138
- const spotHtmlString = variantTemplate(spot, { ...config, prefix });
19139
- return spotHtmlStringToElement(spotHtmlString);
19140
- };
19141
-
19142
- // For the moment, we will only focus on sites that use Google Analytics,
19143
- // but we will add support for other analytics tools in the future.
19144
- var AnalyticsTool;
19145
- (function (AnalyticsTool) {
19146
- AnalyticsTool["GoogleAnalytics"] = "google-analytics";
19147
- AnalyticsTool["Other"] = "Other";
19148
- })(AnalyticsTool || (AnalyticsTool = {}));
19149
-
19150
- class DataLayerMonitor {
19151
- constructor() {
19152
- if (!window.dataLayer) {
19153
- return;
19154
- }
19155
- this.originalPush = window.dataLayer.push;
19156
- }
19157
- static getInstance() {
19158
- if (!DataLayerMonitor.instance) {
19159
- DataLayerMonitor.instance = new DataLayerMonitor();
19160
- }
19161
- return DataLayerMonitor.instance;
19162
- }
19163
- setListener(listener) {
19164
- this.listener = listener;
19165
- }
19166
- start() {
19167
- window.dataLayer.push = (...args) => {
19168
- const result = this.originalPush.apply(window.dataLayer, args);
19169
- const pushedEvent = args[0];
19170
- if (this.listener) {
19171
- const normalizedData = this.cleanEventData(pushedEvent);
19172
- if (normalizedData) {
19173
- this.listener(normalizedData);
19174
- }
19175
- }
19176
- return result;
19177
- };
19178
- }
19179
- cleanEventData(data) {
19180
- const eventName = getEventTypeFromRawEvent(data.event);
19181
- if (!eventName) {
19182
- return null;
19183
- }
19184
- const productIds = extractDeepIds(data);
19185
- return {
19186
- event: eventName,
19187
- productIds,
19188
- };
19189
- }
19190
- stop() {
19191
- if (this.originalPush) {
19192
- window.dataLayer.push = this.originalPush;
19193
- }
19194
- this.listener = undefined;
19195
- }
19196
- }
19601
+ ],
19602
+ button2: [],
19603
+ featurePhoneLargeBanner: [],
19604
+ featurePhoneMediumBanner: [],
19605
+ featurePhoneSmallBanner: [],
19606
+ halfPage: [],
19607
+ inText: [],
19608
+ largeLeaderboard: [],
19609
+ largeRectangle: [],
19610
+ leaderboard: [],
19611
+ mediumRectangle: [],
19612
+ microBar: [],
19613
+ mobilePhoneInterstitial1: [],
19614
+ mobilePhoneInterstitial2: [],
19615
+ mobilePhoneInterstitial3: [],
19616
+ popUp: [],
19617
+ portrait: [],
19618
+ rbProductUpcs: [],
19619
+ skyscraper: [],
19620
+ smallRectangle: [],
19621
+ smallSquare: [],
19622
+ smartphoneBanner1: [],
19623
+ smartphoneBanner2: [],
19624
+ square: [],
19625
+ verticalBanner: [],
19626
+ verticalRectangle: [],
19627
+ wideSkyscraper: [],
19628
+ });
19197
19629
 
19198
- // @TODO: Add support for user to push events to our own data layer, if they don't use any analytics tool.
19199
- // window.rmnDataLayer = window.rmnDataLayer || [];
19200
- class MonitorService {
19201
- constructor() {
19202
- const analyticsTool = this.detectAnalyticsTool();
19203
- switch (analyticsTool) {
19204
- case AnalyticsTool.GoogleAnalytics:
19205
- this.implementedMonitor = DataLayerMonitor.getInstance();
19206
- break;
19207
- case AnalyticsTool.Other:
19208
- default:
19209
- console.warn('This site uses an unsupported analytics tool.');
19210
- break;
19211
- }
19212
- if (analyticsTool === AnalyticsTool.Other) {
19213
- return;
19214
- }
19215
- this.pubSubService = PubsubService.getInstance();
19216
- this.localStorageService = LocalStorageService.getInstance();
19217
- }
19218
- static getInstance() {
19219
- if (!MonitorService.instance) {
19220
- MonitorService.instance = new MonitorService();
19221
- }
19222
- return MonitorService.instance;
19223
- }
19224
- start() {
19225
- if (!this.implementedMonitor)
19226
- return;
19227
- this.implementedMonitor.setListener(async (eventData) => {
19228
- var _a;
19229
- await this.matchAndFireEvent(eventData, (_a = this.localStorageService) === null || _a === void 0 ? void 0 : _a.getSpots());
19230
- });
19231
- this.implementedMonitor.start();
19232
- }
19233
- async matchAndFireEvent(eventData, spots) {
19234
- var _a, _b;
19235
- if (!spots)
19236
- return;
19237
- const eventProductIds = new Set(eventData.productIds.map((productId) => String(productId)));
19238
- for (const spot of Object.values(spots)) {
19239
- if (!spot.productIds.length)
19240
- continue;
19241
- const hasCommonProductIds = spot.productIds.find((productId) => eventProductIds.has(String(productId)));
19242
- if (hasCommonProductIds) {
19243
- if (Object.values(exports.RMN_SPOT_EVENT).includes(eventData.event)) {
19244
- await this.fireAndPublishSpotEvent({
19245
- spotEvent: eventData.event,
19246
- eventUrl: (_b = (_a = spot.events.find((event) => event.event === eventData.event)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
19247
- placementId: spot.placementId,
19248
- spotId: spot.spotId,
19249
- });
19250
- }
19251
- }
19252
- }
19253
- }
19254
- async fireAndPublishSpotEvent({ spotEvent, eventUrl, placementId, spotId, }) {
19255
- await fireEvent({
19256
- event: spotEvent,
19257
- eventUrl,
19258
- });
19259
- if (!this.pubSubService)
19260
- return;
19261
- this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
19262
- eventType: spotEvent,
19263
- placementId,
19264
- spotId,
19265
- });
19266
- }
19267
- detectAnalyticsTool() {
19268
- let analyticsTool = AnalyticsTool.Other;
19269
- // Check for Google Analytics
19270
- if (typeof window.ga !== 'undefined') {
19271
- analyticsTool = AnalyticsTool.GoogleAnalytics;
19272
- }
19273
- // Check for Google Analytics 4
19274
- if (typeof window.gtag !== 'undefined') {
19275
- analyticsTool = AnalyticsTool.GoogleAnalytics;
19276
- }
19277
- // Check for Google Tag Manager
19278
- if (typeof window.google_tag_manager !== 'undefined') {
19279
- analyticsTool = AnalyticsTool.GoogleAnalytics;
19280
- }
19281
- // @TODO: Add support for other analytics tools
19282
- // Check for Heap Analytics
19283
- // Check for Mixpanel
19284
- // Check for Woopra
19285
- // Check for Segment
19286
- // Check for Amplitude
19287
- return analyticsTool;
19630
+ /**
19631
+ * Checks if the current environment is a browser environment.
19632
+ *
19633
+ * @return {boolean} - Whether the current environment is a browser environment.
19634
+ */
19635
+ function isBrowserEnvironment() {
19636
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
19637
+ console.warn('LiquidCommerce Rmn Sdk: Methods which create elements are only available in browser environments.');
19638
+ return false;
19288
19639
  }
19640
+ return true;
19289
19641
  }
19290
-
19291
- class EventService {
19292
- constructor() {
19293
- this.pubSubService = PubsubService.getInstance();
19294
- this.localStorageService = LocalStorageService.getInstance();
19295
- this.activeSpots = new Map();
19296
- this.spotStates = new Map();
19297
- this.intersectionObserver = new IntersectionObserverService();
19298
- // Start the user monitor, which will track and check user interactions
19299
- MonitorService.getInstance().start();
19300
- }
19301
- static getInstance() {
19302
- if (!EventService.instance) {
19303
- EventService.instance = new EventService();
19304
- }
19305
- return EventService.instance;
19306
- }
19307
- subscribe(eventType, callback) {
19308
- return this.pubSubService.subscribe(eventType, callback);
19309
- }
19310
- publish(eventType, data) {
19311
- this.pubSubService.publish(eventType, data);
19312
- }
19313
- registerSpot(params) {
19314
- const { placementId, spot, spotElement } = params;
19315
- this.activeSpots.set(placementId, { spotElement });
19316
- // Fire impression event
19317
- this.fireImpressionEvent(placementId, spot);
19318
- // Handle intersection observer
19319
- this.handleIntersectionObserver(placementId, spot, spotElement);
19320
- // Attach click event listener
19321
- spotElement.addEventListener('click', async () => await this.handleClick(params));
19322
- }
19323
- unregisterSpot(placementId) {
19324
- const placementIdClean = placementId.replace('#', '');
19325
- const spotData = this.activeSpots.get(placementIdClean);
19326
- if (!spotData) {
19327
- this.handleSpotState(placementIdClean, {
19328
- state: {
19329
- error: `Active spot with placementId ${placementIdClean} not found.`,
19330
- },
19331
- });
19332
- return;
19333
- }
19334
- this.intersectionObserver.unobserve(spotData.spotElement);
19335
- this.handleSpotState(placementIdClean, {
19336
- dom: {
19337
- spotElement: undefined,
19338
- visibleOnViewport: false,
19339
- },
19340
- state: {
19341
- unmounted: true,
19342
- mounted: false,
19343
- },
19344
- });
19345
- this.activeSpots.delete(placementIdClean);
19346
- const placementElement = document.getElementById(placementIdClean);
19347
- if (!placementElement) {
19348
- this.handleSpotState(placementIdClean, {
19642
+ /**
19643
+ * Validates the inject data by preventing duplicate placement ids and non-existent spot types.
19644
+ *
19645
+ * @param {IInjectSpotElement[]} inject - The inject data.
19646
+ * @return {IInjectSpotElement[]} - The validated inject data.
19647
+ */
19648
+ function validateInjectData(inject) {
19649
+ const eventService = EventService.getInstance();
19650
+ const placementIds = new Set();
19651
+ const validSpotTypes = new Set(Object.values(exports.RMN_SPOT_TYPE));
19652
+ const validatedInject = [];
19653
+ for (const item of inject) {
19654
+ const placementId = item.placementId.replace('#', '');
19655
+ // Check for duplicate placement ids
19656
+ if (placementIds.has(placementId)) {
19657
+ eventService.handleSpotState(placementId, {
19349
19658
  state: {
19350
- error: `Placement element with id ${placementIdClean} not found.`,
19659
+ error: `Duplicate placement id (${placementId}) found. Please provide a unique placement id for each spot element.`,
19351
19660
  },
19352
19661
  });
19353
- return;
19354
- }
19355
- placementElement.innerHTML = '';
19356
- }
19357
- /**
19358
- * Updates the state of a spot.
19359
- *
19360
- * @param {string} placementId - The placement ID of the spot.
19361
- * @param {Partial<ILifecycleState>} updates - The updates to apply to the spot state.
19362
- * @param {boolean} publish - Whether to publish the updated state.
19363
- *
19364
- * @returns {void}
19365
- */
19366
- handleSpotState(placementId, updates, publish = true) {
19367
- let currentState = this.spotStates.get(placementId);
19368
- if (!currentState) {
19369
- currentState = {
19370
- identifier: {
19371
- placementId,
19372
- spotId: '',
19373
- spotType: '',
19374
- },
19375
- dom: {
19376
- spotElement: undefined,
19377
- visibleOnViewport: false,
19378
- },
19379
- state: {
19380
- mounted: false,
19381
- unmounted: false,
19382
- loading: false,
19383
- error: undefined,
19384
- },
19385
- displayConfig: {
19386
- isCarousel: false,
19387
- isCarouselItem: false,
19388
- isSingleItem: false,
19389
- },
19390
- };
19391
- }
19392
- const merged = this.deepMerge(currentState, updates);
19393
- this.spotStates.set(placementId, merged);
19394
- if (publish) {
19395
- this.pubSubService.publish(exports.RMN_EVENT.LIFECYCLE_STATE, this.spotStates.get(placementId));
19396
- }
19397
- }
19398
- deepMerge(current, updates) {
19399
- return {
19400
- identifier: updates.identifier
19401
- ? { ...current.identifier, ...updates.identifier }
19402
- : current.identifier,
19403
- dom: updates.dom ? { ...current.dom, ...updates.dom } : current.dom,
19404
- state: updates.state ? { ...current.state, ...updates.state } : current.state,
19405
- displayConfig: updates.displayConfig
19406
- ? { ...current.displayConfig, ...updates.displayConfig }
19407
- : current.displayConfig,
19408
- };
19409
- }
19410
- async handleClick({ placementId, spot }) {
19411
- var _a, _b, _c;
19412
- this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
19413
- eventType: exports.RMN_SPOT_EVENT.CLICK,
19414
- placementId,
19415
- spotId: spot.id,
19416
- });
19417
- await fireEvent({
19418
- event: exports.RMN_SPOT_EVENT.CLICK,
19419
- eventUrl: (_b = (_a = spot.events.find((event) => event.event === exports.RMN_SPOT_EVENT.CLICK)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
19420
- });
19421
- // Save spot to local storage for event tracking
19422
- this.localStorageService.setSpot(spot.id, {
19423
- placementId,
19424
- spotId: spot.id,
19425
- spotType: spot.spot,
19426
- events: spot.events,
19427
- productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [1, 2, 3],
19428
- });
19429
- }
19430
- handleIntersectionObserver(placementId, _spot, spotElement) {
19431
- const spotIsVisibleCallback = async () => {
19432
- this.intersectionObserver.unobserve(spotElement);
19433
- this.handleSpotState(placementId, {
19434
- dom: {
19435
- spotElement,
19436
- visibleOnViewport: true,
19662
+ continue;
19663
+ }
19664
+ // Check for non-existent spot types
19665
+ if (!validSpotTypes.has(item.spotType)) {
19666
+ eventService.handleSpotState(placementId, {
19667
+ state: {
19668
+ error: `Invalid spot type (${item.spotType}) found. Please provide a valid spot type for each spot element.`,
19437
19669
  },
19438
19670
  });
19439
- };
19440
- this.intersectionObserver.observe(spotElement, spotIsVisibleCallback);
19441
- }
19442
- fireImpressionEvent(placementId, spot) {
19443
- this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
19444
- eventType: exports.RMN_SPOT_EVENT.IMPRESSION,
19445
- placementId,
19446
- spotId: spot.id,
19447
- });
19448
- (async () => {
19449
- var _a, _b;
19450
- await fireEvent({
19451
- event: exports.RMN_SPOT_EVENT.IMPRESSION,
19452
- eventUrl: (_b = (_a = spot.events.find((event) => event.event === exports.RMN_SPOT_EVENT.IMPRESSION)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
19453
- });
19454
- })();
19671
+ continue;
19672
+ }
19673
+ placementIds.add(placementId);
19674
+ validatedInject.push(item);
19455
19675
  }
19676
+ return validatedInject;
19456
19677
  }
19457
-
19458
- const SELECTION_API_PATH = '/spots/selection';
19459
-
19460
- class SelectionService extends BaseApi {
19461
- constructor(auth) {
19462
- super(auth);
19678
+ /**
19679
+ * Clears the placement element by removing all its children.
19680
+ *
19681
+ * @param {string} placementId - The placement id.
19682
+ *
19683
+ * @return {void}
19684
+ */
19685
+ function clearPlacement(placementId) {
19686
+ var _a;
19687
+ (_a = document.getElementById(placementId)) === null || _a === void 0 ? void 0 : _a.replaceChildren();
19688
+ }
19689
+ /**
19690
+ * Prepares the spot placement for rendering by taking care of its styling.
19691
+ *
19692
+ * @param {HTMLElement} placement - The placement element.
19693
+ *
19694
+ * @return {void}
19695
+ */
19696
+ function prepareSpotPlacement(placement) {
19697
+ placement.removeAttribute('style');
19698
+ placement.removeAttribute('class');
19699
+ const styles = {
19700
+ width: '100%',
19701
+ height: '100%',
19702
+ display: 'flex',
19703
+ justifyContent: 'center',
19704
+ };
19705
+ Object.assign(placement.style, styles);
19706
+ }
19707
+ /**
19708
+ * Waits for the DOM to be ready before continuing.
19709
+ *
19710
+ * @return {Promise<void>} - A promise that resolves when the DOM is ready.
19711
+ */
19712
+ async function waitForDOM() {
19713
+ if (!isBrowserEnvironment()) {
19714
+ return;
19463
19715
  }
19464
- static getInstance(auth) {
19465
- return SingletonManager.getInstance('SelectionService', () => new SelectionService(auth));
19716
+ if (document.readyState === 'complete' || document.readyState === 'interactive') {
19717
+ return;
19466
19718
  }
19467
- /**
19468
- * Makes a selection request on our server based on the provided data.
19469
- *
19470
- * @param {ISpotSelectionParams} data - Spots selection parameters.
19471
- *
19472
- * @return {Promise<ISpots | { error: string }>} - The spots response object.
19473
- */
19474
- async spotSelection(data) {
19475
- const { isOk, val, isErr } = await this.post(SELECTION_API_PATH, data, {});
19476
- if (isErr) {
19477
- return { error: `There was an error during spot selection: (${isErr === null || isErr === void 0 ? void 0 : isErr.errorMessage})` };
19478
- }
19479
- if (isOk && val && val.data && (val === null || val === void 0 ? void 0 : val.refresh.token)) {
19480
- this.authInfo.authenticated = true;
19481
- this.authInfo.token = val.refresh.token;
19482
- return val.data.spots;
19483
- }
19484
- return { error: 'Spot selection response was not successful' };
19719
+ return new Promise((resolve) => {
19720
+ document.addEventListener('DOMContentLoaded', () => {
19721
+ resolve();
19722
+ });
19723
+ });
19724
+ }
19725
+ // Sets the id for the user who is browsing the website
19726
+ // This id is used to identify the user and provide personalized content
19727
+ function setUserId() {
19728
+ if (isBrowserEnvironment()) {
19729
+ const localStorageService = LocalStorageService.getInstance();
19730
+ localStorageService.setUserId();
19485
19731
  }
19486
19732
  }
19487
19733
 
19734
+ /**
19735
+ * LiquidCommerce Rmn Client
19736
+ * @class
19737
+ */
19488
19738
  class LiquidCommerceRmnClient {
19489
19739
  constructor(auth) {
19490
19740
  this.selectionService = SelectionService.getInstance(auth);
@@ -19513,14 +19763,11 @@ class LiquidCommerceRmnClient {
19513
19763
  */
19514
19764
  async injectSpotElement(params) {
19515
19765
  var _a;
19516
- if (typeof window === 'undefined' || typeof document === 'undefined') {
19517
- console.warn('LiquidCommerce Rmn Sdk: injectSpotElement method is only available in the browser environment.');
19518
- return;
19519
- }
19766
+ // Wait for the DOM to be ready before continuing, to avoid issues with the spot placements
19767
+ await waitForDOM();
19520
19768
  const config = params.config;
19521
- let inject = params.inject;
19522
19769
  // Handle no spots error state
19523
- if (!inject.length) {
19770
+ if (!params.inject.length) {
19524
19771
  this.eventService.handleSpotState('all', {
19525
19772
  state: {
19526
19773
  error: 'No spot elements provided for injection.',
@@ -19528,49 +19775,32 @@ class LiquidCommerceRmnClient {
19528
19775
  });
19529
19776
  return;
19530
19777
  }
19531
- // Identify the spot elements
19778
+ // Validate inject data
19779
+ const inject = validateInjectData(params.inject);
19532
19780
  for (const item of inject) {
19781
+ // Identify the spot element
19533
19782
  this.eventService.handleSpotState(item.placementId, {
19534
19783
  identifier: {
19535
19784
  placementId: item.placementId,
19536
19785
  spotType: item.spotType,
19537
19786
  },
19538
19787
  }, false);
19539
- }
19540
- // Prevent duplicate placement ids
19541
- const hasDuplicatePlacementIds = this.preventDuplicateSpotPlacementIds(inject);
19542
- if (!hasDuplicatePlacementIds) {
19543
- return;
19544
- }
19545
- // Prevent non-existent spot types
19546
- inject = this.preventNonExistentSpotTypes(inject);
19547
- // Add Intersection Observer to the spot elements to track visibility
19548
- // This is useful for lazy loading the spot elements
19549
- for (const item of inject) {
19550
- const placementId = item.placementId.replace('#', '');
19551
- const placement = document.getElementById(placementId);
19788
+ const placement = document.getElementById(item.placementId);
19789
+ // Handle placement not found error state
19552
19790
  if (!placement) {
19553
- // Handle placement not found error state
19554
19791
  this.eventService.handleSpotState(item.placementId, {
19555
19792
  state: {
19556
- error: `Placement not found for id "${placementId}".`,
19793
+ error: `Placement not found for id "${item.placementId}".`,
19557
19794
  },
19558
19795
  });
19559
19796
  continue;
19560
19797
  }
19561
- // Take over placement styles
19562
- placement.removeAttribute('style');
19563
- placement.removeAttribute('class');
19564
- Object.assign(placement.style, {
19565
- width: '100%',
19566
- height: '100%',
19567
- display: 'flex',
19568
- justifyContent: 'center',
19569
- });
19798
+ prepareSpotPlacement(placement);
19570
19799
  const skeletonElement = this.elementService.createSkeletonElement({
19571
19800
  fluid: (_a = config === null || config === void 0 ? void 0 : config.fluid) !== null && _a !== void 0 ? _a : false,
19572
19801
  spotType: item.spotType,
19573
19802
  });
19803
+ // Handle skeleton loader error state
19574
19804
  if (!skeletonElement) {
19575
19805
  this.eventService.handleSpotState(item.placementId, {
19576
19806
  state: {
@@ -19582,19 +19812,15 @@ class LiquidCommerceRmnClient {
19582
19812
  continue;
19583
19813
  }
19584
19814
  placement.replaceChildren(skeletonElement);
19585
- const spotPlacementIsNear = async () => {
19815
+ const spotPlacementIsNearCallback = async () => {
19586
19816
  var _a;
19587
- // Set the spot element to loading state
19588
- this.eventService.handleSpotState(item.placementId, {
19589
- state: {
19590
- loading: true,
19591
- },
19592
- });
19593
- // Stop observing the placement
19817
+ // Stop observing the placement, as we only need to do this once
19594
19818
  this.intersectionObserver.unobserve(placement);
19819
+ // Set the spot element to loading state
19820
+ this.eventService.handleSpotState(item.placementId, { state: { loading: true } });
19595
19821
  // Make the spot selection request
19596
- const response = await this.spotSelectionRequest({ ...params, inject: [item] });
19597
- // const response = await this.useSpotSelectionExample(inject);
19822
+ const response = await this.injectSpotSelectionRequest({ ...params, inject: [item] });
19823
+ // const response = await useSpotSelectionExample(inject);
19598
19824
  // Handle request error state
19599
19825
  if (typeof response === 'object' && 'error' in response) {
19600
19826
  this.eventService.handleSpotState(item.placementId, {
@@ -19604,13 +19830,13 @@ class LiquidCommerceRmnClient {
19604
19830
  loading: false,
19605
19831
  },
19606
19832
  });
19607
- this.clearPlacement(item.placementId);
19833
+ clearPlacement(item.placementId);
19608
19834
  return;
19609
19835
  }
19610
19836
  const itemConfig = (_a = item.config) !== null && _a !== void 0 ? _a : config;
19611
19837
  const spots = response[item.placementId];
19838
+ // Handle no spots found error state
19612
19839
  if (!(spots === null || spots === void 0 ? void 0 : spots.length)) {
19613
- // Handle no spots found error state
19614
19840
  this.eventService.handleSpotState(item.placementId, {
19615
19841
  state: {
19616
19842
  error: `No spots found for type "${item.spotType}".`,
@@ -19618,22 +19844,106 @@ class LiquidCommerceRmnClient {
19618
19844
  loading: false,
19619
19845
  },
19620
19846
  });
19621
- this.clearPlacement(item.placementId);
19847
+ clearPlacement(item.placementId);
19622
19848
  return;
19623
19849
  }
19624
19850
  // Handle single spot
19625
19851
  if (spots.length === 1) {
19626
- this.injectOneSpotElement(item, placement, spots[0], itemConfig);
19852
+ this.injectOneSpotElement(placement, spots[0], itemConfig);
19627
19853
  }
19628
19854
  // Handle multiple spots (carousel)
19629
19855
  if (spots.length > 1) {
19630
19856
  this.injectCarouselSpotElement(placement, spots, itemConfig);
19631
19857
  }
19632
19858
  };
19633
- this.intersectionObserver.observe(placement, spotPlacementIsNear, { rootMargin: '500px' });
19859
+ /**
19860
+ * Observe the placement element to check if it is near the viewport.
19861
+ * If it is near, make the spot selection request.
19862
+ */
19863
+ this.intersectionObserver.observe(placement, spotPlacementIsNearCallback, {
19864
+ rootMargin: '1000px',
19865
+ threshold: 0,
19866
+ });
19867
+ }
19868
+ }
19869
+ /**
19870
+ * Injects a single spot element into the provided placement.
19871
+ *
19872
+ * @param {HTMLElement} placement - The placement element.
19873
+ * @param {ISpot} spot - The spot data.
19874
+ * @param {IInjectSpotElementConfig} config - The configuration object.
19875
+ *
19876
+ * @return {void}
19877
+ */
19878
+ injectOneSpotElement(placement, spot, config) {
19879
+ var _a;
19880
+ const placementId = placement.id;
19881
+ const spotType = spot.spot;
19882
+ this.eventService.handleSpotState(placementId, {
19883
+ identifier: {
19884
+ placementId,
19885
+ spotType,
19886
+ spotId: spot.id,
19887
+ },
19888
+ displayConfig: {
19889
+ isSingleItem: true,
19890
+ isCarousel: false,
19891
+ isCarouselItem: false,
19892
+ },
19893
+ });
19894
+ const spotData = this.elementService.overrideSpotColors(spot, config === null || config === void 0 ? void 0 : config.colors);
19895
+ const content = SPOT_TEMPLATE_HTML_ELEMENT(spotData, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
19896
+ if (!content) {
19897
+ this.eventService.handleSpotState(placementId, {
19898
+ state: {
19899
+ error: `Failed to inject spot element. Could not create element for type "${spotType}".`,
19900
+ mounted: false,
19901
+ loading: false,
19902
+ },
19903
+ });
19904
+ clearPlacement(placementId);
19905
+ return;
19906
+ }
19907
+ // Create the spot element
19908
+ const spotElement = this.elementService.createSpotElement({
19909
+ content,
19910
+ config: {
19911
+ fluid: config === null || config === void 0 ? void 0 : config.fluid,
19912
+ spot: spot.spot,
19913
+ width: spot.width,
19914
+ height: spot.height,
19915
+ minScale: (_a = config === null || config === void 0 ? void 0 : config.minScale) !== null && _a !== void 0 ? _a : 0.25, // Scale down to 25% of the original size
19916
+ },
19917
+ });
19918
+ if (!spotElement) {
19919
+ this.eventService.handleSpotState(placementId, {
19920
+ state: {
19921
+ error: `Failed to inject spot element. Could not create element for type "${spotType}".`,
19922
+ mounted: false,
19923
+ loading: false,
19924
+ },
19925
+ });
19926
+ clearPlacement(placementId);
19927
+ return;
19634
19928
  }
19929
+ this.eventService.registerSpot({
19930
+ spot: spotData,
19931
+ placementId,
19932
+ spotElement,
19933
+ });
19934
+ placement.replaceChildren(spotElement);
19935
+ this.eventService.handleSpotState(placementId, {
19936
+ dom: {
19937
+ spotElement,
19938
+ visibleOnViewport: false,
19939
+ },
19940
+ state: {
19941
+ mounted: true,
19942
+ loading: false,
19943
+ error: undefined,
19944
+ },
19945
+ });
19635
19946
  }
19636
- /** ========================= HELPER METHODS ========================= **/
19637
19947
  /**
19638
19948
  * Injects a carousel element with the provided spots into the placement.
19639
19949
  *
@@ -19645,11 +19955,12 @@ class LiquidCommerceRmnClient {
19645
19955
  */
19646
19956
  injectCarouselSpotElement(placement, spots, config) {
19647
19957
  var _a;
19958
+ const placementId = placement.id;
19648
19959
  const carouselSlides = [];
19649
19960
  for (const spotItem of spots) {
19650
- this.eventService.handleSpotState(placement.id, {
19961
+ this.eventService.handleSpotState(placementId, {
19651
19962
  identifier: {
19652
- placementId: placement.id,
19963
+ placementId,
19653
19964
  spotType: spotItem.spot,
19654
19965
  spotId: spotItem.id,
19655
19966
  },
@@ -19662,7 +19973,7 @@ class LiquidCommerceRmnClient {
19662
19973
  const spot = this.elementService.overrideSpotColors(spotItem, config === null || config === void 0 ? void 0 : config.colors);
19663
19974
  const content = SPOT_TEMPLATE_HTML_ELEMENT(spot, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
19664
19975
  if (!content) {
19665
- this.eventService.handleSpotState(placement.id, {
19976
+ this.eventService.handleSpotState(placementId, {
19666
19977
  state: {
19667
19978
  error: `Failed to inject carousel spot item element. Could not create element for type "${spot.spot}".`,
19668
19979
  mounted: false,
@@ -19673,7 +19984,7 @@ class LiquidCommerceRmnClient {
19673
19984
  }
19674
19985
  this.eventService.registerSpot({
19675
19986
  spot,
19676
- placementId: placement.id,
19987
+ placementId,
19677
19988
  spotElement: content,
19678
19989
  });
19679
19990
  carouselSlides.push(content);
@@ -19695,18 +20006,18 @@ class LiquidCommerceRmnClient {
19695
20006
  },
19696
20007
  });
19697
20008
  if (!carouselElement) {
19698
- this.eventService.handleSpotState(placement.id, {
20009
+ this.eventService.handleSpotState(placementId, {
19699
20010
  state: {
19700
20011
  error: `Failed to inject spot carousel element. Could not create spot carousel element.`,
19701
20012
  mounted: false,
19702
20013
  loading: false,
19703
20014
  },
19704
20015
  });
19705
- this.clearPlacement(placement.id);
20016
+ clearPlacement(placementId);
19706
20017
  return;
19707
20018
  }
19708
20019
  placement.replaceChildren(carouselElement);
19709
- this.eventService.handleSpotState(placement.id, {
20020
+ this.eventService.handleSpotState(placementId, {
19710
20021
  dom: {
19711
20022
  spotElement: carouselElement,
19712
20023
  visibleOnViewport: false,
@@ -19718,94 +20029,6 @@ class LiquidCommerceRmnClient {
19718
20029
  },
19719
20030
  });
19720
20031
  }
19721
- /**
19722
- * Injects a single spot element into the provided placement.
19723
- *
19724
- * @param {IInjectSpotElement} injectItem - The inject item data.
19725
- * @param {HTMLElement} placement - The placement element.
19726
- * @param {ISpot} spot - The spot data.
19727
- * @param {IInjectSpotElementConfig} config - The configuration object.
19728
- *
19729
- * @return {void}
19730
- */
19731
- injectOneSpotElement(injectItem, placement, spot, config) {
19732
- var _a;
19733
- this.eventService.handleSpotState(injectItem.placementId, {
19734
- identifier: {
19735
- placementId: injectItem.placementId,
19736
- spotType: injectItem.spotType,
19737
- spotId: spot.id,
19738
- },
19739
- displayConfig: {
19740
- isSingleItem: true,
19741
- isCarousel: false,
19742
- isCarouselItem: false,
19743
- },
19744
- });
19745
- const spotData = this.elementService.overrideSpotColors(spot, config === null || config === void 0 ? void 0 : config.colors);
19746
- const content = SPOT_TEMPLATE_HTML_ELEMENT(spotData, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
19747
- if (!content) {
19748
- this.eventService.handleSpotState(injectItem.placementId, {
19749
- state: {
19750
- error: `Failed to inject spot element. Could not create element for type "${injectItem.spotType}".`,
19751
- mounted: false,
19752
- loading: false,
19753
- },
19754
- });
19755
- this.clearPlacement(injectItem.placementId);
19756
- return;
19757
- }
19758
- // Create the spot element
19759
- const spotElement = this.elementService.createSpotElement({
19760
- content,
19761
- config: {
19762
- fluid: config === null || config === void 0 ? void 0 : config.fluid,
19763
- spot: spot.spot,
19764
- width: spot.width,
19765
- height: spot.height,
19766
- minScale: (_a = config === null || config === void 0 ? void 0 : config.minScale) !== null && _a !== void 0 ? _a : 0.25, // Scale down to 25% of the original size
19767
- },
19768
- });
19769
- if (!spotElement) {
19770
- this.eventService.handleSpotState(injectItem.placementId, {
19771
- state: {
19772
- error: `Failed to inject spot element. Could not create element for type "${injectItem.spotType}".`,
19773
- mounted: false,
19774
- loading: false,
19775
- },
19776
- });
19777
- this.clearPlacement(injectItem.placementId);
19778
- return;
19779
- }
19780
- this.eventService.registerSpot({
19781
- spot: spotData,
19782
- placementId: injectItem.placementId,
19783
- spotElement,
19784
- });
19785
- placement.replaceChildren(spotElement);
19786
- this.eventService.handleSpotState(injectItem.placementId, {
19787
- dom: {
19788
- spotElement,
19789
- visibleOnViewport: false,
19790
- },
19791
- state: {
19792
- mounted: true,
19793
- loading: false,
19794
- error: undefined,
19795
- },
19796
- });
19797
- }
19798
- /**
19799
- * Clears the placement element by removing all its children.
19800
- *
19801
- * @param {string} placementId - The placement id.
19802
- *
19803
- * @return {void}
19804
- */
19805
- clearPlacement(placementId) {
19806
- var _a;
19807
- (_a = document.getElementById(placementId)) === null || _a === void 0 ? void 0 : _a.replaceChildren();
19808
- }
19809
20032
  /**
19810
20033
  * Makes a selection request on our server based on the provided data.
19811
20034
  *
@@ -19813,77 +20036,21 @@ class LiquidCommerceRmnClient {
19813
20036
  *
19814
20037
  * @return {Promise<ISpots | {error: string}>} - The spots response object.
19815
20038
  */
19816
- async spotSelectionRequest(params) {
20039
+ async injectSpotSelectionRequest(params) {
19817
20040
  const { inject, filter, config } = params;
20041
+ const spots = inject.map((item) => ({
20042
+ placementId: item.placementId,
20043
+ spot: item.spotType,
20044
+ count: item === null || item === void 0 ? void 0 : item.count,
20045
+ ...item === null || item === void 0 ? void 0 : item.filter,
20046
+ }));
19818
20047
  const request = {
19819
20048
  url: config === null || config === void 0 ? void 0 : config.url,
19820
20049
  filter,
19821
- spots: inject.map((item) => ({
19822
- placementId: item.placementId,
19823
- spot: item.spotType,
19824
- count: item === null || item === void 0 ? void 0 : item.count,
19825
- ...item === null || item === void 0 ? void 0 : item.filter,
19826
- })),
20050
+ spots,
19827
20051
  };
19828
20052
  return this.spotSelection(request);
19829
20053
  }
19830
- /**
19831
- * Prevents duplicate placement ids in the inject data.
19832
- *
19833
- * @param {IInjectSpotElement[]} inject - The inject data.
19834
- *
19835
- * @throws {Error} - If a duplicate placement id is found.
19836
- *
19837
- * @return {void}
19838
- */
19839
- preventDuplicateSpotPlacementIds(inject) {
19840
- const placementIds = new Set();
19841
- for (const item of inject) {
19842
- if (placementIds.has(item.placementId)) {
19843
- this.eventService.handleSpotState(item.placementId, {
19844
- state: {
19845
- error: `Duplicate placement id (${item.placementId}) found. Please provide a unique placement id for each spot element.`,
19846
- },
19847
- });
19848
- return false;
19849
- }
19850
- placementIds.add(item.placementId);
19851
- }
19852
- return true;
19853
- }
19854
- /**
19855
- * Prevents non-existent spot types in the inject data.
19856
- *
19857
- * @param {IInjectSpotElement[]} inject - The inject data.
19858
- * @return {IInjectSpotElement[]} - The filtered inject data.
19859
- */
19860
- preventNonExistentSpotTypes(inject) {
19861
- const newInject = [];
19862
- for (const item of inject) {
19863
- if (!Object.values(exports.RMN_SPOT_TYPE).includes(item.spotType)) {
19864
- this.eventService.handleSpotState(item.placementId, {
19865
- state: {
19866
- error: `Invalid spot type (${item.spotType}) found. Please provide a valid spot type for each spot element.`,
19867
- },
19868
- });
19869
- continue;
19870
- }
19871
- newInject.push(item);
19872
- }
19873
- return newInject;
19874
- }
19875
- // Use spot selection example data for private testing
19876
- useSpotSelectionExample(inject) {
19877
- const examples = { ...RB_SPOTS_SELECTION_EXAMPLE, ...IAB_SPOTS_SELECTION_EXAMPLE };
19878
- const data = {};
19879
- inject.map((item) => {
19880
- var _a, _b, _c;
19881
- data[item.placementId] = (_c = (_a = examples[item.spotType]) === null || _a === void 0 ? void 0 : _a.slice(0, (_b = item.count) !== null && _b !== void 0 ? _b : 1)) !== null && _c !== void 0 ? _c : [];
19882
- });
19883
- return new Promise((resolve) => {
19884
- resolve(data);
19885
- });
19886
- }
19887
20054
  }
19888
20055
  /**
19889
20056
  * Creates a new instance of the RmnClient.
@@ -19896,6 +20063,7 @@ class LiquidCommerceRmnClient {
19896
20063
  async function RmnClient(apiKey, config) {
19897
20064
  const authService = AuthService.getInstance(apiKey, config.env);
19898
20065
  const credentials = await authService.initialize();
20066
+ setUserId();
19899
20067
  return new LiquidCommerceRmnClient(credentials);
19900
20068
  }
19901
20069
  /**