@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,226 @@
1
+ import type {
2
+ Cache,
3
+ Order,
4
+ OrderQueryById,
5
+ RequestContext,
6
+ Result,
7
+ NotFoundError,
8
+ IdentityIdentifier,
9
+ CostBreakDown,
10
+ Currency,
11
+ OrderItem,
12
+ CartItem,
13
+ ProductVariantIdentifier,
14
+ ItemCostBreakdown,
15
+ OrderStatus,
16
+ InventoryStatus,
17
+ OrderInventoryStatus,
18
+ PaymentMethodIdentifier,
19
+ PaymentInstruction,
20
+ } from '@reactionary/core';
21
+ import {
22
+ OrderProvider,
23
+ OrderQueryByIdSchema,
24
+ OrderSchema,
25
+ Reactionary,
26
+ success,
27
+ error,
28
+ ProductVariantIdentifierSchema,
29
+ } from '@reactionary/core';
30
+ import createDebug from 'debug';
31
+ import type { MedusaAPI } from '../core/client.js';
32
+ import type { MedusaConfiguration } from '../schema/configuration.schema.js';
33
+ import { handleProviderError } from '../utils/medusa-helpers.js';
34
+ import type { StoreOrder, StoreOrderLineItem, StorePaymentCollection } from '@medusajs/types';
35
+ import { parseMedusaItemPrice, parseMedusaCostBreakdown } from '../utils/medusa-helpers.js'
36
+ const debug = createDebug('reactionary:medusa:order');
37
+
38
+ export class MedusaOrderProvider extends OrderProvider {
39
+ protected config: MedusaConfiguration;
40
+
41
+ constructor(
42
+ config: MedusaConfiguration,
43
+ cache: Cache,
44
+ context: RequestContext,
45
+ public medusaApi: MedusaAPI
46
+ ) {
47
+ super(cache, context);
48
+
49
+ this.config = config;
50
+ }
51
+
52
+ @Reactionary({
53
+ inputSchema: OrderQueryByIdSchema,
54
+ outputSchema: OrderSchema,
55
+ })
56
+ public async getById(payload: OrderQueryById): Promise<Result<Order, NotFoundError>> {
57
+ debug('getById', payload);
58
+
59
+ const medusa = await this.medusaApi.getClient();
60
+
61
+ try {
62
+ // TODO: Implement actual order retrieval logic
63
+ // const response = await medusa.store.order.retrieve(payload.order.key);
64
+ const response = await medusa.store.order.retrieve(payload.order.key)
65
+
66
+ const order = this.parseSingle(response.order);
67
+
68
+ return success(order);
69
+
70
+ } catch (err) {
71
+ return handleProviderError('order', err);
72
+ }
73
+ }
74
+
75
+
76
+ /**
77
+ * Extension point to control the parsing of a single cart item price
78
+ * @param remoteItem
79
+ * @param currency
80
+ * @returns
81
+ */
82
+ protected parseItemPrice(
83
+ remoteItem: StoreOrderLineItem,
84
+ currency: Currency
85
+ ): ItemCostBreakdown {
86
+ return parseMedusaItemPrice(remoteItem, currency);
87
+ }
88
+
89
+ /**
90
+ * Extension point to control the parsing of the cost breakdown of a cart
91
+ * @param remote
92
+ * @returns
93
+ */
94
+ protected parseCostBreakdown(remote: StoreOrder): CostBreakDown {
95
+ return parseMedusaCostBreakdown(remote);
96
+ }
97
+
98
+ /**
99
+ * Extension point to control the parsing of a single cart item
100
+ * @param remoteItem
101
+ * @param currency
102
+ * @returns
103
+ */
104
+ protected parseOrderItem(
105
+ remoteItem: StoreOrderLineItem,
106
+ currency: Currency
107
+ ): OrderItem {
108
+ const item: OrderItem = {
109
+ identifier: {
110
+ key: remoteItem.id,
111
+ },
112
+ variant: ProductVariantIdentifierSchema.parse({
113
+ sku: remoteItem.variant_sku || '',
114
+ } satisfies ProductVariantIdentifier),
115
+ quantity: remoteItem.quantity || 1,
116
+ price: this.parseItemPrice(remoteItem, currency),
117
+ inventoryStatus: 'Allocated'
118
+ };
119
+ return item;
120
+ }
121
+
122
+ protected parsePaymentInstruction(remotePayment: StorePaymentCollection, order: StoreOrder) {
123
+ const paymentMethodIdentifier: PaymentMethodIdentifier = {
124
+ method: remotePayment.payment_providers?.[0]?.id || 'unknown',
125
+ name: remotePayment.payment_providers?.[0]?.id || 'unknown',
126
+ paymentProcessor: remotePayment.payment_providers?.[0]?.id || 'unknown',
127
+ };
128
+
129
+ let status: PaymentInstruction['status'] = 'pending';
130
+ switch (remotePayment.status) {
131
+ case 'not_paid':
132
+ status = 'pending';
133
+ break;
134
+ case 'awaiting':
135
+ status = 'pending';
136
+ break;
137
+ case 'authorized':
138
+ status = 'authorized';
139
+ break;
140
+ case 'partially_authorized':
141
+ status = 'pending';
142
+ break;
143
+ case 'canceled':
144
+ status = 'canceled';
145
+ break;
146
+ case 'failed':
147
+ status = 'canceled';
148
+ break;
149
+ case 'completed':
150
+ status = 'capture';
151
+ break;
152
+ }
153
+
154
+ const paymentData = remotePayment.payments?.[0].data || {};
155
+ const pi = {
156
+ identifier: {
157
+ key: remotePayment.id,
158
+ },
159
+ amount: {
160
+ value: remotePayment.amount,
161
+ currency: remotePayment.currency_code?.toUpperCase() as Currency,
162
+ },
163
+ paymentMethod: paymentMethodIdentifier,
164
+ protocolData: paymentData
165
+ ? Object.entries(paymentData).map(([key, value]) => ({
166
+ key,
167
+ value: String(value),
168
+ }))
169
+ : [],
170
+ status,
171
+ } satisfies PaymentInstruction;
172
+ return pi;
173
+ }
174
+
175
+
176
+
177
+
178
+ protected parseSingle(body: StoreOrder): Order {
179
+
180
+ const identifier = { key: body.id };
181
+ const userId: IdentityIdentifier = {
182
+ userId: body.customer_id || '',
183
+ }
184
+
185
+ const items = (body.items || []).map((item) => {
186
+ return this.parseOrderItem(item, body.currency_code.toUpperCase() as Currency);
187
+ });
188
+
189
+ const price = this.parseCostBreakdown(body);
190
+
191
+ let orderStatus: OrderStatus = 'AwaitingPayment'
192
+ if (body.status === 'draft') {
193
+ orderStatus = 'AwaitingPayment';
194
+ }
195
+ if (body.status === 'pending') {
196
+ orderStatus = 'ReleasedToFulfillment';
197
+ }
198
+ if (body.status === 'completed') {
199
+ orderStatus = 'Shipped';
200
+ }
201
+ if (body.status === 'canceled') {
202
+ orderStatus = 'Cancelled';
203
+ }
204
+
205
+ let inventoryStatus: OrderInventoryStatus = 'NotAllocated'
206
+ // Medusa does not have direct mapping for inventory status on orders
207
+ // This is a placeholder logic and may need to be adjusted based on actual requirements
208
+ if(body.fulfillment_status === "fulfilled") {
209
+ inventoryStatus = 'Allocated';
210
+ }
211
+
212
+ const paymentInstructions: PaymentInstruction[] = body.payment_collections?.map( (pc) => {
213
+ return this.parsePaymentInstruction(pc, body);
214
+ }) || [];
215
+
216
+ return {
217
+ identifier,
218
+ userId,
219
+ items,
220
+ price,
221
+ orderStatus,
222
+ inventoryStatus,
223
+ paymentInstructions
224
+ } satisfies Order;
225
+ }
226
+ }
@@ -0,0 +1,140 @@
1
+ import type { StoreProductVariant } from '@medusajs/types';
2
+ import {
3
+ PriceProvider,
4
+ PriceSchema,
5
+ CustomerPriceQuerySchema,
6
+ ListPriceQuerySchema,
7
+ Reactionary,
8
+ success,
9
+ type Cache,
10
+ type Currency,
11
+ type CustomerPriceQuery,
12
+ type ListPriceQuery,
13
+ type Price,
14
+ type RequestContext,
15
+ type PriceIdentifier,
16
+ type MonetaryAmount,
17
+ type Result
18
+ } from '@reactionary/core';
19
+ import createDebug from 'debug';
20
+ import type z from 'zod';
21
+ import type { MedusaAPI } from '../core/client.js';
22
+ import type { MedusaConfiguration } from '../schema/configuration.schema.js';
23
+
24
+ const debug = createDebug('reactionary:medusa:price');
25
+
26
+ export class MedusaPriceProvider extends PriceProvider {
27
+ protected config: MedusaConfiguration;
28
+
29
+ constructor(
30
+ config: MedusaConfiguration,
31
+ cache: Cache,
32
+ context: RequestContext,
33
+ public medusaApi: MedusaAPI
34
+ ) {
35
+ super(cache, context);
36
+ this.config = config;
37
+ }
38
+
39
+ @Reactionary({
40
+ inputSchema: ListPriceQuerySchema,
41
+ outputSchema: PriceSchema,
42
+ })
43
+ public override async getListPrice(payload: ListPriceQuery): Promise<Result<Price>> {
44
+ const result = await this.getBySKU(payload);
45
+
46
+ return success(result);
47
+ }
48
+
49
+ @Reactionary({
50
+ inputSchema: CustomerPriceQuerySchema,
51
+ outputSchema: PriceSchema,
52
+ })
53
+ public override async getCustomerPrice(payload: CustomerPriceQuery): Promise<Result<Price>> {
54
+ const result = await this.getBySKU(payload);
55
+
56
+ return success(result);
57
+ }
58
+
59
+
60
+ protected async getBySKU(payload: ListPriceQuery | CustomerPriceQuery ): Promise<Price> {
61
+ const sku = payload.variant.sku;
62
+
63
+ if (debug.enabled) {
64
+ debug(`Fetching price for SKU: ${sku}`);
65
+ }
66
+
67
+ try {
68
+ const productForSKU = await this.medusaApi.resolveProductForSKU(payload.variant.sku);
69
+
70
+ const client = await this.medusaApi.getClient();
71
+ const product = (
72
+ await client.store.product.retrieve(
73
+ productForSKU.id || '',
74
+ { region_id: (await this.medusaApi.getActiveRegion()).id }
75
+ )
76
+ ).product;
77
+
78
+
79
+ const variant = product.variants?.find((v) => v.sku === sku);
80
+ if (!variant) {
81
+ if (debug.enabled) {
82
+ debug(
83
+ `Variant with SKU ${sku} not found in product ${product.id}`
84
+ );
85
+ }
86
+ return this.createEmptyPriceResult(sku);
87
+ }
88
+
89
+ // For simplicity, return the first matched product
90
+ return this.parseSingle(variant);
91
+ } catch (error) {
92
+ if (debug.enabled) {
93
+ debug(
94
+ `Error fetching price for SKU ${sku}: ${(error as Error).message}`
95
+ );
96
+ }
97
+ return this.createEmptyPriceResult(sku);
98
+ }
99
+ }
100
+
101
+ protected parseSingle(variant: StoreProductVariant): Price {
102
+ const identifier = {
103
+ variant: {
104
+ sku: variant.sku || '',
105
+ },
106
+ } satisfies PriceIdentifier;
107
+
108
+ // In Medusa v2, calculated_price contains the final price for the variant
109
+ // based on the region, currency, and any applicable price lists
110
+ const calculatedPrice = variant.calculated_price;
111
+
112
+ let unitPrice;
113
+ if (calculatedPrice) {
114
+ unitPrice = {
115
+ value: calculatedPrice.calculated_amount || 0,
116
+ currency: (calculatedPrice.currency_code?.toUpperCase() ||
117
+ this.context.languageContext.currencyCode) as Currency,
118
+ } satisfies MonetaryAmount;
119
+ } else {
120
+ // Fallback to empty price if no calculated price available
121
+ unitPrice = {
122
+ value: -1,
123
+ currency: this.context.languageContext.currencyCode as Currency,
124
+ } satisfies MonetaryAmount;
125
+ }
126
+
127
+ const result = {
128
+ identifier,
129
+ tieredPrices: [],
130
+ unitPrice
131
+ } satisfies Price;
132
+
133
+ return result;
134
+ }
135
+
136
+
137
+ protected override getResourceName(): string {
138
+ return 'price';
139
+ }
140
+ }
@@ -0,0 +1,243 @@
1
+ import {
2
+ ProductSearchProvider,
3
+ ProductSearchQueryByTermSchema,
4
+ type Cache,
5
+ type RequestContext,
6
+ type ProductSearchQueryByTerm,
7
+ type ProductSearchResult,
8
+ type ProductSearchResultItem,
9
+ ImageSchema,
10
+ ProductVariantIdentifierSchema,
11
+ type ProductVariantIdentifier,
12
+ ProductVariantOptionSchema,
13
+ type ProductVariantOption,
14
+ ProductOptionIdentifierSchema,
15
+ type ProductOptionIdentifier,
16
+ ProductSearchResultItemVariantSchema,
17
+ type ProductSearchResultItemVariant,
18
+ type FacetIdentifier,
19
+ type FacetValueIdentifier,
20
+ type ProductSearchResultFacet,
21
+ type ProductSearchResultFacetValue,
22
+ Reactionary,
23
+ ProductSearchResultSchema,
24
+ type ProductSearchQueryCreateNavigationFilter,
25
+ FacetValueIdentifierSchema,
26
+ FacetIdentifierSchema,
27
+ type Result,
28
+ success,
29
+ } from '@reactionary/core';
30
+ import createDebug from 'debug';
31
+ import type z from 'zod';
32
+ import type { MedusaConfiguration } from '../schema/configuration.schema.js';
33
+ import type { MedusaAPI } from '../core/client.js';
34
+ import type {
35
+ StoreProduct,
36
+ StoreProductCategory,
37
+ StoreProductListResponse,
38
+ StoreProductVariant,
39
+ } from '@medusajs/types';
40
+
41
+ const debug = createDebug('reactionary:medusa:search');
42
+
43
+ export class MedusaSearchProvider extends ProductSearchProvider {
44
+ protected config: MedusaConfiguration;
45
+
46
+ constructor(
47
+ config: MedusaConfiguration,
48
+ cache: Cache,
49
+ context: RequestContext,
50
+ public medusaApi: MedusaAPI
51
+ ) {
52
+ super(cache, context);
53
+ this.config = config;
54
+ }
55
+
56
+ protected async resolveCategoryIdByExternalId(
57
+ externalId: string
58
+ ): Promise<StoreProductCategory | null> {
59
+ const sdk = await this.medusaApi.getClient();
60
+ let offset = 0;
61
+ const limit = 50;
62
+ let candidate: StoreProductCategory | undefined = undefined;
63
+ while (true) {
64
+ try {
65
+ const categoryResult = await sdk.store.category.list({
66
+ offset,
67
+ limit,
68
+ });
69
+
70
+ if (categoryResult.product_categories.length === 0) {
71
+ break;
72
+ }
73
+
74
+ candidate = categoryResult.product_categories.find(
75
+ (cat) => cat.metadata?.['external_id'] === externalId
76
+ );
77
+ if (candidate) {
78
+ break;
79
+ }
80
+ offset += limit;
81
+ } catch (error) {
82
+ throw new Error(
83
+ 'Category not found ' + externalId + ' due to error: ' + error
84
+ );
85
+ break;
86
+ }
87
+ }
88
+ return candidate || null;
89
+ }
90
+
91
+ @Reactionary({
92
+ inputSchema: ProductSearchQueryByTermSchema,
93
+ outputSchema: ProductSearchResultSchema,
94
+ cache: true,
95
+ cacheTimeToLiveInSeconds: 300,
96
+ currencyDependentCaching: false,
97
+ localeDependentCaching: true
98
+ })
99
+ public override async queryByTerm(
100
+ payload: ProductSearchQueryByTerm
101
+ ): Promise<Result<ProductSearchResult>> {
102
+ const client = await this.medusaApi.getClient();
103
+
104
+ let categoryIdToFind: string | null = null;
105
+ if (payload.search.categoryFilter?.key) {
106
+ debug(
107
+ `Resolving category filter for key: ${payload.search.categoryFilter.key}`
108
+ );
109
+
110
+ const category = await this.resolveCategoryIdByExternalId(
111
+ payload.search.categoryFilter.key
112
+ );
113
+ if (category) {
114
+ categoryIdToFind = category.id;
115
+ debug(
116
+ `Resolved category filter key ${payload.search.categoryFilter.key} to id: ${categoryIdToFind}`
117
+ );
118
+ } else {
119
+ debug(
120
+ `Could not resolve category filter for key: ${payload.search.categoryFilter.key}`
121
+ );
122
+ }
123
+ }
124
+ const finalSearch = (payload.search.term || '').trim().replace('*', '');
125
+ const response = await client.store.product.list({
126
+ q: finalSearch,
127
+ ...(categoryIdToFind ? { category_id: categoryIdToFind } : {}),
128
+ limit: payload.search.paginationOptions.pageSize,
129
+ offset:
130
+ (payload.search.paginationOptions.pageNumber - 1) *
131
+ payload.search.paginationOptions.pageSize,
132
+ });
133
+
134
+ const result = this.parsePaginatedResult(response) as ProductSearchResult;
135
+ if (debug.enabled) {
136
+ debug(
137
+ `Search for term "${payload.search.term}" returned ${response.products.length} products (page ${payload.search.paginationOptions.pageNumber} of ${result.totalPages})`
138
+ );
139
+ }
140
+
141
+ // Set result metadata
142
+ result.identifier = {
143
+ ...payload.search,
144
+ };
145
+
146
+ return success(result);
147
+ }
148
+
149
+ protected parsePaginatedResult(remote: StoreProductListResponse) {
150
+ // Parse facets
151
+ // no facets available from Medusa at the moment
152
+
153
+ const products: ProductSearchResultItem[] = remote.products.map((p) =>
154
+ this.parseSingle(p)
155
+ );
156
+
157
+ const result = {
158
+ identifier: {
159
+ facets: [],
160
+ filters: [],
161
+ paginationOptions: {
162
+ pageNumber: 1,
163
+ pageSize: 0,
164
+ },
165
+ term: '',
166
+ },
167
+ pageNumber: (Math.ceil(remote.offset / remote.limit) || 0) + 1,
168
+ pageSize: remote.limit,
169
+ totalCount: remote.count,
170
+ totalPages: Math.ceil(remote.count / remote.limit || 0) + 1,
171
+ items: products,
172
+ facets: [],
173
+ } satisfies ProductSearchResult;
174
+
175
+ (result as ProductSearchResult).facets = [];
176
+ return result;
177
+ }
178
+
179
+ protected parseSingle(_body: StoreProduct): ProductSearchResultItem {
180
+ const heroVariant = _body.variants?.[0];
181
+ const identifier = { key: _body.id };
182
+ const slug = _body.handle;
183
+ const name = heroVariant?.title || _body.title;
184
+ const variants = [];
185
+ if (heroVariant) {
186
+ variants.push(this.parseVariant(heroVariant, _body));
187
+ }
188
+
189
+ const result = {
190
+ identifier,
191
+ name,
192
+ slug,
193
+ variants,
194
+ } satisfies ProductSearchResultItem;
195
+
196
+ return result;
197
+ }
198
+
199
+ protected parseVariant(
200
+ variant: StoreProductVariant,
201
+ product: StoreProduct
202
+ ): ProductSearchResultItemVariant {
203
+ const img = ImageSchema.parse({
204
+ sourceUrl: product.images?.[0].url ?? '',
205
+ altText: product.title || undefined,
206
+ });
207
+
208
+ return ProductSearchResultItemVariantSchema.parse({
209
+ variant: ProductVariantIdentifierSchema.parse({
210
+ sku: variant.sku || '',
211
+ } satisfies ProductVariantIdentifier),
212
+ image: img,
213
+ } satisfies Partial<ProductSearchResultItemVariant>);
214
+ }
215
+
216
+ public override async createCategoryNavigationFilter(
217
+ payload: ProductSearchQueryCreateNavigationFilter
218
+ ): Promise<Result<FacetValueIdentifier>> {
219
+ const facetIdentifier = FacetIdentifierSchema.parse({
220
+ key: 'categories',
221
+ });
222
+ const facetValueIdentifier = FacetValueIdentifierSchema.parse({
223
+ facet: facetIdentifier,
224
+ key: payload.categoryPath[payload.categoryPath.length - 1].identifier.key,
225
+ });
226
+
227
+ return success(facetValueIdentifier);
228
+ }
229
+
230
+ protected override parseFacetValue(
231
+ facetValueIdentifier: FacetValueIdentifier,
232
+ label: string,
233
+ count: number
234
+ ): ProductSearchResultFacetValue {
235
+ throw new Error('Method not implemented.');
236
+ }
237
+ protected override parseFacet(
238
+ facetIdentifier: FacetIdentifier,
239
+ facetValue: unknown
240
+ ): ProductSearchResultFacet {
241
+ throw new Error('Method not implemented.');
242
+ }
243
+ }