swell-js 5.9.3 → 5.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,7 +13,7 @@ Universal JavaScript client for Swell's Frontend API, providing client-safe acce
13
13
 
14
14
  **About Swell**
15
15
 
16
- [Swell](https://www.swell.is) is a customizable, API-first platform for powering modern B2C/B2B shopping experiences and marketplaces. Build and connect anything using your favorite technologies, and provide admins with an easy to use dashboard.
16
+ [Swell](https://www.swell.is) is an ecommerce platform with subscriptions and B2B pricing built in. Run your store on a Swell theme, or [go headless](https://www.swell.is/headless-commerce) with its APIs.
17
17
 
18
18
  ## Documentation
19
19
 
package/dist/api.mjs CHANGED
@@ -1,21 +1,21 @@
1
- import methods$5 from './card.mjs';
1
+ import methods$6 from './card.mjs';
2
2
  import { getCookie, setCookie } from './cookie.mjs';
3
- import methods$4 from './cache.mjs';
4
- import methods$3 from './cart.mjs';
5
- import methods$6 from './account.mjs';
6
- import { j as isFunction, l as loadScript, e as isObject, v as vaultRequest, k as isEmpty, d as defaultMethods, x as trimEnd, y as utils, z as trimStart, A as trimBoth, b as toSnake, o as base64Encode, i as isServer, t as toCamel } from './index.76eb52e5.mjs';
3
+ import methods$5 from './cache.mjs';
4
+ import methods$4 from './cart.mjs';
5
+ import methods$7 from './account.mjs';
6
+ import { j as isFunction, l as loadScript, e as isObject, v as vaultRequest, k as isEmpty, d as defaultMethods, i as isServer, x as trimEnd, y as utils, z as trimStart, A as trimBoth, b as toSnake, o as base64Encode, t as toCamel } from './index.76eb52e5.mjs';
7
7
  import { Q as stringifyQuery } from './find.ba551a2e.mjs';
8
8
  import 'deepmerge';
9
9
  import 'fast-case';
10
- import methods$7 from './products.mjs';
11
- import methods$8 from './categories.mjs';
12
- import methods$9 from './attributes.mjs';
13
- import methods$a from './subscriptions.mjs';
14
- import methods$b from './content.mjs';
15
- import methods$c from './settings.mjs';
10
+ import methods$8 from './products.mjs';
11
+ import methods$9 from './categories.mjs';
12
+ import methods$a from './attributes.mjs';
13
+ import methods$b from './subscriptions.mjs';
14
+ import methods$c from './content.mjs';
15
+ import methods$d from './settings.mjs';
16
16
  import PaymentController from './payment.mjs';
17
- import methods$d from './locale.mjs';
18
- import methods$e, { isCurrencyCode } from './currency.mjs';
17
+ import methods$e from './locale.mjs';
18
+ import methods$f, { isCurrencyCode } from './currency.mjs';
19
19
  import './round.c68b8be5.mjs';
20
20
 
21
21
  class AppComponent {
@@ -81,7 +81,7 @@ class AppComponent {
81
81
  }
82
82
 
83
83
  async updateCart(data) {
84
- const cart = await methods$3(this.api, this.options).update(data);
84
+ const cart = await methods$4(this.api, this.options).update(data);
85
85
 
86
86
  this.setProps({ cart });
87
87
  }
@@ -317,7 +317,7 @@ class AppController {
317
317
  }
318
318
  }
319
319
 
320
- function methods$2(api) {
320
+ function methods$3(api) {
321
321
  const { get, list } = defaultMethods(api, '/invoices', ['list', 'get']);
322
322
  return {
323
323
  get: (id, ...args) => {
@@ -328,7 +328,7 @@ function methods$2(api) {
328
328
  };
329
329
  }
330
330
 
331
- function methods$1(api, opt) {
331
+ function methods$2(api, opt) {
332
332
  return {
333
333
  /**
334
334
  * Get the decoded session as an object of session values
@@ -355,7 +355,7 @@ function methods$1(api, opt) {
355
355
  };
356
356
  }
357
357
 
358
- function methods(api, _opt) {
358
+ function methods$1(api, _opt) {
359
359
  return {
360
360
  /**
361
361
  * Make a request to an app function and greceiveet a response
@@ -424,6 +424,85 @@ function methods(api, _opt) {
424
424
  };
425
425
  }
426
426
 
427
+ const PURPOSES = ['analytics', 'marketing', 'sale_of_data'];
428
+
429
+ /**
430
+ * The page the shopper landed on, which the consent call sends along: the
431
+ * first URL of this document and its referrer. Read when needed, so nothing
432
+ * is kept before the shopper answers.
433
+ *
434
+ * @returns {{url: string, referrer: string} | undefined}
435
+ */
436
+ function getLanding() {
437
+ if (isServer()) {
438
+ return undefined;
439
+ }
440
+
441
+ let url = window.location.href;
442
+
443
+ try {
444
+ const [navigation] = window.performance.getEntriesByType('navigation');
445
+ if (navigation?.name) {
446
+ url = navigation.name;
447
+ }
448
+ } catch {
449
+ // Navigation timing is not available
450
+ }
451
+
452
+ return { url, referrer: window.document.referrer || '' };
453
+ }
454
+
455
+ function methods(api) {
456
+ // Calls run one after another. Consent tools fire several events for one
457
+ // answer, and the first call may set the visitor cookie that the next one
458
+ // has to send: in parallel, each would start a visitor of its own.
459
+ let queue = Promise.resolve();
460
+
461
+ return {
462
+ /**
463
+ * Records the shopper's consent for attribution, from the storefront's
464
+ * consent banner. Purposes left out stay as they are.
465
+ *
466
+ * Calls the storefront's own origin, so that Swell sees the page's
467
+ * cookies and sets the visitor cookie on the storefront's domain.
468
+ *
469
+ * @param {{analytics?: boolean, marketing?: boolean, sale_of_data?: boolean}} consent
470
+ */
471
+ set(consent = {}) {
472
+ if (isServer()) {
473
+ return Promise.reject(new Error('consent.set() runs in the browser'));
474
+ }
475
+
476
+ const data = {};
477
+
478
+ for (const purpose of PURPOSES) {
479
+ // camelCase clients pass saleOfData
480
+ const camel = purpose.replace(/_(\w)/g, (_m, c) => c.toUpperCase());
481
+ const value = consent[purpose] ?? consent[camel];
482
+
483
+ if (typeof value === 'boolean') {
484
+ data[purpose] = value;
485
+ }
486
+ }
487
+
488
+ const result = queue.then(() =>
489
+ api.request(
490
+ 'put',
491
+ '/session/consent',
492
+ { ...data, landing: getLanding() },
493
+ undefined,
494
+ { fullUrl: `${window.location.origin}/api/session/consent` },
495
+ ),
496
+ );
497
+
498
+ // A failed call doesn't hold back the next one
499
+ queue = result.catch(() => undefined);
500
+
501
+ return result;
502
+ },
503
+ };
504
+ }
505
+
427
506
  /**
428
507
  * @template {'snake' | 'camel'} C
429
508
  * @typedef {import('../types').InitOptions<C>} InitOptions<C>
@@ -455,7 +534,7 @@ function swell(initStore = undefined, initKey, initOptions = {}) {
455
534
  const api = {};
456
535
 
457
536
  Object.assign(api, {
458
- version: '5.9.3',
537
+ version: '5.9.4',
459
538
  options,
460
539
  request,
461
540
 
@@ -509,39 +588,41 @@ function swell(initStore = undefined, initKey, initOptions = {}) {
509
588
  return api.request('delete', url, data);
510
589
  },
511
590
 
512
- cache: methods$4(options),
591
+ cache: methods$5(options),
513
592
 
514
- card: methods$5(options),
593
+ card: methods$6(options),
515
594
 
516
- cart: methods$3(api, options),
595
+ cart: methods$4(api, options),
517
596
 
518
- account: methods$6(api),
597
+ account: methods$7(api),
519
598
 
520
599
  app: new AppController(api, options),
521
600
 
522
- products: methods$7(api, options),
601
+ products: methods$8(api, options),
523
602
 
524
- categories: methods$8(api),
603
+ categories: methods$9(api),
525
604
 
526
- attributes: methods$9(api),
605
+ attributes: methods$a(api),
527
606
 
528
- subscriptions: methods$a(api),
607
+ subscriptions: methods$b(api),
529
608
 
530
- invoices: methods$2(api),
609
+ invoices: methods$3(api),
531
610
 
532
- content: methods$b(api, options),
611
+ content: methods$c(api, options),
533
612
 
534
- settings: methods$c(api, options),
613
+ settings: methods$d(api, options),
535
614
 
536
615
  payment: new PaymentController(api, options),
537
616
 
538
- locale: methods$d(api, options),
617
+ locale: methods$e(api, options),
618
+
619
+ currency: methods$f(api, options),
539
620
 
540
- currency: methods$e(api, options),
621
+ session: methods$2(api, options),
541
622
 
542
- session: methods$1(api, options),
623
+ functions: methods$1(api),
543
624
 
544
- functions: methods(api),
625
+ consent: methods(api),
545
626
 
546
627
  utils,
547
628
 
@@ -615,6 +696,13 @@ function swell(initStore = undefined, initKey, initOptions = {}) {
615
696
  reqHeaders['X-Session'] = session;
616
697
  }
617
698
 
699
+ // The storefront's visitor, for attribution: the cookie is on the
700
+ // storefront's domain and doesn't reach the store's API host
701
+ const visitor = allOptions.getCookie('swell-visitor');
702
+ if (visitor) {
703
+ reqHeaders['X-Visitor'] = visitor;
704
+ }
705
+
618
706
  if (locale) {
619
707
  reqHeaders['X-Locale'] = locale;
620
708
  }
package/dist/swell.cjs CHANGED
@@ -4966,7 +4966,7 @@ const utils = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
4966
4966
  vaultRequest
4967
4967
  }, Symbol.toStringTag, { value: 'Module' }));
4968
4968
 
4969
- function methods$e(options) {
4969
+ function methods$f(options) {
4970
4970
  return {
4971
4971
  async createToken(data) {
4972
4972
  let error = null;
@@ -5183,7 +5183,7 @@ function setCookie(name, value, options = {}) {
5183
5183
 
5184
5184
  const RECORD_TIMEOUT = 5000;
5185
5185
 
5186
- function methods$d(opt) {
5186
+ function methods$e(opt) {
5187
5187
  let VALUES = {
5188
5188
  /*
5189
5189
  [model]: {
@@ -5345,7 +5345,7 @@ function methods$d(opt) {
5345
5345
  };
5346
5346
  }
5347
5347
 
5348
- function methods$c(api) {
5348
+ function methods$d(api) {
5349
5349
  const { get, list } = defaultMethods(api, '/attributes', ['list', 'get']);
5350
5350
 
5351
5351
  return {
@@ -5357,7 +5357,7 @@ function methods$c(api) {
5357
5357
  };
5358
5358
  }
5359
5359
 
5360
- function methods$b(api, opt) {
5360
+ function methods$c(api, opt) {
5361
5361
  const { get, list } = defaultMethods(api, '/products', ['list', 'get']);
5362
5362
  return {
5363
5363
  get: (id, ...args) => {
@@ -5566,7 +5566,7 @@ function findPurchaseOption(product, purchaseOption) {
5566
5566
  }
5567
5567
 
5568
5568
  async function getFilterableAttributeFilters(api, products, options) {
5569
- const { results: filterableAttributes } = await methods$c(api).list({
5569
+ const { results: filterableAttributes } = await methods$d(api).list({
5570
5570
  filterable: true,
5571
5571
  });
5572
5572
 
@@ -5750,7 +5750,7 @@ function getPriceRange(products) {
5750
5750
  };
5751
5751
  }
5752
5752
 
5753
- function methods$a(api, options) {
5753
+ function methods$b(api, options) {
5754
5754
  return {
5755
5755
  state: null,
5756
5756
  order: null,
@@ -5935,7 +5935,7 @@ function methods$a(api, options) {
5935
5935
  };
5936
5936
  }
5937
5937
 
5938
- function methods$9(api) {
5938
+ function methods$a(api) {
5939
5939
  return {
5940
5940
  state: null,
5941
5941
 
@@ -6109,7 +6109,7 @@ class AppComponent {
6109
6109
  }
6110
6110
 
6111
6111
  async updateCart(data) {
6112
- const cart = await methods$a(this.api, this.options).update(data);
6112
+ const cart = await methods$b(this.api, this.options).update(data);
6113
6113
 
6114
6114
  this.setProps({ cart });
6115
6115
  }
@@ -6345,7 +6345,7 @@ class AppController {
6345
6345
  }
6346
6346
  }
6347
6347
 
6348
- function methods$8(api) {
6348
+ function methods$9(api) {
6349
6349
  const { get, list } = defaultMethods(api, '/categories', ['list', 'get']);
6350
6350
 
6351
6351
  return {
@@ -6357,7 +6357,7 @@ function methods$8(api) {
6357
6357
  };
6358
6358
  }
6359
6359
 
6360
- function methods$7(api) {
6360
+ function methods$8(api) {
6361
6361
  const { get, list } = defaultMethods(api, '/subscriptions', ['list', 'get']);
6362
6362
  return {
6363
6363
  get: (id, ...args) => {
@@ -6422,7 +6422,7 @@ function methods$7(api) {
6422
6422
  };
6423
6423
  }
6424
6424
 
6425
- function methods$6(api) {
6425
+ function methods$7(api) {
6426
6426
  const { get, list } = defaultMethods(api, '/invoices', ['list', 'get']);
6427
6427
  return {
6428
6428
  get: (id, ...args) => {
@@ -6433,7 +6433,7 @@ function methods$6(api) {
6433
6433
  };
6434
6434
  }
6435
6435
 
6436
- function methods$5(api, opt) {
6436
+ function methods$6(api, opt) {
6437
6437
  return {
6438
6438
  get: (type, id, query) => {
6439
6439
  return api.cache.getFetch(`content_${type}`, id, () =>
@@ -6449,7 +6449,7 @@ function methods$5(api, opt) {
6449
6449
  };
6450
6450
  }
6451
6451
 
6452
- function methods$4(api, opt) {
6452
+ function methods$5(api, opt) {
6453
6453
  return {
6454
6454
  state: null,
6455
6455
  menuState: null,
@@ -6744,7 +6744,7 @@ function decodeLocaleValue(locale, values, key, configs, opt) {
6744
6744
  }
6745
6745
  }
6746
6746
 
6747
- function methods$3(api, opt) {
6747
+ function methods$4(api, opt) {
6748
6748
  return {
6749
6749
  /**
6750
6750
  * Get the decoded session as an object of session values
@@ -7132,7 +7132,7 @@ class Payment {
7132
7132
  * @returns {Promise<Cart>}
7133
7133
  */
7134
7134
  async getCart() {
7135
- const cart = await methods$a(this.api, this.options).get();
7135
+ const cart = await methods$b(this.api, this.options).get();
7136
7136
 
7137
7137
  if (!cart) {
7138
7138
  throw new Error('Cart not found');
@@ -7160,7 +7160,7 @@ class Payment {
7160
7160
  }
7161
7161
  }
7162
7162
 
7163
- const updatedCart = await methods$a(this.api, this.options).update(
7163
+ const updatedCart = await methods$b(this.api, this.options).update(
7164
7164
  updateData,
7165
7165
  );
7166
7166
 
@@ -7173,7 +7173,7 @@ class Payment {
7173
7173
  * @returns {Promise<object>}
7174
7174
  */
7175
7175
  async getSettings() {
7176
- return methods$4(this.api, this.options).get();
7176
+ return methods$5(this.api, this.options).get();
7177
7177
  }
7178
7178
 
7179
7179
  /**
@@ -12789,7 +12789,7 @@ class PaymentController {
12789
12789
  }
12790
12790
 
12791
12791
  async _getPaymentMethods() {
12792
- const paymentMethods = await methods$4(this.api, this.options).payments();
12792
+ const paymentMethods = await methods$5(this.api, this.options).payments();
12793
12793
 
12794
12794
  if (paymentMethods.error) {
12795
12795
  throw new Error(paymentMethods.error);
@@ -13019,7 +13019,7 @@ class PaymentController {
13019
13019
  }
13020
13020
  }
13021
13021
 
13022
- function methods$2(api, opt) {
13022
+ function methods$3(api, opt) {
13023
13023
  return {
13024
13024
  code: null,
13025
13025
  state: null,
@@ -13075,7 +13075,7 @@ function defaultLocale() {
13075
13075
  return typeof navigator === 'object' ? navigator.language : 'en-US';
13076
13076
  }
13077
13077
 
13078
- function methods$1(api, opt) {
13078
+ function methods$2(api, opt) {
13079
13079
  // Reads a store setting without ever returning a Promise. Until settings
13080
13080
  // load, this starts the request and reports `loaded: false`.
13081
13081
  function readSetting(path, def) {
@@ -13296,7 +13296,7 @@ function methods$1(api, opt) {
13296
13296
  };
13297
13297
  }
13298
13298
 
13299
- function methods(api, _opt) {
13299
+ function methods$1(api, _opt) {
13300
13300
  return {
13301
13301
  /**
13302
13302
  * Make a request to an app function and greceiveet a response
@@ -13365,6 +13365,85 @@ function methods(api, _opt) {
13365
13365
  };
13366
13366
  }
13367
13367
 
13368
+ const PURPOSES = ['analytics', 'marketing', 'sale_of_data'];
13369
+
13370
+ /**
13371
+ * The page the shopper landed on, which the consent call sends along: the
13372
+ * first URL of this document and its referrer. Read when needed, so nothing
13373
+ * is kept before the shopper answers.
13374
+ *
13375
+ * @returns {{url: string, referrer: string} | undefined}
13376
+ */
13377
+ function getLanding() {
13378
+ if (isServer()) {
13379
+ return undefined;
13380
+ }
13381
+
13382
+ let url = window.location.href;
13383
+
13384
+ try {
13385
+ const [navigation] = window.performance.getEntriesByType('navigation');
13386
+ if (navigation?.name) {
13387
+ url = navigation.name;
13388
+ }
13389
+ } catch {
13390
+ // Navigation timing is not available
13391
+ }
13392
+
13393
+ return { url, referrer: window.document.referrer || '' };
13394
+ }
13395
+
13396
+ function methods(api) {
13397
+ // Calls run one after another. Consent tools fire several events for one
13398
+ // answer, and the first call may set the visitor cookie that the next one
13399
+ // has to send: in parallel, each would start a visitor of its own.
13400
+ let queue = Promise.resolve();
13401
+
13402
+ return {
13403
+ /**
13404
+ * Records the shopper's consent for attribution, from the storefront's
13405
+ * consent banner. Purposes left out stay as they are.
13406
+ *
13407
+ * Calls the storefront's own origin, so that Swell sees the page's
13408
+ * cookies and sets the visitor cookie on the storefront's domain.
13409
+ *
13410
+ * @param {{analytics?: boolean, marketing?: boolean, sale_of_data?: boolean}} consent
13411
+ */
13412
+ set(consent = {}) {
13413
+ if (isServer()) {
13414
+ return Promise.reject(new Error('consent.set() runs in the browser'));
13415
+ }
13416
+
13417
+ const data = {};
13418
+
13419
+ for (const purpose of PURPOSES) {
13420
+ // camelCase clients pass saleOfData
13421
+ const camel = purpose.replace(/_(\w)/g, (_m, c) => c.toUpperCase());
13422
+ const value = consent[purpose] ?? consent[camel];
13423
+
13424
+ if (typeof value === 'boolean') {
13425
+ data[purpose] = value;
13426
+ }
13427
+ }
13428
+
13429
+ const result = queue.then(() =>
13430
+ api.request(
13431
+ 'put',
13432
+ '/session/consent',
13433
+ { ...data, landing: getLanding() },
13434
+ undefined,
13435
+ { fullUrl: `${window.location.origin}/api/session/consent` },
13436
+ ),
13437
+ );
13438
+
13439
+ // A failed call doesn't hold back the next one
13440
+ queue = result.catch(() => undefined);
13441
+
13442
+ return result;
13443
+ },
13444
+ };
13445
+ }
13446
+
13368
13447
  /**
13369
13448
  * @template {'snake' | 'camel'} C
13370
13449
  * @typedef {import('../types').InitOptions<C>} InitOptions<C>
@@ -13396,7 +13475,7 @@ function swell(initStore = undefined, initKey, initOptions = {}) {
13396
13475
  const api = {};
13397
13476
 
13398
13477
  Object.assign(api, {
13399
- version: '5.9.3',
13478
+ version: '5.9.4',
13400
13479
  options,
13401
13480
  request,
13402
13481
 
@@ -13450,39 +13529,41 @@ function swell(initStore = undefined, initKey, initOptions = {}) {
13450
13529
  return api.request('delete', url, data);
13451
13530
  },
13452
13531
 
13453
- cache: methods$d(options),
13532
+ cache: methods$e(options),
13454
13533
 
13455
- card: methods$e(options),
13534
+ card: methods$f(options),
13456
13535
 
13457
- cart: methods$a(api, options),
13536
+ cart: methods$b(api, options),
13458
13537
 
13459
- account: methods$9(api),
13538
+ account: methods$a(api),
13460
13539
 
13461
13540
  app: new AppController(api, options),
13462
13541
 
13463
- products: methods$b(api, options),
13542
+ products: methods$c(api, options),
13464
13543
 
13465
- categories: methods$8(api),
13544
+ categories: methods$9(api),
13466
13545
 
13467
- attributes: methods$c(api),
13546
+ attributes: methods$d(api),
13468
13547
 
13469
- subscriptions: methods$7(api),
13548
+ subscriptions: methods$8(api),
13470
13549
 
13471
- invoices: methods$6(api),
13550
+ invoices: methods$7(api),
13472
13551
 
13473
- content: methods$5(api, options),
13552
+ content: methods$6(api, options),
13474
13553
 
13475
- settings: methods$4(api, options),
13554
+ settings: methods$5(api, options),
13476
13555
 
13477
13556
  payment: new PaymentController(api, options),
13478
13557
 
13479
- locale: methods$2(api, options),
13558
+ locale: methods$3(api, options),
13480
13559
 
13481
- currency: methods$1(api, options),
13560
+ currency: methods$2(api, options),
13482
13561
 
13483
- session: methods$3(api, options),
13562
+ session: methods$4(api, options),
13484
13563
 
13485
- functions: methods(api),
13564
+ functions: methods$1(api),
13565
+
13566
+ consent: methods(api),
13486
13567
 
13487
13568
  utils,
13488
13569
 
@@ -13556,6 +13637,13 @@ function swell(initStore = undefined, initKey, initOptions = {}) {
13556
13637
  reqHeaders['X-Session'] = session;
13557
13638
  }
13558
13639
 
13640
+ // The storefront's visitor, for attribution: the cookie is on the
13641
+ // storefront's domain and doesn't reach the store's API host
13642
+ const visitor = allOptions.getCookie('swell-visitor');
13643
+ if (visitor) {
13644
+ reqHeaders['X-Visitor'] = visitor;
13645
+ }
13646
+
13559
13647
  if (locale) {
13560
13648
  reqHeaders['X-Locale'] = locale;
13561
13649
  }