@reactionary/source 0.3.18 → 0.6.2

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 (191) hide show
  1. package/README.md +28 -14
  2. package/core/src/client/client-builder.ts +54 -6
  3. package/core/src/factories/cart.factory.ts +40 -0
  4. package/core/src/factories/category.factory.ts +40 -0
  5. package/core/src/factories/checkout.factory.ts +59 -0
  6. package/core/src/factories/identity.factory.ts +26 -0
  7. package/core/src/factories/index.ts +15 -0
  8. package/core/src/factories/inventory.factory.ts +26 -0
  9. package/core/src/factories/order-search.factory.ts +33 -0
  10. package/core/src/factories/order.factory.ts +21 -0
  11. package/core/src/factories/price.factory.ts +29 -0
  12. package/core/src/factories/product-associations.factory.ts +30 -0
  13. package/core/src/factories/product-list.factory.ts +82 -0
  14. package/core/src/factories/product-reviews.factory.ts +58 -0
  15. package/core/src/factories/product-search.factory.ts +31 -0
  16. package/core/src/factories/product.factory.ts +21 -0
  17. package/core/src/factories/profile.factory.ts +23 -0
  18. package/core/src/factories/store.factory.ts +21 -0
  19. package/core/src/index.ts +3 -1
  20. package/core/src/providers/cart.provider.ts +12 -10
  21. package/core/src/providers/category.provider.ts +9 -7
  22. package/core/src/providers/checkout.provider.ts +14 -10
  23. package/core/src/providers/identity.provider.ts +7 -7
  24. package/core/src/providers/inventory.provider.ts +4 -4
  25. package/core/src/providers/order-search.provider.ts +4 -2
  26. package/core/src/providers/order.provider.ts +4 -4
  27. package/core/src/providers/price.provider.ts +5 -5
  28. package/core/src/providers/product-associations.provider.ts +6 -4
  29. package/core/src/providers/product-list.provider.ts +13 -8
  30. package/core/src/providers/product-reviews.provider.ts +8 -4
  31. package/core/src/providers/product-search.provider.ts +15 -28
  32. package/core/src/providers/product.provider.ts +6 -6
  33. package/core/src/providers/profile.provider.ts +8 -8
  34. package/core/src/providers/store.provider.ts +2 -2
  35. package/core/src/test/client-builder.spec.ts +81 -0
  36. package/examples/node/package.json +7 -7
  37. package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +10 -4
  38. package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +8 -2
  39. package/examples/node/src/basic/basic-node-setup.spec.ts +5 -1
  40. package/examples/node/src/basic/client-creation.spec.ts +3 -3
  41. package/examples/node/src/utils.ts +41 -41
  42. package/examples/node/tsconfig.lib.json +2 -1
  43. package/package.json +1 -1
  44. package/providers/algolia/src/core/initialize.ts +76 -21
  45. package/providers/algolia/src/core/initialize.types.ts +107 -0
  46. package/providers/algolia/src/factories/index.ts +1 -0
  47. package/providers/algolia/src/factories/product-search/product-search.factory.ts +182 -0
  48. package/providers/algolia/src/index.ts +2 -2
  49. package/providers/algolia/src/providers/analytics.provider.ts +4 -4
  50. package/providers/algolia/src/providers/product-search.provider.ts +67 -184
  51. package/providers/algolia/src/schema/capabilities.schema.ts +81 -7
  52. package/providers/algolia/src/test/analytics.spec.ts +9 -1
  53. package/providers/algolia/src/test/client-builder-product-search-extension.example.ts +85 -0
  54. package/providers/commercetools/src/core/capability-descriptors.ts +324 -0
  55. package/providers/commercetools/src/core/initialize.ts +35 -151
  56. package/providers/commercetools/src/core/initialize.types.ts +174 -0
  57. package/providers/commercetools/src/factories/cart/cart.factory.ts +142 -0
  58. package/providers/commercetools/src/factories/category/category.factory.ts +77 -0
  59. package/providers/commercetools/src/factories/checkout/checkout-initializer-overrides.example.ts +94 -0
  60. package/providers/commercetools/src/factories/checkout/checkout.factory.ts +338 -0
  61. package/providers/commercetools/src/factories/identity/identity.factory.ts +26 -0
  62. package/providers/commercetools/src/factories/inventory/inventory.factory.ts +49 -0
  63. package/providers/commercetools/src/factories/order/order.factory.ts +149 -0
  64. package/providers/commercetools/src/factories/order-search/order-search.factory.ts +108 -0
  65. package/providers/commercetools/src/factories/price/price.factory.ts +76 -0
  66. package/providers/commercetools/src/factories/product/product-factory-baseline.example.ts +14 -0
  67. package/providers/commercetools/src/factories/product/product-factory-schema-and-parse-extension.example.ts +35 -0
  68. package/providers/commercetools/src/factories/product/product-factory-schema-extension.example.ts +23 -0
  69. package/providers/commercetools/src/factories/product/product-initializer-factory-extension.example.ts +41 -0
  70. package/providers/commercetools/src/factories/product/product-provider-custom-method-only.example.ts +47 -0
  71. package/providers/commercetools/src/factories/product/product-provider-schema-signature-extension.example.ts +61 -0
  72. package/providers/commercetools/src/factories/product/product.factory.ts +220 -0
  73. package/providers/commercetools/src/factories/product/utils.example.ts +9 -0
  74. package/providers/commercetools/src/factories/product-associations/product-associations.factory.ts +103 -0
  75. package/providers/commercetools/src/factories/product-list/product-list.factory.ts +122 -0
  76. package/providers/commercetools/src/factories/product-reviews/product-reviews.factory.ts +81 -0
  77. package/providers/commercetools/src/factories/product-search/product-search.factory.ts +182 -0
  78. package/providers/commercetools/src/factories/profile/profile.factory.ts +94 -0
  79. package/providers/commercetools/src/factories/store/store.factory.ts +49 -0
  80. package/providers/commercetools/src/index.ts +15 -0
  81. package/providers/commercetools/src/providers/cart.provider.ts +67 -193
  82. package/providers/commercetools/src/providers/category.provider.ts +24 -64
  83. package/providers/commercetools/src/providers/checkout.provider.ts +50 -322
  84. package/providers/commercetools/src/providers/identity.provider.ts +35 -15
  85. package/providers/commercetools/src/providers/inventory.provider.ts +13 -31
  86. package/providers/commercetools/src/providers/order-search.provider.ts +16 -110
  87. package/providers/commercetools/src/providers/order.provider.ts +13 -144
  88. package/providers/commercetools/src/providers/price.provider.ts +37 -51
  89. package/providers/commercetools/src/providers/product-associations.provider.ts +39 -104
  90. package/providers/commercetools/src/providers/product-list.provider.ts +38 -23
  91. package/providers/commercetools/src/providers/product-reviews.provider.ts +34 -14
  92. package/providers/commercetools/src/providers/product-search.provider.ts +17 -170
  93. package/providers/commercetools/src/providers/product.provider.ts +20 -199
  94. package/providers/commercetools/src/providers/profile.provider.ts +27 -73
  95. package/providers/commercetools/src/providers/store.provider.ts +13 -31
  96. package/providers/commercetools/src/schema/capabilities.schema.ts +258 -20
  97. package/providers/commercetools/src/test/caching.spec.ts +18 -2
  98. package/providers/commercetools/src/test/client-builder-merge-extensions.example.ts +125 -0
  99. package/providers/fake/src/core/initialize.ts +213 -44
  100. package/providers/fake/src/core/initialize.types.ts +164 -0
  101. package/providers/fake/src/factories/cart/cart.factory.ts +34 -0
  102. package/providers/fake/src/factories/category/category.factory.ts +40 -0
  103. package/providers/fake/src/factories/checkout/checkout.factory.ts +53 -0
  104. package/providers/fake/src/factories/identity/identity.factory.ts +25 -0
  105. package/providers/fake/src/factories/index.ts +14 -0
  106. package/providers/fake/src/factories/inventory/inventory.factory.ts +25 -0
  107. package/providers/fake/src/factories/order/order.factory.ts +22 -0
  108. package/providers/fake/src/factories/order-search/order-search.factory.ts +27 -0
  109. package/providers/fake/src/factories/price/price.factory.ts +26 -0
  110. package/providers/fake/src/factories/product/product.factory.ts +22 -0
  111. package/providers/fake/src/factories/product-associations/product-associations.factory.ts +25 -0
  112. package/providers/fake/src/factories/product-reviews/product-reviews.factory.ts +53 -0
  113. package/providers/fake/src/factories/product-search/product-search.factory.ts +27 -0
  114. package/providers/fake/src/factories/profile/profile.factory.ts +22 -0
  115. package/providers/fake/src/factories/store/store.factory.ts +22 -0
  116. package/providers/fake/src/index.ts +2 -0
  117. package/providers/fake/src/providers/cart.provider.ts +23 -14
  118. package/providers/fake/src/providers/category.provider.ts +120 -105
  119. package/providers/fake/src/providers/checkout.provider.ts +39 -20
  120. package/providers/fake/src/providers/identity.provider.ts +40 -34
  121. package/providers/fake/src/providers/inventory.provider.ts +26 -24
  122. package/providers/fake/src/providers/order-search.provider.ts +38 -30
  123. package/providers/fake/src/providers/order.provider.ts +21 -37
  124. package/providers/fake/src/providers/price.provider.ts +42 -34
  125. package/providers/fake/src/providers/product-associations.provider.ts +23 -10
  126. package/providers/fake/src/providers/product-reviews.provider.ts +71 -69
  127. package/providers/fake/src/providers/product-search.provider.ts +43 -70
  128. package/providers/fake/src/providers/product.provider.ts +34 -32
  129. package/providers/fake/src/providers/profile.provider.ts +62 -55
  130. package/providers/fake/src/providers/store.provider.ts +38 -22
  131. package/providers/fake/src/schema/capabilities.schema.ts +175 -18
  132. package/providers/fake/src/test/cart.provider.spec.ts +20 -3
  133. package/providers/fake/src/test/category.provider.spec.ts +4 -1
  134. package/providers/fake/src/test/checkout.provider.spec.ts +12 -2
  135. package/providers/fake/src/test/client-builder-product-extension.example.ts +75 -0
  136. package/providers/fake/src/test/order-search.provider.spec.ts +4 -7
  137. package/providers/fake/src/test/order.provider.spec.ts +4 -6
  138. package/providers/fake/src/test/price.provider.spec.ts +3 -1
  139. package/providers/fake/src/test/product.provider.spec.ts +8 -2
  140. package/providers/fake/src/test/profile.provider.spec.ts +4 -2
  141. package/providers/google-analytics/src/core/initialize.ts +37 -12
  142. package/providers/google-analytics/src/core/initialize.types.ts +47 -0
  143. package/providers/google-analytics/src/index.ts +1 -0
  144. package/providers/google-analytics/src/schema/capabilities.schema.ts +31 -5
  145. package/providers/medusa/src/core/initialize.ts +324 -81
  146. package/providers/medusa/src/core/initialize.types.ts +184 -0
  147. package/providers/medusa/src/factories/cart/cart.factory.ts +34 -0
  148. package/providers/medusa/src/factories/category/category.factory.ts +37 -0
  149. package/providers/medusa/src/factories/checkout/checkout.factory.ts +50 -0
  150. package/providers/medusa/src/factories/identity/identity.factory.ts +22 -0
  151. package/providers/medusa/src/factories/index.ts +12 -0
  152. package/providers/medusa/src/factories/inventory/inventory.factory.ts +25 -0
  153. package/providers/medusa/src/factories/order/order.factory.ts +22 -0
  154. package/providers/medusa/src/factories/order-search/order-search.factory.ts +27 -0
  155. package/providers/medusa/src/factories/price/price.factory.ts +26 -0
  156. package/providers/medusa/src/factories/product/product.factory.ts +22 -0
  157. package/providers/medusa/src/factories/product-associations/product-associations.factory.ts +25 -0
  158. package/providers/medusa/src/factories/product-search/product-search.factory.ts +27 -0
  159. package/providers/medusa/src/factories/profile/profile.factory.ts +22 -0
  160. package/providers/medusa/src/index.ts +2 -0
  161. package/providers/medusa/src/providers/cart.provider.ts +33 -20
  162. package/providers/medusa/src/providers/category.provider.ts +30 -12
  163. package/providers/medusa/src/providers/checkout.provider.ts +42 -17
  164. package/providers/medusa/src/providers/identity.provider.ts +1 -1
  165. package/providers/medusa/src/providers/inventory.provider.ts +21 -7
  166. package/providers/medusa/src/providers/order-search.provider.ts +16 -5
  167. package/providers/medusa/src/providers/order.provider.ts +17 -5
  168. package/providers/medusa/src/providers/price.provider.ts +26 -7
  169. package/providers/medusa/src/providers/product-associations.provider.ts +19 -8
  170. package/providers/medusa/src/providers/product-search.provider.ts +19 -31
  171. package/providers/medusa/src/providers/product.provider.ts +47 -11
  172. package/providers/medusa/src/providers/profile.provider.ts +35 -11
  173. package/providers/medusa/src/schema/capabilities.schema.ts +229 -18
  174. package/providers/medusa/src/test/cart.provider.spec.ts +18 -2
  175. package/providers/medusa/src/test/category.provider.spec.ts +4 -1
  176. package/providers/medusa/src/test/checkout.spec.ts +9 -9
  177. package/providers/medusa/src/test/inventory.provider.spec.ts +3 -1
  178. package/providers/medusa/src/test/large-cart.provider.spec.ts +8 -2
  179. package/providers/medusa/src/test/price.provider.spec.ts +8 -1
  180. package/providers/medusa/src/test/product.provider.spec.ts +3 -1
  181. package/providers/medusa/src/test/search.provider.spec.ts +16 -3
  182. package/providers/meilisearch/src/core/initialize.ts +88 -21
  183. package/providers/meilisearch/src/core/initialize.types.ts +119 -0
  184. package/providers/meilisearch/src/factories/index.ts +2 -0
  185. package/providers/meilisearch/src/factories/order-search/order-search.factory.ts +27 -0
  186. package/providers/meilisearch/src/factories/product-search/product-search.factory.ts +27 -0
  187. package/providers/meilisearch/src/index.ts +2 -0
  188. package/providers/meilisearch/src/providers/index.ts +1 -0
  189. package/providers/meilisearch/src/providers/order-search.provider.ts +21 -6
  190. package/providers/meilisearch/src/providers/product-search.provider.ts +24 -8
  191. package/providers/meilisearch/src/schema/capabilities.schema.ts +95 -8
@@ -1,6 +1,9 @@
1
1
  import {
2
2
  CartIdentifierSchema,
3
- CartItemSchema,
3
+ type CartFactory,
4
+ type CartFactoryCartOutput,
5
+ type CartFactoryIdentifierOutput,
6
+ type CartFactoryWithOutput,
4
7
  CartMutationApplyCouponSchema,
5
8
  CartMutationChangeCurrencySchema,
6
9
  CartMutationDeleteCartSchema,
@@ -18,72 +21,75 @@ import {
18
21
  assertSuccess,
19
22
  } from '@reactionary/core';
20
23
  import type {
21
- CartItem,
22
24
  CartMutationItemAdd,
23
25
  CartMutationItemQuantityChange,
24
26
  CartMutationItemRemove,
25
27
  CartQueryById,
26
- CartIdentifier,
27
28
  CartMutationApplyCoupon,
28
29
  CartMutationDeleteCart,
29
30
  CartMutationRemoveCoupon,
30
31
  CartMutationChangeCurrency,
31
32
  RequestContext,
32
- Cart,
33
- Currency,
33
+ CartIdentifier,
34
34
  Cache,
35
- CostBreakDown,
36
35
  Result,
37
36
  NotFoundError,
38
37
  Promotion,
39
38
  } from '@reactionary/core';
40
39
  import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
41
- import type {
42
- Cart as CTCart,
43
- LineItem,
44
- MyCartUpdateAction,
45
- } from '@commercetools/platform-sdk';
40
+ import type { MyCartUpdateAction } from '@commercetools/platform-sdk';
46
41
  import type { CommercetoolsCartIdentifier } from '../schema/commercetools.schema.js';
47
42
  import { CommercetoolsCartIdentifierSchema } from '../schema/commercetools.schema.js';
48
43
  import type { CommercetoolsAPI } from '../core/client.js';
44
+ import type { CommercetoolsCartFactory } from '../factories/cart/cart.factory.js';
49
45
 
50
- export class CommercetoolsCartProvider extends CartProvider {
46
+ export class CommercetoolsCartProvider<
47
+ TFactory extends CartFactory = CommercetoolsCartFactory,
48
+ > extends CartProvider<CartFactoryCartOutput<TFactory>, CartIdentifier> {
51
49
  protected config: CommercetoolsConfiguration;
52
50
  protected commercetools: CommercetoolsAPI;
53
51
  protected expandedCartFields = ['discountCodes[*].discountCode'];
52
+ protected factory: CartFactoryWithOutput<TFactory>;
53
+
54
54
  constructor(
55
55
  config: CommercetoolsConfiguration,
56
56
  cache: Cache,
57
57
  context: RequestContext,
58
- commercetools: CommercetoolsAPI
58
+ commercetools: CommercetoolsAPI,
59
+ factory: CartFactoryWithOutput<TFactory>,
59
60
  ) {
60
61
  super(cache, context);
61
62
 
62
63
  this.config = config;
63
64
  this.commercetools = commercetools;
65
+ this.factory = factory;
64
66
  }
65
67
 
66
68
  @Reactionary({
67
69
  inputSchema: CartQueryByIdSchema,
68
70
  outputSchema: CartSchema,
69
71
  })
70
- public override async getById(payload: CartQueryById): Promise<Result<Cart, NotFoundError>> {
72
+ public override async getById(
73
+ payload: CartQueryById,
74
+ ): Promise<Result<CartFactoryCartOutput<TFactory>, NotFoundError>> {
71
75
  const client = await this.getClient();
72
76
  const ctId = payload.cart as CommercetoolsCartIdentifier;
73
77
 
74
78
  try {
75
- const remote = await client.carts.withId({ ID: ctId.key }).get(
76
- {
79
+ const remote = await client.carts
80
+ .withId({ ID: ctId.key })
81
+ .get({
77
82
  queryArgs: {
78
- expand: this.expandedCartFields
79
- }
80
- }).execute();
83
+ expand: this.expandedCartFields,
84
+ },
85
+ })
86
+ .execute();
81
87
 
82
- return success(this.parseSingle(remote.body));
83
- } catch(err) {
88
+ return success(this.factory.parseCart(this.context, remote.body));
89
+ } catch (err) {
84
90
  return error<NotFoundError>({
85
91
  type: 'NotFound',
86
- identifier: ctId
92
+ identifier: ctId,
87
93
  });
88
94
  }
89
95
  }
@@ -92,13 +98,16 @@ export class CommercetoolsCartProvider extends CartProvider {
92
98
  inputSchema: CartMutationItemAddSchema,
93
99
  outputSchema: CartSchema,
94
100
  })
95
- public override async add(payload: CartMutationItemAdd): Promise<Result<Cart>> {
101
+ public override async add(
102
+ payload: CartMutationItemAdd,
103
+ ): Promise<Result<CartFactoryCartOutput<TFactory>>> {
96
104
  let cartIdentifier = payload.cart;
97
105
  if (!cartIdentifier) {
98
106
  cartIdentifier = await this.createCart();
99
107
  }
100
108
 
101
- const channelId = await this.commercetools.resolveChannelIdByRole('Primary');
109
+ const channelId =
110
+ await this.commercetools.resolveChannelIdByRole('Primary');
102
111
 
103
112
  const result = await this.applyActions(cartIdentifier, [
104
113
  {
@@ -123,7 +132,9 @@ export class CommercetoolsCartProvider extends CartProvider {
123
132
  inputSchema: CartMutationItemRemoveSchema,
124
133
  outputSchema: CartSchema,
125
134
  })
126
- public override async remove(payload: CartMutationItemRemove): Promise<Result<Cart>> {
135
+ public override async remove(
136
+ payload: CartMutationItemRemove,
137
+ ): Promise<Result<CartFactoryCartOutput<TFactory>>> {
127
138
  const result = await this.applyActions(payload.cart, [
128
139
  {
129
140
  action: 'removeLineItem',
@@ -142,8 +153,8 @@ export class CommercetoolsCartProvider extends CartProvider {
142
153
  outputSchema: CartSchema,
143
154
  })
144
155
  public override async changeQuantity(
145
- payload: CartMutationItemQuantityChange
146
- ): Promise<Result<Cart>> {
156
+ payload: CartMutationItemQuantityChange,
157
+ ): Promise<Result<CartFactoryCartOutput<TFactory>>> {
147
158
  if (payload.quantity === 0) {
148
159
  // Changing quantity to 0 is not allowed. Use the remove call instead. This is done to avoid accidental removal of item.
149
160
  // Calls with quantity 0 will just be ignored.
@@ -172,11 +183,13 @@ export class CommercetoolsCartProvider extends CartProvider {
172
183
  @Reactionary({
173
184
  outputSchema: CartIdentifierSchema,
174
185
  })
175
- public override async getActiveCartId(): Promise<Result<CartIdentifier, NotFoundError>> {
186
+ public override async getActiveCartId(): Promise<
187
+ Result<CartIdentifier, NotFoundError>
188
+ > {
176
189
  const client = await this.getClient();
177
190
  try {
178
191
  const carts = await client.activeCart.get().execute();
179
- const result = await CommercetoolsCartIdentifierSchema.parse({
192
+ const result = this.factory.parseCartIdentifier(this.context, {
180
193
  key: carts.body.id,
181
194
  version: carts.body.version || 0,
182
195
  });
@@ -185,8 +198,8 @@ export class CommercetoolsCartProvider extends CartProvider {
185
198
  } catch (e: any) {
186
199
  return error<NotFoundError>({
187
200
  type: 'NotFound',
188
- identifier: {}
189
- })
201
+ identifier: {},
202
+ });
190
203
  }
191
204
  }
192
205
 
@@ -194,7 +207,7 @@ export class CommercetoolsCartProvider extends CartProvider {
194
207
  inputSchema: CartMutationDeleteCartSchema,
195
208
  })
196
209
  public override async deleteCart(
197
- payload: CartMutationDeleteCart
210
+ payload: CartMutationDeleteCart,
198
211
  ): Promise<Result<void>> {
199
212
  const client = await this.getClient();
200
213
  if (payload.cart.key) {
@@ -218,9 +231,9 @@ export class CommercetoolsCartProvider extends CartProvider {
218
231
  inputSchema: CartMutationApplyCouponSchema,
219
232
  outputSchema: CartSchema,
220
233
  })
221
- public override async applyCouponCode(
222
- payload: CartMutationApplyCoupon
223
- ): Promise<Result<Cart>> {
234
+ public override async applyCouponCode(
235
+ payload: CartMutationApplyCoupon,
236
+ ): Promise<Result<CartFactoryCartOutput<TFactory>>> {
224
237
  const result = await this.applyActions(payload.cart, [
225
238
  {
226
239
  action: 'addDiscountCode',
@@ -239,24 +252,25 @@ export class CommercetoolsCartProvider extends CartProvider {
239
252
  outputSchema: CartSchema,
240
253
  })
241
254
  public override async removeCouponCode(
242
- payload: CartMutationRemoveCoupon
243
- ): Promise<Result<Cart>> {
244
-
255
+ payload: CartMutationRemoveCoupon,
256
+ ): Promise<Result<CartFactoryCartOutput<TFactory>>> {
245
257
  const client = await this.getClient();
246
258
  const currentCart = await client.carts
247
259
  .withId({ ID: payload.cart.key })
248
260
  .get({
249
261
  queryArgs: {
250
- expand: this.expandedCartFields
251
- }
262
+ expand: this.expandedCartFields,
263
+ },
252
264
  })
253
265
  .execute();
254
266
 
255
- const discountCodeReference = currentCart.body.discountCodes?.find(dc => dc.discountCode.obj?.code === payload.couponCode)?.discountCode;
267
+ const discountCodeReference = currentCart.body.discountCodes?.find(
268
+ (dc) => dc.discountCode.obj?.code === payload.couponCode,
269
+ )?.discountCode;
256
270
 
257
271
  if (!discountCodeReference) {
258
272
  // Coupon code is not applied to the cart, so we can just return the cart as is.
259
- return success(this.parseSingle(currentCart.body));
273
+ return success(this.factory.parseCart(this.context, currentCart.body));
260
274
  }
261
275
  const result = await this.applyActions(payload.cart, [
262
276
  {
@@ -279,8 +293,8 @@ export class CommercetoolsCartProvider extends CartProvider {
279
293
  outputSchema: CartSchema,
280
294
  })
281
295
  public override async changeCurrency(
282
- payload: CartMutationChangeCurrency
283
- ): Promise<Result<Cart>> {
296
+ payload: CartMutationChangeCurrency,
297
+ ): Promise<Result<CartFactoryCartOutput<TFactory>>> {
284
298
  // ok, to do this we have to actually build a new cart, copy over all the items, and then delete the old cart.
285
299
  // because Commercetools does not support changing currency of an existing cart.
286
300
 
@@ -310,7 +324,7 @@ export class CommercetoolsCartProvider extends CartProvider {
310
324
  action: 'addLineItem',
311
325
  sku: item.variant.sku || '',
312
326
  quantity: item.quantity,
313
- })
327
+ }),
314
328
  );
315
329
 
316
330
  const response = await this.applyActions(newCartId, [
@@ -344,12 +358,12 @@ export class CommercetoolsCartProvider extends CartProvider {
344
358
  locale: this.context.languageContext.locale,
345
359
  },
346
360
  queryArgs: {
347
- expand: this.expandedCartFields
348
- }
361
+ expand: this.expandedCartFields,
362
+ },
349
363
  })
350
364
  .execute();
351
365
 
352
- return CommercetoolsCartIdentifierSchema.parse({
366
+ return this.factory.parseCartIdentifier(this.context, {
353
367
  key: response.body.id,
354
368
  version: response.body.version || 0,
355
369
  });
@@ -357,8 +371,8 @@ export class CommercetoolsCartProvider extends CartProvider {
357
371
 
358
372
  protected async applyActions(
359
373
  cart: CartIdentifier,
360
- actions: MyCartUpdateAction[]
361
- ): Promise<Cart> {
374
+ actions: MyCartUpdateAction[],
375
+ ): Promise<CartFactoryCartOutput<TFactory>> {
362
376
  const client = await this.getClient();
363
377
  const ctId = cart as CommercetoolsCartIdentifier;
364
378
 
@@ -371,15 +385,15 @@ export class CommercetoolsCartProvider extends CartProvider {
371
385
  actions,
372
386
  },
373
387
  queryArgs: {
374
- expand: this.expandedCartFields
375
- }
388
+ expand: this.expandedCartFields,
389
+ },
376
390
  })
377
391
  .execute();
378
392
 
379
393
  if (response.error) {
380
394
  console.error(response.error);
381
395
  }
382
- return this.parseSingle(response.body);
396
+ return this.factory.parseCart(this.context, response.body);
383
397
  } catch (e: any) {
384
398
  console.error('Error applying actions to cart:', e);
385
399
  throw e;
@@ -404,144 +418,4 @@ export class CommercetoolsCartProvider extends CartProvider {
404
418
  orders: clientWithProject.me().orders(),
405
419
  };
406
420
  }
407
-
408
- protected parseCartItem(remoteItem: LineItem): CartItem {
409
- const unitPrice = remoteItem.price.value.centAmount;
410
- const totalPrice = remoteItem.totalPrice.centAmount || 0;
411
- let itemDiscount = 0;
412
-
413
- // look, discounts are weird in commercetools.... i think the .price.discount only applies for embedded prices maybe?
414
-
415
- if (remoteItem.discountedPricePerQuantity && remoteItem.discountedPricePerQuantity.length > 0) {
416
- itemDiscount = remoteItem.discountedPricePerQuantity.reduce((sum, discPrQty) => {
417
- return sum + discPrQty.quantity * discPrQty.discountedPrice?.includedDiscounts?.
418
- reduce((sum, discount) => sum + discount.discountedAmount.centAmount, 0) || 0
419
- }, 0);
420
- }
421
-
422
- const totalDiscount = (remoteItem.price.discounted?.value.centAmount || 0) + itemDiscount;
423
-
424
- const unitDiscount = totalDiscount / remoteItem.quantity;
425
- const currency =
426
- remoteItem.price.value.currencyCode.toUpperCase() as Currency;
427
-
428
- const item = {
429
- identifier: {
430
- key: remoteItem.id,
431
- },
432
- product: {
433
- key: remoteItem.productId,
434
- },
435
- variant: {
436
- sku: remoteItem.variant.sku || '',
437
- },
438
- quantity: remoteItem.quantity,
439
- price: {
440
- unitPrice: {
441
- value: unitPrice / 100,
442
- currency,
443
- },
444
- unitDiscount: {
445
- value: unitDiscount / 100,
446
- currency,
447
- },
448
- totalPrice: {
449
- value: (totalPrice || 0) / 100,
450
- currency,
451
- },
452
- totalDiscount: {
453
- value: totalDiscount / 100,
454
- currency,
455
- },
456
- },
457
- } satisfies CartItem;
458
-
459
- return CartItemSchema.parse(item);
460
- }
461
-
462
- protected parseSingle(remote: CTCart): Cart {
463
- const identifier = {
464
- key: remote.id,
465
- version: remote.version || 0,
466
- } satisfies CommercetoolsCartIdentifier;
467
-
468
-
469
- const items = new Array<CartItem>();
470
- for (const remoteItem of remote.lineItems) {
471
- const item = this.parseCartItem(remoteItem);
472
-
473
- items.push(item);
474
- }
475
-
476
-
477
- const grandTotal = remote.totalPrice.centAmount || 0;
478
- const shippingTotal = remote.shippingInfo?.price.centAmount || 0;
479
- const productTotal = grandTotal - shippingTotal;
480
- const taxTotal = remote.taxedPrice?.totalTax?.centAmount || 0;
481
-
482
- // i think this is missing some elements still?
483
- const discountTotal =
484
- (remote.discountOnTotalPrice?.discountedAmount.centAmount || 0) + items.reduce((sum, item) => sum + (item.price.totalDiscount.value * 100 || 0), 0);
485
- const surchargeTotal = 0;
486
- const currency = remote.totalPrice.currencyCode as Currency;
487
-
488
- const price = {
489
- totalTax: {
490
- value: taxTotal / 100,
491
- currency,
492
- },
493
- totalDiscount: {
494
- value: discountTotal / 100,
495
- currency,
496
- },
497
- totalSurcharge: {
498
- value: surchargeTotal / 100,
499
- currency,
500
- },
501
- totalShipping: {
502
- value: shippingTotal / 100,
503
- currency,
504
- },
505
- totalProductPrice: {
506
- value: productTotal / 100,
507
- currency,
508
- },
509
- grandTotal: {
510
- value: grandTotal / 100,
511
- currency,
512
- },
513
- } satisfies CostBreakDown;
514
-
515
-
516
- const localeString = this.context.languageContext.locale || 'en'
517
- const appliedPromotions = [];
518
- if (remote.discountCodes) {
519
- for (const promo of remote.discountCodes) {
520
- appliedPromotions.push({
521
- code: promo.discountCode.obj?.code || '',
522
- isCouponCode: true,
523
- name: promo.discountCode.obj?.name?.[localeString] || '',
524
- description: promo.discountCode.obj?.description?.[localeString] || '',
525
- } satisfies Promotion);
526
- }
527
- }
528
-
529
- // if we want to include the nice name and description of the non-coupon promotions, we have to do some extra work to fetch the referenced promotions and include them here,
530
- // as the max expand level is 3, and the information is at level 5.
531
- // For now, we will just include the coupon codes, as that is the most common use case.
532
-
533
- const cart = {
534
- identifier,
535
- userId: {
536
- userId: '???',
537
- },
538
- name: remote.custom?.fields['name'] || '',
539
- description: remote.custom?.fields['description'] || '',
540
- price,
541
- appliedPromotions,
542
- items,
543
- } satisfies Cart;
544
-
545
- return cart;
546
- }
547
421
  }
@@ -1,5 +1,8 @@
1
1
  import {
2
2
  CategoryPaginatedResultSchema,
3
+ type CategoryFactory,
4
+ type CategoryFactoryCategoryOutput,
5
+ type CategoryFactoryWithOutput,
3
6
  CategoryProvider,
4
7
  CategoryQueryByIdSchema,
5
8
  CategoryQueryBySlugSchema,
@@ -19,9 +22,7 @@ import type {
19
22
  CategoryQueryForTopCategories,
20
23
  RequestContext,
21
24
  Cache,
22
- Category,
23
25
  CategoryPaginatedResult,
24
- CategoryIdentifier,
25
26
  Result,
26
27
  NotFoundError,
27
28
  } from '@reactionary/core';
@@ -29,25 +30,32 @@ import * as z from 'zod';
29
30
  import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
30
31
  import type {
31
32
  ByProjectKeyCategoriesRequestBuilder,
32
- CategoryPagedQueryResponse,
33
- Category as CTCategory,
34
33
  } from '@commercetools/platform-sdk';
35
34
  import type { CommercetoolsAPI } from '../core/client.js';
35
+ import type { CommercetoolsCategoryFactory } from '../factories/category/category.factory.js';
36
36
 
37
- export class CommercetoolsCategoryProvider extends CategoryProvider {
37
+ export class CommercetoolsCategoryProvider<
38
+ TFactory extends CategoryFactory = CommercetoolsCategoryFactory,
39
+ > extends CategoryProvider<
40
+ CategoryFactoryCategoryOutput<TFactory>,
41
+ CategoryPaginatedResult
42
+ > {
38
43
  protected config: CommercetoolsConfiguration;
39
44
  protected commercetools: CommercetoolsAPI;
45
+ protected factory: CategoryFactoryWithOutput<TFactory>;
40
46
 
41
47
  constructor(
42
48
  config: CommercetoolsConfiguration,
43
49
  cache: Cache,
44
50
  context: RequestContext,
45
- commercetools: CommercetoolsAPI
51
+ commercetools: CommercetoolsAPI,
52
+ factory: CategoryFactoryWithOutput<TFactory>,
46
53
  ) {
47
54
  super(cache, context);
48
55
 
49
56
  this.config = config;
50
57
  this.commercetools = commercetools;
58
+ this.factory = factory;
51
59
  }
52
60
 
53
61
  protected async getClient(): Promise<ByProjectKeyCategoriesRequestBuilder> {
@@ -66,14 +74,14 @@ export class CommercetoolsCategoryProvider extends CategoryProvider {
66
74
  })
67
75
  public override async getById(
68
76
  payload: CategoryQueryById
69
- ): Promise<Result<Category, NotFoundError>> {
77
+ ): Promise<Result<CategoryFactoryCategoryOutput<TFactory>, NotFoundError>> {
70
78
  const client = await this.getClient();
71
79
  try {
72
80
  const response = await client
73
81
  .withKey({ key: payload.id.key })
74
82
  .get()
75
83
  .execute();
76
- return success(this.parseSingle(response.body));
84
+ return success(this.factory.parseCategory(this.context, response.body));
77
85
  } catch (err) {
78
86
  return error<NotFoundError>({
79
87
  type: 'NotFound',
@@ -91,7 +99,7 @@ export class CommercetoolsCategoryProvider extends CategoryProvider {
91
99
  })
92
100
  public override async getBySlug(
93
101
  payload: CategoryQueryBySlug
94
- ): Promise<Result<Category, NotFoundError>> {
102
+ ): Promise<Result<CategoryFactoryCategoryOutput<TFactory>, NotFoundError>> {
95
103
  const client = await this.getClient();
96
104
  try {
97
105
  const response = await client
@@ -111,7 +119,7 @@ export class CommercetoolsCategoryProvider extends CategoryProvider {
111
119
  identifier: payload.slug,
112
120
  });
113
121
  }
114
- return success(this.parseSingle(response.body.results[0]));
122
+ return success(this.factory.parseCategory(this.context, response.body.results[0]));
115
123
  } catch (err) {
116
124
  console.error(`Error fetching category by slug:`, error);
117
125
  return error<NotFoundError>({
@@ -131,9 +139,9 @@ export class CommercetoolsCategoryProvider extends CategoryProvider {
131
139
  })
132
140
  public override async getBreadcrumbPathToCategory(
133
141
  payload: CategoryQueryForBreadcrumb
134
- ): Promise<Result<Category[]>> {
142
+ ): Promise<Result<Array<CategoryFactoryCategoryOutput<TFactory>>>> {
135
143
  const client = await this.getClient();
136
- const path = new Array<Category>();
144
+ const path: Array<CategoryFactoryCategoryOutput<TFactory>> = [];
137
145
  try {
138
146
  const response = await client
139
147
  .withKey({ key: payload.id.key })
@@ -144,10 +152,10 @@ export class CommercetoolsCategoryProvider extends CategoryProvider {
144
152
  })
145
153
  .execute();
146
154
 
147
- const category = this.parseSingle(response.body);
155
+ const category = this.factory.parseCategory(this.context, response.body);
148
156
  for (const anc of response.body.ancestors || []) {
149
157
  if (anc.obj) {
150
- const parsedAnc = this.parseSingle(anc.obj);
158
+ const parsedAnc = this.factory.parseCategory(this.context, anc.obj);
151
159
  path.push(parsedAnc);
152
160
  }
153
161
  }
@@ -205,7 +213,7 @@ export class CommercetoolsCategoryProvider extends CategoryProvider {
205
213
  })
206
214
  .execute();
207
215
 
208
- const result = this.parsePaginatedResult(response.body);
216
+ const result = this.factory.parseCategoryPaginatedResult(this.context, response.body);
209
217
  return success(result);
210
218
  } catch (error) {
211
219
  console.error(
@@ -248,7 +256,7 @@ export class CommercetoolsCategoryProvider extends CategoryProvider {
248
256
  })
249
257
  .execute();
250
258
 
251
- const result = this.parsePaginatedResult(response.body);
259
+ const result = this.factory.parseCategoryPaginatedResult(this.context, response.body);
252
260
  return success(result);
253
261
  } catch (error) {
254
262
  console.error(`Error fetching category top categories:`, error);
@@ -269,52 +277,4 @@ export class CommercetoolsCategoryProvider extends CategoryProvider {
269
277
  * Handler for parsing a response from a remote provider and converting it
270
278
  * into the typed domain model.
271
279
  */
272
- protected parseSingle(_body: unknown): Category {
273
- const body = _body as CTCategory;
274
- const languageContext = this.context.languageContext;
275
-
276
- const identifier = { key: body.key! } satisfies CategoryIdentifier;
277
- const model = {
278
- identifier,
279
- name: body.name[languageContext.locale] || 'No Name',
280
- slug: body.slug ? body.slug[languageContext.locale] || '' : '',
281
- text: body.description
282
- ? body.description[languageContext.locale] || ''
283
- : '',
284
- parentCategory:
285
- body.parent && body.parent.obj && body.parent.obj?.key
286
- ? { key: body.parent.obj.key }
287
- : undefined,
288
- images: (body.assets || [])
289
- .filter((asset) => asset.sources.length > 0)
290
- .filter((x) => x.sources[0].contentType?.startsWith('image/'))
291
- .map((asset) => {
292
- return {
293
- sourceUrl: asset.sources[0].uri,
294
- altText:
295
- asset.description?.[languageContext.locale] ||
296
- asset.name[languageContext.locale] ||
297
- '',
298
- height: asset.sources[0].dimensions?.h || 0,
299
- width: asset.sources[0].dimensions?.w || 0,
300
- };
301
- }),
302
- } satisfies Category;
303
-
304
- return model;
305
- }
306
-
307
- protected parsePaginatedResult(_body: unknown) {
308
- const body = _body as CategoryPagedQueryResponse;
309
- const items = body.results.map((x) => this.parseSingle(x));
310
- const result = {
311
- pageNumber: Math.floor(body.offset / body.count) + 1,
312
- pageSize: body.count,
313
- totalCount: body.total || 0,
314
- totalPages: Math.ceil((body.total ?? 0) / body.count),
315
- items: items,
316
- } satisfies CategoryPaginatedResult;
317
-
318
- return result;
319
- }
320
280
  }