@reactionary/source 0.0.52 → 0.2.16

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 (293) hide show
  1. package/.env-template +19 -0
  2. package/.github/workflows/pull-request.yml +3 -1
  3. package/.github/workflows/release.yml +9 -0
  4. package/.vscode/extensions.json +0 -2
  5. package/LICENSE +21 -0
  6. package/README.md +175 -23
  7. package/core/package.json +6 -3
  8. package/core/src/cache/cache.interface.ts +1 -0
  9. package/core/src/cache/index.ts +4 -0
  10. package/core/src/cache/memory-cache.ts +30 -2
  11. package/core/src/cache/noop-cache.ts +15 -1
  12. package/core/src/cache/redis-cache.ts +20 -0
  13. package/core/src/client/client-builder.ts +71 -54
  14. package/core/src/client/client.ts +9 -47
  15. package/core/src/client/index.ts +2 -0
  16. package/core/src/decorators/index.ts +1 -0
  17. package/core/src/decorators/reactionary.decorator.ts +203 -34
  18. package/core/src/index.ts +6 -19
  19. package/core/src/initialization.ts +1 -18
  20. package/core/src/metrics/metrics.ts +67 -0
  21. package/core/src/providers/analytics.provider.ts +1 -6
  22. package/core/src/providers/base.provider.ts +5 -69
  23. package/core/src/providers/cart.provider.ts +15 -55
  24. package/core/src/providers/category.provider.ts +7 -11
  25. package/core/src/providers/checkout.provider.ts +17 -15
  26. package/core/src/providers/identity.provider.ts +6 -8
  27. package/core/src/providers/index.ts +2 -1
  28. package/core/src/providers/inventory.provider.ts +15 -5
  29. package/core/src/providers/order-search.provider.ts +29 -0
  30. package/core/src/providers/order.provider.ts +47 -15
  31. package/core/src/providers/price.provider.ts +30 -36
  32. package/core/src/providers/product-search.provider.ts +61 -0
  33. package/core/src/providers/product.provider.ts +71 -12
  34. package/core/src/providers/profile.provider.ts +74 -14
  35. package/core/src/providers/store.provider.ts +3 -5
  36. package/core/src/schemas/capabilities.schema.ts +10 -3
  37. package/core/src/schemas/errors/generic.error.ts +9 -0
  38. package/core/src/schemas/errors/index.ts +4 -0
  39. package/core/src/schemas/errors/invalid-input.error.ts +9 -0
  40. package/core/src/schemas/errors/invalid-output.error.ts +9 -0
  41. package/core/src/schemas/errors/not-found.error.ts +9 -0
  42. package/core/src/schemas/index.ts +7 -0
  43. package/core/src/schemas/models/analytics.model.ts +2 -1
  44. package/core/src/schemas/models/base.model.ts +6 -24
  45. package/core/src/schemas/models/cart.model.ts +5 -8
  46. package/core/src/schemas/models/category.model.ts +4 -9
  47. package/core/src/schemas/models/checkout.model.ts +6 -7
  48. package/core/src/schemas/models/cost.model.ts +4 -3
  49. package/core/src/schemas/models/currency.model.ts +2 -1
  50. package/core/src/schemas/models/identifiers.model.ts +106 -62
  51. package/core/src/schemas/models/identity.model.ts +10 -19
  52. package/core/src/schemas/models/index.ts +2 -1
  53. package/core/src/schemas/models/inventory.model.ts +8 -5
  54. package/core/src/schemas/models/order-search.model.ts +28 -0
  55. package/core/src/schemas/models/order.model.ts +20 -26
  56. package/core/src/schemas/models/payment.model.ts +14 -17
  57. package/core/src/schemas/models/price.model.ts +11 -11
  58. package/core/src/schemas/models/product-search.model.ts +42 -0
  59. package/core/src/schemas/models/product.model.ts +64 -22
  60. package/core/src/schemas/models/profile.model.ts +19 -22
  61. package/core/src/schemas/models/shipping-method.model.ts +24 -29
  62. package/core/src/schemas/models/store.model.ts +9 -5
  63. package/core/src/schemas/mutations/analytics.mutation.ts +8 -7
  64. package/core/src/schemas/mutations/base.mutation.ts +2 -1
  65. package/core/src/schemas/mutations/cart.mutation.ts +33 -33
  66. package/core/src/schemas/mutations/checkout.mutation.ts +23 -30
  67. package/core/src/schemas/mutations/identity.mutation.ts +4 -3
  68. package/core/src/schemas/mutations/profile.mutation.ts +38 -3
  69. package/core/src/schemas/queries/base.query.ts +2 -1
  70. package/core/src/schemas/queries/cart.query.ts +3 -3
  71. package/core/src/schemas/queries/category.query.ts +18 -18
  72. package/core/src/schemas/queries/checkout.query.ts +7 -9
  73. package/core/src/schemas/queries/identity.query.ts +2 -1
  74. package/core/src/schemas/queries/index.ts +2 -1
  75. package/core/src/schemas/queries/inventory.query.ts +5 -5
  76. package/core/src/schemas/queries/order-search.query.ts +10 -0
  77. package/core/src/schemas/queries/order.query.ts +3 -2
  78. package/core/src/schemas/queries/price.query.ts +10 -4
  79. package/core/src/schemas/queries/product-search.query.ts +16 -0
  80. package/core/src/schemas/queries/product.query.ts +13 -6
  81. package/core/src/schemas/queries/profile.query.ts +5 -2
  82. package/core/src/schemas/queries/store.query.ts +6 -5
  83. package/core/src/schemas/result.ts +107 -0
  84. package/core/src/schemas/session.schema.ts +4 -4
  85. package/core/src/test/reactionary.decorator.spec.ts +249 -0
  86. package/core/src/zod-utils.ts +19 -0
  87. package/core/tsconfig.json +1 -1
  88. package/core/tsconfig.spec.json +2 -26
  89. package/core/vitest.config.ts +14 -0
  90. package/documentation/1-purpose.md +114 -0
  91. package/documentation/2-getting-started.md +229 -0
  92. package/documentation/3-querying-and-changing-data.md +74 -0
  93. package/documentation/4-product-data.md +107 -0
  94. package/documentation/5-cart-and-checkout.md +211 -0
  95. package/documentation/6-product-search.md +143 -0
  96. package/documentation/7-marketing.md +3 -0
  97. package/eslint.config.mjs +1 -0
  98. package/examples/node/eslint.config.mjs +1 -4
  99. package/examples/node/package.json +10 -3
  100. package/examples/node/project.json +4 -1
  101. package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +22 -23
  102. package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +15 -11
  103. package/examples/node/src/basic/basic-node-setup.spec.ts +44 -28
  104. package/examples/node/src/basic/client-creation.spec.ts +53 -0
  105. package/examples/node/src/capabilities/cart.spec.ts +255 -0
  106. package/examples/node/src/capabilities/category.spec.ts +193 -0
  107. package/examples/node/src/capabilities/checkout.spec.ts +341 -0
  108. package/examples/node/src/capabilities/identity.spec.ts +93 -0
  109. package/examples/node/src/capabilities/inventory.spec.ts +66 -0
  110. package/examples/node/src/capabilities/order-search.spec.ts +265 -0
  111. package/examples/node/src/capabilities/order.spec.ts +91 -0
  112. package/examples/node/src/capabilities/price.spec.ts +51 -0
  113. package/examples/node/src/capabilities/product-search.spec.ts +293 -0
  114. package/examples/node/src/capabilities/product.spec.ts +122 -0
  115. package/examples/node/src/capabilities/profile.spec.ts +316 -0
  116. package/examples/node/src/capabilities/store.spec.ts +26 -0
  117. package/examples/node/src/utils.ts +147 -0
  118. package/examples/node/tsconfig.json +9 -12
  119. package/examples/node/tsconfig.lib.json +1 -2
  120. package/examples/node/tsconfig.spec.json +2 -14
  121. package/examples/node/vitest.config.ts +14 -0
  122. package/migrations.json +22 -5
  123. package/nx.json +8 -47
  124. package/package.json +24 -96
  125. package/providers/algolia/README.md +39 -2
  126. package/providers/algolia/package.json +2 -1
  127. package/providers/algolia/src/core/initialize.ts +7 -14
  128. package/providers/algolia/src/index.ts +2 -4
  129. package/providers/algolia/src/providers/index.ts +1 -0
  130. package/providers/algolia/src/providers/product-search.provider.ts +241 -0
  131. package/providers/algolia/src/schema/capabilities.schema.ts +2 -3
  132. package/providers/algolia/src/schema/index.ts +3 -0
  133. package/providers/algolia/src/schema/search.schema.ts +8 -8
  134. package/providers/algolia/tsconfig.json +1 -1
  135. package/providers/algolia/tsconfig.lib.json +1 -1
  136. package/providers/algolia/tsconfig.spec.json +2 -14
  137. package/providers/algolia/vitest.config.ts +14 -0
  138. package/providers/commercetools/README.md +30 -3
  139. package/providers/commercetools/package.json +2 -1
  140. package/providers/commercetools/src/core/client.ts +178 -99
  141. package/providers/commercetools/src/core/initialize.ts +130 -74
  142. package/providers/commercetools/src/core/token-cache.ts +45 -0
  143. package/providers/commercetools/src/index.ts +3 -2
  144. package/providers/commercetools/src/providers/cart.provider.ts +281 -341
  145. package/providers/commercetools/src/providers/category.provider.ts +223 -138
  146. package/providers/commercetools/src/providers/checkout.provider.ts +631 -449
  147. package/providers/commercetools/src/providers/identity.provider.ts +50 -29
  148. package/providers/commercetools/src/providers/index.ts +2 -2
  149. package/providers/commercetools/src/providers/inventory.provider.ts +76 -74
  150. package/providers/commercetools/src/providers/order-search.provider.ts +220 -0
  151. package/providers/commercetools/src/providers/order.provider.ts +96 -61
  152. package/providers/commercetools/src/providers/price.provider.ts +147 -117
  153. package/providers/commercetools/src/providers/product-search.provider.ts +528 -0
  154. package/providers/commercetools/src/providers/product.provider.ts +249 -74
  155. package/providers/commercetools/src/providers/profile.provider.ts +445 -28
  156. package/providers/commercetools/src/providers/store.provider.ts +54 -40
  157. package/providers/commercetools/src/schema/capabilities.schema.ts +3 -1
  158. package/providers/commercetools/src/schema/commercetools.schema.ts +17 -3
  159. package/providers/commercetools/src/schema/configuration.schema.ts +1 -0
  160. package/providers/commercetools/src/schema/session.schema.ts +7 -0
  161. package/providers/commercetools/src/test/caching.spec.ts +82 -0
  162. package/providers/commercetools/src/test/identity.spec.ts +109 -0
  163. package/providers/commercetools/src/test/test-utils.ts +21 -19
  164. package/providers/commercetools/tsconfig.json +1 -1
  165. package/providers/commercetools/tsconfig.lib.json +1 -1
  166. package/providers/commercetools/tsconfig.spec.json +2 -14
  167. package/providers/commercetools/vitest.config.ts +15 -0
  168. package/providers/fake/README.md +20 -4
  169. package/providers/fake/package.json +2 -1
  170. package/providers/fake/src/core/initialize.ts +47 -49
  171. package/providers/fake/src/providers/analytics.provider.ts +5 -7
  172. package/providers/fake/src/providers/cart.provider.ts +163 -92
  173. package/providers/fake/src/providers/category.provider.ts +78 -50
  174. package/providers/fake/src/providers/checkout.provider.ts +254 -0
  175. package/providers/fake/src/providers/identity.provider.ts +57 -65
  176. package/providers/fake/src/providers/index.ts +6 -2
  177. package/providers/fake/src/providers/inventory.provider.ts +40 -36
  178. package/providers/fake/src/providers/order-search.provider.ts +78 -0
  179. package/providers/fake/src/providers/order.provider.ts +106 -0
  180. package/providers/fake/src/providers/price.provider.ts +93 -41
  181. package/providers/fake/src/providers/product-search.provider.ts +206 -0
  182. package/providers/fake/src/providers/product.provider.ts +56 -41
  183. package/providers/fake/src/providers/profile.provider.ts +147 -0
  184. package/providers/fake/src/providers/store.provider.ts +30 -20
  185. package/providers/fake/src/schema/capabilities.schema.ts +5 -1
  186. package/providers/fake/src/test/cart.provider.spec.ts +59 -80
  187. package/providers/fake/src/test/category.provider.spec.ts +145 -87
  188. package/providers/fake/src/test/checkout.provider.spec.ts +222 -0
  189. package/providers/fake/src/test/order-search.provider.spec.ts +50 -0
  190. package/providers/fake/src/test/order.provider.spec.ts +44 -0
  191. package/providers/fake/src/test/price.provider.spec.ts +50 -45
  192. package/providers/fake/src/test/product.provider.spec.ts +15 -7
  193. package/providers/fake/src/test/profile.provider.spec.ts +167 -0
  194. package/providers/fake/tsconfig.json +1 -1
  195. package/providers/fake/tsconfig.lib.json +1 -1
  196. package/providers/fake/tsconfig.spec.json +2 -12
  197. package/providers/fake/vitest.config.ts +14 -0
  198. package/providers/medusa/README.md +30 -0
  199. package/providers/medusa/TESTING.md +98 -0
  200. package/providers/medusa/eslint.config.mjs +19 -0
  201. package/providers/medusa/package.json +22 -0
  202. package/providers/medusa/project.json +34 -0
  203. package/providers/medusa/src/core/client.ts +370 -0
  204. package/providers/medusa/src/core/initialize.ts +78 -0
  205. package/providers/medusa/src/index.ts +13 -0
  206. package/providers/medusa/src/providers/cart.provider.ts +575 -0
  207. package/providers/medusa/src/providers/category.provider.ts +247 -0
  208. package/providers/medusa/src/providers/checkout.provider.ts +636 -0
  209. package/providers/medusa/src/providers/identity.provider.ts +137 -0
  210. package/providers/medusa/src/providers/inventory.provider.ts +173 -0
  211. package/providers/medusa/src/providers/order-search.provider.ts +202 -0
  212. package/providers/medusa/src/providers/order.provider.ts +226 -0
  213. package/providers/medusa/src/providers/price.provider.ts +140 -0
  214. package/providers/medusa/src/providers/product-search.provider.ts +243 -0
  215. package/providers/medusa/src/providers/product.provider.ts +261 -0
  216. package/providers/medusa/src/providers/profile.provider.ts +392 -0
  217. package/providers/medusa/src/schema/capabilities.schema.ts +18 -0
  218. package/providers/medusa/src/schema/configuration.schema.ts +11 -0
  219. package/providers/medusa/src/schema/medusa.schema.ts +31 -0
  220. package/providers/medusa/src/test/cart.provider.spec.ts +240 -0
  221. package/providers/medusa/src/test/category.provider.spec.ts +231 -0
  222. package/providers/medusa/src/test/checkout.spec.ts +349 -0
  223. package/providers/medusa/src/test/identity.provider.spec.ts +122 -0
  224. package/providers/medusa/src/test/inventory.provider.spec.ts +88 -0
  225. package/providers/medusa/src/test/large-cart.provider.spec.ts +103 -0
  226. package/providers/medusa/src/test/price.provider.spec.ts +104 -0
  227. package/providers/medusa/src/test/product.provider.spec.ts +146 -0
  228. package/providers/medusa/src/test/search.provider.spec.ts +203 -0
  229. package/providers/medusa/src/test/test-utils.ts +13 -0
  230. package/providers/medusa/src/utils/medusa-helpers.ts +89 -0
  231. package/providers/medusa/tsconfig.json +21 -0
  232. package/providers/medusa/tsconfig.lib.json +9 -0
  233. package/providers/medusa/tsconfig.spec.json +4 -0
  234. package/providers/medusa/vitest.config.ts +15 -0
  235. package/providers/meilisearch/README.md +48 -0
  236. package/providers/meilisearch/eslint.config.mjs +22 -0
  237. package/providers/meilisearch/package.json +13 -0
  238. package/providers/meilisearch/project.json +34 -0
  239. package/providers/meilisearch/src/core/initialize.ts +21 -0
  240. package/providers/meilisearch/src/index.ts +6 -0
  241. package/providers/meilisearch/src/providers/index.ts +1 -0
  242. package/providers/meilisearch/src/providers/order-search.provider.ts +222 -0
  243. package/providers/meilisearch/src/providers/product-search.provider.ts +251 -0
  244. package/providers/meilisearch/src/schema/capabilities.schema.ts +10 -0
  245. package/providers/meilisearch/src/schema/configuration.schema.ts +11 -0
  246. package/providers/meilisearch/src/schema/index.ts +3 -0
  247. package/providers/meilisearch/src/schema/search.schema.ts +14 -0
  248. package/providers/meilisearch/tsconfig.json +24 -0
  249. package/providers/meilisearch/tsconfig.lib.json +10 -0
  250. package/providers/meilisearch/tsconfig.spec.json +4 -0
  251. package/providers/meilisearch/vitest.config.ts +14 -0
  252. package/providers/posthog/package.json +2 -1
  253. package/providers/posthog/tsconfig.json +1 -1
  254. package/tsconfig.base.json +5 -0
  255. package/vitest.config.ts +10 -0
  256. package/core/src/providers/search.provider.ts +0 -18
  257. package/core/src/schemas/models/search.model.ts +0 -36
  258. package/core/src/schemas/queries/search.query.ts +0 -9
  259. package/examples/next/.swcrc +0 -30
  260. package/examples/next/eslint.config.mjs +0 -21
  261. package/examples/next/index.d.ts +0 -6
  262. package/examples/next/next-env.d.ts +0 -5
  263. package/examples/next/next.config.js +0 -31
  264. package/examples/next/project.json +0 -9
  265. package/examples/next/public/.gitkeep +0 -0
  266. package/examples/next/public/favicon.ico +0 -0
  267. package/examples/next/src/app/global.css +0 -0
  268. package/examples/next/src/app/layout.tsx +0 -18
  269. package/examples/next/src/app/page.module.scss +0 -2
  270. package/examples/next/src/app/page.tsx +0 -47
  271. package/examples/next/src/instrumentation.ts +0 -9
  272. package/examples/next/tsconfig.json +0 -44
  273. package/examples/node/jest.config.ts +0 -10
  274. package/jest.config.ts +0 -6
  275. package/jest.preset.js +0 -3
  276. package/providers/algolia/jest.config.ts +0 -10
  277. package/providers/algolia/src/providers/product.provider.ts +0 -66
  278. package/providers/algolia/src/providers/search.provider.ts +0 -106
  279. package/providers/algolia/src/test/search.provider.spec.ts +0 -91
  280. package/providers/commercetools/jest.config.cjs +0 -10
  281. package/providers/commercetools/src/providers/search.provider.ts +0 -96
  282. package/providers/commercetools/src/test/cart.provider.spec.ts +0 -199
  283. package/providers/commercetools/src/test/category.provider.spec.ts +0 -168
  284. package/providers/commercetools/src/test/checkout.provider.spec.ts +0 -312
  285. package/providers/commercetools/src/test/identity.provider.spec.ts +0 -88
  286. package/providers/commercetools/src/test/inventory.provider.spec.ts +0 -41
  287. package/providers/commercetools/src/test/price.provider.spec.ts +0 -81
  288. package/providers/commercetools/src/test/product.provider.spec.ts +0 -80
  289. package/providers/commercetools/src/test/profile.provider.spec.ts +0 -49
  290. package/providers/commercetools/src/test/search.provider.spec.ts +0 -61
  291. package/providers/commercetools/src/test/store.provider.spec.ts +0 -37
  292. package/providers/fake/jest.config.cjs +0 -10
  293. package/providers/fake/src/providers/search.provider.ts +0 -132
@@ -1,169 +1,195 @@
1
1
  import {
2
+ CartIdentifierSchema,
2
3
  CartItemSchema,
3
- CartProvider
4
+ CartMutationApplyCouponSchema,
5
+ CartMutationChangeCurrencySchema,
6
+ CartMutationDeleteCartSchema,
7
+ CartMutationItemAddSchema,
8
+ CartMutationItemQuantityChangeSchema,
9
+ CartMutationItemRemoveSchema,
10
+ CartMutationRemoveCouponSchema,
11
+ CartProvider,
12
+ CartQueryByIdSchema,
13
+ CartSchema,
14
+ Reactionary,
15
+ success,
16
+ error,
17
+ unwrapValue,
18
+ assertSuccess,
4
19
  } from '@reactionary/core';
5
20
  import type {
21
+ CartItem,
6
22
  CartMutationItemAdd,
7
23
  CartMutationItemQuantityChange,
8
24
  CartMutationItemRemove,
9
25
  CartQueryById,
10
- CartIdentifier, CartMutationApplyCoupon,
11
- CartMutationCheckout,
26
+ CartIdentifier,
27
+ CartMutationApplyCoupon,
12
28
  CartMutationDeleteCart,
13
- CartMutationRemoveCoupon, CartMutationSetBillingAddress,
14
- CartMutationSetShippingInfo,
15
- CartMutationChangeCurrency, OrderIdentifier,
29
+ CartMutationRemoveCoupon,
30
+ CartMutationChangeCurrency,
16
31
  RequestContext,
17
32
  Cart,
18
- Currency
19
- ,
20
- Cache
33
+ Currency,
34
+ Cache,
35
+ CostBreakDown,
36
+ Result,
37
+ NotFoundError,
21
38
  } from '@reactionary/core';
22
-
23
39
  import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
24
- import type { z } from 'zod';
25
- import { CommercetoolsClient } from '../core/client.js';
26
40
  import type {
27
41
  Cart as CTCart,
42
+ LineItem,
28
43
  MyCartUpdateAction,
29
44
  } from '@commercetools/platform-sdk';
30
- import type {
31
- CommercetoolsCartIdentifier} from '../schema/commercetools.schema.js';
32
- import {
33
- CommercetoolsCartIdentifierSchema,
34
- CommercetoolsOrderIdentifierSchema,
35
- } from '../schema/commercetools.schema.js';
45
+ import type { CommercetoolsCartIdentifier } from '../schema/commercetools.schema.js';
46
+ import { CommercetoolsCartIdentifierSchema } from '../schema/commercetools.schema.js';
47
+ import type { CommercetoolsAPI } from '../core/client.js';
36
48
 
37
- export class CommercetoolsCartProvider<
38
- T extends Cart = Cart
39
- > extends CartProvider<T> {
49
+ export class CommercetoolsCartProvider extends CartProvider {
40
50
  protected config: CommercetoolsConfiguration;
51
+ protected commercetools: CommercetoolsAPI;
41
52
 
42
53
  constructor(
43
54
  config: CommercetoolsConfiguration,
44
- schema: z.ZodType<T>,
45
- cache: Cache
55
+ cache: Cache,
56
+ context: RequestContext,
57
+ commercetools: CommercetoolsAPI
46
58
  ) {
47
- super(schema, cache);
59
+ super(cache, context);
60
+
48
61
  this.config = config;
62
+ this.commercetools = commercetools;
49
63
  }
50
64
 
51
- public override async getById(
52
- payload: CartQueryById,
53
- reqCtx: RequestContext
54
- ): Promise<T> {
55
- try {
56
- const client = await this.getClient(reqCtx);
57
-
58
- const ctId = payload.cart as CommercetoolsCartIdentifier;
65
+ @Reactionary({
66
+ inputSchema: CartQueryByIdSchema,
67
+ outputSchema: CartSchema,
68
+ })
69
+ public override async getById(payload: CartQueryById): Promise<Result<Cart, NotFoundError>> {
70
+ const client = await this.getClient();
71
+ const ctId = payload.cart as CommercetoolsCartIdentifier;
59
72
 
73
+ try {
60
74
  const remote = await client.carts.withId({ ID: ctId.key }).get().execute();
61
75
 
62
- return this.parseSingle(remote.body, reqCtx);
63
- } catch (e) {
64
- return this.createEmptyCart();
76
+ return success(this.parseSingle(remote.body));
77
+ } catch(err) {
78
+ return error<NotFoundError>({
79
+ type: 'NotFound',
80
+ identifier: ctId
81
+ });
65
82
  }
66
83
  }
67
84
 
68
- public override async add(
69
- payload: CartMutationItemAdd,
70
- reqCtx: RequestContext
71
- ): Promise<T> {
72
- const client = await this.getClient(reqCtx);
73
-
85
+ @Reactionary({
86
+ inputSchema: CartMutationItemAddSchema,
87
+ outputSchema: CartSchema,
88
+ })
89
+ public override async add(payload: CartMutationItemAdd): Promise<Result<Cart>> {
74
90
  let cartIdentifier = payload.cart;
75
- if (!cartIdentifier.key) {
76
- cartIdentifier = await this.createCart(reqCtx);
91
+ if (!cartIdentifier) {
92
+ cartIdentifier = await this.createCart();
77
93
  }
78
94
 
79
- return this.applyActions(
80
- cartIdentifier,
81
- [
82
- {
83
- action: 'addLineItem',
84
- quantity: payload.quantity,
85
- sku: payload.sku.key,
86
- },
87
- {
88
- action: 'recalculate',
95
+ const result = await this.applyActions(cartIdentifier, [
96
+ {
97
+ action: 'addLineItem',
98
+ quantity: payload.quantity,
99
+ sku: payload.variant.sku,
100
+ // FIXME: This should be dynamic, probably as part of the context...
101
+ distributionChannel: {
102
+ typeId: 'channel',
103
+ key: 'OnlineFfmChannel',
89
104
  },
90
- ],
91
- reqCtx
92
- );
105
+ },
106
+ {
107
+ action: 'recalculate',
108
+ },
109
+ ]);
110
+
111
+ return success(result);
93
112
  }
94
113
 
95
- public override async remove(
96
- payload: CartMutationItemRemove,
97
- reqCtx: RequestContext
98
- ): Promise<T> {
99
- return this.applyActions(
100
- payload.cart,
101
- [
102
- {
103
- action: 'removeLineItem',
104
- lineItemId: payload.item.key,
105
- },
106
- {
107
- action: 'recalculate',
108
- },
109
- ],
110
- reqCtx
111
- );
114
+ @Reactionary({
115
+ inputSchema: CartMutationItemRemoveSchema,
116
+ outputSchema: CartSchema,
117
+ })
118
+ public override async remove(payload: CartMutationItemRemove): Promise<Result<Cart>> {
119
+ const result = await this.applyActions(payload.cart, [
120
+ {
121
+ action: 'removeLineItem',
122
+ lineItemId: payload.item.key,
123
+ },
124
+ {
125
+ action: 'recalculate',
126
+ },
127
+ ]);
128
+
129
+ return success(result);
112
130
  }
113
131
 
132
+ @Reactionary({
133
+ inputSchema: CartMutationItemQuantityChangeSchema,
134
+ outputSchema: CartSchema,
135
+ })
114
136
  public override async changeQuantity(
115
- payload: CartMutationItemQuantityChange,
116
- reqCtx: RequestContext
117
- ): Promise<T> {
137
+ payload: CartMutationItemQuantityChange
138
+ ): Promise<Result<Cart>> {
118
139
  if (payload.quantity === 0) {
119
140
  // Changing quantity to 0 is not allowed. Use the remove call instead. This is done to avoid accidental removal of item.
120
141
  // Calls with quantity 0 will just be ignored.
121
- return this.getById({ cart: payload.cart }, reqCtx);
142
+ const existing = await this.getById({ cart: payload.cart });
143
+
144
+ // TODO: Should we instead allow returning NotFound here, if that is indeed the case?
145
+ assertSuccess(existing);
146
+
147
+ return existing;
122
148
  }
123
149
 
124
- return this.applyActions(
125
- payload.cart,
126
- [
127
- {
128
- action: 'changeLineItemQuantity',
129
- lineItemId: payload.item.key,
130
- quantity: payload.quantity,
131
- },
132
- {
133
- action: 'recalculate',
134
- },
135
- ],
136
- reqCtx
137
- );
150
+ const result = await this.applyActions(payload.cart, [
151
+ {
152
+ action: 'changeLineItemQuantity',
153
+ lineItemId: payload.item.key,
154
+ quantity: payload.quantity,
155
+ },
156
+ {
157
+ action: 'recalculate',
158
+ },
159
+ ]);
160
+
161
+ return success(result);
138
162
  }
139
163
 
140
- public override async getActiveCartId(
141
- reqCtx: RequestContext
142
- ): Promise<CartIdentifier> {
143
- const client = await this.getClient(reqCtx);
164
+ @Reactionary({
165
+ outputSchema: CartIdentifierSchema,
166
+ })
167
+ public override async getActiveCartId(): Promise<Result<CartIdentifier, NotFoundError>> {
168
+ const client = await this.getClient();
144
169
  try {
145
- const carts = await client.activeCart
146
- .get()
147
- .execute();
148
-
149
- return CommercetoolsCartIdentifierSchema.parse({
170
+ const carts = await client.activeCart.get().execute();
171
+ const result = await CommercetoolsCartIdentifierSchema.parse({
150
172
  key: carts.body.id,
151
- version: carts.body.version || 0
173
+ version: carts.body.version || 0,
152
174
  });
153
- } catch (e: any) {
154
175
 
155
- return CommercetoolsCartIdentifierSchema.parse({
156
- key: '',
157
- version: 0
158
- });
176
+ return success(result);
177
+ } catch (e: any) {
178
+ return error<NotFoundError>({
179
+ type: 'NotFound',
180
+ identifier: {}
181
+ })
159
182
  }
160
183
  }
161
184
 
185
+ @Reactionary({
186
+ inputSchema: CartMutationDeleteCartSchema,
187
+ outputSchema: CartSchema,
188
+ })
162
189
  public override async deleteCart(
163
- payload: CartMutationDeleteCart,
164
- reqCtx: RequestContext
165
- ): Promise<T> {
166
- const client = await this.getClient(reqCtx);
190
+ payload: CartMutationDeleteCart
191
+ ): Promise<Result<void>> {
192
+ const client = await this.getClient();
167
193
  if (payload.cart.key) {
168
194
  const ctId = payload.cart as CommercetoolsCartIdentifier;
169
195
 
@@ -178,154 +204,65 @@ export class CommercetoolsCartProvider<
178
204
  .execute();
179
205
  }
180
206
 
181
- const activeCartId = await this.getActiveCartId(reqCtx);
182
- return this.getById({ cart: activeCartId }, reqCtx);
207
+ return success(undefined);
183
208
  }
184
209
 
185
- public override async setShippingInfo(
186
- payload: CartMutationSetShippingInfo,
187
- reqCtx: RequestContext
188
- ): Promise<T> {
189
- const client = await this.getClient(reqCtx);
190
- const ctId = payload.cart as CommercetoolsCartIdentifier;
191
-
192
- const actions: MyCartUpdateAction[] = new Array<MyCartUpdateAction>();
193
- if (payload.shippingMethod) {
194
- actions.push({
195
- action: 'setShippingMethod',
196
- shippingMethod: {
197
- typeId: 'shipping-method',
198
- id: payload.shippingMethod.key,
199
- },
200
- });
201
- }
202
-
203
- if (payload.shippingAddress) {
204
- actions.push({
205
- action: 'setShippingAddress',
206
- address: {
207
- country: payload.shippingAddress.countryCode || reqCtx.taxJurisdiction.countryCode || 'US',
208
- firstName: payload.shippingAddress.firstName,
209
- lastName: payload.shippingAddress.lastName,
210
- city: payload.shippingAddress.city,
211
- postalCode: payload.shippingAddress.postalCode,
212
- streetName: payload.shippingAddress.streetAddress,
213
- streetNumber: payload.shippingAddress.streetNumber,
214
- },
215
- });
216
- }
217
-
218
- return this.applyActions(payload.cart, actions, reqCtx);
219
- }
210
+ @Reactionary({
211
+ inputSchema: CartMutationApplyCouponSchema,
212
+ outputSchema: CartSchema,
213
+ })
214
+ public override async applyCouponCode(
215
+ payload: CartMutationApplyCoupon
216
+ ): Promise<Result<Cart>> {
217
+ const result = await this.applyActions(payload.cart, [
218
+ {
219
+ action: 'addDiscountCode',
220
+ code: payload.couponCode,
221
+ },
222
+ {
223
+ action: 'recalculate',
224
+ },
225
+ ]);
220
226
 
221
- public override setBillingAddress(
222
- payload: CartMutationSetBillingAddress,
223
- reqCtx: RequestContext
224
- ): Promise<T> {
225
- return this.applyActions(
226
- payload.cart,
227
- [
228
- {
229
- action: 'setBillingAddress',
230
- address: {
231
- email: payload.notificationEmailAddress,
232
- mobile: payload.notificationPhoneNumber,
233
- country: payload.billingAddress.countryCode || reqCtx.taxJurisdiction.countryCode || 'US',
234
- firstName: payload.billingAddress.firstName,
235
- lastName: payload.billingAddress.lastName,
236
- city: payload.billingAddress.city,
237
- postalCode: payload.billingAddress.postalCode,
238
- streetName: payload.billingAddress.streetAddress,
239
- streetNumber: payload.billingAddress.streetNumber,
240
- },
241
- },
242
- {
243
- action: 'setCustomerEmail',
244
- email: payload.notificationEmailAddress,
245
- },
246
- {
247
- action: 'setCountry',
248
- country: payload.billingAddress.countryCode || reqCtx.taxJurisdiction.countryCode || 'US',
249
- },
250
- ],
251
- reqCtx
252
- );
227
+ return success(result);
253
228
  }
254
229
 
255
- public override applyCouponCode(
256
- payload: CartMutationApplyCoupon,
257
- reqCtx: RequestContext
258
- ): Promise<T> {
259
- return this.applyActions(
260
- payload.cart,
261
- [
262
- {
263
- action: 'addDiscountCode',
264
- code: payload.couponCode,
230
+ @Reactionary({
231
+ inputSchema: CartMutationRemoveCouponSchema,
232
+ outputSchema: CartSchema,
233
+ })
234
+ public override async removeCouponCode(
235
+ payload: CartMutationRemoveCoupon
236
+ ): Promise<Result<Cart>> {
237
+ const result = await this.applyActions(payload.cart, [
238
+ {
239
+ action: 'removeDiscountCode',
240
+ discountCode: {
241
+ id: payload.couponCode,
242
+ typeId: 'discount-code',
265
243
  },
266
- {
267
- action: 'recalculate',
268
- },
269
- ],
270
- reqCtx
271
- );
272
- }
244
+ },
245
+ {
246
+ action: 'recalculate',
247
+ },
248
+ ]);
273
249
 
274
- public override removeCouponCode(
275
- payload: CartMutationRemoveCoupon,
276
- reqCtx: RequestContext
277
- ): Promise<T> {
278
- return this.applyActions(
279
- payload.cart,
280
- [
281
- {
282
- action: 'removeDiscountCode',
283
- discountCode: {
284
- id: payload.couponCode,
285
- typeId: 'discount-code',
286
- },
287
- },
288
- {
289
- action: 'recalculate',
290
- },
291
- ],
292
- reqCtx
293
- );
294
- }
295
-
296
- public override async checkout(
297
- payload: CartMutationCheckout,
298
- reqCtx: RequestContext
299
- ): Promise<OrderIdentifier> {
300
- // In Commercetools, checkout is done by creating an order from the cart.
301
-
302
- const client = await this.getClient(reqCtx);
303
- const ctId = payload.cart as CommercetoolsCartIdentifier;
304
-
305
- const orderResponse = await client.orders
306
- .post({
307
- body: {
308
- version: ctId.version,
309
- id: ctId.key,
310
- }
311
- })
312
- .execute();
313
- return CommercetoolsOrderIdentifierSchema.parse({
314
- key: orderResponse.body.id,
315
- version: orderResponse.body.version || 0,
316
- });
250
+ return success(result);
317
251
  }
318
252
 
253
+ @Reactionary({
254
+ inputSchema: CartMutationChangeCurrencySchema,
255
+ outputSchema: CartSchema,
256
+ })
319
257
  public override async changeCurrency(
320
- payload: CartMutationChangeCurrency,
321
- reqCtx: RequestContext
322
- ): Promise<T> {
258
+ payload: CartMutationChangeCurrency
259
+ ): Promise<Result<Cart>> {
323
260
  // ok, to do this we have to actually build a new cart, copy over all the items, and then delete the old cart.
324
261
  // because Commercetools does not support changing currency of an existing cart.
325
262
 
326
263
  // This is obviously not ideal, but it is what it is.
327
264
 
328
- const client = await this.getClient(reqCtx);
265
+ const client = await this.getClient();
329
266
  const currentCart = await client.carts
330
267
  .withId({ ID: payload.cart.key })
331
268
  .get()
@@ -334,7 +271,7 @@ export class CommercetoolsCartProvider<
334
271
  .post({
335
272
  body: {
336
273
  currency: payload.newCurrency,
337
- locale: reqCtx.languageContext.locale,
274
+ locale: this.context.languageContext.locale,
338
275
  },
339
276
  })
340
277
  .execute();
@@ -352,16 +289,12 @@ export class CommercetoolsCartProvider<
352
289
  })
353
290
  );
354
291
 
355
- const response = await this.applyActions(
356
- newCartId,
357
- [
358
- ...cartItemAdds,
359
- {
360
- action: 'recalculate',
361
- },
362
- ],
363
- reqCtx
364
- );
292
+ const response = await this.applyActions(newCartId, [
293
+ ...cartItemAdds,
294
+ {
295
+ action: 'recalculate',
296
+ },
297
+ ]);
365
298
 
366
299
  // now delete the old cart.
367
300
  await client.carts
@@ -374,17 +307,17 @@ export class CommercetoolsCartProvider<
374
307
  })
375
308
  .execute();
376
309
 
377
- return response;
310
+ return success(response);
378
311
  }
379
312
 
380
- protected async createCart(reqCtx: RequestContext): Promise<CartIdentifier> {
381
- const client = await this.getClient(reqCtx);
313
+ protected async createCart(): Promise<CartIdentifier> {
314
+ const client = await this.getClient();
382
315
  const response = await client.carts
383
316
  .post({
384
317
  body: {
385
- currency: reqCtx.languageContext.currencyCode || 'USD',
386
- country: reqCtx.taxJurisdiction.countryCode || 'US',
387
- locale: reqCtx.languageContext.locale,
318
+ currency: this.context.languageContext.currencyCode || 'USD',
319
+ country: this.context.taxJurisdiction.countryCode || 'US',
320
+ locale: this.context.languageContext.locale,
388
321
  },
389
322
  })
390
323
  .execute();
@@ -397,66 +330,98 @@ export class CommercetoolsCartProvider<
397
330
 
398
331
  protected async applyActions(
399
332
  cart: CartIdentifier,
400
- actions: MyCartUpdateAction[],
401
- reqCtx: RequestContext
402
- ): Promise<T> {
403
- const client = await this.getClient(reqCtx);
333
+ actions: MyCartUpdateAction[]
334
+ ): Promise<Cart> {
335
+ const client = await this.getClient();
404
336
  const ctId = cart as CommercetoolsCartIdentifier;
405
337
 
338
+ try {
339
+ const response = await client.carts
340
+ .withId({ ID: ctId.key })
341
+ .post({
342
+ body: {
343
+ version: ctId.version,
344
+ actions,
345
+ },
346
+ })
347
+ .execute();
406
348
 
407
- try {
408
- const response = await client.carts
409
- .withId({ ID: ctId.key })
410
- .post({
411
- body: {
412
- version: ctId.version,
413
- actions,
414
- },
415
- })
416
- .execute();
417
-
418
- if (response.error) {
419
- console.error(response.error);
420
- }
421
- return this.parseSingle(response.body, reqCtx);
422
- } catch (e: any) {
423
- console.error('Error applying actions to cart:', e);
424
- throw e;
425
- }
426
-
349
+ if (response.error) {
350
+ console.error(response.error);
351
+ }
352
+ return this.parseSingle(response.body);
353
+ } catch (e: any) {
354
+ console.error('Error applying actions to cart:', e);
355
+ throw e;
356
+ }
427
357
  }
428
358
 
429
359
  /**
430
360
  * Creates a new Commercetools client, optionally upgrading it from Anonymous mode to Guest mode.
431
361
  * For now, any Query or Mutation will require an upgrade to Guest mode.
432
362
  * In the future, maybe we can delay this upgrade until we actually need it.
433
- *
434
- * @param reqCtx
435
- * @param anonymousCall
436
- * @returns
437
363
  */
438
- protected async getClient(reqCtx: RequestContext) {
439
- const client = await new CommercetoolsClient(this.config).getClient(reqCtx);
364
+ protected async getClient() {
365
+ const client = await this.commercetools.getClient();
366
+
367
+ const clientWithProject = client.withProjectKey({
368
+ projectKey: this.config.projectKey,
369
+ });
440
370
 
441
- const clientWithProject = client.withProjectKey({ projectKey: this.config.projectKey });
442
371
  return {
443
372
  carts: clientWithProject.me().carts(),
444
373
  activeCart: clientWithProject.me().activeCart(),
445
374
  orders: clientWithProject.me().orders(),
446
-
447
- }
375
+ };
448
376
  }
449
377
 
450
- protected override parseSingle(remote: CTCart, reqCtx: RequestContext): T {
451
- const result = this.newModel();
378
+ protected parseCartItem(remoteItem: LineItem): CartItem {
379
+ const unitPrice = remoteItem.price.value.centAmount;
380
+ const totalPrice = remoteItem.totalPrice.centAmount || 0;
381
+ const totalDiscount = remoteItem.price.discounted?.value.centAmount || 0;
382
+ const unitDiscount = totalDiscount / remoteItem.quantity;
383
+ const currency =
384
+ remoteItem.price.value.currencyCode.toUpperCase() as Currency;
385
+
386
+ const item = {
387
+ identifier: {
388
+ key: remoteItem.id,
389
+ },
390
+ product: {
391
+ key: remoteItem.productId,
392
+ },
393
+ variant: {
394
+ sku: remoteItem.variant.sku || '',
395
+ },
396
+ quantity: remoteItem.quantity,
397
+ price: {
398
+ unitPrice: {
399
+ value: unitPrice / 100,
400
+ currency,
401
+ },
402
+ unitDiscount: {
403
+ value: unitDiscount / 100,
404
+ currency,
405
+ },
406
+ totalPrice: {
407
+ value: (totalPrice || 0) / 100,
408
+ currency,
409
+ },
410
+ totalDiscount: {
411
+ value: totalDiscount / 100,
412
+ currency,
413
+ },
414
+ },
415
+ } satisfies CartItem;
416
+
417
+ return CartItemSchema.parse(item);
418
+ }
452
419
 
453
- result.identifier = CommercetoolsCartIdentifierSchema.parse({
420
+ protected parseSingle(remote: CTCart): Cart {
421
+ const identifier = {
454
422
  key: remote.id,
455
423
  version: remote.version || 0,
456
- });
457
-
458
- result.name = remote.custom?.fields['name'] || '';
459
- result.description = remote.custom?.fields['description'] || '';
424
+ } satisfies CommercetoolsCartIdentifier;
460
425
 
461
426
  const grandTotal = remote.totalPrice.centAmount || 0;
462
427
  const shippingTotal = remote.shippingInfo?.price.centAmount || 0;
@@ -467,7 +432,7 @@ export class CommercetoolsCartProvider<
467
432
  const surchargeTotal = 0;
468
433
  const currency = remote.totalPrice.currencyCode as Currency;
469
434
 
470
- result.price = {
435
+ const price = {
471
436
  totalTax: {
472
437
  value: taxTotal / 100,
473
438
  currency,
@@ -482,7 +447,7 @@ export class CommercetoolsCartProvider<
482
447
  },
483
448
  totalShipping: {
484
449
  value: shippingTotal / 100,
485
- currency: remote.shippingInfo?.price.currencyCode as Currency,
450
+ currency,
486
451
  },
487
452
  totalProductPrice: {
488
453
  value: productTotal / 100,
@@ -492,51 +457,26 @@ export class CommercetoolsCartProvider<
492
457
  value: grandTotal / 100,
493
458
  currency,
494
459
  },
495
- };
460
+ } satisfies CostBreakDown;
496
461
 
462
+ const items = new Array<CartItem>();
497
463
  for (const remoteItem of remote.lineItems) {
498
- const item = CartItemSchema.parse({});
499
-
500
- item.identifier.key = remoteItem.id;
501
- item.product.key = remoteItem.productId;
502
- item.sku.key = remoteItem.variant.sku || '';
503
- item.quantity = remoteItem.quantity;
504
-
505
- const unitPrice = remoteItem.price.value.centAmount;
506
- const totalPrice = remoteItem.totalPrice.centAmount || 0;
507
- const totalDiscount = remoteItem.price.discounted?.value.centAmount || 0;
508
- const unitDiscount = totalDiscount / remoteItem.quantity;
509
-
510
- item.price = {
511
- unitPrice: {
512
- value: unitPrice / 100,
513
- currency,
514
- },
515
- unitDiscount: {
516
- value: unitDiscount / 100,
517
- currency,
518
- },
519
- totalPrice: {
520
- value: (totalPrice || 0) / 100,
521
- currency,
522
- },
523
- totalDiscount: {
524
- value: totalDiscount / 100,
525
- currency,
526
- },
527
- };
464
+ const item = this.parseCartItem(remoteItem);
528
465
 
529
- result.items.push(item);
466
+ items.push(item);
530
467
  }
531
468
 
532
- result.meta = {
533
- cache: {
534
- hit: false,
535
- key: this.generateCacheKeySingle(result.identifier, reqCtx),
469
+ const cart = {
470
+ identifier,
471
+ userId: {
472
+ userId: '???',
536
473
  },
537
- placeholder: false,
538
- };
474
+ name: remote.custom?.fields['name'] || '',
475
+ description: remote.custom?.fields['description'] || '',
476
+ price,
477
+ items,
478
+ } satisfies Cart;
539
479
 
540
- return this.assert(result);
480
+ return cart;
541
481
  }
542
482
  }