@searchspring/snap-preact 0.27.8 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +2 -1
  2. package/dist/cjs/Instantiators/RecommendationInstantiator.d.ts +6 -5
  3. package/dist/cjs/Instantiators/RecommendationInstantiator.d.ts.map +1 -1
  4. package/dist/cjs/Instantiators/RecommendationInstantiator.js +19 -5
  5. package/dist/cjs/Snap.d.ts +67 -13
  6. package/dist/cjs/Snap.d.ts.map +1 -1
  7. package/dist/cjs/Snap.js +223 -81
  8. package/dist/cjs/create/createAutocompleteController.d.ts +1 -1
  9. package/dist/cjs/create/createAutocompleteController.d.ts.map +1 -1
  10. package/dist/cjs/create/createAutocompleteController.js +6 -1
  11. package/dist/cjs/create/createFinderController.d.ts +1 -1
  12. package/dist/cjs/create/createFinderController.d.ts.map +1 -1
  13. package/dist/cjs/create/createFinderController.js +6 -1
  14. package/dist/cjs/create/createRecommendationController.d.ts +1 -1
  15. package/dist/cjs/create/createRecommendationController.d.ts.map +1 -1
  16. package/dist/cjs/create/createRecommendationController.js +6 -1
  17. package/dist/cjs/create/createSearchController.d.ts +1 -1
  18. package/dist/cjs/create/createSearchController.d.ts.map +1 -1
  19. package/dist/cjs/create/createSearchController.js +6 -1
  20. package/dist/cjs/getBundleDetails/getBundleDetails.d.ts.map +1 -1
  21. package/dist/cjs/getBundleDetails/getBundleDetails.js +3 -2
  22. package/dist/cjs/types.d.ts +11 -5
  23. package/dist/cjs/types.d.ts.map +1 -1
  24. package/dist/esm/Instantiators/RecommendationInstantiator.d.ts +6 -5
  25. package/dist/esm/Instantiators/RecommendationInstantiator.d.ts.map +1 -1
  26. package/dist/esm/Instantiators/RecommendationInstantiator.js +21 -6
  27. package/dist/esm/Snap.d.ts +67 -13
  28. package/dist/esm/Snap.d.ts.map +1 -1
  29. package/dist/esm/Snap.js +185 -61
  30. package/dist/esm/create/createAutocompleteController.d.ts +1 -1
  31. package/dist/esm/create/createAutocompleteController.d.ts.map +1 -1
  32. package/dist/esm/create/createAutocompleteController.js +6 -1
  33. package/dist/esm/create/createFinderController.d.ts +1 -1
  34. package/dist/esm/create/createFinderController.d.ts.map +1 -1
  35. package/dist/esm/create/createFinderController.js +6 -1
  36. package/dist/esm/create/createRecommendationController.d.ts +1 -1
  37. package/dist/esm/create/createRecommendationController.d.ts.map +1 -1
  38. package/dist/esm/create/createRecommendationController.js +6 -1
  39. package/dist/esm/create/createSearchController.d.ts +1 -1
  40. package/dist/esm/create/createSearchController.d.ts.map +1 -1
  41. package/dist/esm/create/createSearchController.js +6 -1
  42. package/dist/esm/getBundleDetails/getBundleDetails.d.ts.map +1 -1
  43. package/dist/esm/getBundleDetails/getBundleDetails.js +3 -2
  44. package/dist/esm/types.d.ts +11 -5
  45. package/dist/esm/types.d.ts.map +1 -1
  46. package/package.json +13 -13
package/dist/esm/Snap.js CHANGED
@@ -3,14 +3,14 @@ import deepmerge from 'deepmerge';
3
3
  import { isPlainObject } from 'is-plain-object';
4
4
  import { render } from 'preact';
5
5
  import { Client } from '@searchspring/snap-client';
6
- import { Logger, LogMode } from '@searchspring/snap-logger';
6
+ import { Logger } from '@searchspring/snap-logger';
7
7
  import { Tracker } from '@searchspring/snap-tracker';
8
- import { version, DomTargeter, url, cookies, featureFlags } from '@searchspring/snap-toolbox';
9
- import { getContext } from '@searchspring/snap-toolbox';
8
+ import { AppMode, version, getContext, DomTargeter, url, cookies, featureFlags } from '@searchspring/snap-toolbox';
10
9
  import { ControllerTypes } from '@searchspring/snap-controller';
11
10
  import { default as createSearchController } from './create/createSearchController';
12
11
  export const BRANCH_COOKIE = 'ssBranch';
13
- export const SS_DEV_COOKIE = 'ssDev';
12
+ export const DEV_COOKIE = 'ssDev';
13
+ const SESSION_ATTRIBUTION = 'ssAttribution';
14
14
  const COMPONENT_ERROR = `Uncaught Error - Invalid value passed as the component.
15
15
  This usually happens when you pass a JSX Element, and not a function that returns the component, in the snap config.
16
16
 
@@ -47,6 +47,10 @@ This usually happens when you pass a JSX Element, and not a function that return
47
47
  The error above happened in the following targeter in the Snap Config`;
48
48
  export class Snap {
49
49
  constructor(config, services) {
50
+ this.mode = AppMode.production;
51
+ this._instantiatorPromises = {};
52
+ this._controllerPromises = {};
53
+ this.controllers = {};
50
54
  this.getInstantiator = (id) => {
51
55
  return this._instantiatorPromises[id] || Promise.reject(`getInstantiator could not find instantiator with id: ${id}`);
52
56
  };
@@ -58,12 +62,22 @@ export class Snap {
58
62
  controllerIds.forEach((id) => getControllerPromises.push(this.getController(id)));
59
63
  return Promise.all(getControllerPromises);
60
64
  };
65
+ // exposed method used for creating controllers dynamically - calls _createController()
61
66
  this.createController = async (type, config, services, urlConfig, context, callback) => {
67
+ if (typeof this._controllerPromises[config.id] != 'undefined') {
68
+ throw new Error(`Controller with id '${config.id}' is already defined`);
69
+ }
70
+ this._controllerPromises[config.id] = new Promise((resolve) => this._createController(type, config, services, urlConfig, context, async (cntrlr) => {
71
+ if (typeof callback == 'function')
72
+ await callback(cntrlr);
73
+ resolve(cntrlr);
74
+ }));
75
+ return this._controllerPromises[config.id];
76
+ };
77
+ // internal use method that creates controllers without verifying if id is in use first
78
+ this._createController = async (type, config, services, urlConfig, context, callback) => {
62
79
  let importPromise;
63
80
  switch (type) {
64
- case ControllerTypes.search:
65
- importPromise = import('./create/createSearchController');
66
- break;
67
81
  case ControllerTypes.autocomplete:
68
82
  importPromise = import('./create/createAutocompleteController');
69
83
  break;
@@ -73,10 +87,17 @@ export class Snap {
73
87
  case ControllerTypes.recommendation:
74
88
  importPromise = import('./create/createRecommendationController');
75
89
  break;
90
+ case ControllerTypes.search:
91
+ default:
92
+ importPromise = import('./create/createSearchController');
93
+ break;
76
94
  }
95
+ // @ts-ignore - we know the config is correct, but complicated typing
77
96
  const creationFunc = (await importPromise).default;
78
97
  if (!this.controllers[config.id]) {
79
- this.controllers[config.id] = creationFunc({
98
+ window.searchspring.controller = window.searchspring.controller || {};
99
+ window.searchspring.controller[config.id] = this.controllers[config.id] = creationFunc({
100
+ mode: this.mode,
80
101
  url: deepmerge(this.config.url || {}, urlConfig || {}),
81
102
  controller: config,
82
103
  context: deepmerge(this.context || {}, context || {}),
@@ -95,15 +116,42 @@ export class Snap {
95
116
  }
96
117
  return this.controllers[config.id];
97
118
  };
119
+ this.handlers = {
120
+ error: (event) => {
121
+ try {
122
+ const { filename } = event;
123
+ if (filename.includes('snapui.searchspring.io') && this.tracker.track.error) {
124
+ const { colno, lineno, error: { stack }, message, timeStamp, } = event;
125
+ const userAgent = navigator.userAgent;
126
+ const href = window.location.href;
127
+ const beaconPayload = {
128
+ userAgent,
129
+ href,
130
+ filename,
131
+ stack,
132
+ message,
133
+ colno,
134
+ lineno,
135
+ timeStamp,
136
+ };
137
+ this.tracker.track.error(beaconPayload);
138
+ }
139
+ }
140
+ catch (e) {
141
+ // prevent error metrics from breaking the app
142
+ }
143
+ },
144
+ };
145
+ window.removeEventListener('error', this.handlers.error);
146
+ window.addEventListener('error', this.handlers.error);
98
147
  this.config = config;
99
- this.logger = services?.logger || new Logger('Snap Preact ');
100
148
  let globalContext = {};
101
149
  try {
102
150
  // get global context
103
151
  globalContext = getContext(['shopper', 'config', 'merchandising']);
104
152
  }
105
153
  catch (err) {
106
- this.logger.error('failed to find global context');
154
+ console.error('Snap failed to find global context');
107
155
  }
108
156
  // merge configs - but only merge plain objects
109
157
  this.config = deepmerge(this.config || {}, globalContext.config || {}, {
@@ -115,8 +163,9 @@ export class Snap {
115
163
  if ((!services?.client || !services?.tracker) && !this.config?.client?.globals?.siteId) {
116
164
  throw new Error(`Snap: config provided must contain a valid config.client.globals.siteId value`);
117
165
  }
118
- if (this.context.merchandising?.segments) {
119
- if (this.config.client.globals.merchandising) {
166
+ // segmented merchandising context -> client globals
167
+ if (this.config.client?.globals && this.context.merchandising?.segments) {
168
+ if (this.config.client.globals?.merchandising) {
120
169
  this.config.client.globals.merchandising.segments = deepmerge(this.config.client.globals.merchandising.segments, this.context.merchandising.segments);
121
170
  }
122
171
  else {
@@ -125,33 +174,73 @@ export class Snap {
125
174
  };
126
175
  }
127
176
  }
128
- this.client = services?.client || new Client(this.config.client.globals, this.config.client.config);
129
- this.tracker = services?.tracker || new Tracker(this.config.client.globals);
130
- this._controllerPromises = {};
131
- this._instantiatorPromises = {};
132
- this.controllers = {};
133
- // TODO environment switch using URL?
134
- this.logger.setMode(process.env.NODE_ENV);
135
- // log version
136
- this.logger.imageText({
137
- url: 'https://snapui.searchspring.io/favicon.svg',
138
- text: `[${version}]`,
139
- style: `color: ${this.logger.colors.indigo}; font-weight: bold;`,
140
- });
141
177
  try {
142
178
  const urlParams = url(window.location.href);
143
- const branchParam = urlParams.params?.query?.branch || cookies.get(BRANCH_COOKIE);
144
- if (branchParam && !document.querySelector(`script[${BRANCH_COOKIE}]`)) {
145
- // set a cookie or localstorage with branch
179
+ const branchOverride = urlParams?.params?.query?.branch || cookies.get(BRANCH_COOKIE);
180
+ /* app mode priority:
181
+ 1. node env
182
+ 2. config
183
+ 3. override via query param / cookie
184
+ */
185
+ // node env
186
+ if (process.env.NODE_ENV && Object.values(AppMode).includes(process.env.NODE_ENV)) {
187
+ this.mode = process.env.NODE_ENV;
188
+ }
189
+ // config
190
+ if (this.config.mode && Object.values(AppMode).includes(this.config.mode)) {
191
+ this.mode = this.config.mode;
192
+ }
193
+ // query param / cookiev override
194
+ if ((urlParams?.params?.query && 'dev' in urlParams.params.query) || !!cookies.get(DEV_COOKIE)) {
195
+ if (urlParams?.params.query?.dev == 'false' || urlParams?.params.query?.dev == '0') {
196
+ cookies.unset(DEV_COOKIE);
197
+ this.mode = AppMode.production;
198
+ }
199
+ else {
200
+ cookies.set(DEV_COOKIE, '1', 'Lax', 0);
201
+ this.mode = AppMode.development;
202
+ }
203
+ }
204
+ // client mode uses client config over snap config
205
+ if (this.config.client) {
206
+ this.config.client.config = this.config.client.config || {};
207
+ this.config.client.config.mode = this.config.client.config.mode || this.mode;
208
+ }
209
+ this.client = services?.client || new Client(this.config.client.globals, this.config.client.config);
210
+ this.tracker = services?.tracker || new Tracker(this.config.client.globals, { framework: 'preact' });
211
+ this.logger = services?.logger || new Logger({ prefix: 'Snap Preact ', mode: this.mode });
212
+ // check for tracking attribution in URL ?ss_attribution=type:id
213
+ const sessionAttribution = window.sessionStorage?.getItem(SESSION_ATTRIBUTION);
214
+ if (urlParams?.params?.query?.ss_attribution) {
215
+ const attribution = urlParams.params.query.ss_attribution.split(':');
216
+ const [type, id] = attribution;
217
+ if (type && id) {
218
+ this.tracker.updateContext('attribution', { type, id });
219
+ }
220
+ // save to session storage
221
+ window.sessionStorage?.setItem(SESSION_ATTRIBUTION, urlParams.params.query.ss_attribution);
222
+ }
223
+ else if (sessionAttribution) {
224
+ const [type, id] = sessionAttribution.split(':');
225
+ if (type && id) {
226
+ this.tracker.updateContext('attribution', { type, id });
227
+ }
228
+ }
229
+ // log version
230
+ this.logger.imageText({
231
+ url: 'https://snapui.searchspring.io/favicon.svg',
232
+ text: `[${version}]`,
233
+ style: `color: ${this.logger.colors.indigo}; font-weight: bold;`,
234
+ });
235
+ if (branchOverride && !document.querySelector(`script[${BRANCH_COOKIE}]`)) {
236
+ this.logger.warn(`...loading build... '${branchOverride}'`);
237
+ // set a cookie with branch
146
238
  if (featureFlags.cookies) {
147
- cookies.set(BRANCH_COOKIE, branchParam, 'Lax', 3600000); // 1 hour
148
- cookies.set(SS_DEV_COOKIE, '1', 'Lax', 0);
239
+ cookies.set(BRANCH_COOKIE, branchOverride, 'Lax', 3600000); // 1 hour
149
240
  }
150
241
  else {
151
242
  this.logger.warn('Cookies are not supported/enabled by this browser, branch overrides will not persist!');
152
243
  }
153
- this.logger.setMode(LogMode.DEVELOPMENT);
154
- this.logger.warn(`...loading build... '${branchParam}'`);
155
244
  // get the path and siteId from the current bundle script in case its not the same as the client config
156
245
  let path = `https://snapui.searchspring.io/${this.config.client.globals.siteId}/`;
157
246
  const script = document.querySelector('script[src*="//snapui.searchspring.io"]');
@@ -163,10 +252,9 @@ export class Snap {
163
252
  }
164
253
  // append script with new branch in path
165
254
  const branchScript = document.createElement('script');
166
- const src = `${path}${branchParam}/bundle.js`;
255
+ const src = `${path}${branchOverride}/bundle.js`;
167
256
  branchScript.src = src;
168
- branchScript.setAttribute(BRANCH_COOKIE, branchParam);
169
- document.head.appendChild(branchScript);
257
+ branchScript.setAttribute(BRANCH_COOKIE, branchOverride);
170
258
  new DomTargeter([
171
259
  {
172
260
  selector: 'body',
@@ -180,32 +268,43 @@ export class Snap {
180
268
  },
181
269
  },
182
270
  ], async (target, elem) => {
183
- let bundleDetails, error;
271
+ const props = {};
184
272
  try {
185
273
  const getBundleDetails = (await import('./getBundleDetails/getBundleDetails')).getBundleDetails;
186
- bundleDetails = await getBundleDetails(src);
274
+ props.details = await getBundleDetails(src);
187
275
  }
188
276
  catch (err) {
189
- error = err;
277
+ props.error = err;
190
278
  }
191
279
  const BranchOverride = (await import('./components/BranchOverride')).BranchOverride;
192
- render(_jsx(BranchOverride, { name: branchParam, details: bundleDetails, error: error, onRemoveClick: () => {
280
+ render(_jsx(BranchOverride, { ...props, name: branchOverride, onRemoveClick: () => {
193
281
  cookies.unset(BRANCH_COOKIE);
194
282
  const urlState = url(window.location.href);
195
- delete urlState.params.query['branch'];
196
- window.location.href = urlState.url();
283
+ delete urlState?.params.query['branch'];
284
+ const newUrl = urlState?.url();
285
+ if (newUrl && newUrl != window.location.href) {
286
+ window.location.href = newUrl;
287
+ }
288
+ else {
289
+ window.location.reload();
290
+ }
197
291
  } }), elem);
292
+ // reset the global searchspring object
293
+ delete window.searchspring;
294
+ document.head.appendChild(branchScript);
198
295
  });
199
296
  // prevent further instantiation of config
200
297
  return;
201
298
  }
202
299
  }
203
- catch (e) { }
204
- if (window.searchspring) {
205
- window.searchspring.context = this.context;
206
- if (this.client)
207
- window.searchspring.client = this.client;
300
+ catch (e) {
301
+ this.logger.error(e);
208
302
  }
303
+ // bind to window global
304
+ window.searchspring = window.searchspring || {};
305
+ window.searchspring.context = this.context;
306
+ if (this.client)
307
+ window.searchspring.client = this.client;
209
308
  // autotrack shopper id from the context
210
309
  if (this.context?.shopper?.id) {
211
310
  this.tracker.track.shopper.login({
@@ -216,7 +315,7 @@ export class Snap {
216
315
  if (this.context?.shopper?.cart) {
217
316
  const cart = this.context.shopper.cart;
218
317
  if (Array.isArray(cart)) {
219
- const cartItems = cart.filter((item) => item?.sku || item?.childSku).map((item) => (item?.sku || item?.childSku).trim());
318
+ const cartItems = cart.filter((item) => item?.sku || item?.childSku).map((item) => (item?.sku || item?.childSku || '').trim());
220
319
  this.tracker.cookies.cart.set(cartItems);
221
320
  }
222
321
  }
@@ -225,7 +324,12 @@ export class Snap {
225
324
  case 'search': {
226
325
  this.config.controllers[type].forEach((controller, index) => {
227
326
  try {
327
+ if (typeof this._controllerPromises[controller.config.id] != 'undefined') {
328
+ this.logger.error(`Controller with id '${controller.config.id}' is already defined`);
329
+ return;
330
+ }
228
331
  const cntrlr = createSearchController({
332
+ mode: this.mode,
229
333
  url: deepmerge(this.config.url || {}, controller.url || {}),
230
334
  controller: controller.config,
231
335
  context: deepmerge(this.context || {}, controller.context || {}),
@@ -238,7 +342,8 @@ export class Snap {
238
342
  logger: controller.services?.logger,
239
343
  tracker: controller.services?.tracker || this.tracker,
240
344
  });
241
- this.controllers[cntrlr.config.id] = cntrlr;
345
+ window.searchspring.controller = window.searchspring.controller || {};
346
+ window.searchspring.controller[cntrlr.config.id] = this.controllers[cntrlr.config.id] = cntrlr;
242
347
  this._controllerPromises[cntrlr.config.id] = new Promise((resolve) => resolve(cntrlr));
243
348
  let searched = false;
244
349
  const runSearch = () => {
@@ -272,7 +377,7 @@ export class Snap {
272
377
  runSearch();
273
378
  }
274
379
  cntrlr.createTargeter({ controller: cntrlr, ...target }, async (target, elem, originalElem) => {
275
- if (target.skeleton) {
380
+ if (target && target.skeleton && elem) {
276
381
  const Skeleton = await target.skeleton();
277
382
  setTimeout(() => {
278
383
  render(_jsx(Skeleton, {}), elem);
@@ -290,6 +395,10 @@ export class Snap {
290
395
  }
291
396
  case 'autocomplete': {
292
397
  this.config.controllers[type].forEach((controller, index) => {
398
+ if (typeof this._controllerPromises[controller.config.id] != 'undefined') {
399
+ this.logger.error(`Controller with id '${controller.config.id}' is already defined`);
400
+ return;
401
+ }
293
402
  this._controllerPromises[controller.config.id] = new Promise((resolve) => {
294
403
  try {
295
404
  let bound = false;
@@ -315,8 +424,9 @@ export class Snap {
315
424
  }
316
425
  };
317
426
  if (!controller?.targeters || controller?.targeters.length === 0) {
318
- this.createController(ControllerTypes.autocomplete, controller.config, controller.services, controller.url, controller.context, (cntrlr) => {
319
- resolve(cntrlr);
427
+ this._createController(ControllerTypes.autocomplete, controller.config, controller.services, controller.url, controller.context, (cntrlr) => {
428
+ if (cntrlr)
429
+ resolve(cntrlr);
320
430
  });
321
431
  }
322
432
  controller?.targeters?.forEach((target, target_index) => {
@@ -342,8 +452,9 @@ export class Snap {
342
452
  ...target,
343
453
  },
344
454
  ], async (target, elem, originalElem) => {
345
- const cntrlr = await this.createController(ControllerTypes.autocomplete, controller.config, controller.services, controller.url, controller.context, (cntrlr) => {
346
- resolve(cntrlr);
455
+ const cntrlr = await this._createController(ControllerTypes.autocomplete, controller.config, controller.services, controller.url, controller.context, (cntrlr) => {
456
+ if (cntrlr)
457
+ resolve(cntrlr);
347
458
  });
348
459
  runBind();
349
460
  targetFunction({ controller: cntrlr, ...target }, elem, originalElem);
@@ -360,6 +471,10 @@ export class Snap {
360
471
  }
361
472
  case 'finder': {
362
473
  this.config.controllers[type].forEach((controller, index) => {
474
+ if (typeof this._controllerPromises[controller.config.id] != 'undefined') {
475
+ this.logger.error(`Controller with id '${controller.config.id}' is already defined`);
476
+ return;
477
+ }
363
478
  this._controllerPromises[controller.config.id] = new Promise((resolve) => {
364
479
  try {
365
480
  let searched = false;
@@ -383,8 +498,9 @@ export class Snap {
383
498
  }
384
499
  };
385
500
  if (!controller?.targeters || controller?.targeters.length === 0) {
386
- this.createController(ControllerTypes.finder, controller.config, controller.services, controller.url, controller.context, (cntrlr) => {
387
- resolve(cntrlr);
501
+ this._createController(ControllerTypes.finder, controller.config, controller.services, controller.url, controller.context, (cntrlr) => {
502
+ if (cntrlr)
503
+ resolve(cntrlr);
388
504
  });
389
505
  }
390
506
  controller?.targeters?.forEach((target, target_index) => {
@@ -395,8 +511,9 @@ export class Snap {
395
511
  throw new Error(`Targets at index ${target_index} missing component value (Component).`);
396
512
  }
397
513
  const targeter = new DomTargeter([{ ...target }], async (target, elem, originalElem) => {
398
- const cntrlr = await this.createController(ControllerTypes.finder, controller.config, controller.services, controller.url, controller.context, (cntrlr) => {
399
- resolve(cntrlr);
514
+ const cntrlr = await this._createController(ControllerTypes.finder, controller.config, controller.services, controller.url, controller.context, (cntrlr) => {
515
+ if (cntrlr)
516
+ resolve(cntrlr);
400
517
  });
401
518
  runSearch();
402
519
  targetFunction({ controller: cntrlr, ...target }, elem, originalElem);
@@ -413,6 +530,10 @@ export class Snap {
413
530
  }
414
531
  case 'recommendation': {
415
532
  this.config.controllers[type].forEach((controller, index) => {
533
+ if (typeof this._controllerPromises[controller.config.id] != 'undefined') {
534
+ this.logger.error(`Controller with id '${controller.config.id}' is already defined`);
535
+ return;
536
+ }
416
537
  this._controllerPromises[controller.config.id] = new Promise((resolve) => {
417
538
  try {
418
539
  let searched = false;
@@ -436,8 +557,9 @@ export class Snap {
436
557
  }
437
558
  };
438
559
  if (!controller?.targeters || controller?.targeters.length === 0) {
439
- this.createController(ControllerTypes.recommendation, controller.config, controller.services, controller.url, controller.context, (cntrlr) => {
440
- resolve(cntrlr);
560
+ this._createController(ControllerTypes.recommendation, controller.config, controller.services, controller.url, controller.context, (cntrlr) => {
561
+ if (cntrlr)
562
+ resolve(cntrlr);
441
563
  });
442
564
  }
443
565
  controller?.targeters?.forEach((target, target_index) => {
@@ -448,8 +570,9 @@ export class Snap {
448
570
  throw new Error(`Targets at index ${target_index} missing component value (Component).`);
449
571
  }
450
572
  const targeter = new DomTargeter([{ ...target }], async (target, elem, originalElem) => {
451
- const cntrlr = await this.createController(ControllerTypes.recommendation, controller.config, controller.services, controller.url, controller.context, (cntrlr) => {
452
- resolve(cntrlr);
573
+ const cntrlr = await this._createController(ControllerTypes.recommendation, controller.config, controller.services, controller.url, controller.context, (cntrlr) => {
574
+ if (cntrlr)
575
+ resolve(cntrlr);
453
576
  });
454
577
  runSearch();
455
578
  targetFunction({ controller: cntrlr, ...target }, elem, originalElem);
@@ -469,6 +592,7 @@ export class Snap {
469
592
  if (this.config?.instantiators?.recommendation) {
470
593
  try {
471
594
  this._instantiatorPromises.recommendation = import('./Instantiators/RecommendationInstantiator').then(({ RecommendationInstantiator }) => {
595
+ this.config.instantiators.recommendation.mode = this.config.instantiators.recommendation.mode || this.mode;
472
596
  return new RecommendationInstantiator(this.config.instantiators.recommendation, {
473
597
  client: this.client,
474
598
  tracker: this.tracker,
@@ -1,5 +1,5 @@
1
1
  import { AutocompleteController } from '@searchspring/snap-controller';
2
2
  import type { SnapControllerServices, SnapAutocompleteControllerConfig } from '../types';
3
- declare const _default: (config: SnapAutocompleteControllerConfig, services?: SnapControllerServices) => AutocompleteController;
3
+ declare const _default: (config: SnapAutocompleteControllerConfig, services?: SnapControllerServices | undefined) => AutocompleteController;
4
4
  export default _default;
5
5
  //# sourceMappingURL=createAutocompleteController.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createAutocompleteController.d.ts","sourceRoot":"","sources":["../../../src/create/createAutocompleteController.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AASvE,OAAO,KAAK,EAAE,sBAAsB,EAAE,gCAAgC,EAAE,MAAM,UAAU,CAAC;iCAIjE,gCAAgC,aAAa,sBAAsB,KAAG,sBAAsB;AAApH,wBAkBE"}
1
+ {"version":3,"file":"createAutocompleteController.d.ts","sourceRoot":"","sources":["../../../src/create/createAutocompleteController.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AASvE,OAAO,KAAK,EAAE,sBAAsB,EAAE,gCAAgC,EAAE,MAAM,UAAU,CAAC;iCAIjE,gCAAgC,oDAAsC,sBAAsB;AAApH,wBAwBE"}
@@ -10,13 +10,18 @@ import { Tracker } from '@searchspring/snap-tracker';
10
10
  configureMobx({ useProxies: 'never' });
11
11
  export default (config, services) => {
12
12
  const urlManager = (services?.urlManager || new UrlManager(new UrlTranslator(config.url), reactLinker)).detach();
13
+ // set client mode
14
+ if (config.mode && config.client) {
15
+ config.client.config = config.client.config || {};
16
+ config.client.config.mode = config.mode;
17
+ }
13
18
  const cntrlr = new AutocompleteController(config.controller, {
14
19
  client: services?.client || new Client(config.client.globals, config.client.config),
15
20
  store: services?.store || new AutocompleteStore(config.controller, { urlManager }),
16
21
  urlManager,
17
22
  eventManager: services?.eventManager || new EventManager(),
18
23
  profiler: services?.profiler || new Profiler(),
19
- logger: services?.logger || new Logger(),
24
+ logger: services?.logger || new Logger({ mode: config.mode }),
20
25
  tracker: services?.tracker || new Tracker(config.client.globals),
21
26
  }, config.context);
22
27
  return cntrlr;
@@ -1,5 +1,5 @@
1
1
  import { FinderController } from '@searchspring/snap-controller';
2
2
  import type { SnapControllerServices, SnapFinderControllerConfig } from '../types';
3
- declare const _default: (config: SnapFinderControllerConfig, services?: SnapControllerServices) => FinderController;
3
+ declare const _default: (config: SnapFinderControllerConfig, services?: SnapControllerServices | undefined) => FinderController;
4
4
  export default _default;
5
5
  //# sourceMappingURL=createFinderController.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createFinderController.d.ts","sourceRoot":"","sources":["../../../src/create/createFinderController.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AASjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;iCAI3D,0BAA0B,aAAa,sBAAsB,KAAG,gBAAgB;AAAxG,wBAkBE"}
1
+ {"version":3,"file":"createFinderController.d.ts","sourceRoot":"","sources":["../../../src/create/createFinderController.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AASjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;iCAI3D,0BAA0B,oDAAsC,gBAAgB;AAAxG,wBAwBE"}
@@ -10,13 +10,18 @@ import { Tracker } from '@searchspring/snap-tracker';
10
10
  configureMobx({ useProxies: 'never' });
11
11
  export default (config, services) => {
12
12
  const urlManager = (services?.urlManager || new UrlManager(new UrlTranslator(config.url), reactLinker)).detach(true);
13
+ // set client mode
14
+ if (config.mode && config.client) {
15
+ config.client.config = config.client.config || {};
16
+ config.client.config.mode = config.mode;
17
+ }
13
18
  const cntrlr = new FinderController(config.controller, {
14
19
  client: services?.client || new Client(config.client.globals, config.client.config),
15
20
  store: services?.store || new FinderStore(config.controller, { urlManager }),
16
21
  urlManager,
17
22
  eventManager: services?.eventManager || new EventManager(),
18
23
  profiler: services?.profiler || new Profiler(),
19
- logger: services?.logger || new Logger(),
24
+ logger: services?.logger || new Logger({ mode: config.mode }),
20
25
  tracker: services?.tracker || new Tracker(config.client.globals),
21
26
  }, config.context);
22
27
  return cntrlr;
@@ -1,5 +1,5 @@
1
1
  import { RecommendationController } from '@searchspring/snap-controller';
2
2
  import type { SnapControllerServices, SnapRecommendationControllerConfig } from '../types';
3
- declare const _default: (config: SnapRecommendationControllerConfig, services?: SnapControllerServices) => RecommendationController;
3
+ declare const _default: (config: SnapRecommendationControllerConfig, services?: SnapControllerServices | undefined) => RecommendationController;
4
4
  export default _default;
5
5
  //# sourceMappingURL=createRecommendationController.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createRecommendationController.d.ts","sourceRoot":"","sources":["../../../src/create/createRecommendationController.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AASzE,OAAO,KAAK,EAAE,sBAAsB,EAAE,kCAAkC,EAAE,MAAM,UAAU,CAAC;iCAInE,kCAAkC,aAAa,sBAAsB,KAAG,wBAAwB;AAAxH,wBAiBE"}
1
+ {"version":3,"file":"createRecommendationController.d.ts","sourceRoot":"","sources":["../../../src/create/createRecommendationController.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AASzE,OAAO,KAAK,EAAE,sBAAsB,EAAE,kCAAkC,EAAE,MAAM,UAAU,CAAC;iCAInE,kCAAkC,oDAAsC,wBAAwB;AAAxH,wBAwBE"}
@@ -10,13 +10,18 @@ import { Tracker } from '@searchspring/snap-tracker';
10
10
  configureMobx({ useProxies: 'never' });
11
11
  export default (config, services) => {
12
12
  const urlManager = (services?.urlManager || new UrlManager(new UrlTranslator(config.url), reactLinker)).detach(true);
13
+ // set client mode
14
+ if (config.mode && config.client) {
15
+ config.client.config = config.client.config || {};
16
+ config.client.config.mode = config.mode;
17
+ }
13
18
  const cntrlr = new RecommendationController(config.controller, {
14
19
  client: services?.client || new Client(config.client.globals, config.client.config),
15
20
  store: services?.store || new RecommendationStore(config.controller, { urlManager }),
16
21
  urlManager,
17
22
  eventManager: services?.eventManager || new EventManager(),
18
23
  profiler: services?.profiler || new Profiler(),
19
- logger: services?.logger || new Logger(),
24
+ logger: services?.logger || new Logger({ mode: config.mode }),
20
25
  tracker: services?.tracker || new Tracker(config.client.globals),
21
26
  }, config.context);
22
27
  return cntrlr;
@@ -1,5 +1,5 @@
1
1
  import { SearchController } from '@searchspring/snap-controller';
2
2
  import type { SnapControllerServices, SnapSearchControllerConfig } from '../types';
3
- declare const _default: (config: SnapSearchControllerConfig, services?: SnapControllerServices) => SearchController;
3
+ declare const _default: (config: SnapSearchControllerConfig, services?: SnapControllerServices | undefined) => SearchController;
4
4
  export default _default;
5
5
  //# sourceMappingURL=createSearchController.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createSearchController.d.ts","sourceRoot":"","sources":["../../../src/create/createSearchController.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAQjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;iCAI3D,0BAA0B,aAAa,sBAAsB,KAAG,gBAAgB;AAAxG,wBAkBE"}
1
+ {"version":3,"file":"createSearchController.d.ts","sourceRoot":"","sources":["../../../src/create/createSearchController.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAQjE,OAAO,KAAK,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;iCAI3D,0BAA0B,oDAAsC,gBAAgB;AAAxG,wBAwBE"}
@@ -10,13 +10,18 @@ import { Tracker } from '@searchspring/snap-tracker';
10
10
  configureMobx({ useProxies: 'never' });
11
11
  export default (config, services) => {
12
12
  const urlManager = services?.urlManager || new UrlManager(new UrlTranslator(config.url), reactLinker);
13
+ // set client mode
14
+ if (config.mode && config.client) {
15
+ config.client.config = config.client.config || {};
16
+ config.client.config.mode = config.mode;
17
+ }
13
18
  const cntrlr = new SearchController(config.controller, {
14
19
  client: services?.client || new Client(config.client.globals, config.client.config),
15
20
  store: services?.store || new SearchStore(config.controller, { urlManager }),
16
21
  urlManager,
17
22
  eventManager: services?.eventManager || new EventManager(),
18
23
  profiler: services?.profiler || new Profiler(),
19
- logger: services?.logger || new Logger(),
24
+ logger: services?.logger || new Logger({ mode: config.mode }),
20
25
  tracker: services?.tracker || new Tracker(config.client.globals),
21
26
  }, config.context);
22
27
  return cntrlr;
@@ -1 +1 @@
1
- {"version":3,"file":"getBundleDetails.d.ts","sourceRoot":"","sources":["../../../src/getBundleDetails/getBundleDetails.ts"],"names":[],"mappings":"AAAA,aAAK,aAAa,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAe,MAAM,KAAG,QAAQ,aAAa,CAuBzE,CAAC"}
1
+ {"version":3,"file":"getBundleDetails.d.ts","sourceRoot":"","sources":["../../../src/getBundleDetails/getBundleDetails.ts"],"names":[],"mappings":"AAAA,aAAK,aAAa,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAe,MAAM,KAAG,QAAQ,aAAa,CAyBzE,CAAC"}
@@ -5,10 +5,11 @@ export const getBundleDetails = async (url) => {
5
5
  request.onreadystatechange = () => {
6
6
  if (request.readyState === request.DONE) {
7
7
  const status = request.status;
8
- if (status === 0 || (status >= 200 && status < 400)) {
8
+ const lastModified = request.getResponseHeader('Last-Modified');
9
+ if ((lastModified && status === 0) || (status >= 200 && status < 400)) {
9
10
  resolve({
10
11
  url,
11
- lastModified: request.getResponseHeader('Last-Modified').split(',')[1].trim(),
12
+ lastModified: lastModified.split(',')[1].trim(),
12
13
  });
13
14
  }
14
15
  else {