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

Sign up to get free protection for your applications and to get access to all the features.
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
  /**