@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,34 @@
1
+ {
2
+ "name": "provider-meilisearch",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "providers/meilisearch/src",
5
+ "projectType": "library",
6
+ "release": {
7
+ "version": {
8
+ "currentVersionResolver": "git-tag",
9
+ "fallbackCurrentVersionResolver": "disk",
10
+ "preserveLocalDependencyProtocols": false,
11
+ "manifestRootsToUpdate": ["dist/{projectRoot}"]
12
+ }
13
+ },
14
+ "tags": [],
15
+ "targets": {
16
+ "build": {
17
+ "executor": "@nx/esbuild:esbuild",
18
+ "outputs": ["{options.outputPath}"],
19
+ "options": {
20
+ "outputPath": "dist/providers/meilisearch",
21
+ "main": "providers/meilisearch/src/index.ts",
22
+ "tsConfig": "providers/meilisearch/tsconfig.lib.json",
23
+ "assets": ["providers/meilisearch/*.md"],
24
+ "format": ["esm"],
25
+ "bundle": false
26
+ }
27
+ },
28
+ "nx-release-publish": {
29
+ "options": {
30
+ "packageRoot": "dist/{projectRoot}"
31
+ }
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,21 @@
1
+ import type { Cache, ClientFromCapabilities, RequestContext } from "@reactionary/core";
2
+ import { MeilisearchSearchProvider } from "../providers/product-search.provider.js";
3
+ import { MeilisearchOrderSearchProvider } from "../providers/order-search.provider.js";
4
+ import type { MeilisearchCapabilities } from "../schema/capabilities.schema.js";
5
+ import type { MeilisearchConfiguration } from "../schema/configuration.schema.js";
6
+
7
+ export function withMeilisearchCapabilities<T extends MeilisearchCapabilities>(configuration: MeilisearchConfiguration, capabilities: T) {
8
+ return (cache: Cache, context: RequestContext): ClientFromCapabilities<T> => {
9
+ const client: any = {};
10
+
11
+ if (capabilities.productSearch) {
12
+ client.productSearch = new MeilisearchSearchProvider(configuration, cache, context);
13
+ }
14
+
15
+ if (capabilities.orderSearch) {
16
+ client.orderSearch = new MeilisearchOrderSearchProvider(configuration, cache, context);
17
+ }
18
+
19
+ return client;
20
+ };
21
+ }
@@ -0,0 +1,6 @@
1
+ export * from './core/initialize.js';
2
+ export * from './providers/product-search.provider.js';
3
+ export * from './providers/order-search.provider.js';
4
+
5
+ export * from './schema/configuration.schema.js';
6
+ export * from './schema/capabilities.schema.js';
@@ -0,0 +1 @@
1
+ export * from './product-search.provider.js';
@@ -0,0 +1,222 @@
1
+ import {
2
+ type Cache,
3
+ type OrderSearchQueryByTerm,
4
+ OrderSearchQueryByTermSchema,
5
+ type OrderSearchResult,
6
+ type OrderSearchResultItem,
7
+ OrderSearchResultSchema,
8
+ OrderSearchProvider,
9
+ Reactionary,
10
+ type RequestContext,
11
+ type Result,
12
+ success,
13
+ type OrderStatus,
14
+ type Address,
15
+ type IdentityIdentifier,
16
+ type MonetaryAmount,
17
+ type Currency,
18
+ type OrderSearchIdentifier,
19
+ AddressIdentifierSchema,
20
+ type AddressIdentifier,
21
+ type OrderInventoryStatus,
22
+ } from '@reactionary/core';
23
+ import { MeiliSearch, type SearchParams, type SearchResponse } from 'meilisearch';
24
+ import type { MeilisearchConfiguration } from '../schema/configuration.schema.js';
25
+
26
+ interface MeilisearchNativeOrderAddress {
27
+ address1: string;
28
+ address2: string;
29
+ city: string;
30
+ postalCode: string;
31
+ country: string;
32
+ }
33
+
34
+ interface MeilisearchNativeOrderRecord {
35
+ orderIdentifier: string;
36
+ userIdentifier: string;
37
+ customerName: string;
38
+ shippingAddress: MeilisearchNativeOrderAddress;
39
+ orderDate: string;
40
+ orderStatus: string;
41
+ inventoryStatus: string;
42
+ totalAmount: number;
43
+ currency: string;
44
+ }
45
+
46
+ export class MeilisearchOrderSearchProvider extends OrderSearchProvider {
47
+ protected config: MeilisearchConfiguration;
48
+
49
+ constructor(config: MeilisearchConfiguration, cache: Cache, context: RequestContext) {
50
+ super(cache, context);
51
+ this.config = config;
52
+ }
53
+
54
+ @Reactionary({
55
+ inputSchema: OrderSearchQueryByTermSchema,
56
+ outputSchema: OrderSearchResultSchema,
57
+ })
58
+ public async queryByTerm(payload: OrderSearchQueryByTerm): Promise<Result<OrderSearchResult>> {
59
+ const client = new MeiliSearch({
60
+ host: this.config.apiUrl,
61
+ apiKey: this.config.apiKey,
62
+ });
63
+
64
+ const index = client.index(this.config.orderIndexName);
65
+
66
+ const filters: string[] = [];
67
+
68
+ // Add status filter
69
+ if (payload.search.orderStatus && payload.search.orderStatus.length > 0) {
70
+ const statusFilters = payload.search.orderStatus
71
+ .map((status) => `orderStatus = "${this.mapOrderStatus(status)}"`)
72
+ .join(' OR ');
73
+ filters.push(`(${statusFilters})`);
74
+ }
75
+
76
+ // Add user ID filter for B2B use cases with hierarchical order access
77
+ if (payload.search.user) {
78
+ filters.push(`userIdentifier = "${payload.search.user.userId}"`);
79
+ }
80
+
81
+ // Add date range filters
82
+ if (payload.search.startDate) {
83
+ filters.push(`orderDate >= ${new Date(payload.search.startDate).getTime()}`);
84
+ }
85
+ if (payload.search.endDate) {
86
+ filters.push(`orderDate <= ${new Date(payload.search.endDate).getTime()}`);
87
+ }
88
+
89
+
90
+ if (payload.search.partNumber && payload.search.partNumber.length > 0) {
91
+ const partNumberFilters = payload.search.partNumber
92
+ .map((partNumber) => `items.sku = "${partNumber}"`)
93
+ .join(' OR ');
94
+ filters.push(`(${partNumberFilters})`);
95
+ }
96
+
97
+ const searchOptions: SearchParams = {
98
+ offset:
99
+ (payload.search.paginationOptions.pageNumber - 1) *
100
+ payload.search.paginationOptions.pageSize,
101
+ limit: payload.search.paginationOptions.pageSize,
102
+ filter: filters.length > 0 ? filters.join(' AND ') : undefined,
103
+ };
104
+
105
+ const remote = await index.search<MeilisearchNativeOrderRecord>(
106
+ payload.search.term || '',
107
+ searchOptions
108
+ );
109
+
110
+ const result = this.parsePaginatedResult(remote, payload) as OrderSearchResult;
111
+
112
+ return success(result);
113
+ }
114
+
115
+ protected mapOrderStatus(status: OrderStatus): string {
116
+ // Map from Reactionary OrderStatus to Meilisearch native status
117
+ const statusMap: Record<OrderStatus, string> = {
118
+ AwaitingPayment: 'awaiting_payment',
119
+ ReleasedToFulfillment: 'released_to_fulfillment',
120
+ Shipped: 'shipped',
121
+ Cancelled: 'cancelled',
122
+ };
123
+ return statusMap[status] || status;
124
+ }
125
+
126
+ protected mapFromNativeOrderStatus(nativeStatus: string): OrderStatus {
127
+ // Map from Meilisearch native status to Reactionary OrderStatus
128
+ const statusMap: Record<string, OrderStatus> = {
129
+ awaiting_payment: 'AwaitingPayment',
130
+ released_to_fulfillment: 'ReleasedToFulfillment',
131
+ shipped: 'Shipped',
132
+ cancelled: 'Cancelled',
133
+ };
134
+ return statusMap[nativeStatus] || 'AwaitingPayment';
135
+ }
136
+
137
+ protected mapFromNativeInventoryStatus(nativeStatus: string): OrderInventoryStatus {
138
+ // Map from Meilisearch native status to Reactionary OrderInventoryStatus
139
+ const statusMap: Record<string, OrderInventoryStatus> = {
140
+ not_allocated: 'NotAllocated',
141
+ allocated: 'Allocated',
142
+ preordered: 'Preordered',
143
+ backordered: 'Backordered',
144
+ };
145
+ return statusMap[nativeStatus] || 'NotAllocated';
146
+ }
147
+
148
+ protected composeAddressFromNativeAddress(
149
+ nativeAddress: MeilisearchNativeOrderAddress
150
+ ): Address {
151
+ return {
152
+ identifier: AddressIdentifierSchema.parse({
153
+ nickName: 'shipping',
154
+ } satisfies AddressIdentifier),
155
+ firstName: '',
156
+ lastName: '',
157
+ streetAddress: nativeAddress.address1,
158
+ streetNumber: nativeAddress.address2,
159
+ city: nativeAddress.city,
160
+ postalCode: nativeAddress.postalCode,
161
+ countryCode: nativeAddress.country,
162
+ region: '',
163
+ };
164
+ }
165
+
166
+ protected parseSingle(body: MeilisearchNativeOrderRecord): OrderSearchResultItem {
167
+ const identifier = { key: body.orderIdentifier };
168
+ const userId: IdentityIdentifier = {
169
+ userId: body.userIdentifier,
170
+ };
171
+ const customerName = body.customerName;
172
+ const shippingAddress = this.composeAddressFromNativeAddress(body.shippingAddress);
173
+ const orderDate = body.orderDate;
174
+ const orderStatus = this.mapFromNativeOrderStatus(body.orderStatus);
175
+ const inventoryStatus = this.mapFromNativeInventoryStatus(body.inventoryStatus);
176
+
177
+ const totalAmount: MonetaryAmount = {
178
+ currency: (body.currency || this.context.languageContext.currencyCode) as Currency,
179
+ value: body.totalAmount,
180
+ };
181
+
182
+ const order = {
183
+ identifier,
184
+ userId,
185
+ customerName,
186
+ shippingAddress,
187
+ orderDate,
188
+ orderStatus,
189
+ inventoryStatus,
190
+ totalAmount,
191
+ } satisfies OrderSearchResultItem;
192
+
193
+ return order;
194
+ }
195
+
196
+ protected parsePaginatedResult(
197
+ body: SearchResponse<MeilisearchNativeOrderRecord>,
198
+ query: OrderSearchQueryByTerm
199
+ ): OrderSearchResult {
200
+ const identifier = {
201
+ ...query.search,
202
+ } satisfies OrderSearchIdentifier;
203
+
204
+ const orders: OrderSearchResultItem[] = body.hits.map((hit) => {
205
+ return this.parseSingle(hit);
206
+ });
207
+
208
+ const totalCount = body.estimatedTotalHits || body.hits.length;
209
+ const totalPages = Math.ceil(totalCount / (body.limit || 1));
210
+
211
+ const result = {
212
+ identifier,
213
+ pageNumber: Math.floor((body.offset || 0) / (body.limit || 1) ) + 1,
214
+ pageSize: body.limit || orders.length,
215
+ totalCount,
216
+ totalPages,
217
+ items: orders,
218
+ } satisfies OrderSearchResult;
219
+
220
+ return result;
221
+ }
222
+ }
@@ -0,0 +1,251 @@
1
+ import {
2
+ type Cache,
3
+ type FacetIdentifier,
4
+ FacetIdentifierSchema,
5
+ type FacetValueIdentifier,
6
+ FacetValueIdentifierSchema,
7
+ ImageSchema,
8
+ ProductSearchProvider,
9
+ type ProductSearchQueryByTerm,
10
+ ProductSearchQueryByTermSchema,
11
+ type ProductSearchQueryCreateNavigationFilter,
12
+ type ProductSearchResult,
13
+ type ProductSearchResultFacet,
14
+ ProductSearchResultFacetSchema,
15
+ type ProductSearchResultFacetValue,
16
+ ProductSearchResultFacetValueSchema,
17
+ type ProductSearchResultItem,
18
+ type ProductSearchResultItemVariant,
19
+ ProductSearchResultItemVariantSchema,
20
+ ProductSearchResultSchema,
21
+ Reactionary,
22
+ type RequestContext,
23
+ type Result,
24
+ success
25
+ } from '@reactionary/core';
26
+ import { MeiliSearch, type SearchParams, type SearchResponse } from 'meilisearch';
27
+ import type { MeilisearchConfiguration } from '../schema/configuration.schema.js';
28
+ import type { MeilisearchProductSearchResult } from '../schema/search.schema.js';
29
+
30
+ interface MeilisearchNativeVariant {
31
+ sku: string;
32
+ image: string;
33
+ }
34
+
35
+ interface MeilisearchNativeRecord {
36
+ objectID: string;
37
+ slug?: string;
38
+ name?: string;
39
+ variants: Array<MeilisearchNativeVariant>;
40
+ }
41
+
42
+
43
+ export class MeilisearchSearchProvider extends ProductSearchProvider {
44
+ protected config: MeilisearchConfiguration;
45
+
46
+ constructor(config: MeilisearchConfiguration, cache: Cache, context: RequestContext) {
47
+ super(cache, context);
48
+ this.config = config;
49
+ }
50
+
51
+ @Reactionary({
52
+ inputSchema: ProductSearchQueryByTermSchema,
53
+ outputSchema: ProductSearchResultSchema,
54
+ cache: true,
55
+ cacheTimeToLiveInSeconds: 300,
56
+ currencyDependentCaching: false,
57
+ localeDependentCaching: true
58
+ })
59
+ public override async queryByTerm(
60
+ payload: ProductSearchQueryByTerm
61
+ ): Promise<Result<ProductSearchResult>> {
62
+ const client = new MeiliSearch({
63
+ host: this.config.apiUrl,
64
+ apiKey: this.config.apiKey,
65
+ });
66
+
67
+ const index = client.index(this.config.indexName);
68
+
69
+ const facetsThatAreNotCategory = payload.search.facets.filter(x => x.facet.key !== 'categories');
70
+ const categoryFacet = payload.search.facets.find(x => x.facet.key === 'categories') || payload.search.categoryFilter;
71
+
72
+ const finalFilters: string[] = [...payload.search.filters || []];
73
+
74
+ const finalFacetFilters: string[] = [
75
+ ...facetsThatAreNotCategory.map(
76
+ (x) => `${x.facet.key}="${x.key}"`
77
+ ),
78
+ ];
79
+
80
+ if (categoryFacet) {
81
+ finalFilters.push(`categories = "${categoryFacet.key}"`);
82
+ }
83
+
84
+ // Combine all filters
85
+ let filterString: string | undefined;
86
+ if (finalFilters.length > 0 || finalFacetFilters.length > 0) {
87
+ const allFilters = [...finalFilters, ...finalFacetFilters];
88
+ filterString = allFilters.join(' AND ');
89
+ }
90
+
91
+ const searchOptions: SearchParams = {
92
+ offset: (payload.search.paginationOptions.pageNumber - 1) * payload.search.paginationOptions.pageSize,
93
+ limit: payload.search.paginationOptions.pageSize,
94
+ facets: ['*'],
95
+ filter: filterString,
96
+ };
97
+
98
+ if (this.config.useAIEmbedding) {
99
+ searchOptions.hybrid = {
100
+ embedder: this.config.useAIEmbedding
101
+ };
102
+ }
103
+
104
+ const remote = await index.search<MeilisearchNativeRecord>(payload.search.term, searchOptions);
105
+
106
+ const result = this.parsePaginatedResult(remote, payload) as MeilisearchProductSearchResult;
107
+
108
+ // mark selected facets as active
109
+ for (const selectedFacet of payload.search.facets) {
110
+ const facet = result.facets.find((f) => f.identifier.key === selectedFacet.facet.key);
111
+ if (facet) {
112
+ const value = facet.values.find((v) => v.identifier.key === selectedFacet.key);
113
+ if (value) {
114
+ value.active = true;
115
+ }
116
+ }
117
+ }
118
+
119
+ return success(result);
120
+ }
121
+
122
+ public override async createCategoryNavigationFilter(payload: ProductSearchQueryCreateNavigationFilter): Promise<Result<FacetValueIdentifier>> {
123
+
124
+ const facetIdentifier = FacetIdentifierSchema.parse({
125
+ key: 'categories'
126
+ });
127
+ const facetValueIdentifier = FacetValueIdentifierSchema.parse({
128
+ facet: facetIdentifier,
129
+ key: payload.categoryPath.map(c => c.name).join(' > ')
130
+ });
131
+ return success(facetValueIdentifier);
132
+ }
133
+
134
+
135
+ protected parseSingle(body: MeilisearchNativeRecord) {
136
+ const product = {
137
+ identifier: { key: body.objectID },
138
+ name: body.name || body.objectID,
139
+ slug: body.slug || body.objectID,
140
+ variants: [...(body.variants || [])].map(variant => this.parseVariant(variant, body)),
141
+ } satisfies ProductSearchResultItem;
142
+
143
+ return product;
144
+ }
145
+
146
+ protected override parseVariant(variant: MeilisearchNativeVariant, product: MeilisearchNativeRecord): ProductSearchResultItemVariant {
147
+ const result = ProductSearchResultItemVariantSchema.parse({
148
+ variant: {
149
+ sku: variant.sku
150
+ },
151
+ image: ImageSchema.parse({
152
+ sourceUrl: variant.image,
153
+ altText: product.name || '',
154
+ })
155
+ } satisfies Partial<ProductSearchResultItemVariant>);
156
+
157
+ return result;
158
+ }
159
+
160
+ protected parsePaginatedResult(body: SearchResponse<MeilisearchNativeRecord>, query: ProductSearchQueryByTerm) {
161
+ const items = body.hits.map((hit) => this.parseSingle(hit));
162
+ let facets: ProductSearchResultFacet[] = [];
163
+
164
+ if (body.facetDistribution) {
165
+ for (const id in body.facetDistribution) {
166
+ const f = body.facetDistribution[id];
167
+ const facetId = FacetIdentifierSchema.parse({
168
+ key: id
169
+ });
170
+ const facet = this.parseFacet(facetId, f);
171
+ if (facet.values.length > 0) {
172
+ facets.push(facet);
173
+ }
174
+ }
175
+ }
176
+
177
+ // Handle category hierarchy similar to Algolia
178
+ const selectedCategoryFacet = query.search.facets.find(x => x.facet.key === 'categories') || query.search.categoryFilter;
179
+ let subCategoryFacet;
180
+ if (selectedCategoryFacet) {
181
+ const valueDepth = selectedCategoryFacet.key.split(' > ').length;
182
+ subCategoryFacet = facets.find(f => f.identifier.key === `hierarchy.lvl${valueDepth}`);
183
+ } else {
184
+ subCategoryFacet = facets.find(f => f.identifier.key === 'hierarchy.lvl0');
185
+ }
186
+
187
+ if (subCategoryFacet) {
188
+ // remap to 'categories' facet
189
+ subCategoryFacet.identifier = FacetIdentifierSchema.parse({
190
+ key: 'categories'
191
+ });
192
+ subCategoryFacet.name = 'Categories';
193
+ for (const v of subCategoryFacet.values) {
194
+ const pathParts = v.identifier.key.split(' > ');
195
+ v.identifier.facet = subCategoryFacet.identifier;
196
+ v.name = pathParts[pathParts.length - 1];
197
+ }
198
+ }
199
+
200
+ // remove other hierarchy facets
201
+ facets = facets.filter(f => !f.identifier.key.startsWith('hierarchy.lvl'));
202
+
203
+ const totalPages = Math.ceil((body.estimatedTotalHits || 0) / query.search.paginationOptions.pageSize);
204
+
205
+ const result = {
206
+ identifier: {
207
+ term: query.search.term,
208
+ facets: query.search.facets,
209
+ filters: query.search.filters,
210
+ paginationOptions: query.search.paginationOptions,
211
+ },
212
+ pageNumber: query.search.paginationOptions.pageNumber,
213
+ pageSize: query.search.paginationOptions.pageSize,
214
+ totalCount: body.estimatedTotalHits || 0,
215
+ totalPages: totalPages,
216
+ items: items,
217
+ facets,
218
+ } satisfies ProductSearchResult;
219
+
220
+ return result;
221
+ }
222
+
223
+ protected parseFacet(facetIdentifier: FacetIdentifier, facetValues: Record<string, number>): ProductSearchResultFacet {
224
+ const result: ProductSearchResultFacet = ProductSearchResultFacetSchema.parse({
225
+ identifier: facetIdentifier,
226
+ name: facetIdentifier.key.replace(/_/g, ' '),
227
+ values: []
228
+ });
229
+
230
+ for (const vid in facetValues) {
231
+ const fv = facetValues[vid];
232
+
233
+ const facetValueIdentifier = FacetValueIdentifierSchema.parse({
234
+ facet: facetIdentifier,
235
+ key: vid
236
+ } satisfies Partial<FacetValueIdentifier>);
237
+
238
+ result.values.push(this.parseFacetValue(facetValueIdentifier, vid, fv));
239
+ }
240
+ return result;
241
+ }
242
+
243
+ protected parseFacetValue(facetValueIdentifier: FacetValueIdentifier, label: string, count: number): ProductSearchResultFacetValue {
244
+ return ProductSearchResultFacetValueSchema.parse({
245
+ identifier: facetValueIdentifier,
246
+ name: label,
247
+ count: count,
248
+ active: false,
249
+ } satisfies Partial<ProductSearchResultFacetValue>);
250
+ }
251
+ }
@@ -0,0 +1,10 @@
1
+ import { CapabilitiesSchema } from "@reactionary/core";
2
+ import type { z } from 'zod';
3
+
4
+ export const MeilisearchCapabilitiesSchema = CapabilitiesSchema.pick({
5
+ productSearch: true,
6
+ orderSearch: true,
7
+ analytics: true
8
+ }).partial();
9
+
10
+ export type MeilisearchCapabilities = z.infer<typeof MeilisearchCapabilitiesSchema>;
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod';
2
+
3
+ export const MeilisearchConfigurationSchema = z.looseObject({
4
+ apiUrl: z.string(),
5
+ apiKey: z.string(),
6
+ indexName: z.string(),
7
+ orderIndexName: z.string(),
8
+ useAIEmbedding: z.string().optional()
9
+ });
10
+
11
+ export type MeilisearchConfiguration = z.infer<typeof MeilisearchConfigurationSchema>;
@@ -0,0 +1,3 @@
1
+ export * from './configuration.schema.js';
2
+ export * from './capabilities.schema.js';
3
+ export * from './search.schema.js';
@@ -0,0 +1,14 @@
1
+ import { ProductSearchIdentifierSchema, ProductSearchResultSchema } from '@reactionary/core';
2
+ import { z } from 'zod';
3
+
4
+ export const MeilisearchProductSearchIdentifierSchema = ProductSearchIdentifierSchema.extend({
5
+ key: z.string(),
6
+ index: z.string(),
7
+ });
8
+
9
+ export const MeilisearchProductSearchResultSchema = ProductSearchResultSchema.extend({
10
+ identifier: MeilisearchProductSearchIdentifierSchema.default(() => MeilisearchProductSearchIdentifierSchema.parse({}))
11
+ });
12
+
13
+ export type MeilisearchProductSearchResult = z.infer<typeof MeilisearchProductSearchResultSchema>;
14
+ export type MeilisearchProductSearchIdentifier = z.infer<typeof MeilisearchProductSearchIdentifierSchema>;
@@ -0,0 +1,24 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "module": "NodeNext",
5
+ "moduleResolution": "nodenext",
6
+ "forceConsistentCasingInFileNames": true,
7
+ "strict": true,
8
+ "importHelpers": true,
9
+ "noImplicitOverride": true,
10
+ "noImplicitReturns": true,
11
+ "noFallthroughCasesInSwitch": true,
12
+ "noPropertyAccessFromIndexSignature": true
13
+ },
14
+ "files": [],
15
+ "include": [],
16
+ "references": [
17
+ {
18
+ "path": "./tsconfig.lib.json"
19
+ },
20
+ {
21
+ "path": "./tsconfig.spec.json"
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "declaration": true,
6
+ "types": ["node"]
7
+ },
8
+ "include": ["src/**/*.ts"],
9
+ "exclude": ["src/**/*.spec.ts", "vitest.config.ts"]
10
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "./tsconfig.lib.json",
3
+ "exclude": []
4
+ }
@@ -0,0 +1,14 @@
1
+ import { defineConfig, defineProject } from 'vitest/config';
2
+ import tsconfigPaths from 'vite-tsconfig-paths';
3
+ import { resolve } from 'path';
4
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
5
+
6
+ export default defineProject({
7
+ plugins: [nxViteTsPaths()],
8
+ test: {
9
+ root: resolve(__dirname),
10
+ globals: true,
11
+ environment: 'node',
12
+ include: ['src/**/*.spec.ts'],
13
+ },
14
+ });
@@ -7,5 +7,6 @@
7
7
  "@reactionary/core": "0.0.1",
8
8
  "zod": "4.1.9"
9
9
  },
10
- "type": "module"
10
+ "type": "module",
11
+ "sideEffects": false
11
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "extends": "../../tsconfig.base.json",
3
3
  "compilerOptions": {
4
- "module": "nodenext",
4
+ "module": "NodeNext",
5
5
  "moduleResolution": "nodenext",
6
6
  "forceConsistentCasingInFileNames": true,
7
7
  "strict": true,
@@ -17,7 +17,12 @@
17
17
  "paths": {
18
18
  "@reactionary/core": ["core/src/index.ts"],
19
19
  "@reactionary/examples-node": ["examples/node/src/index.ts"],
20
+ "@reactionary/provider-medusa": ["providers/medusa/src/index.ts"],
21
+ "@reactionary/otel": ["otel/src/index.ts"],
20
22
  "@reactionary/provider-algolia": ["providers/algolia/src/index.ts"],
23
+ "@reactionary/provider-meilisearch": [
24
+ "providers/meilisearch/src/index.ts"
25
+ ],
21
26
  "@reactionary/provider-commercetools": [
22
27
  "providers/commercetools/src/index.ts"
23
28
  ],
@@ -0,0 +1,10 @@
1
+ import { resolve } from 'path'
2
+ import { defineConfig } from 'vitest/config'
3
+
4
+ export default defineConfig({
5
+ test: {
6
+ projects: ['providers/*', 'examples/*', 'core'],
7
+ reporters: ['default', 'verbose', 'github-actions'],
8
+
9
+ },
10
+ })