@evenicanpm/storefront-core 2.3.1 → 2.4.1

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 (90) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/package.json +42 -9
  3. package/src/api-manager/datasources/d365/d365-address.datasource.ts +148 -61
  4. package/src/api-manager/datasources/d365/d365-cart.datasource.ts +23 -1
  5. package/src/api-manager/datasources/d365/d365-order.datasource.ts +6 -1
  6. package/src/api-manager/datasources/d365/d365-user.datasource.ts +109 -37
  7. package/src/api-manager/datasources/d365/utils/get-context-cookie.ts +44 -10
  8. package/src/api-manager/index.ts +2 -1
  9. package/src/api-manager/lib/get-graphql-client.ts +35 -7
  10. package/src/api-manager/schemas/product.schema.ts +1 -1
  11. package/src/api-manager/services/create-query.ts +36 -3
  12. package/src/api-manager/services/get-query-client.ts +10 -0
  13. package/src/auth/better-auth.ts +282 -15
  14. package/src/cms/blocks/block-manager.tsx +1 -1
  15. package/src/cms/blocks/components/product-section-fullwidth/index.tsx +3 -1
  16. package/src/cms/endpoints.ts +7 -2
  17. package/src/components/BazaarMenu.tsx +1 -1
  18. package/src/components/Typography.tsx +4 -2
  19. package/src/components/carousel-cards/carousel-card-1/CarouselCard1.stories.tsx +1 -1
  20. package/src/components/categories/category-list/category-list.tsx +9 -5
  21. package/src/components/categories/category-menu.tsx +1 -1
  22. package/src/components/countries-input.tsx +4 -0
  23. package/src/components/header/__tests__/user.test.tsx +5 -107
  24. package/src/components/header/components/user.tsx +72 -45
  25. package/src/components/header/sticky-header.tsx +0 -1
  26. package/src/components/navbar/mobile-menu/mobile-menu.test.tsx +1 -1
  27. package/src/components/navbar/utils/transform-nav.test.ts +1 -1
  28. package/src/components/navbar/utils/transform-nav.ts +1 -1
  29. package/src/components/product-cards/product-card/product-card.tsx +5 -2
  30. package/src/components/product-cards/product-card/styles/index.ts +1 -5
  31. package/src/components/products-view/compound/products-grid-view.tsx +5 -1
  32. package/src/components/products-view/compound/products-list-view.tsx +5 -1
  33. package/src/global.d.ts +3 -0
  34. package/src/hooks/use-nextauth-session.ts +0 -33
  35. package/src/lib/auth-client.ts +14 -0
  36. package/src/lib/auth.ts +4 -0
  37. package/src/lib/entra-native-auth.ts +138 -0
  38. package/src/lib/native-session.ts +434 -0
  39. package/src/pages/account/addresses/address-form.tsx +20 -2
  40. package/src/pages/account/profile/__tests__/profile-form.test.tsx +173 -0
  41. package/src/pages/account/profile/profile-form.tsx +285 -0
  42. package/src/pages/account/profile/profile-validation.ts +52 -0
  43. package/src/pages/account/wishlist/wishlist-item.tsx +1 -2
  44. package/src/pages/auth/auth-activate-page.tsx +509 -0
  45. package/src/pages/auth/auth-layout.tsx +73 -0
  46. package/src/pages/auth/auth-login-page.tsx +241 -0
  47. package/src/pages/auth/auth-reset-password-page.tsx +487 -0
  48. package/src/pages/auth/compound/auth-form.tsx +182 -0
  49. package/src/pages/auth/compound/auth-pages.tsx +21 -0
  50. package/src/pages/auth/lib/friendly-error.ts +70 -0
  51. package/src/pages/auth/lib/index.ts +2 -0
  52. package/src/pages/auth/lib/types.ts +5 -0
  53. package/src/pages/cart/cart-item.tsx +1 -7
  54. package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +14 -3
  55. package/src/pages/checkout/checkout-alt-form/steps/address/address-card.tsx +5 -3
  56. package/src/pages/checkout/checkout-alt-form/steps/address/delivery-address.tsx +20 -6
  57. package/src/pages/checkout/checkout-alt-form/steps/address/new-address-form.tsx +1 -1
  58. package/src/pages/checkout/checkout-alt-form/steps/customer-info/customer-information.tsx +1 -1
  59. package/src/pages/confirmation/ordered-products.tsx +3 -1
  60. package/src/pages/product-details/bopis/find-in-store-modal.tsx +4 -4
  61. package/src/pages/product-details/bopis/pickup-option-select.tsx +2 -2
  62. package/src/pages/product-details/bopis/search-header.tsx +2 -2
  63. package/src/pages/product-details/product-intro/compound/context.ts +8 -3
  64. package/src/pages/product-details/product-intro/compound/product-info.tsx +25 -19
  65. package/src/pages/product-list/product-list-view.tsx +2 -1
  66. package/src/providers/nav-provider/index.tsx +1 -1
  67. package/src/providers/nav-provider/utils/createLink.ts +1 -1
  68. package/tsconfig.json +20 -15
  69. package/__mocks__/countries.ts +0 -11
  70. package/__mocks__/create-mutation.ts +0 -68
  71. package/__mocks__/create-query.ts +0 -94
  72. package/__mocks__/data/categories.json +0 -795
  73. package/__mocks__/get-product-by-id.ts +0 -6
  74. package/__mocks__/next-auth-react.ts +0 -9
  75. package/__mocks__/next-font.js +0 -4
  76. package/__mocks__/next-headers.js +0 -13
  77. package/__mocks__/next-image.tsx +0 -18
  78. package/__mocks__/next-link.js +0 -19
  79. package/__mocks__/next-navigation.ts +0 -29
  80. package/__mocks__/product-dimensions.ts +0 -635
  81. package/__mocks__/products.ts +0 -533
  82. package/__mocks__/query-client.ts +0 -3
  83. package/__mocks__/wishlists.json +0 -408
  84. package/src/auth/msal.ts +0 -68
  85. package/src/cms/blog.ts +0 -229
  86. package/src/cms/interfaces/navigation.ts +0 -52
  87. package/src/cms/interfaces/product-data.ts +0 -83
  88. package/src/cms/pages.ts +0 -149
  89. package/src/pages/account/profile/profile-button.test.tsx +0 -59
  90. package/src/pages/account/profile/profile-button.tsx +0 -51
package/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # @evenicanpm/storefront-core
2
+
3
+ ## 2.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - fix: republish with resolved workspace dependencies
8
+ - Updated dependencies
9
+ - @evenicanpm/cms@2.4.1
10
+ - @evenicanpm/ui@2.4.1
11
+
12
+ ## 2.4.0
13
+
14
+ ### Patch Changes
15
+
16
+ - @evenicanpm/cms@2.4.0
17
+ - @evenicanpm/ui@2.4.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evenicanpm/storefront-core",
3
- "version": "2.3.1",
3
+ "version": "2.4.1",
4
4
  "description": "Core module for D365/e4 Headless Storefront",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -16,33 +16,67 @@
16
16
  "author": "Evenica",
17
17
  "license": "ISC",
18
18
  "dependencies": {
19
- "@evenicanpm/cms": "^2.3.1",
20
- "@evenicanpm/ui": "^2.0.0",
21
- "strapi-sdk-js": "^3.0.0"
19
+ "@evenicanpm/cms": "workspace:*",
20
+ "@evenicanpm/ui": "workspace:*",
21
+ "react-dropzone": "^14.3.8",
22
+ "react-slick": "^0.30.1",
23
+ "slick-carousel": "^1.8.1",
24
+ "strapi-sdk-js": "^3.0.0",
25
+ "clsx": "^2.1.1",
26
+ "react-icons": "^5.4.0",
27
+ "simplebar-react": "^3.3.0",
28
+ "formik": "^2.4.6",
29
+ "formik-validator-zod": "^2.0.1",
30
+ "currency.js": "^2.0.4",
31
+ "@hookform/resolvers": "^3.9.0",
32
+ "react-hook-form": "^7.53.0",
33
+ "yup": "^1.3.3",
34
+ "nuqs": "^2.8.9"
22
35
  },
23
36
  "devDependencies": {
37
+ "@emotion/react": "catalog:",
38
+ "@emotion/styled": "catalog:",
39
+ "@mui/icons-material": "catalog:",
40
+ "@mui/material": "catalog:",
41
+ "@mui/types": "catalog:",
42
+ "@mui/system": "catalog:",
24
43
  "@storybook/addon-docs": "^9.1.6",
25
44
  "@storybook/addon-webpack5-compiler-swc": "^4.0.1",
26
45
  "@storybook/nextjs": "^9.1.6",
46
+ "@storybook/react": "^9.1.6",
27
47
  "@storybook/react-webpack5": "^9.1.6",
28
48
  "@types/jsonwebtoken": "^9.0.9",
29
49
  "@types/lodash": "^4.17.17",
30
50
  "@types/node": "^22.15.20",
31
51
  "@types/object-mapper": "^6.2.2",
52
+ "@types/react": "catalog:",
53
+ "@types/react-dom": "catalog:",
54
+ "react": "catalog:",
55
+ "react-dom": "catalog:",
32
56
  "storybook": "^9.1.6"
33
57
  },
34
58
  "peerDependencies": {
59
+ "@emotion/react": "catalog:",
60
+ "@emotion/styled": "catalog:",
35
61
  "@msdyn365-commerce/retail-proxy": "9.50",
62
+ "@mui/icons-material": "catalog:",
63
+ "@mui/material": "catalog:",
64
+ "@mui/system": "catalog:",
36
65
  "@opentelemetry/api": "^1.9.1",
37
66
  "@strapi/blocks-react-renderer": "^1.0.1",
38
- "@tanstack/react-query": "^5.76.1",
39
- "better-auth": "*",
67
+ "@tanstack/react-query": "catalog:",
68
+ "@tanstack/query-core": "catalog:",
69
+ "better-auth": "catalog:",
40
70
  "graphql": "^16.11.0",
41
71
  "graphql-request": "^7.1.2",
42
72
  "jsonwebtoken": "^9.0.2",
43
73
  "lodash": "^4.17.21",
44
- "next": "^15.2.1",
74
+ "next": "catalog:",
75
+ "next-intl": "catalog:",
45
76
  "object-mapper": "^6.2.0",
77
+ "pg": "^8.8.0",
78
+ "react": "catalog:",
79
+ "react-dom": "catalog:",
46
80
  "set-cookie-parser": "^2.7.1",
47
81
  "typedoc": "^0.28.9",
48
82
  "typedoc-plugin-merge-modules": "^7.0.0",
@@ -53,6 +87,5 @@
53
87
  "@storybook/builder-manager": {
54
88
  "esbuild": "0.19.11"
55
89
  }
56
- },
57
- "gitHead": "7a5a6a8c3c3dd9efe96fbbb884b21e7681d32550"
90
+ }
58
91
  }
@@ -7,10 +7,13 @@ import {
7
7
  type CountryRegionInfo,
8
8
  type DeliveryOption,
9
9
  FilterDeliveryModeOption,
10
+ type ICallerContext,
11
+ type IContext,
10
12
  type StateProvinceInfo,
11
13
  } from "@msdyn365-commerce/retail-proxy";
12
14
  import { getDeliveryOptionsAsync } from "@msdyn365-commerce/retail-proxy/dist/DataActions/CartsDataActions.g";
13
15
  import {
16
+ createCustomerAsync,
14
17
  readAsync,
15
18
  updateAsync,
16
19
  } from "@msdyn365-commerce/retail-proxy/dist/DataActions/CustomersDataActions.g";
@@ -18,6 +21,7 @@ import {
18
21
  getCountryRegionsByLanguageIdAsync,
19
22
  getStateProvincesAsync,
20
23
  } from "@msdyn365-commerce/retail-proxy/dist/DataActions/StoreOperationsDataActions.g";
24
+ import jwt from "jsonwebtoken";
21
25
  import type {
22
26
  Address,
23
27
  GetCountriesInput,
@@ -25,80 +29,160 @@ import type {
25
29
  GetStatesInput,
26
30
  } from "@/schemas/address.schema";
27
31
 
32
+ type IdTokenClaims = jwt.JwtPayload & {
33
+ emails?: string[];
34
+ email?: string;
35
+ preferred_username?: string;
36
+ upn?: string;
37
+ unique_name?: string;
38
+ given_name?: string;
39
+ family_name?: string;
40
+ };
41
+
42
+ function extractEmailFromClaims(token: IdTokenClaims): string | undefined {
43
+ return [
44
+ Array.isArray(token.emails) ? token.emails[0] : undefined,
45
+ token.email,
46
+ token.preferred_username,
47
+ token.upn,
48
+ token.unique_name,
49
+ ]
50
+ .map((v) => (typeof v === "string" ? v.trim() : ""))
51
+ .find((v) => v.includes("@"));
52
+ }
53
+
54
+ function decodeIdTokenClaims(token: string): IdTokenClaims {
55
+ const decoded = jwt.decode(token);
56
+ if (!decoded || typeof decoded !== "object") {
57
+ return {} as IdTokenClaims;
58
+ }
59
+ return decoded;
60
+ }
61
+
28
62
  class D365AddressDatasource extends D365DatasourceBase implements AddressApi {
29
- async createAddress(address: Address): Promise<Address> {
30
- const context = await getContextFromCookie();
31
- const customer = await readAsync(context as any, "");
32
- const accountNumber = customer.AccountNumber;
33
-
34
- const prevAddresses = customer.Addresses ?? [];
35
- customer.Addresses = [...prevAddresses, address as any];
36
-
37
- const updatedCustomer = await updateAsync(context as any, {
38
- ...customer,
39
- AccountNumber: accountNumber,
40
- });
41
-
42
- const added = updatedCustomer.Addresses?.find((a) =>
43
- prevAddresses.every((old) => old.RecordId !== a.RecordId),
44
- );
45
- if (added?.RecordId == null) {
46
- throw new Error("Failed to create new address");
47
- }
63
+ private getRetailContext(context: ICallerContext): IContext {
64
+ return context as unknown as IContext;
65
+ }
48
66
 
49
- return {
50
- ...added,
51
- Id: added.RecordId.toString(),
52
- };
67
+ private async getOrCreateCustomer(context: ICallerContext) {
68
+ try {
69
+ return await readAsync(this.getRetailContext(context), "");
70
+ } catch {
71
+ const token = decodeIdTokenClaims(
72
+ context.requestContext?.user?.token || "",
73
+ );
74
+ const email = extractEmailFromClaims(token);
75
+ if (!email) {
76
+ throw new Error(
77
+ "Cannot resolve or create customer profile: missing email in token claims.",
78
+ );
79
+ }
80
+
81
+ return createCustomerAsync(this.getRetailContext(context), {
82
+ AccountNumber: "",
83
+ Email: email,
84
+ FirstName: token.given_name,
85
+ LastName: token.family_name || ".",
86
+ });
87
+ }
53
88
  }
54
89
 
55
- async updateAddress(address: Address): Promise<Address> {
56
- const context = await getContextFromCookie();
57
- const customer = await readAsync(context as any, "");
58
- customer.Addresses = customer.Addresses ?? [];
59
-
60
- const idx = customer.Addresses.findIndex(
61
- (a) => a.RecordId?.toString() === address.Id,
62
- );
63
- const updatedRecord = { ...(address as any) };
64
-
65
- if (idx >= 0) {
66
- customer.Addresses[idx] = {
67
- ...customer.Addresses[idx],
68
- ...updatedRecord,
90
+ async createAddress(address: Address): Promise<Address> {
91
+ try {
92
+ const context = await getContextFromCookie();
93
+ const customer = await this.getOrCreateCustomer(context);
94
+ const accountNumber = customer.AccountNumber;
95
+
96
+ const prevAddresses = customer.Addresses ?? [];
97
+ // biome-ignore lint: api datasources should not be typed
98
+ customer.Addresses = [...prevAddresses, address as any];
99
+
100
+ // biome-ignore lint: api datasources should not be typed
101
+ const updatedCustomer = await updateAsync(context as any, {
102
+ ...customer,
103
+ AccountNumber: accountNumber,
104
+ });
105
+
106
+ const added = updatedCustomer.Addresses?.find((a) =>
107
+ prevAddresses.every((old) => old.RecordId !== a.RecordId),
108
+ );
109
+ if (added?.RecordId == null) {
110
+ throw new Error("Failed to create new address");
111
+ }
112
+
113
+ return {
114
+ ...added,
115
+ Id: added.RecordId.toString(),
69
116
  };
70
- } else {
71
- customer.Addresses.push(updatedRecord);
117
+ } catch (error) {
118
+ const message =
119
+ error instanceof Error ? error.message : JSON.stringify(error);
120
+ throw new Error(`createAddress failed: ${message}`);
72
121
  }
122
+ }
123
+
124
+ async updateAddress(address: Address): Promise<Address> {
125
+ try {
126
+ const context = await getContextFromCookie();
127
+ const customer = await this.getOrCreateCustomer(context);
128
+ customer.Addresses = customer.Addresses ?? [];
73
129
 
74
- const updatedCustomer = await updateAsync(context as any, customer);
75
- const rec = updatedCustomer?.Addresses?.[idx];
76
- if (rec?.RecordId == null) {
77
- throw new Error(
78
- `Failed to update address Id=${address.Id}. Updated address record with a matching id not found.`,
130
+ const idx = customer.Addresses.findIndex(
131
+ (a) => a.RecordId?.toString() === address.Id,
79
132
  );
133
+ // biome-ignore lint: api datasources should not be typed
134
+ const updatedRecord = { ...(address as any) };
135
+
136
+ if (idx >= 0) {
137
+ customer.Addresses[idx] = {
138
+ ...customer.Addresses[idx],
139
+ ...updatedRecord,
140
+ };
141
+ } else {
142
+ customer.Addresses.push(updatedRecord);
143
+ }
144
+
145
+ // biome-ignore lint: api datasources should not be typed
146
+ const updatedCustomer = await updateAsync(context as any, customer);
147
+ const rec = updatedCustomer?.Addresses?.[idx];
148
+ if (rec?.RecordId == null) {
149
+ throw new Error(
150
+ `Failed to update address Id=${address.Id}. Updated address record with a matching id not found.`,
151
+ );
152
+ }
153
+
154
+ return {
155
+ ...rec,
156
+ Id: rec.RecordId.toString(),
157
+ };
158
+ } catch (error) {
159
+ const message =
160
+ error instanceof Error ? error.message : JSON.stringify(error);
161
+ throw new Error(`updateAddress failed: ${message}`);
80
162
  }
81
-
82
- return {
83
- ...rec,
84
- Id: rec.RecordId.toString(),
85
- };
86
163
  }
87
164
 
88
165
  async deleteAddress(addressId: string): Promise<string> {
89
- const context = await getContextFromCookie();
90
- const customer = await readAsync(context as any, "");
91
- customer.Addresses = customer.Addresses ?? [];
92
-
93
- const rec = customer.Addresses.find(
94
- (a) => a.RecordId?.toString() === addressId,
95
- );
96
- if (rec) {
97
- rec.Deactivate = true;
98
- await updateAsync(context as any, customer);
99
- }
166
+ try {
167
+ const context = await getContextFromCookie();
168
+ const customer = await this.getOrCreateCustomer(context);
169
+ customer.Addresses = customer.Addresses ?? [];
100
170
 
101
- return addressId;
171
+ const rec = customer.Addresses.find(
172
+ (a) => a.RecordId?.toString() === addressId,
173
+ );
174
+ if (rec) {
175
+ rec.Deactivate = true;
176
+ // biome-ignore lint: api datasources should not be typed
177
+ await updateAsync(context as any, customer);
178
+ }
179
+
180
+ return addressId;
181
+ } catch (error) {
182
+ const message =
183
+ error instanceof Error ? error.message : JSON.stringify(error);
184
+ throw new Error(`deleteAddress failed: ${message}`);
185
+ }
102
186
  }
103
187
 
104
188
  async getCountries(input: GetCountriesInput): Promise<CountryRegionInfo[]> {
@@ -108,6 +192,7 @@ class D365AddressDatasource extends D365DatasourceBase implements AddressApi {
108
192
  throw new Error("Locale is required to fetch countries");
109
193
  }
110
194
 
195
+ // biome-ignore lint: api datasources should not be typed
111
196
  return getCountryRegionsByLanguageIdAsync(context as any, locale);
112
197
  }
113
198
 
@@ -117,6 +202,7 @@ class D365AddressDatasource extends D365DatasourceBase implements AddressApi {
117
202
  throw new Error("countryId is required to fetch states");
118
203
  }
119
204
 
205
+ // biome-ignore lint: api datasources should not be typed
120
206
  return getStateProvincesAsync(context as any, input.countryId);
121
207
  }
122
208
 
@@ -126,6 +212,7 @@ class D365AddressDatasource extends D365DatasourceBase implements AddressApi {
126
212
  const context = await getContextFromCookie();
127
213
  try {
128
214
  return getDeliveryOptionsAsync(
215
+ // biome-ignore lint: api datasources should not be typed
129
216
  context as any,
130
217
  input.cartId,
131
218
  input.shippingAddress,
@@ -11,14 +11,17 @@ function serializeError(error: unknown): string {
11
11
  const allKeys = new Set<string>();
12
12
  let proto = value;
13
13
  while (proto && proto !== Object.prototype) {
14
+ // biome-ignore lint: api datasources should not be typed
14
15
  Object.getOwnPropertyNames(proto).forEach((k) => allKeys.add(k));
15
16
  proto = Object.getPrototypeOf(proto);
16
17
  }
18
+ // biome-ignore lint: api datasources should not be typed
17
19
  Object.keys(value).forEach((k) => allKeys.add(k));
18
20
  const result: Record<string, unknown> = {};
19
21
  for (const key of allKeys) {
20
22
  if (key !== "constructor") {
21
23
  try {
24
+ // biome-ignore lint: api datasources should not be typed
22
25
  result[key] = (value as any)[key];
23
26
  } catch {}
24
27
  }
@@ -135,6 +138,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
135
138
  }
136
139
 
137
140
  const result = await checkoutAsync(
141
+ // biome-ignore lint: api datasources should not be typed
138
142
  context as any,
139
143
  input.id,
140
144
  input.receiptEmail,
@@ -164,6 +168,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
164
168
  const { cartId, resultAccessCode, settings } = input;
165
169
  try {
166
170
  const result = await retrieveCardPaymentAcceptResultAsync(
171
+ // biome-ignore lint: api datasources should not be typed
167
172
  context as any,
168
173
  resultAccessCode,
169
174
  [],
@@ -194,6 +199,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
194
199
  };
195
200
  try {
196
201
  const data = await getCardPaymentAcceptPointAsync(
202
+ // biome-ignore lint: api datasources should not be typed
197
203
  context as any,
198
204
  input.cartId,
199
205
  paymentAcceptSettings,
@@ -210,6 +216,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
210
216
  const context = this.context;
211
217
  const { cartId, cartLineDeliverySpecification } = input;
212
218
  const cart = await updateLineDeliverySpecificationsAsync(
219
+ // biome-ignore lint: api datasources should not be typed
213
220
  context as any,
214
221
  cartId,
215
222
  cartLineDeliverySpecification,
@@ -231,6 +238,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
231
238
 
232
239
  // Update cartlines to remove the delivery mode
233
240
  const cartWithoutDeliveryMode = await updateCartLinesAsync(
241
+ // biome-ignore lint: api datasources should not be typed
234
242
  context as any,
235
243
  String(newCart?.Id),
236
244
  newCart?.CartLines,
@@ -251,6 +259,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
251
259
  if (!input.cartId) {
252
260
  throw new Error("CartId is required to remove a discount code");
253
261
  }
262
+ // biome-ignore lint: api datasources should not be typed
254
263
  const cart = await removeDiscountCodesAsync(context as any, input.cartId, [
255
264
  input.discountCode,
256
265
  ]);
@@ -263,6 +272,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
263
272
  throw new Error("CartId is required to add a discount code");
264
273
  }
265
274
  const cart = await addDiscountCodeAsync(
275
+ // biome-ignore lint: api datasources should not be typed
266
276
  context as any,
267
277
  cartId,
268
278
  discountCode,
@@ -279,6 +289,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
279
289
  omit(line, "Product"),
280
290
  );
281
291
  const cart = await updateCartLinesAsync(
292
+ // biome-ignore lint: api datasources should not be typed
282
293
  context as any,
283
294
  cartId,
284
295
  cartLinesWithoutProduct,
@@ -296,6 +307,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
296
307
 
297
308
  try {
298
309
  const updatedCart = await addCartLinesAsync(
310
+ // biome-ignore lint: api datasources should not be typed
299
311
  context as any,
300
312
  cartId,
301
313
  _cartLines,
@@ -304,7 +316,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
304
316
  return await mergeCartWithProducts(updatedCart, this.context);
305
317
  } catch (err) {
306
318
  console.error(err);
307
- throw new Error("Error adding to cart: " + JSON.stringify(err));
319
+ throw new Error(`Error adding to cart: ${JSON.stringify(err)}`);
308
320
  }
309
321
  }
310
322
  async updateCart(input: UpdateCartInput): Promise<Cart> {
@@ -313,6 +325,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
313
325
  if (cart.CartLines) {
314
326
  cart.CartLines.map((cl) => omit(cl, "Product"));
315
327
  }
328
+ // biome-ignore lint: api datasources should not be typed
316
329
  const updatedCart = await updateAsync(context as any, <DynCart>cart);
317
330
  return await mergeCartWithProducts(updatedCart, this.context);
318
331
  }
@@ -326,6 +339,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
326
339
  throw new Error("CartId and Version are required to delete a cart line");
327
340
  }
328
341
  const cart = await removeCartLinesAsync(
342
+ // biome-ignore lint: api datasources should not be typed
329
343
  context as any,
330
344
  cartId,
331
345
  cartLineIds,
@@ -339,6 +353,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
339
353
  let cart: DynCart;
340
354
 
341
355
  if (id) {
356
+ // biome-ignore lint: api datasources should not be typed
342
357
  cart = (await readAsync(context as any, id)) as DynCart;
343
358
  if (!cart) {
344
359
  return this.createCart(context);
@@ -359,6 +374,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
359
374
  omit(line, "Product"),
360
375
  );
361
376
  const cart = await updateCartLinesAsync(
377
+ // biome-ignore lint: api datasources should not be typed
362
378
  context as any,
363
379
  cartId,
364
380
  cartLinesWithoutProduct,
@@ -411,6 +427,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
411
427
  }
412
428
 
413
429
  async createCart(context: ICallerContext): Promise<Cart> {
430
+ // biome-ignore lint: api datasources should not be typed
414
431
  return (await createCartAsync(context as any, <DynCart>{
415
432
  CartTypeValue: CartType.Checkout,
416
433
  })) as Cart;
@@ -432,6 +449,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
432
449
  CartTypeValue: CartType.Checkout,
433
450
  IncludeAnonymous: false,
434
451
  };
452
+ // biome-ignore lint: api datasources should not be typed
435
453
  const ctx = { ...context } as any;
436
454
  ctx.queryResultSettings = queryResultSettings;
437
455
  const cartResult = await searchAsync(ctx, searchCriteria);
@@ -473,6 +491,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
473
491
  FilterByChannelFulfillmentGroup: true,
474
492
  };
475
493
  const shippingAsyncResult = await getEstimatedAvailabilityAsync(
494
+ // biome-ignore lint: api datasources should not be typed
476
495
  context as any,
477
496
  searchCriteria,
478
497
  );
@@ -489,6 +508,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
489
508
  try {
490
509
  const warehouseAsyncResult =
491
510
  await getEstimatedProductWarehouseAvailabilityAsync(
511
+ // biome-ignore lint: api datasources should not be typed
492
512
  context as any,
493
513
  bopisItems,
494
514
  2,
@@ -534,6 +554,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
534
554
  if (cart.Version) {
535
555
  try {
536
556
  const updatedCart = await addInvoicesAsync(
557
+ // biome-ignore lint: api datasources should not be typed
537
558
  context as any,
538
559
  cart.Id,
539
560
  invoiceIds,
@@ -566,6 +587,7 @@ export class D365CartDatasource extends D365DatasourceBase implements CartApi {
566
587
  if (input.cartId) {
567
588
  try {
568
589
  const updatedCart = await setInvoiceLinePriceAsync(
590
+ // biome-ignore lint: api datasources should not be typed
569
591
  context as any,
570
592
  input.cartId,
571
593
  input.cartLineId,
@@ -30,13 +30,14 @@ class D365OrderDatasource extends D365DatasourceBase implements OrderApi {
30
30
  input: GetSalesOrdersInput,
31
31
  ): Promise<SalesOrder[]> => {
32
32
  const context = await getContextFromCookie();
33
+ // biome-ignore lint: api datasources should not be typed
33
34
  const customer = await readAsync(context as any, "");
34
35
 
35
36
  const searchCriteria: OrderSearchCriteria = {
36
37
  ...input.orderSearchCriteria,
37
38
  OrderType: CustomerOrderType.SalesOrder,
38
39
  CustomerAccountNumber: customer.AccountNumber,
39
- };
40
+ } as OrderSearchCriteria;
40
41
 
41
42
  const queryResultSettings: QueryResultSettings = {};
42
43
  if (input.paging) queryResultSettings.Paging = input.paging;
@@ -45,10 +46,12 @@ class D365OrderDatasource extends D365DatasourceBase implements OrderApi {
45
46
  Columns: [input.sort],
46
47
  };
47
48
 
49
+ // biome-ignore lint: api datasources should not be typed
48
50
  (context as any).queryResultSettings = queryResultSettings;
49
51
 
50
52
  try {
51
53
  const searchOrdersResults = await searchOrdersAsync(
54
+ // biome-ignore lint: api datasources should not be typed
52
55
  context as any,
53
56
  searchCriteria,
54
57
  null,
@@ -72,6 +75,7 @@ class D365OrderDatasource extends D365DatasourceBase implements OrderApi {
72
75
  if (transactionId) {
73
76
  try {
74
77
  data = await getSalesOrderDetailsByTransactionIdAsync(
78
+ // biome-ignore lint: api datasources should not be typed
75
79
  context as any,
76
80
  transactionId,
77
81
  SearchLocation.All,
@@ -83,6 +87,7 @@ class D365OrderDatasource extends D365DatasourceBase implements OrderApi {
83
87
  } else {
84
88
  try {
85
89
  data = await getSalesOrderDetailsBySalesIdAsync(
90
+ // biome-ignore lint: api datasources should not be typed
86
91
  context as any,
87
92
  salesId,
88
93
  );