@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,106 @@
1
+ import {
2
+ OrderProvider,
3
+ OrderQueryByIdSchema,
4
+ OrderSchema,
5
+ Reactionary,
6
+ success,
7
+ type Cache,
8
+ type NotFoundError,
9
+ type Order,
10
+ type OrderQueryById,
11
+ type RequestContext,
12
+ type Result,
13
+ } from '@reactionary/core';
14
+ import type { FakeConfiguration } from '../schema/configuration.schema.js';
15
+ import { Faker, en, base } from '@faker-js/faker';
16
+
17
+ export class FakeOrderProvider extends OrderProvider {
18
+ protected config: FakeConfiguration;
19
+ protected generator: Faker;
20
+
21
+ constructor(
22
+ config: FakeConfiguration,
23
+ cache: Cache,
24
+ context: RequestContext
25
+ ) {
26
+ super(cache, context);
27
+
28
+ this.config = config;
29
+
30
+ this.generator = new Faker({
31
+ locale: [en, base],
32
+ seed: config.seeds.product,
33
+ });
34
+ }
35
+
36
+ @Reactionary({
37
+ inputSchema: OrderQueryByIdSchema,
38
+ outputSchema: OrderSchema,
39
+ })
40
+ public override async getById(
41
+ payload: OrderQueryById
42
+ ): Promise<Result<Order, NotFoundError>> {
43
+ const order = {
44
+ identifier: payload.order,
45
+ inventoryStatus: 'Allocated',
46
+ items: [],
47
+ orderStatus: 'Shipped',
48
+ paymentInstructions: [],
49
+ userId: {
50
+ userId: '1234'
51
+ },
52
+ price: {
53
+ grandTotal: {
54
+ currency: 'USD',
55
+ value: this.generator.number.float({
56
+ min: 100,
57
+ max: 10000,
58
+ multipleOf: 25,
59
+ }),
60
+ },
61
+ totalDiscount: {
62
+ currency: 'USD',
63
+ value: this.generator.number.float({
64
+ min: 100,
65
+ max: 10000,
66
+ multipleOf: 25,
67
+ }),
68
+ },
69
+ totalProductPrice: {
70
+ currency: 'USD',
71
+ value: this.generator.number.float({
72
+ min: 100,
73
+ max: 10000,
74
+ multipleOf: 25,
75
+ }),
76
+ },
77
+ totalShipping: {
78
+ currency: 'USD',
79
+ value: this.generator.number.float({
80
+ min: 100,
81
+ max: 10000,
82
+ multipleOf: 25,
83
+ }),
84
+ },
85
+ totalSurcharge: {
86
+ currency: 'USD',
87
+ value: this.generator.number.float({
88
+ min: 100,
89
+ max: 10000,
90
+ multipleOf: 25,
91
+ }),
92
+ },
93
+ totalTax: {
94
+ currency: 'USD',
95
+ value: this.generator.number.float({
96
+ min: 100,
97
+ max: 10000,
98
+ multipleOf: 25,
99
+ }),
100
+ },
101
+ },
102
+ } satisfies Order;
103
+
104
+ return success(order);
105
+ }
106
+ }
@@ -1,46 +1,109 @@
1
1
  import {
2
- type Price,
3
- type PriceQueryBySku,
4
2
  type RequestContext,
5
3
  type Cache,
6
4
  PriceProvider,
5
+ type CustomerPriceQuery,
6
+ type ListPriceQuery,
7
+ type Price,
8
+ Reactionary,
9
+ CustomerPriceQuerySchema,
10
+ PriceSchema,
11
+ ListPriceQuerySchema,
12
+ error,
13
+ success,
14
+ type Result,
7
15
  } from '@reactionary/core';
8
16
  import type z from 'zod';
9
17
  import type { FakeConfiguration } from '../schema/configuration.schema.js';
10
18
  import { base, en, Faker } from '@faker-js/faker';
11
19
 
12
- export class FakePriceProvider<
13
- T extends Price = Price
14
- > extends PriceProvider<T> {
20
+ export class FakePriceProvider extends PriceProvider {
15
21
  protected config: FakeConfiguration;
16
22
 
17
- constructor(config: FakeConfiguration, schema: z.ZodType<T>, cache: Cache) {
18
- super(schema, cache);
23
+ constructor(
24
+ config: FakeConfiguration,
25
+ cache: Cache,
26
+ context: RequestContext
27
+ ) {
28
+ super(cache, context);
19
29
 
20
30
  this.config = config;
21
31
  }
22
32
 
23
- public override async getBySKUs(payload: PriceQueryBySku[], reqCtx: RequestContext): Promise<T[]> {
33
+ @Reactionary({
34
+ inputSchema: ListPriceQuerySchema,
35
+ outputSchema: PriceSchema
36
+ })
37
+ public override async getListPrice(payload: ListPriceQuery): Promise<Result<Price>> {
38
+ if (payload.variant.sku === 'unknown-sku') {
39
+ return success(this.createEmptyPriceResult(payload.variant.sku));
40
+ }
24
41
 
25
- const promises = payload.map(p => this.getBySKU(p, reqCtx));
26
- const result = await Promise.all(promises);
27
- return result;
28
- }
42
+ // Generate a simple hash from the SKU key string for seeding
43
+ let hash = 0;
44
+ const skuString = payload.variant.sku;
45
+ for (let i = 0; i < skuString.length; i++) {
46
+ hash = (hash << 5) - hash + skuString.charCodeAt(i);
47
+ hash = hash & hash; // Convert to 32bit integer
48
+ }
29
49
 
30
- public override async getBySKU(
31
- payload: PriceQueryBySku,
32
- _reqCtx: RequestContext
33
- ): Promise<T> {
50
+ const generator = new Faker({
51
+ seed: hash || 42,
52
+ locale: [en, base],
53
+ });
34
54
 
35
- if (payload.sku.key === 'unknown-sku') {
36
- return this.createEmptyPriceResult(payload.sku.key, _reqCtx.languageContext.currencyCode);
55
+ const model = {
56
+ identifier: {
57
+ variant: payload.variant,
58
+ },
59
+ unitPrice: {
60
+ value: generator.number.int({ min: 300, max: 100000 }) / 100,
61
+ currency: this.context.languageContext.currencyCode,
62
+ },
63
+ } as Price;
64
+
65
+ if (skuString.includes('with-tiers')) {
66
+ const unitPrice = model.unitPrice?.value || 0;
67
+ // Ensure tiered prices are less than the unit price
68
+ const tier1Price = unitPrice * 0.8;
69
+ const tier2Price = tier1Price * 0.8;
70
+ model.tieredPrices = [
71
+ {
72
+ minimumQuantity: generator.number.int({ min: 2, max: 5 }),
73
+ price: {
74
+ value: tier1Price,
75
+ currency: this.context.languageContext.currencyCode,
76
+ },
77
+ },
78
+ {
79
+ minimumQuantity: generator.number.int({ min: 6, max: 10 }),
80
+ price: {
81
+ value: tier2Price,
82
+ currency: this.context.languageContext.currencyCode,
83
+ },
84
+ },
85
+ ];
86
+ } else {
87
+ model.tieredPrices = [];
88
+ }
89
+
90
+ return success(model);
91
+ }
92
+
93
+ @Reactionary({
94
+ inputSchema: CustomerPriceQuerySchema,
95
+ outputSchema: PriceSchema
96
+ })
97
+ public override async getCustomerPrice(payload: CustomerPriceQuery): Promise<Result<Price>> {
98
+ if (payload.variant.sku === 'unknown-sku') {
99
+ return success(this.createEmptyPriceResult(payload.variant.sku));
37
100
  }
38
101
 
39
102
  // Generate a simple hash from the SKU key string for seeding
40
103
  let hash = 0;
41
- const skuString = payload.sku.key;
104
+ const skuString = payload.variant.sku;
42
105
  for (let i = 0; i < skuString.length; i++) {
43
- hash = ((hash << 5) - hash) + skuString.charCodeAt(i);
106
+ hash = (hash << 5) - hash + skuString.charCodeAt(i);
44
107
  hash = hash & hash; // Convert to 32bit integer
45
108
  }
46
109
 
@@ -49,24 +112,15 @@ export class FakePriceProvider<
49
112
  locale: [en, base],
50
113
  });
51
114
 
52
-
53
- const model = this.newModel();
54
- Object.assign(model, {
115
+ const model = {
55
116
  identifier: {
56
- sku: payload.sku,
117
+ variant: payload.variant,
57
118
  },
58
119
  unitPrice: {
59
120
  value: generator.number.int({ min: 300, max: 100000 }) / 100,
60
- currency: _reqCtx.languageContext.currencyCode,
121
+ currency: this.context.languageContext.currencyCode,
61
122
  },
62
- meta: {
63
- cache: {
64
- hit: false,
65
- key: payload.sku.key,
66
- },
67
- placeholder: false,
68
- },
69
- });
123
+ } as Price;
70
124
 
71
125
  if (skuString.includes('with-tiers')) {
72
126
  const unitPrice = model.unitPrice?.value || 0;
@@ -78,23 +132,21 @@ export class FakePriceProvider<
78
132
  minimumQuantity: generator.number.int({ min: 2, max: 5 }),
79
133
  price: {
80
134
  value: tier1Price,
81
- currency: _reqCtx.languageContext.currencyCode,
82
- }
135
+ currency: this.context.languageContext.currencyCode,
136
+ },
83
137
  },
84
138
  {
85
139
  minimumQuantity: generator.number.int({ min: 6, max: 10 }),
86
140
  price: {
87
141
  value: tier2Price,
88
- currency: _reqCtx.languageContext.currencyCode,
89
- }
90
- }
142
+ currency: this.context.languageContext.currencyCode,
143
+ },
144
+ },
91
145
  ];
92
146
  } else {
93
147
  model.tieredPrices = [];
94
148
  }
95
149
 
96
-
97
-
98
- return this.assert(model);
150
+ return success(model);
99
151
  }
100
152
  }
@@ -0,0 +1,206 @@
1
+ import {
2
+ ImageSchema,
3
+ ProductSearchProvider,
4
+ ProductSearchQueryByTermSchema,
5
+ ProductSearchResultItemSchema,
6
+ ProductSearchResultSchema,
7
+ Reactionary,
8
+ success,
9
+ error,
10
+ } from '@reactionary/core';
11
+ import type {
12
+ ProductSearchResult,
13
+ ProductSearchResultFacet,
14
+ ProductSearchResultItem,
15
+ Cache as ReactionaryCache,
16
+ Image,
17
+ FacetIdentifier,
18
+ FacetValueIdentifier,
19
+ ProductSearchResultFacetValue,
20
+ ProductSearchResultItemVariant,
21
+ ProductSearchQueryCreateNavigationFilter,
22
+ Result,
23
+ } from '@reactionary/core';
24
+ import type {
25
+ RequestContext,
26
+ ProductSearchQueryByTerm,
27
+ } from '@reactionary/core';
28
+ import type { FakeConfiguration } from '../schema/configuration.schema.js';
29
+ import { Faker, en, base } from '@faker-js/faker';
30
+ import { jitter } from '../utilities/jitter.js';
31
+
32
+ export class FakeSearchProvider extends ProductSearchProvider {
33
+ protected config: FakeConfiguration;
34
+
35
+ constructor(
36
+ config: FakeConfiguration,
37
+ cache: ReactionaryCache,
38
+ context: RequestContext
39
+ ) {
40
+ super(cache, context);
41
+
42
+ this.config = config;
43
+ }
44
+
45
+ @Reactionary({
46
+ inputSchema: ProductSearchQueryByTermSchema,
47
+ outputSchema: ProductSearchResultSchema,
48
+ })
49
+ public override async queryByTerm(
50
+ payload: ProductSearchQueryByTerm
51
+ ): Promise<Result<ProductSearchResult>> {
52
+ await jitter(this.config.jitter.mean, this.config.jitter.deviation);
53
+
54
+ const query = payload.search;
55
+
56
+ const querySpecificity =
57
+ 20 -
58
+ query.term.length -
59
+ query.paginationOptions.pageNumber -
60
+ query.facets.length;
61
+ const totalProducts = 10 * querySpecificity;
62
+ const totalPages = Math.ceil(
63
+ totalProducts / query.paginationOptions.pageSize
64
+ );
65
+ const productsOnPage = Math.min(
66
+ totalProducts,
67
+ query.paginationOptions.pageSize
68
+ );
69
+
70
+ const productGenerator = new Faker({
71
+ seed: querySpecificity,
72
+ locale: [en, base],
73
+ });
74
+
75
+ const facetGenerator = new Faker({
76
+ seed: 100,
77
+ locale: [en, base],
78
+ });
79
+
80
+ const products = new Array<ProductSearchResultItem>();
81
+ const facets = new Array<ProductSearchResultFacet>();
82
+
83
+ for (let i = 0; i < productsOnPage; i++) {
84
+ const srcUrl = productGenerator.image.urlPicsumPhotos({
85
+ height: 300,
86
+ width: 300,
87
+ grayscale: true,
88
+ blur: 8,
89
+ });
90
+
91
+ const img = ImageSchema.parse({
92
+ sourceUrl: srcUrl,
93
+ altText: 'Fake product image',
94
+ height: 300,
95
+ width: 300,
96
+ } satisfies Partial<Image>);
97
+
98
+ products.push(
99
+ ProductSearchResultItemSchema.parse({
100
+ identifier: {
101
+ key: 'product_' + productGenerator.commerce.isbn(),
102
+ },
103
+ name: productGenerator.commerce.productName(),
104
+ slug: productGenerator.lorem.slug(),
105
+ variants: [
106
+ {
107
+ variant: {
108
+ sku: productGenerator.commerce.isbn(),
109
+ },
110
+ image: img,
111
+ options: undefined,
112
+ } satisfies Partial<ProductSearchResultItemVariant>,
113
+ ],
114
+ } satisfies Partial<ProductSearchResultItem>)
115
+ );
116
+ }
117
+
118
+ const facetBase = ['color', 'size'];
119
+
120
+ for (const base of facetBase) {
121
+ const facet: ProductSearchResultFacet = {
122
+ identifier: {
123
+ key: base,
124
+ },
125
+ name: base,
126
+ values: [],
127
+ };
128
+
129
+ for (let i = 0; i < 10; i++) {
130
+ const valueKey = i.toString();
131
+ const isActive =
132
+ query.facets.find(
133
+ (x) => x.facet.key === facet.identifier.key && x.key === valueKey
134
+ ) !== undefined;
135
+
136
+ facet.values.push({
137
+ active: isActive,
138
+ count: facetGenerator.number.int({ min: 1, max: 50 }),
139
+ identifier: {
140
+ facet: {
141
+ key: facet.identifier.key,
142
+ },
143
+ key: valueKey,
144
+ },
145
+ name: facetGenerator.color.human(),
146
+ });
147
+ }
148
+
149
+ facets.push(facet);
150
+ }
151
+
152
+ const result = ProductSearchResultSchema.parse({
153
+ identifier: {
154
+ term: query.term,
155
+ paginationOptions: {
156
+ pageNumber: query.paginationOptions.pageNumber,
157
+ pageSize: query.paginationOptions.pageSize,
158
+ },
159
+ facets: query.facets,
160
+ filters: [],
161
+ },
162
+ facets: facets,
163
+ items: products,
164
+ pageNumber: query.paginationOptions.pageNumber,
165
+ pageSize: query.paginationOptions.pageSize,
166
+ totalCount: totalProducts,
167
+ totalPages: totalPages,
168
+ } satisfies ProductSearchResult);
169
+
170
+ return success(result);
171
+ }
172
+
173
+ public override async createCategoryNavigationFilter(
174
+ payload: ProductSearchQueryCreateNavigationFilter
175
+ ): Promise<Result<FacetValueIdentifier>> {
176
+ const facetIdentifier = {
177
+ key: 'category',
178
+ } satisfies FacetIdentifier;
179
+ const facetValueIdentifier = {
180
+ facet: facetIdentifier,
181
+ key: payload.categoryPath[payload.categoryPath.length - 1].identifier.key,
182
+ } satisfies FacetValueIdentifier;
183
+
184
+ return success(facetValueIdentifier);
185
+ }
186
+
187
+ protected override parseFacetValue(
188
+ facetValueIdentifier: FacetValueIdentifier,
189
+ label: string,
190
+ count: number
191
+ ): ProductSearchResultFacetValue {
192
+ throw new Error('Method not implemented.');
193
+ }
194
+ protected override parseFacet(
195
+ facetIdentifier: FacetIdentifier,
196
+ facetValue: unknown
197
+ ): ProductSearchResultFacet {
198
+ throw new Error('Method not implemented.');
199
+ }
200
+ protected override parseVariant(
201
+ variant: unknown,
202
+ product: unknown
203
+ ): ProductSearchResultItemVariant {
204
+ throw new Error('Method not implemented.');
205
+ }
206
+ }
@@ -1,5 +1,4 @@
1
1
  import {
2
- type Product,
3
2
  type ProductQueryById,
4
3
  type ProductQueryBySlug,
5
4
  type RequestContext,
@@ -7,79 +6,95 @@ import {
7
6
  ProductProvider,
8
7
  Reactionary,
9
8
  type ProductQueryBySKU,
9
+ type Product,
10
+ ProductQueryByIdSchema,
11
+ ProductSchema,
12
+ ProductQueryBySlugSchema,
13
+ ProductQueryBySKUSchema,
14
+ type Result,
15
+ error,
16
+ success,
17
+ type NotFoundError
10
18
  } from '@reactionary/core';
11
19
  import type z from 'zod';
12
20
  import type { FakeConfiguration } from '../schema/configuration.schema.js';
13
21
  import { base, en, Faker } from '@faker-js/faker';
14
22
 
15
- export class FakeProductProvider<
16
- T extends Product = Product
17
- > extends ProductProvider<T> {
23
+ export class FakeProductProvider extends ProductProvider {
18
24
  protected config: FakeConfiguration;
19
25
 
20
- constructor(config: FakeConfiguration, schema: z.ZodType<T>, cache: ReactinaryCache) {
21
- super(schema, cache);
26
+ constructor(config: FakeConfiguration, cache: ReactinaryCache, context: RequestContext) {
27
+ super(cache, context);
22
28
 
23
29
  this.config = config;
24
30
  }
25
31
 
26
- @Reactionary({})
32
+ @Reactionary({
33
+ inputSchema: ProductQueryByIdSchema,
34
+ outputSchema: ProductSchema
35
+ })
27
36
  public override async getById(
28
- payload: ProductQueryById,
29
- _reqCtx: RequestContext
30
- ): Promise<T> {
31
- return this.parseSingle(payload);
37
+ payload: ProductQueryById
38
+ ): Promise<Result<Product>> {
39
+ return success(this.parseSingle(payload.identifier.key));
32
40
  }
33
41
 
34
- @Reactionary({})
42
+ @Reactionary({
43
+ inputSchema: ProductQueryBySlugSchema,
44
+ outputSchema: ProductSchema
45
+ })
35
46
  public override async getBySlug(
36
- payload: ProductQueryBySlug,
37
- _reqCtx: RequestContext
38
- ): Promise<T> {
39
- return this.parseSingle(payload);
47
+ payload: ProductQueryBySlug
48
+ ): Promise<Result<Product, NotFoundError>> {
49
+ return success(this.parseSingle(payload.slug));
40
50
  }
41
51
 
42
- @Reactionary({})
43
- public override async getBySKU(payload: ProductQueryBySKU, reqCtx: RequestContext): Promise<T> {
44
- return this.parseSingle(payload);
52
+ @Reactionary({
53
+ inputSchema: ProductQueryBySKUSchema,
54
+ outputSchema: ProductSchema,
55
+ })
56
+ public override async getBySKU(payload: ProductQueryBySKU): Promise<Result<Product>> {
57
+ return success(this.parseSingle(payload.variant.sku));
45
58
  }
46
59
 
47
-
48
- protected override parseSingle(body: ProductQueryById | ProductQueryBySlug | ProductQueryBySKU): T {
60
+ protected parseSingle(body: string): Product {
49
61
  const generator = new Faker({
50
62
  seed: 42,
51
63
  locale: [en, base],
52
64
  });
53
65
 
54
- const key = body.slug || body.id;
55
-
56
- // Create a model instance based on the schema
57
- const model = this.newModel();
66
+ const key = body;
58
67
 
59
68
  // Merge the generated data into the model
60
- Object.assign(model, {
69
+ const result = {
61
70
  identifier: {
62
71
  key: key,
63
72
  },
64
73
  name: generator.commerce.productName(),
65
74
  slug: key,
66
- attributes: [],
67
- description: generator.commerce.productDescription(),
68
- image: generator.image.urlPicsumPhotos({
69
- width: 600,
70
- height: 600,
71
- }),
72
- images: [],
73
- meta: {
74
- cache: {
75
- hit: false,
76
- key: key,
75
+ brand: '',
76
+ longDescription: '',
77
+ mainVariant: {
78
+ barcode: '',
79
+ ean: '',
80
+ gtin: '',
81
+ identifier: {
82
+ sku: ''
77
83
  },
78
- placeholder: false,
84
+ images: [],
85
+ name: '',
86
+ options: [],
87
+ upc: ''
79
88
  },
80
- skus: [],
81
- });
89
+ description: generator.commerce.productDescription(),
90
+ manufacturer: '',
91
+ options: [],
92
+ parentCategories: [],
93
+ published: true,
94
+ sharedAttributes: [],
95
+ variants: [],
96
+ } satisfies Product
82
97
 
83
- return this.assert(model);
98
+ return result;
84
99
  }
85
100
  }