@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
@@ -0,0 +1,575 @@
1
+ import type {
2
+ Cache,
3
+ Cart,
4
+ CartIdentifier,
5
+ CartItem,
6
+ CartMutationApplyCoupon,
7
+ CartMutationChangeCurrency,
8
+ CartMutationDeleteCart,
9
+ CartMutationItemAdd,
10
+ CartMutationItemQuantityChange,
11
+ CartMutationItemRemove,
12
+ CartMutationRemoveCoupon,
13
+ CartQueryById,
14
+ CostBreakDown,
15
+ Currency,
16
+ ItemCostBreakdown,
17
+ NotFoundError,
18
+ ProductVariantIdentifier,
19
+ RequestContext,
20
+ Result,
21
+ } from '@reactionary/core';
22
+ import {
23
+ CartIdentifierSchema,
24
+ CartMutationApplyCouponSchema,
25
+ CartMutationChangeCurrencySchema,
26
+ CartMutationDeleteCartSchema,
27
+ CartMutationItemAddSchema,
28
+ CartMutationItemQuantityChangeSchema,
29
+ CartMutationItemRemoveSchema,
30
+ CartMutationRemoveCouponSchema,
31
+ CartProvider,
32
+ CartQueryByIdSchema,
33
+ CartSchema,
34
+ ProductVariantIdentifierSchema,
35
+ Reactionary,
36
+ success,
37
+ error,
38
+ } from '@reactionary/core';
39
+
40
+ import createDebug from 'debug';
41
+ import type { MedusaAPI } from '../core/client.js';
42
+ import type { MedusaConfiguration } from '../schema/configuration.schema.js';
43
+ import type { MedusaCartIdentifier } from '../schema/medusa.schema.js';
44
+ import { MedusaCartIdentifierSchema } from '../schema/medusa.schema.js';
45
+ import {
46
+ handleProviderError,
47
+ parseMedusaCostBreakdown,
48
+ parseMedusaItemPrice,
49
+ } from '../utils/medusa-helpers.js';
50
+ import type MedusaTypes = require('@medusajs/types');
51
+ import type StoreCartPromotion = require('@medusajs/types');
52
+
53
+ const debug = createDebug('reactionary:medusa:cart');
54
+
55
+ export class MedusaCartProvider extends CartProvider {
56
+ protected config: MedusaConfiguration;
57
+ /**
58
+ * This controls which fields are always included when fetching a cart
59
+ * You can override this in a subclass to add more fields as needed.
60
+ *
61
+ * example: this.includedFields = [includedFields, '+discounts.*'].join(',');
62
+ */
63
+ protected includedFields: string = ['+items.*'].join(',');
64
+
65
+ constructor(
66
+ config: MedusaConfiguration,
67
+ cache: Cache,
68
+ context: RequestContext,
69
+ public medusaApi: MedusaAPI
70
+ ) {
71
+ super(cache, context);
72
+ this.config = config;
73
+ }
74
+
75
+ @Reactionary({
76
+ inputSchema: CartQueryByIdSchema,
77
+ outputSchema: CartSchema,
78
+ })
79
+ public override async getById(
80
+ payload: CartQueryById
81
+ ): Promise<Result<Cart, NotFoundError>> {
82
+ try {
83
+ const client = await this.getClient();
84
+ const medusaId = payload.cart as MedusaCartIdentifier;
85
+
86
+ if (debug.enabled) {
87
+ debug('Fetching cart by ID:', medusaId.key);
88
+ }
89
+
90
+ const cartResponse = await client.store.cart.retrieve(medusaId.key);
91
+
92
+ if (debug.enabled) {
93
+ debug('Received cart response:', cartResponse);
94
+ }
95
+
96
+ if (cartResponse.cart) {
97
+ return success(this.parseSingle(cartResponse.cart));
98
+ }
99
+
100
+ return error<NotFoundError>({
101
+ type: 'NotFound',
102
+ identifier: payload,
103
+ });
104
+ } catch (err) {
105
+ debug('Failed to get cart by ID:', error);
106
+
107
+ return error<NotFoundError>({
108
+ type: 'NotFound',
109
+ identifier: payload,
110
+ });
111
+ }
112
+ }
113
+
114
+ @Reactionary({
115
+ inputSchema: CartMutationItemAddSchema,
116
+ outputSchema: CartSchema,
117
+ })
118
+ public override async add(
119
+ payload: CartMutationItemAdd
120
+ ): Promise<Result<Cart>> {
121
+ try {
122
+ const client = await this.getClient();
123
+
124
+ let cartIdentifier = payload.cart;
125
+ if (!cartIdentifier) {
126
+ cartIdentifier = await this.createCart();
127
+ }
128
+
129
+ const medusaId = cartIdentifier as MedusaCartIdentifier;
130
+
131
+ if (debug.enabled) {
132
+ debug(
133
+ 'Adding item to cart ID:',
134
+ medusaId.key,
135
+ 'SKU:',
136
+ payload.variant.sku,
137
+ 'Quantity:',
138
+ payload.quantity
139
+ );
140
+ }
141
+
142
+ // TODO: Convert from global SKU identifier, to something medusa understands.....
143
+
144
+ // the SKU identifier is supposed to be a globally understood identifier,
145
+
146
+ // but medusa only accepts variant IDs , so we have to resolve it somehow...
147
+ const variantId = await this.medusaApi.resolveVariantId(payload.variant.sku);
148
+
149
+ const response = await client.store.cart.createLineItem(
150
+ medusaId.key,
151
+ {
152
+ variant_id: variantId,
153
+ quantity: payload.quantity,
154
+ },
155
+ {
156
+ fields: this.includedFields,
157
+ }
158
+ );
159
+
160
+ if (debug.enabled) {
161
+ debug('Received add item response:', response);
162
+ }
163
+
164
+ if (response.cart) {
165
+ return success(this.parseSingle(response.cart));
166
+ }
167
+
168
+ throw new Error('Failed to add item to cart');
169
+ } catch (error) {
170
+ handleProviderError('add item to cart', error);
171
+ }
172
+ }
173
+
174
+ @Reactionary({
175
+ inputSchema: CartMutationItemRemoveSchema,
176
+ outputSchema: CartSchema,
177
+ })
178
+ public override async remove(
179
+ payload: CartMutationItemRemove
180
+ ): Promise<Result<Cart>> {
181
+ try {
182
+ const client = await this.getClient();
183
+ const medusaId = payload.cart as MedusaCartIdentifier;
184
+
185
+ const response = await client.store.cart.deleteLineItem(
186
+ medusaId.key,
187
+ payload.item.key,
188
+ {
189
+ fields: this.includedFields,
190
+ }
191
+ );
192
+
193
+ if (response.parent) {
194
+ return success(this.parseSingle(response.parent));
195
+ }
196
+
197
+ throw new Error('Failed to remove item from cart');
198
+ } catch (error) {
199
+ handleProviderError('remove item from cart', error);
200
+ }
201
+ }
202
+
203
+ @Reactionary({
204
+ inputSchema: CartMutationItemQuantityChangeSchema,
205
+ outputSchema: CartSchema,
206
+ })
207
+ public override async changeQuantity(
208
+ payload: CartMutationItemQuantityChange
209
+ ): Promise<Result<Cart>> {
210
+ if (payload.quantity < 1) {
211
+ throw new Error(
212
+ 'Changing quantity to 0 is not allowed. Use the remove call instead.'
213
+ );
214
+ // Changing quantity to 0 is not allowed. Use the remove call instead.
215
+ // return this.getById({ cart: payload.cart }, reqCtx);
216
+ }
217
+
218
+ try {
219
+ const client = await this.getClient();
220
+ const medusaId = payload.cart as MedusaCartIdentifier;
221
+
222
+ const response = await client.store.cart.updateLineItem(
223
+ medusaId.key,
224
+ payload.item.key,
225
+ {
226
+ quantity: payload.quantity,
227
+ },
228
+ {
229
+ fields: this.includedFields,
230
+ }
231
+ );
232
+
233
+ if (response.cart) {
234
+ return success(this.parseSingle(response.cart));
235
+ }
236
+
237
+ throw new Error('Failed to change item quantity');
238
+ } catch (error) {
239
+ handleProviderError('change item quantity', error);
240
+ }
241
+ }
242
+
243
+ @Reactionary({
244
+ outputSchema: CartIdentifierSchema,
245
+ })
246
+ public override async getActiveCartId(): Promise<
247
+ Result<CartIdentifier, NotFoundError>
248
+ > {
249
+ try {
250
+ const client = await this.getClient();
251
+ const sessionData = this.medusaApi.getSessionData();
252
+ // Check if customer has an active cart in session storage or create new one
253
+ const activeCartId = sessionData.activeCartId;
254
+
255
+ if (activeCartId) {
256
+ try {
257
+ await client.store.cart.retrieve(activeCartId);
258
+ return success(
259
+ MedusaCartIdentifierSchema.parse({
260
+ key: activeCartId,
261
+ region_id: (await this.medusaApi.getActiveRegion()).id,
262
+ })
263
+ );
264
+ } catch {
265
+ // Cart doesn't exist, create new one
266
+ }
267
+ }
268
+
269
+ // For guest users or if no active cart exists, return empty identifier
270
+ return error<NotFoundError>({
271
+ type: 'NotFound',
272
+ identifier: undefined,
273
+ });
274
+ } catch (err) {
275
+ debug('Failed to get active cart ID:', error);
276
+
277
+ return error<NotFoundError>({
278
+ type: 'NotFound',
279
+ identifier: undefined,
280
+ });
281
+ }
282
+ }
283
+
284
+ @Reactionary({
285
+ inputSchema: CartMutationDeleteCartSchema,
286
+ outputSchema: CartSchema,
287
+ })
288
+ public override async deleteCart(
289
+ payload: CartMutationDeleteCart
290
+ ): Promise<Result<void>> {
291
+ try {
292
+ const client = await this.getClient();
293
+ const medusaId = payload.cart as MedusaCartIdentifier;
294
+
295
+ if (medusaId.key) {
296
+ const sessionData = this.medusaApi.getSessionData();
297
+ if (sessionData.activeCartId) {
298
+ delete sessionData.activeCartId;
299
+ this.medusaApi.setSessionData({
300
+ activeCartId: undefined,
301
+ });
302
+ }
303
+ }
304
+ // then delete it. But there is not really a deleteCart method, so we just orphan it.
305
+ // await client.store.cart.deleteCart(medusaId.key);
306
+
307
+ // lets delete all items
308
+ const cartResponse = await client.store.cart.retrieve(medusaId.key);
309
+ if (cartResponse.cart) {
310
+ for (const item of cartResponse.cart.items || []) {
311
+ await client.store.cart.deleteLineItem(medusaId.key, item.id);
312
+ }
313
+ }
314
+
315
+ return success(undefined);
316
+ } catch (error) {
317
+ debug('Failed to delete cart:', error);
318
+ return success(undefined);
319
+ }
320
+ }
321
+
322
+ @Reactionary({
323
+ inputSchema: CartMutationApplyCouponSchema,
324
+ outputSchema: CartSchema,
325
+ })
326
+ public override async applyCouponCode(
327
+ payload: CartMutationApplyCoupon
328
+ ): Promise<Result<Cart>> {
329
+ try {
330
+ const client = await this.getClient();
331
+ const medusaId = payload.cart as MedusaCartIdentifier;
332
+
333
+ const response = await client.store.cart.update(
334
+ medusaId.key,
335
+ {
336
+ promo_codes: [payload.couponCode],
337
+ },
338
+ {
339
+ fields: this.includedFields,
340
+ }
341
+ );
342
+
343
+ if (response.cart) {
344
+ return success(this.parseSingle(response.cart));
345
+ }
346
+
347
+ throw new Error('Failed to apply coupon code');
348
+ } catch (error) {
349
+ handleProviderError('apply coupon code', error);
350
+ }
351
+ }
352
+
353
+ @Reactionary({
354
+ inputSchema: CartMutationRemoveCouponSchema,
355
+ outputSchema: CartSchema,
356
+ })
357
+ public override async removeCouponCode(
358
+ payload: CartMutationRemoveCoupon
359
+ ): Promise<Result<Cart>> {
360
+ try {
361
+ const client = await this.getClient();
362
+ const medusaId = payload.cart as MedusaCartIdentifier;
363
+
364
+ // Get current cart to find the discount to remove
365
+ const cartResponse = await client.store.cart.retrieve(medusaId.key);
366
+
367
+ if (cartResponse.cart?.promotions) {
368
+ const manualDiscounts = cartResponse.cart.promotions.filter(
369
+ (x) => !x.is_automatic && x.code
370
+ );
371
+
372
+ const remainingCodes = (manualDiscounts
373
+ .filter((x) => x.code !== payload.couponCode)
374
+ .map((promotion: MedusaTypes.StoreCartPromotion) => promotion.code) ||
375
+ []) as string[];
376
+ const response = await client.store.cart.update(
377
+ medusaId.key,
378
+ {
379
+ promo_codes: remainingCodes || [],
380
+ },
381
+ {
382
+ fields: this.includedFields,
383
+ }
384
+ );
385
+
386
+ if (response.cart) {
387
+ return success(this.parseSingle(response.cart));
388
+ }
389
+ }
390
+
391
+ throw new Error('Failed to remove coupon code');
392
+ } catch (error) {
393
+ handleProviderError('remove coupon code', error);
394
+ }
395
+ }
396
+
397
+ @Reactionary({
398
+ inputSchema: CartMutationChangeCurrencySchema,
399
+ outputSchema: CartSchema,
400
+ })
401
+ public override async changeCurrency(
402
+ payload: CartMutationChangeCurrency
403
+ ): Promise<Result<Cart>> {
404
+ try {
405
+ const client = await this.getClient();
406
+
407
+ // Get current cart
408
+ const currentCartResponse = await client.store.cart.retrieve(
409
+ payload.cart.key
410
+ );
411
+ client.store.cart.update(
412
+ payload.cart.key,
413
+ {
414
+ region_id: (await this.medusaApi.getActiveRegion()).id,
415
+ },
416
+ {
417
+ fields: this.includedFields,
418
+ }
419
+ );
420
+ if (!currentCartResponse.cart) {
421
+ throw new Error('Cart not found');
422
+ }
423
+
424
+ // Get the new cart
425
+ const newCartResponse = await client.store.cart.retrieve(
426
+ payload.cart.key
427
+ );
428
+
429
+ if (newCartResponse.cart) {
430
+ // Update session to use new cart
431
+ this.medusaApi.setSessionData({
432
+ activeCartId: newCartResponse.cart.id,
433
+ });
434
+
435
+ return success(this.parseSingle(newCartResponse.cart));
436
+ }
437
+
438
+ throw new Error('Failed to change currency');
439
+ } catch (error) {
440
+ handleProviderError('change currency', error);
441
+ }
442
+ }
443
+
444
+ protected async createCart(currency?: string): Promise<CartIdentifier> {
445
+ try {
446
+ const client = await this.getClient();
447
+
448
+ const response = await client.store.cart.create(
449
+ {
450
+ currency_code: (
451
+ currency ||
452
+ this.context.languageContext.currencyCode ||
453
+ ''
454
+ ).toLowerCase(),
455
+ },
456
+ {
457
+ fields: this.includedFields,
458
+ }
459
+ );
460
+
461
+ if (response.cart) {
462
+ const cartIdentifier = MedusaCartIdentifierSchema.parse({
463
+ key: response.cart.id,
464
+ region_id: response.cart.region_id,
465
+ });
466
+
467
+ // Store cart ID in session
468
+ this.medusaApi.setSessionData({
469
+ activeCartId: cartIdentifier.key,
470
+ });
471
+
472
+ return cartIdentifier;
473
+ }
474
+
475
+ throw new Error('Failed to create cart');
476
+ } catch (error) {
477
+ handleProviderError('create cart', error);
478
+ }
479
+ }
480
+
481
+ protected async getClient() {
482
+ return this.medusaApi.getClient();
483
+ }
484
+
485
+ /**
486
+ * Extension point to control the parsing of a single cart item price
487
+ * @param remoteItem
488
+ * @param currency
489
+ * @returns
490
+ */
491
+ protected parseItemPrice(
492
+ remoteItem: MedusaTypes.StoreCartLineItem,
493
+ currency: Currency
494
+ ): ItemCostBreakdown {
495
+ return parseMedusaItemPrice(remoteItem, currency);
496
+ }
497
+
498
+ /**
499
+ * Extension point to control the parsing of the cost breakdown of a cart
500
+ * @param remote
501
+ * @returns
502
+ */
503
+ protected parseCostBreakdown(remote: MedusaTypes.StoreCart): CostBreakDown {
504
+ return parseMedusaCostBreakdown(remote);
505
+ }
506
+
507
+ /**
508
+ * Extension point to control the parsing of a single cart item
509
+ * @param remoteItem
510
+ * @param currency
511
+ * @returns
512
+ */
513
+ protected parseCartItem(
514
+ remoteItem: MedusaTypes.StoreCartLineItem,
515
+ currency: Currency
516
+ ): CartItem {
517
+ const item: CartItem = {
518
+ identifier: {
519
+ key: remoteItem.id,
520
+ },
521
+ product: {
522
+ key: remoteItem.product_id || '',
523
+ },
524
+ variant: ProductVariantIdentifierSchema.parse({
525
+ sku: remoteItem.variant_sku || '',
526
+ } satisfies ProductVariantIdentifier),
527
+ quantity: remoteItem.quantity || 1,
528
+ price: parseMedusaItemPrice(remoteItem, currency),
529
+ };
530
+ return item;
531
+ }
532
+
533
+ /**
534
+ * Extension point to control the parsing of a single cart
535
+ * @param remote
536
+ * @returns
537
+ */
538
+ protected parseSingle(remote: MedusaTypes.StoreCart): Cart {
539
+ const identifier = MedusaCartIdentifierSchema.parse({
540
+ key: remote.id,
541
+ region_id: remote.region_id,
542
+ } satisfies MedusaCartIdentifier);
543
+
544
+ const name = '' + (remote.metadata?.['name'] || '');
545
+ const description = '' + (remote.metadata?.['description'] || '');
546
+
547
+ const price = this.parseCostBreakdown(remote);
548
+
549
+ // Parse cart items
550
+ const items = new Array<CartItem>();
551
+
552
+ const allItems = remote.items || [];
553
+ allItems.sort((a, b) =>
554
+ a.created_at && b.created_at
555
+ ? new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
556
+ : 0
557
+ );
558
+ for (const remoteItem of allItems) {
559
+ items.push(this.parseCartItem(remoteItem, price.grandTotal.currency));
560
+ }
561
+
562
+ const result = {
563
+ identifier,
564
+ name,
565
+ description,
566
+ price,
567
+ items,
568
+ userId: {
569
+ userId: '???',
570
+ },
571
+ } satisfies Cart;
572
+
573
+ return result;
574
+ }
575
+ }