@scayle/storefront-core 7.38.0 → 7.39.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @scayle/storefront-core
2
2
 
3
+ ## 7.39.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add `sortable` option to `groupFilterableValuesByKey` filter helper
8
+ - Add `sortable` option to `getGroupedFilterableValues` to provide the ability for custom sorting implementation for specific filtersGroups
9
+ - Authentication RPC methods include status codes in error cases
10
+
11
+ ## 7.38.1
12
+
13
+ ### Patch Changes
14
+
15
+ - Fix potential empty user after login
16
+
3
17
  ## 7.38.0
4
18
 
5
19
  ### Minor Changes
package/README.md CHANGED
@@ -1,10 +1,24 @@
1
- # Storefront Core
2
-
3
- [![npm version][npm-version-src]][npm-version-href]
4
- [![npm downloads][npm-downloads-src]][npm-downloads-href]
5
- [![License][license-src]][license-href]
6
-
7
- This is a shared package for framework-independent components of the Storefront Core.
1
+ <div align="center">
2
+ <img src="https://cdn-prod.scayle.com/public/media/general/SCAYLE-Commerce-Engine-header.png" />
3
+ </div>
4
+
5
+ <div align="center">
6
+ <h1>SCAYLE Storefront Core</h1>
7
+ </div>
8
+
9
+ <div align="center">
10
+ <h4><a href="https://scayle.dev">Documentation</a> | <a href="https://www.scayle.com/">Website</a></h4>
11
+ </div>
12
+
13
+ <div align="center">
14
+ The SCAYLE <strong>Storefront Core SDK</strong> package contains framework-independent components of the Storefront Boilerplate.
15
+ </div>
16
+ <br/>
17
+ <div align="center">
18
+ <a href="https://www.npmjs.com/package/@scayle/storefront-core"><img src="https://img.shields.io/npm/v/@scayle/storefront-core/latest.svg?style=flat&colorB=007ec6" /></a>
19
+ <a href="https://www.npmjs.com/package/@scayle/storefront-core"><img src="https://img.shields.io/npm/dm/@scayle/storefront-core.svg?style=flat&colorB=007ec6" /></a>
20
+ <a href="https://www.npmjs.com/package/@scayle/storefront-core"><img src="https://img.shields.io/badge/license-MIT-blue.svg" /></a>
21
+ </div>
8
22
 
9
23
  ## Installation
10
24
 
@@ -19,11 +33,23 @@ yarn add @scayle/storefront-core
19
33
  npm install @scayle/storefront-core
20
34
  ```
21
35
 
22
- <!-- Badges -->
36
+ ## Getting Started
37
+
38
+ Visit the [Quickstart Guide](https://scayle.dev/en/dev/storefront-core/getting-started-sfb) to get started with Storefront Core.
39
+
40
+ Visit the [Docs](https://scayle.dev) to learn more about our system requirements.
41
+
42
+ ## What is SCAYLE?
43
+
44
+ [SCAYLE](https://scayle.com) is a full-featured e-commerce software solution that comes with flexible APIs. Within SCAYLE, you can manage all aspects of your shop, such as products, stocks, customers, and transactions.
45
+
46
+ Learn more about [Scayle’s architecture](https://scayle.dev/en/dev/getting-started/introduction) and [commerce modules](https://scayle.dev/en/dev/getting-started/introduction) in the Docs.
47
+
48
+ ## Other channels
49
+
50
+ - [LinkedIn](https://www.linkedin.com/company/scaylecommerce/)
51
+ - [Jobs](https://careers.smartrecruiters.com/ABOUTYOUGmbH/scayle)
52
+
53
+ ## License
23
54
 
24
- [npm-version-src]: https://img.shields.io/npm/v/@scayle/storefront-core/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
25
- [npm-version-href]: https://npmjs.com/package/@scayle/storefront-core
26
- [npm-downloads-src]: https://img.shields.io/npm/dm/@scayle/storefront-core.svg?style=flat&colorA=18181B&colorB=28CF8D
27
- [npm-downloads-href]: https://npmjs.com/package/@scayle/storefront-core
28
- [license-src]: https://img.shields.io/npm/l/@scayle/storefront-core.svg?style=flat&colorA=18181B&colorB=28CF8D
29
- [license-href]: https://npmjs.com/package/@scayle/storefront-core
55
+ Licensed under the [MIT](https://opensource.org/license/mit/)
@@ -50,16 +50,16 @@ class CustomerAPIClient {
50
50
  * When not logged in will return undefined.
51
51
  * @see https://scayle.dev/api/oauth/latest/fetch-authorized-payload
52
52
  */
53
- async getMe(shopId) {
53
+ async getMe(shopId, accessToken) {
54
54
  const response = await fetch(`${this.baseURL}/api/oauth/me`, {
55
55
  headers: {
56
56
  ...this.headers,
57
+ ...(accessToken ? {
58
+ Authorization: `Bearer ${accessToken}`
59
+ } : {}),
57
60
  "X-Shop-Id": shopId.toString()
58
61
  }
59
62
  });
60
- if (response.status === _httpStatus.HttpStatusCode.FORBIDDEN) {
61
- return void 0;
62
- }
63
63
  return this.handleResponse(response);
64
64
  }
65
65
  /**
@@ -49,7 +49,7 @@ export declare class CustomerAPIClient {
49
49
  * When not logged in will return undefined.
50
50
  * @see https://scayle.dev/api/oauth/latest/fetch-authorized-payload
51
51
  */
52
- getMe(shopId: number): Promise<ShopUser | undefined>;
52
+ getMe(shopId: number, accessToken?: string): Promise<ShopUser>;
53
53
  /**
54
54
  * Fetch a customer's order
55
55
  * @see https://scayle.dev/api/oauth/latest/fetch-order-by-id
@@ -44,16 +44,14 @@ export class CustomerAPIClient {
44
44
  * When not logged in will return undefined.
45
45
  * @see https://scayle.dev/api/oauth/latest/fetch-authorized-payload
46
46
  */
47
- async getMe(shopId) {
47
+ async getMe(shopId, accessToken) {
48
48
  const response = await fetch(`${this.baseURL}/api/oauth/me`, {
49
49
  headers: {
50
50
  ...this.headers,
51
+ ...accessToken ? { Authorization: `Bearer ${accessToken}` } : {},
51
52
  "X-Shop-Id": shopId.toString()
52
53
  }
53
54
  });
54
- if (response.status === HttpStatusCode.FORBIDDEN) {
55
- return void 0;
56
- }
57
55
  return this.handleResponse(response);
58
56
  }
59
57
  /**
@@ -34,19 +34,24 @@ const transformToWhereCondition = conditions => {
34
34
  return where;
35
35
  };
36
36
  exports.transformToWhereCondition = transformToWhereCondition;
37
- const groupFilterableValuesByKey = (filters, filterableKeys) => {
37
+ const groupFilterableValuesByKey = (filters, filterableKeys, {
38
+ sortable
39
+ } = {
40
+ sortable: true
41
+ }) => {
38
42
  const enabledFilters = filterableKeys ? filters.filter(filter => filterableKeys.includes(filter.slug)) : filters;
39
- const availableFilters = [];
40
- enabledFilters.forEach(filter => {
41
- ;
43
+ const transformedAvailableFilters = enabledFilters.reduce((availableFilters, filter) => {
42
44
  filter.values.forEach(value => {
43
- if (filter.type === "boolean" && value.name === false) {
45
+ const isBoolean = filter.type === _backbone.FilterTypes.BOOLEAN;
46
+ const hasNameInValue = ("name" in value);
47
+ if (isBoolean && hasNameInValue && value.name === false) {
44
48
  return;
45
49
  }
46
50
  const filterValue = _getFilterValueByType(value, filter.type);
51
+ const nameValue = hasNameInValue ? String(value.name) : "";
47
52
  availableFilters.push({
48
53
  key: filter.slug,
49
- displayName: filter.type === "boolean" ? filter.name : value.name,
54
+ displayName: isBoolean ? filter.name : nameValue,
50
55
  count: value.productCount,
51
56
  value: filterValue,
52
57
  whereCondition: {
@@ -54,8 +59,9 @@ const groupFilterableValuesByKey = (filters, filterableKeys) => {
54
59
  }
55
60
  });
56
61
  });
57
- });
58
- return availableFilters.sort((a, b) => a.displayName < b.displayName ? -1 : 1);
62
+ return availableFilters;
63
+ }, []);
64
+ return sortable ? transformedAvailableFilters.sort((a, b) => a.displayName.localeCompare(b.displayName)) : transformedAvailableFilters;
59
65
  };
60
66
  exports.groupFilterableValuesByKey = groupFilterableValuesByKey;
61
67
  const transformStateToFilters = state => {
@@ -99,10 +105,16 @@ const transformSortToFilter = sort => {
99
105
  return (0, _sortingHelper.getSortByValue)(sort);
100
106
  };
101
107
  exports.transformSortToFilter = transformSortToFilter;
102
- const getGroupedFilterableValues = (keys, filterableValues) => {
103
- return keys.reduce((acc, key) => {
104
- acc[key] = groupFilterableValuesByKey(filterableValues, [key]);
105
- return acc;
108
+ const getGroupedFilterableValues = (keys, filterableValues, {
109
+ sortable
110
+ } = {
111
+ sortable: true
112
+ }) => {
113
+ return keys.reduce((filtersGroup, key) => {
114
+ filtersGroup[key] = groupFilterableValuesByKey(filterableValues, [key], {
115
+ sortable
116
+ });
117
+ return filtersGroup;
106
118
  }, {});
107
119
  };
108
120
  exports.getGroupedFilterableValues = getGroupedFilterableValues;
@@ -25,7 +25,9 @@ export declare const transformToWhereCondition: (conditions: Record<string, any>
25
25
  * @param filters
26
26
  * @param filterableKeys
27
27
  */
28
- export declare const groupFilterableValuesByKey: (filters: ProductFilter[], filterableKeys?: Array<string>) => Array<TransformedFilter>;
28
+ export declare const groupFilterableValuesByKey: (filters: ProductFilter[], filterableKeys?: string[], { sortable }?: {
29
+ sortable: boolean;
30
+ }) => TransformedFilter[];
29
31
  export declare const transformStateToFilters: (state: {
30
32
  [key: string]: TransformedFilter[];
31
33
  }) => any;
@@ -63,7 +65,9 @@ export declare const transformSortToFilter: (sort: string) => {
63
65
  name: "reduction_asc";
64
66
  query: "reduction-asc";
65
67
  };
66
- export declare const getGroupedFilterableValues: (keys: Array<string>, filterableValues: ProductFilter[]) => Record<string, any>;
68
+ export declare const getGroupedFilterableValues: (keys: Array<string>, filterableValues: ProductFilter[], { sortable }?: {
69
+ sortable: boolean;
70
+ }) => Record<string, TransformedFilter[]>;
67
71
  export declare const isFilterActive: (filters: Record<string, any>, filterToCheck: TransformedFilter) => any;
68
72
  export declare const getFilterablePriceValue: (filterableValues: Record<string, any>, key?: 'min' | 'max') => any;
69
73
  export type SerializedFilter = Record<string, string>;
@@ -25,19 +25,20 @@ export const transformToWhereCondition = (conditions) => {
25
25
  });
26
26
  return where;
27
27
  };
28
- export const groupFilterableValuesByKey = (filters, filterableKeys) => {
28
+ export const groupFilterableValuesByKey = (filters, filterableKeys, { sortable } = { sortable: true }) => {
29
29
  const enabledFilters = filterableKeys ? filters.filter((filter) => filterableKeys.includes(filter.slug)) : filters;
30
- const availableFilters = [];
31
- enabledFilters.forEach((filter) => {
32
- ;
30
+ const transformedAvailableFilters = enabledFilters.reduce((availableFilters, filter) => {
33
31
  filter.values.forEach((value) => {
34
- if (filter.type === "boolean" && value.name === false) {
32
+ const isBoolean = filter.type === FilterTypes.BOOLEAN;
33
+ const hasNameInValue = "name" in value;
34
+ if (isBoolean && hasNameInValue && value.name === false) {
35
35
  return;
36
36
  }
37
37
  const filterValue = _getFilterValueByType(value, filter.type);
38
+ const nameValue = hasNameInValue ? String(value.name) : "";
38
39
  availableFilters.push({
39
40
  key: filter.slug,
40
- displayName: filter.type === "boolean" ? filter.name : value.name,
41
+ displayName: isBoolean ? filter.name : nameValue,
41
42
  count: value.productCount,
42
43
  value: filterValue,
43
44
  whereCondition: {
@@ -45,10 +46,11 @@ export const groupFilterableValuesByKey = (filters, filterableKeys) => {
45
46
  }
46
47
  });
47
48
  });
48
- });
49
- return availableFilters.sort(
50
- (a, b) => a.displayName < b.displayName ? -1 : 1
51
- );
49
+ return availableFilters;
50
+ }, []);
51
+ return sortable ? transformedAvailableFilters.sort(
52
+ (a, b) => a.displayName.localeCompare(b.displayName)
53
+ ) : transformedAvailableFilters;
52
54
  };
53
55
  export const transformStateToFilters = (state) => {
54
56
  return Object.values(state).reduce(
@@ -92,11 +94,16 @@ export const transformMinAndMaxPriceToFilter = (prices) => {
92
94
  export const transformSortToFilter = (sort) => {
93
95
  return getSortByValue(sort);
94
96
  };
95
- export const getGroupedFilterableValues = (keys, filterableValues) => {
96
- return keys.reduce((acc, key) => {
97
- acc[key] = groupFilterableValuesByKey(filterableValues, [key]);
98
- return acc;
99
- }, {});
97
+ export const getGroupedFilterableValues = (keys, filterableValues, { sortable } = { sortable: true }) => {
98
+ return keys.reduce(
99
+ (filtersGroup, key) => {
100
+ filtersGroup[key] = groupFilterableValuesByKey(filterableValues, [key], {
101
+ sortable
102
+ });
103
+ return filtersGroup;
104
+ },
105
+ {}
106
+ );
100
107
  };
101
108
  export const isFilterActive = (filters, filterToCheck) => {
102
109
  const key = filterToCheck.key;
@@ -8,6 +8,7 @@ var _errors = require("../../../errors/index.cjs");
8
8
  var _types = require("../../../types/index.cjs");
9
9
  var _constants = require("../../../constants/index.cjs");
10
10
  var _user = require("../../../utils/user.cjs");
11
+ var _response = require("../../../utils/response.cjs");
11
12
  const BAPI_ERROR_NAME = "BAPI ERROR";
12
13
  function getWithParams(params, context) {
13
14
  return params.with ?? context.withParams?.basket ?? _constants.MIN_WITH_PARAMS_BASKET;
@@ -145,7 +146,7 @@ const removeItemFromBasket = exports.removeItemFromBasket = async function remov
145
146
  });
146
147
  };
147
148
  const clearBasket = exports.clearBasket = async function clearBasket2(context) {
148
- const basket = await getBasket({}, context);
149
+ const basket = await (0, _response.unwrap)(getBasket({}, context));
149
150
  await Promise.all(basket.items.map(async item => {
150
151
  await removeItemFromBasket({
151
152
  itemKey: item.key
@@ -7,6 +7,7 @@ import {
7
7
  MIN_WITH_PARAMS_BASKET
8
8
  } from "../../../constants/index.mjs";
9
9
  import { mergeBaskets as mergeBasketFunction } from "../../../utils/user.mjs";
10
+ import { unwrap } from "../../../utils/response.mjs";
10
11
  const BAPI_ERROR_NAME = "BAPI ERROR";
11
12
  function getWithParams(params, context) {
12
13
  return params.with ?? context.withParams?.basket ?? MIN_WITH_PARAMS_BASKET;
@@ -147,7 +148,7 @@ export const removeItemFromBasket = async function removeItemFromBasket2(options
147
148
  });
148
149
  };
149
150
  export const clearBasket = async function clearBasket2(context) {
150
- const basket = await getBasket({}, context);
151
+ const basket = await unwrap(getBasket({}, context));
151
152
  await Promise.all(
152
153
  basket.items.map(async (item) => {
153
154
  await removeItemFromBasket({ itemKey: item.key }, context);
@@ -7,6 +7,7 @@ exports.getProductsCount = exports.getProductsByIds = exports.getProductsByCateg
7
7
  var _helpers = require("../../helpers/index.cjs");
8
8
  var _constants = require("../../constants/index.cjs");
9
9
  var _cache = require("../../cache/index.cjs");
10
+ var _response = require("../../utils/response.cjs");
10
11
  const MAX_PER_PAGE = 100;
11
12
  const getProductById = exports.getProductById = async function getProductById2(options, {
12
13
  bapiClient,
@@ -66,12 +67,12 @@ const getProductsByCategory = exports.getProductsByCategory = async function get
66
67
  }
67
68
  const {
68
69
  filters
69
- } = await getFilters({
70
+ } = await (0, _response.unwrap)(getFilters({
70
71
  category,
71
72
  includeSoldOut,
72
73
  where,
73
74
  includeSellableForFree
74
- }, context);
75
+ }, context));
75
76
  const {
76
77
  entities: products,
77
78
  pagination
@@ -127,13 +128,13 @@ const getFilters = exports.getFilters = async function getFilters2({
127
128
  result = await cached(bapiClient.categories.getByPath, {
128
129
  cacheKeyPrefix: `getByPath-categories-${category}`
129
130
  })((0, _helpers.splitAndRemoveEmpty)(category));
130
- allFiltersForCategory = await fetchAllFiltersForCategory({
131
+ allFiltersForCategory = await (0, _response.unwrap)(fetchAllFiltersForCategory({
131
132
  category: {
132
133
  id: result.id,
133
134
  slug: category
134
135
  },
135
136
  includedFilters
136
- }, context);
137
+ }, context));
137
138
  }
138
139
  const sanitizedAttributes = sanitizeAttributesForBAPI({
139
140
  attributes: where?.attributes || [],
@@ -1,6 +1,7 @@
1
1
  import { splitAndRemoveEmpty } from "../../helpers/index.mjs";
2
2
  import { MIN_WITH_PARAMS_PRODUCT } from "../../constants/index.mjs";
3
3
  import { MINUTE } from "../../cache/index.mjs";
4
+ import { unwrap } from "../../utils/response.mjs";
4
5
  const MAX_PER_PAGE = 100;
5
6
  export const getProductById = async function getProductById2(options, { bapiClient, cached, campaignKey, withParams }) {
6
7
  return await cached(bapiClient.products.getById, {
@@ -44,9 +45,11 @@ export const getProductsByCategory = async function getProductsByCategory2({
44
45
  cacheKeyPrefix: `getByPath-categories-${category}`
45
46
  })(splitAndRemoveEmpty(category));
46
47
  }
47
- const { filters } = await getFilters(
48
- { category, includeSoldOut, where, includeSellableForFree },
49
- context
48
+ const { filters } = await unwrap(
49
+ getFilters(
50
+ { category, includeSoldOut, where, includeSellableForFree },
51
+ context
52
+ )
50
53
  );
51
54
  const { entities: products, pagination } = await cached(
52
55
  bapiClient.products.query,
@@ -102,9 +105,11 @@ export const getFilters = async function getFilters2({
102
105
  result = await cached(bapiClient.categories.getByPath, {
103
106
  cacheKeyPrefix: `getByPath-categories-${category}`
104
107
  })(splitAndRemoveEmpty(category));
105
- allFiltersForCategory = await fetchAllFiltersForCategory(
106
- { category: { id: result.id, slug: category }, includedFilters },
107
- context
108
+ allFiltersForCategory = await unwrap(
109
+ fetchAllFiltersForCategory(
110
+ { category: { id: result.id, slug: category }, includedFilters },
111
+ context
112
+ )
108
113
  );
109
114
  }
110
115
  const sanitizedAttributes = sanitizeAttributesForBAPI({
@@ -10,26 +10,26 @@ var _constants = require("../../constants/index.cjs");
10
10
  var _fetch = require("../../utils/fetch.cjs");
11
11
  var _user = require("../../utils/user.cjs");
12
12
  var _user2 = require("../../rpc/methods/user.cjs");
13
+ var _response = require("../../utils/response.cjs");
13
14
  var _oauth = require("../../api/oauth.cjs");
14
15
  const convertErrorForRpcCall = (error, httpStatuses) => {
15
16
  if (error instanceof _fetch.FetchError && httpStatuses.includes(error.response.status)) {
16
- return error;
17
+ return new Response(null, {
18
+ status: error.response.status
19
+ });
17
20
  }
18
21
  };
19
22
  exports.convertErrorForRpcCall = convertErrorForRpcCall;
20
- const saveUserOnSession = async (accessToken, context) => {
21
- const user = await (0, _user2.fetchUser)({
22
- accessToken,
23
- callback: ""
24
- }, context);
25
- context.updateUser(user);
26
- };
27
23
  async function postLogin(context, tokens) {
24
+ const user = await (0, _response.unwrap)((0, _user2.fetchUser)({
25
+ accessToken: tokens.access_token,
26
+ callback: ""
27
+ }, context));
28
28
  context.updateTokens({
29
29
  accessToken: tokens.access_token,
30
30
  refreshToken: tokens.refresh_token
31
31
  });
32
- await saveUserOnSession(tokens.access_token, context);
32
+ context.updateUser(user);
33
33
  const {
34
34
  customerId
35
35
  } = (0, _jose.decodeJwt)(tokens.access_token);
@@ -50,11 +50,15 @@ const oauthLogin = async (login, context) => {
50
50
  });
51
51
  await postLogin(context, tokens);
52
52
  } catch (error) {
53
- const err = convertErrorForRpcCall(error, [_constants.HttpStatusCode.INTERNAL_SERVER_ERROR, _constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.UNAUTHORIZED, _constants.HttpStatusCode.NOT_FOUND]);
54
- if (err) {
55
- throw err;
53
+ const res = convertErrorForRpcCall(error, [_constants.HttpStatusCode.INTERNAL_SERVER_ERROR, _constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.UNAUTHORIZED, _constants.HttpStatusCode.NOT_FOUND, _constants.HttpStatusCode.FORBIDDEN]);
54
+ if (res) {
55
+ return res;
56
56
  }
57
+ throw error;
57
58
  }
59
+ return new Response(null, {
60
+ status: _constants.HttpStatusCode.NO_CONTENT
61
+ });
58
62
  };
59
63
  exports.oauthLogin = oauthLogin;
60
64
  const oauthRegister = async (register, context) => {
@@ -68,11 +72,15 @@ const oauthRegister = async (register, context) => {
68
72
  });
69
73
  await postLogin(context, tokens);
70
74
  } catch (error) {
71
- const err = convertErrorForRpcCall(error, [_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.UNAUTHORIZED, _constants.HttpStatusCode.CONFLICT]);
72
- if (err) {
73
- throw err;
75
+ const res = convertErrorForRpcCall(error, [_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.UNAUTHORIZED, _constants.HttpStatusCode.CONFLICT, _constants.HttpStatusCode.FORBIDDEN]);
76
+ if (res) {
77
+ return res;
74
78
  }
79
+ throw error;
75
80
  }
81
+ return new Response(null, {
82
+ status: _constants.HttpStatusCode.NO_CONTENT
83
+ });
76
84
  };
77
85
  exports.oauthRegister = oauthRegister;
78
86
  const oauthGuestLogin = async (guest, context) => {
@@ -86,11 +94,15 @@ const oauthGuestLogin = async (guest, context) => {
86
94
  });
87
95
  await postLogin(context, tokens);
88
96
  } catch (error) {
89
- const err = convertErrorForRpcCall(error, [_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.UNAUTHORIZED, _constants.HttpStatusCode.CONFLICT]);
90
- if (err) {
91
- throw err;
97
+ const res = convertErrorForRpcCall(error, [_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.UNAUTHORIZED, _constants.HttpStatusCode.CONFLICT, _constants.HttpStatusCode.FORBIDDEN]);
98
+ if (res) {
99
+ return res;
92
100
  }
101
+ throw error;
93
102
  }
103
+ return new Response(null, {
104
+ status: _constants.HttpStatusCode.NO_CONTENT
105
+ });
94
106
  };
95
107
  exports.oauthGuestLogin = oauthGuestLogin;
96
108
  const refreshAccessToken = async context => {
@@ -113,9 +125,9 @@ const refreshAccessToken = async context => {
113
125
  success: !!tokens
114
126
  };
115
127
  } catch (error) {
116
- const err = convertErrorForRpcCall(error, [_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.UNAUTHORIZED]);
117
- if (err) {
118
- throw err;
128
+ const res = convertErrorForRpcCall(error, [_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.UNAUTHORIZED]);
129
+ if (res) {
130
+ return res;
119
131
  }
120
132
  return {
121
133
  success: false
@@ -137,9 +149,9 @@ const oauthRevokeToken = async context => {
137
149
  result: true
138
150
  };
139
151
  } catch (error) {
140
- const err = convertErrorForRpcCall(error, [_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.UNAUTHORIZED, _constants.HttpStatusCode.NOT_FOUND]);
141
- if (err) {
142
- throw err;
152
+ const res = convertErrorForRpcCall(error, [_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.UNAUTHORIZED, _constants.HttpStatusCode.NOT_FOUND]);
153
+ if (res) {
154
+ return res;
143
155
  }
144
156
  return {
145
157
  result: false
@@ -172,9 +184,9 @@ const oauthForgetPassword = async ({
172
184
  success: true
173
185
  };
174
186
  } catch (error) {
175
- const err = convertErrorForRpcCall(error, [_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.NOT_FOUND, _constants.HttpStatusCode.UNAUTHORIZED, _constants.HttpStatusCode.FORBIDDEN]);
176
- if (err) {
177
- throw err;
187
+ const res = convertErrorForRpcCall(error, [_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.NOT_FOUND, _constants.HttpStatusCode.UNAUTHORIZED, _constants.HttpStatusCode.FORBIDDEN]);
188
+ if (res) {
189
+ return res;
178
190
  }
179
191
  return {
180
192
  success: false
@@ -197,10 +209,14 @@ const updatePasswordByHash = async (passwordHash, context) => {
197
209
  });
198
210
  await context.createUserBoundSession();
199
211
  } catch (error) {
200
- const err = convertErrorForRpcCall(error, [_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.UNAUTHORIZED, _constants.HttpStatusCode.NOT_ACCEPTABLE]);
201
- if (err) {
202
- throw err;
212
+ const res = convertErrorForRpcCall(error, [_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.UNAUTHORIZED, _constants.HttpStatusCode.NOT_ACCEPTABLE]);
213
+ if (res) {
214
+ return res;
203
215
  }
216
+ throw error;
204
217
  }
218
+ return new Response(null, {
219
+ status: _constants.HttpStatusCode.NO_CONTENT
220
+ });
205
221
  };
206
222
  exports.updatePasswordByHash = updatePasswordByHash;
@@ -3,7 +3,7 @@ import type { GuestRequest, LoginRequest, ParamRpcHandler, RegisterRequest, RpcH
3
3
  /**
4
4
  * Ensure error is a fetch error with one of the status codes
5
5
  */
6
- export declare const convertErrorForRpcCall: (error: any, httpStatuses: Array<number>) => Error | undefined;
6
+ export declare const convertErrorForRpcCall: (error: any, httpStatuses: Array<number>) => Response | undefined;
7
7
  export declare const oauthLogin: ParamRpcHandler<Optional<LoginRequest, 'shop_id'>, undefined>;
8
8
  export declare const oauthRegister: ParamRpcHandler<Optional<RegisterRequest, 'shop_id'>, undefined>;
9
9
  export declare const oauthGuestLogin: ParamRpcHandler<Optional<GuestRequest, 'shop_id'>, undefined>;
@@ -4,22 +4,22 @@ import { DEFAULT_WITH_LISTING, HttpStatusCode } from "../../constants/index.mjs"
4
4
  import { FetchError } from "../../utils/fetch.mjs";
5
5
  import { mergeBaskets, mergeWishlists } from "../../utils/user.mjs";
6
6
  import { fetchUser } from "../../rpc/methods/user.mjs";
7
+ import { unwrap } from "../../utils/response.mjs";
7
8
  import { getOAuthClient } from "../../api/oauth.mjs";
8
9
  export const convertErrorForRpcCall = (error, httpStatuses) => {
9
10
  if (error instanceof FetchError && httpStatuses.includes(error.response.status)) {
10
- return error;
11
+ return new Response(null, { status: error.response.status });
11
12
  }
12
13
  };
13
- const saveUserOnSession = async (accessToken, context) => {
14
- const user = await fetchUser({ accessToken, callback: "" }, context);
15
- context.updateUser(user);
16
- };
17
14
  async function postLogin(context, tokens) {
15
+ const user = await unwrap(
16
+ fetchUser({ accessToken: tokens.access_token, callback: "" }, context)
17
+ );
18
18
  context.updateTokens({
19
19
  accessToken: tokens.access_token,
20
20
  refreshToken: tokens.refresh_token
21
21
  });
22
- await saveUserOnSession(tokens.access_token, context);
22
+ context.updateUser(user);
23
23
  const { customerId } = decodeJwt(tokens.access_token);
24
24
  await Promise.all([
25
25
  mergeBaskets(
@@ -50,16 +50,19 @@ export const oauthLogin = async (login, context) => {
50
50
  const tokens = await client.login({ ...login, shop_id: shopId });
51
51
  await postLogin(context, tokens);
52
52
  } catch (error) {
53
- const err = convertErrorForRpcCall(error, [
53
+ const res = convertErrorForRpcCall(error, [
54
54
  HttpStatusCode.INTERNAL_SERVER_ERROR,
55
55
  HttpStatusCode.BAD_REQUEST,
56
56
  HttpStatusCode.UNAUTHORIZED,
57
- HttpStatusCode.NOT_FOUND
57
+ HttpStatusCode.NOT_FOUND,
58
+ HttpStatusCode.FORBIDDEN
58
59
  ]);
59
- if (err) {
60
- throw err;
60
+ if (res) {
61
+ return res;
61
62
  }
63
+ throw error;
62
64
  }
65
+ return new Response(null, { status: HttpStatusCode.NO_CONTENT });
63
66
  };
64
67
  export const oauthRegister = async (register, context) => {
65
68
  assertSession(context);
@@ -72,15 +75,18 @@ export const oauthRegister = async (register, context) => {
72
75
  });
73
76
  await postLogin(context, tokens);
74
77
  } catch (error) {
75
- const err = convertErrorForRpcCall(error, [
78
+ const res = convertErrorForRpcCall(error, [
76
79
  HttpStatusCode.BAD_REQUEST,
77
80
  HttpStatusCode.UNAUTHORIZED,
78
- HttpStatusCode.CONFLICT
81
+ HttpStatusCode.CONFLICT,
82
+ HttpStatusCode.FORBIDDEN
79
83
  ]);
80
- if (err) {
81
- throw err;
84
+ if (res) {
85
+ return res;
82
86
  }
87
+ throw error;
83
88
  }
89
+ return new Response(null, { status: HttpStatusCode.NO_CONTENT });
84
90
  };
85
91
  export const oauthGuestLogin = async (guest, context) => {
86
92
  assertSession(context);
@@ -93,15 +99,18 @@ export const oauthGuestLogin = async (guest, context) => {
93
99
  });
94
100
  await postLogin(context, tokens);
95
101
  } catch (error) {
96
- const err = convertErrorForRpcCall(error, [
102
+ const res = convertErrorForRpcCall(error, [
97
103
  HttpStatusCode.BAD_REQUEST,
98
104
  HttpStatusCode.UNAUTHORIZED,
99
- HttpStatusCode.CONFLICT
105
+ HttpStatusCode.CONFLICT,
106
+ HttpStatusCode.FORBIDDEN
100
107
  ]);
101
- if (err) {
102
- throw err;
108
+ if (res) {
109
+ return res;
103
110
  }
111
+ throw error;
104
112
  }
113
+ return new Response(null, { status: HttpStatusCode.NO_CONTENT });
105
114
  };
106
115
  export const refreshAccessToken = async (context) => {
107
116
  assertSession(context);
@@ -121,12 +130,12 @@ export const refreshAccessToken = async (context) => {
121
130
  });
122
131
  return { success: !!tokens };
123
132
  } catch (error) {
124
- const err = convertErrorForRpcCall(error, [
133
+ const res = convertErrorForRpcCall(error, [
125
134
  HttpStatusCode.BAD_REQUEST,
126
135
  HttpStatusCode.UNAUTHORIZED
127
136
  ]);
128
- if (err) {
129
- throw err;
137
+ if (res) {
138
+ return res;
130
139
  }
131
140
  return { success: false };
132
141
  }
@@ -143,13 +152,13 @@ export const oauthRevokeToken = async (context) => {
143
152
  await client.revokeToken(accessToken);
144
153
  return { result: true };
145
154
  } catch (error) {
146
- const err = convertErrorForRpcCall(error, [
155
+ const res = convertErrorForRpcCall(error, [
147
156
  HttpStatusCode.BAD_REQUEST,
148
157
  HttpStatusCode.UNAUTHORIZED,
149
158
  HttpStatusCode.NOT_FOUND
150
159
  ]);
151
- if (err) {
152
- throw err;
160
+ if (res) {
161
+ return res;
153
162
  }
154
163
  return { result: false };
155
164
  }
@@ -175,14 +184,14 @@ export const oauthForgetPassword = async ({ email }, context) => {
175
184
  });
176
185
  return { success: true };
177
186
  } catch (error) {
178
- const err = convertErrorForRpcCall(error, [
187
+ const res = convertErrorForRpcCall(error, [
179
188
  HttpStatusCode.BAD_REQUEST,
180
189
  HttpStatusCode.NOT_FOUND,
181
190
  HttpStatusCode.UNAUTHORIZED,
182
191
  HttpStatusCode.FORBIDDEN
183
192
  ]);
184
- if (err) {
185
- throw err;
193
+ if (res) {
194
+ return res;
186
195
  }
187
196
  return { success: false };
188
197
  }
@@ -202,13 +211,15 @@ export const updatePasswordByHash = async (passwordHash, context) => {
202
211
  });
203
212
  await context.createUserBoundSession();
204
213
  } catch (error) {
205
- const err = convertErrorForRpcCall(error, [
214
+ const res = convertErrorForRpcCall(error, [
206
215
  HttpStatusCode.BAD_REQUEST,
207
216
  HttpStatusCode.UNAUTHORIZED,
208
217
  HttpStatusCode.NOT_ACCEPTABLE
209
218
  ]);
210
- if (err) {
211
- throw err;
219
+ if (res) {
220
+ return res;
212
221
  }
222
+ throw error;
213
223
  }
224
+ return new Response(null, { status: HttpStatusCode.NO_CONTENT });
214
225
  };
@@ -19,17 +19,12 @@ const fetchUser = exports.fetchUser = async function fetchUser2(payload, context
19
19
  shopId
20
20
  } = context;
21
21
  const client = new _customer.CustomerAPIClient(context);
22
- const user = await client.getMe(shopId);
23
- return {
24
- ...user,
25
- authentication: {
26
- ...user?.authentication,
27
- storefrontAccessToken: accessToken
28
- },
29
- ...{
30
- loginShopId: shopId
31
- }
32
- };
22
+ const user = await client.getMe(shopId, accessToken);
23
+ if (user.authentication) {
24
+ user.authentication.storefrontAccessToken = accessToken;
25
+ }
26
+ user.loginShopId = shopId;
27
+ return user;
33
28
  };
34
29
  const refreshUser = exports.refreshUser = async function refreshUser2(context) {
35
30
  (0, _types.assertSession)(context);
@@ -38,8 +33,8 @@ const refreshUser = exports.refreshUser = async function refreshUser2(context) {
38
33
  shopId
39
34
  } = context;
40
35
  const client = new _customer.CustomerAPIClient(context);
41
- const user = await client.getMe(shopId);
42
- if (user?.id) {
36
+ try {
37
+ const user = await client.getMe(shopId);
43
38
  context.updateUser({
44
39
  ...user,
45
40
  authentication: user.authentication ? {
@@ -53,9 +48,10 @@ const refreshUser = exports.refreshUser = async function refreshUser2(context) {
53
48
  return {
54
49
  user
55
50
  };
51
+ } catch {
52
+ await context.destroySession();
53
+ return {
54
+ user: void 0
55
+ };
56
56
  }
57
- await context.destroySession();
58
- return {
59
- user: void 0
60
- };
61
57
  };
@@ -9,22 +9,19 @@ const fetchUser = async function fetchUser2(payload, context) {
9
9
  const { accessToken } = payload;
10
10
  const { shopId } = context;
11
11
  const client = new CustomerAPIClient(context);
12
- const user = await client.getMe(shopId);
13
- return {
14
- ...user,
15
- authentication: {
16
- ...user?.authentication,
17
- storefrontAccessToken: accessToken
18
- },
19
- ...{ loginShopId: shopId }
20
- };
12
+ const user = await client.getMe(shopId, accessToken);
13
+ if (user.authentication) {
14
+ user.authentication.storefrontAccessToken = accessToken;
15
+ }
16
+ user.loginShopId = shopId;
17
+ return user;
21
18
  };
22
19
  const refreshUser = async function refreshUser2(context) {
23
20
  assertSession(context);
24
21
  const { accessToken, shopId } = context;
25
22
  const client = new CustomerAPIClient(context);
26
- const user = await client.getMe(shopId);
27
- if (user?.id) {
23
+ try {
24
+ const user = await client.getMe(shopId);
28
25
  context.updateUser({
29
26
  ...user,
30
27
  authentication: user.authentication ? {
@@ -34,8 +31,9 @@ const refreshUser = async function refreshUser2(context) {
34
31
  ...{ loginShopId: shopId }
35
32
  });
36
33
  return { user };
34
+ } catch {
35
+ await context.destroySession();
36
+ return { user: void 0 };
37
37
  }
38
- await context.destroySession();
39
- return { user: void 0 };
40
38
  };
41
39
  export { getUser, fetchUser, refreshUser };
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.removeItemFromWishlist = exports.getWishlist = exports.clearWishlist = exports.addItemToWishlist = void 0;
7
7
  var _types = require("../../types/index.cjs");
8
8
  var _constants = require("../../constants/index.cjs");
9
+ var _response = require("../../utils/response.cjs");
9
10
  function getWithParams(params, context) {
10
11
  return params.with ?? context.withParams?.basket ?? _constants.MIN_WITH_PARAMS_WISHLIST;
11
12
  }
@@ -79,7 +80,7 @@ const removeItemFromWishlist = exports.removeItemFromWishlist = async function r
79
80
  });
80
81
  };
81
82
  const clearWishlist = exports.clearWishlist = async function clearWishlist2(context) {
82
- const wishlist = await getWishlist({}, context);
83
+ const wishlist = await (0, _response.unwrap)(getWishlist({}, context));
83
84
  await Promise.all(wishlist.items.map(async item => {
84
85
  await removeItemFromWishlist({
85
86
  itemKey: item.key
@@ -1,5 +1,6 @@
1
1
  import { assertSession } from "../../types/index.mjs";
2
2
  import { MIN_WITH_PARAMS_WISHLIST } from "../../constants/index.mjs";
3
+ import { unwrap } from "../../utils/response.mjs";
3
4
  function getWithParams(params, context) {
4
5
  return params.with ?? context.withParams?.basket ?? MIN_WITH_PARAMS_WISHLIST;
5
6
  }
@@ -49,7 +50,7 @@ export const removeItemFromWishlist = async function removeItemFromWishlist2(opt
49
50
  });
50
51
  };
51
52
  export const clearWishlist = async function clearWishlist2(context) {
52
- const wishlist = await getWishlist({}, context);
53
+ const wishlist = await unwrap(getWishlist({}, context));
53
54
  await Promise.all(
54
55
  wishlist.items.map(async (item) => {
55
56
  await removeItemFromWishlist({ itemKey: item.key }, context);
@@ -1,4 +1,4 @@
1
1
  import { RpcContext } from './context';
2
- export type ParamRpcHandler<Params extends Record<string, any>, ResponseType> = (params: Params, context: RpcContext) => Promise<ResponseType> | ResponseType;
3
- export type NoParamRpcHandler<ResponseType> = (context: RpcContext) => Promise<ResponseType> | ResponseType;
2
+ export type ParamRpcHandler<Params extends Record<string, any>, ResponseType> = (params: Params, context: RpcContext) => Promise<(ResponseType extends undefined ? void : ResponseType) | Response> | ResponseType | Response;
3
+ export type NoParamRpcHandler<ResponseType> = (context: RpcContext) => Promise<ResponseType | Response> | ResponseType | Response;
4
4
  export type RpcHandler<A, B = void> = B extends void ? NoParamRpcHandler<A> : ParamRpcHandler<A, B>;
@@ -4,9 +4,9 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.unwrap = unwrap;
7
- async function unwrap(response) {
7
+ async function unwrap(res) {
8
+ const response = await res;
8
9
  if (response instanceof Response) {
9
- console.log("unwrapping for ", response.url);
10
10
  return await response.json();
11
11
  }
12
12
  return response;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Unwrap a possible response into its body
3
- * @param x
3
+ * @param res
4
4
  * @returns
5
5
  */
6
- export declare function unwrap<T>(response: Response | T): Promise<T>;
6
+ export declare function unwrap<T>(res: Promise<Response | T> | Response | T): Promise<T>;
@@ -1,6 +1,6 @@
1
- export async function unwrap(response) {
1
+ export async function unwrap(res) {
2
+ const response = await res;
2
3
  if (response instanceof Response) {
3
- console.log("unwrapping for ", response.url);
4
4
  return await response.json();
5
5
  }
6
6
  return response;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scayle/storefront-core",
3
- "version": "7.38.0",
3
+ "version": "7.39.0",
4
4
  "description": "Collection of essential utilities to work with the Storefront API",
5
5
  "author": "SCAYLE Commerce Engine",
6
6
  "license": "MIT",
@@ -72,9 +72,9 @@
72
72
  "@scayle/eslint-config-storefront": "3.2.6",
73
73
  "@scayle/prettier-config-storefront": "2.0.2",
74
74
  "@types/crypto-js": "4.2.2",
75
- "@types/node": "20.11.5",
75
+ "@types/node": "20.11.10",
76
76
  "@types/webpack-env": "1.18.4",
77
- "@vitest/coverage-v8": "1.2.1",
77
+ "@vitest/coverage-v8": "1.2.2",
78
78
  "eslint": "8.56.0",
79
79
  "eslint-formatter-gitlab": "5.1.0",
80
80
  "prettier": "3.0.0",
@@ -84,7 +84,7 @@
84
84
  "typescript": "5.3.3",
85
85
  "unbuild": "2.0.0",
86
86
  "unstorage": "1.10.1",
87
- "vitest": "1.2.1"
87
+ "vitest": "1.2.2"
88
88
  },
89
89
  "optionalDependencies": {
90
90
  "redis": "4"