@reactionary/source 0.0.52 → 0.2.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/.env-template +19 -0
  2. package/.github/workflows/pull-request.yml +3 -1
  3. package/.github/workflows/release.yml +9 -0
  4. package/.vscode/extensions.json +0 -2
  5. package/LICENSE +21 -0
  6. package/README.md +175 -23
  7. package/core/package.json +6 -3
  8. package/core/src/cache/cache.interface.ts +1 -0
  9. package/core/src/cache/index.ts +4 -0
  10. package/core/src/cache/memory-cache.ts +30 -2
  11. package/core/src/cache/noop-cache.ts +15 -1
  12. package/core/src/cache/redis-cache.ts +20 -0
  13. package/core/src/client/client-builder.ts +71 -54
  14. package/core/src/client/client.ts +9 -47
  15. package/core/src/client/index.ts +2 -0
  16. package/core/src/decorators/index.ts +1 -0
  17. package/core/src/decorators/reactionary.decorator.ts +203 -34
  18. package/core/src/index.ts +6 -19
  19. package/core/src/initialization.ts +1 -18
  20. package/core/src/metrics/metrics.ts +67 -0
  21. package/core/src/providers/analytics.provider.ts +1 -6
  22. package/core/src/providers/base.provider.ts +5 -69
  23. package/core/src/providers/cart.provider.ts +15 -55
  24. package/core/src/providers/category.provider.ts +7 -11
  25. package/core/src/providers/checkout.provider.ts +17 -15
  26. package/core/src/providers/identity.provider.ts +6 -8
  27. package/core/src/providers/index.ts +2 -1
  28. package/core/src/providers/inventory.provider.ts +15 -5
  29. package/core/src/providers/order-search.provider.ts +29 -0
  30. package/core/src/providers/order.provider.ts +47 -15
  31. package/core/src/providers/price.provider.ts +30 -36
  32. package/core/src/providers/product-search.provider.ts +61 -0
  33. package/core/src/providers/product.provider.ts +71 -12
  34. package/core/src/providers/profile.provider.ts +74 -14
  35. package/core/src/providers/store.provider.ts +3 -5
  36. package/core/src/schemas/capabilities.schema.ts +10 -3
  37. package/core/src/schemas/errors/generic.error.ts +9 -0
  38. package/core/src/schemas/errors/index.ts +4 -0
  39. package/core/src/schemas/errors/invalid-input.error.ts +9 -0
  40. package/core/src/schemas/errors/invalid-output.error.ts +9 -0
  41. package/core/src/schemas/errors/not-found.error.ts +9 -0
  42. package/core/src/schemas/index.ts +7 -0
  43. package/core/src/schemas/models/analytics.model.ts +2 -1
  44. package/core/src/schemas/models/base.model.ts +6 -24
  45. package/core/src/schemas/models/cart.model.ts +5 -8
  46. package/core/src/schemas/models/category.model.ts +4 -9
  47. package/core/src/schemas/models/checkout.model.ts +6 -7
  48. package/core/src/schemas/models/cost.model.ts +4 -3
  49. package/core/src/schemas/models/currency.model.ts +2 -1
  50. package/core/src/schemas/models/identifiers.model.ts +106 -62
  51. package/core/src/schemas/models/identity.model.ts +10 -19
  52. package/core/src/schemas/models/index.ts +2 -1
  53. package/core/src/schemas/models/inventory.model.ts +8 -5
  54. package/core/src/schemas/models/order-search.model.ts +28 -0
  55. package/core/src/schemas/models/order.model.ts +20 -26
  56. package/core/src/schemas/models/payment.model.ts +14 -17
  57. package/core/src/schemas/models/price.model.ts +11 -11
  58. package/core/src/schemas/models/product-search.model.ts +42 -0
  59. package/core/src/schemas/models/product.model.ts +64 -22
  60. package/core/src/schemas/models/profile.model.ts +19 -22
  61. package/core/src/schemas/models/shipping-method.model.ts +24 -29
  62. package/core/src/schemas/models/store.model.ts +9 -5
  63. package/core/src/schemas/mutations/analytics.mutation.ts +8 -7
  64. package/core/src/schemas/mutations/base.mutation.ts +2 -1
  65. package/core/src/schemas/mutations/cart.mutation.ts +33 -33
  66. package/core/src/schemas/mutations/checkout.mutation.ts +23 -30
  67. package/core/src/schemas/mutations/identity.mutation.ts +4 -3
  68. package/core/src/schemas/mutations/profile.mutation.ts +38 -3
  69. package/core/src/schemas/queries/base.query.ts +2 -1
  70. package/core/src/schemas/queries/cart.query.ts +3 -3
  71. package/core/src/schemas/queries/category.query.ts +18 -18
  72. package/core/src/schemas/queries/checkout.query.ts +7 -9
  73. package/core/src/schemas/queries/identity.query.ts +2 -1
  74. package/core/src/schemas/queries/index.ts +2 -1
  75. package/core/src/schemas/queries/inventory.query.ts +5 -5
  76. package/core/src/schemas/queries/order-search.query.ts +10 -0
  77. package/core/src/schemas/queries/order.query.ts +3 -2
  78. package/core/src/schemas/queries/price.query.ts +10 -4
  79. package/core/src/schemas/queries/product-search.query.ts +16 -0
  80. package/core/src/schemas/queries/product.query.ts +13 -6
  81. package/core/src/schemas/queries/profile.query.ts +5 -2
  82. package/core/src/schemas/queries/store.query.ts +6 -5
  83. package/core/src/schemas/result.ts +107 -0
  84. package/core/src/schemas/session.schema.ts +4 -4
  85. package/core/src/test/reactionary.decorator.spec.ts +249 -0
  86. package/core/src/zod-utils.ts +19 -0
  87. package/core/tsconfig.json +1 -1
  88. package/core/tsconfig.spec.json +2 -26
  89. package/core/vitest.config.ts +14 -0
  90. package/documentation/1-purpose.md +114 -0
  91. package/documentation/2-getting-started.md +229 -0
  92. package/documentation/3-querying-and-changing-data.md +74 -0
  93. package/documentation/4-product-data.md +107 -0
  94. package/documentation/5-cart-and-checkout.md +211 -0
  95. package/documentation/6-product-search.md +143 -0
  96. package/documentation/7-marketing.md +3 -0
  97. package/eslint.config.mjs +1 -0
  98. package/examples/node/eslint.config.mjs +1 -4
  99. package/examples/node/package.json +10 -3
  100. package/examples/node/project.json +4 -1
  101. package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +22 -23
  102. package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +15 -11
  103. package/examples/node/src/basic/basic-node-setup.spec.ts +44 -28
  104. package/examples/node/src/basic/client-creation.spec.ts +53 -0
  105. package/examples/node/src/capabilities/cart.spec.ts +255 -0
  106. package/examples/node/src/capabilities/category.spec.ts +193 -0
  107. package/examples/node/src/capabilities/checkout.spec.ts +341 -0
  108. package/examples/node/src/capabilities/identity.spec.ts +93 -0
  109. package/examples/node/src/capabilities/inventory.spec.ts +66 -0
  110. package/examples/node/src/capabilities/order-search.spec.ts +265 -0
  111. package/examples/node/src/capabilities/order.spec.ts +91 -0
  112. package/examples/node/src/capabilities/price.spec.ts +51 -0
  113. package/examples/node/src/capabilities/product-search.spec.ts +293 -0
  114. package/examples/node/src/capabilities/product.spec.ts +122 -0
  115. package/examples/node/src/capabilities/profile.spec.ts +316 -0
  116. package/examples/node/src/capabilities/store.spec.ts +26 -0
  117. package/examples/node/src/utils.ts +147 -0
  118. package/examples/node/tsconfig.json +9 -12
  119. package/examples/node/tsconfig.lib.json +1 -2
  120. package/examples/node/tsconfig.spec.json +2 -14
  121. package/examples/node/vitest.config.ts +14 -0
  122. package/migrations.json +22 -5
  123. package/nx.json +8 -47
  124. package/package.json +24 -96
  125. package/providers/algolia/README.md +39 -2
  126. package/providers/algolia/package.json +2 -1
  127. package/providers/algolia/src/core/initialize.ts +7 -14
  128. package/providers/algolia/src/index.ts +2 -4
  129. package/providers/algolia/src/providers/index.ts +1 -0
  130. package/providers/algolia/src/providers/product-search.provider.ts +241 -0
  131. package/providers/algolia/src/schema/capabilities.schema.ts +2 -3
  132. package/providers/algolia/src/schema/index.ts +3 -0
  133. package/providers/algolia/src/schema/search.schema.ts +8 -8
  134. package/providers/algolia/tsconfig.json +1 -1
  135. package/providers/algolia/tsconfig.lib.json +1 -1
  136. package/providers/algolia/tsconfig.spec.json +2 -14
  137. package/providers/algolia/vitest.config.ts +14 -0
  138. package/providers/commercetools/README.md +30 -3
  139. package/providers/commercetools/package.json +2 -1
  140. package/providers/commercetools/src/core/client.ts +178 -99
  141. package/providers/commercetools/src/core/initialize.ts +130 -74
  142. package/providers/commercetools/src/core/token-cache.ts +45 -0
  143. package/providers/commercetools/src/index.ts +3 -2
  144. package/providers/commercetools/src/providers/cart.provider.ts +281 -341
  145. package/providers/commercetools/src/providers/category.provider.ts +223 -138
  146. package/providers/commercetools/src/providers/checkout.provider.ts +631 -449
  147. package/providers/commercetools/src/providers/identity.provider.ts +50 -29
  148. package/providers/commercetools/src/providers/index.ts +2 -2
  149. package/providers/commercetools/src/providers/inventory.provider.ts +76 -74
  150. package/providers/commercetools/src/providers/order-search.provider.ts +220 -0
  151. package/providers/commercetools/src/providers/order.provider.ts +96 -61
  152. package/providers/commercetools/src/providers/price.provider.ts +147 -117
  153. package/providers/commercetools/src/providers/product-search.provider.ts +528 -0
  154. package/providers/commercetools/src/providers/product.provider.ts +249 -74
  155. package/providers/commercetools/src/providers/profile.provider.ts +445 -28
  156. package/providers/commercetools/src/providers/store.provider.ts +54 -40
  157. package/providers/commercetools/src/schema/capabilities.schema.ts +3 -1
  158. package/providers/commercetools/src/schema/commercetools.schema.ts +17 -3
  159. package/providers/commercetools/src/schema/configuration.schema.ts +1 -0
  160. package/providers/commercetools/src/schema/session.schema.ts +7 -0
  161. package/providers/commercetools/src/test/caching.spec.ts +82 -0
  162. package/providers/commercetools/src/test/identity.spec.ts +109 -0
  163. package/providers/commercetools/src/test/test-utils.ts +21 -19
  164. package/providers/commercetools/tsconfig.json +1 -1
  165. package/providers/commercetools/tsconfig.lib.json +1 -1
  166. package/providers/commercetools/tsconfig.spec.json +2 -14
  167. package/providers/commercetools/vitest.config.ts +15 -0
  168. package/providers/fake/README.md +20 -4
  169. package/providers/fake/package.json +2 -1
  170. package/providers/fake/src/core/initialize.ts +47 -49
  171. package/providers/fake/src/providers/analytics.provider.ts +5 -7
  172. package/providers/fake/src/providers/cart.provider.ts +163 -92
  173. package/providers/fake/src/providers/category.provider.ts +78 -50
  174. package/providers/fake/src/providers/checkout.provider.ts +254 -0
  175. package/providers/fake/src/providers/identity.provider.ts +57 -65
  176. package/providers/fake/src/providers/index.ts +6 -2
  177. package/providers/fake/src/providers/inventory.provider.ts +40 -36
  178. package/providers/fake/src/providers/order-search.provider.ts +78 -0
  179. package/providers/fake/src/providers/order.provider.ts +106 -0
  180. package/providers/fake/src/providers/price.provider.ts +93 -41
  181. package/providers/fake/src/providers/product-search.provider.ts +206 -0
  182. package/providers/fake/src/providers/product.provider.ts +56 -41
  183. package/providers/fake/src/providers/profile.provider.ts +147 -0
  184. package/providers/fake/src/providers/store.provider.ts +30 -20
  185. package/providers/fake/src/schema/capabilities.schema.ts +5 -1
  186. package/providers/fake/src/test/cart.provider.spec.ts +59 -80
  187. package/providers/fake/src/test/category.provider.spec.ts +145 -87
  188. package/providers/fake/src/test/checkout.provider.spec.ts +222 -0
  189. package/providers/fake/src/test/order-search.provider.spec.ts +50 -0
  190. package/providers/fake/src/test/order.provider.spec.ts +44 -0
  191. package/providers/fake/src/test/price.provider.spec.ts +50 -45
  192. package/providers/fake/src/test/product.provider.spec.ts +15 -7
  193. package/providers/fake/src/test/profile.provider.spec.ts +167 -0
  194. package/providers/fake/tsconfig.json +1 -1
  195. package/providers/fake/tsconfig.lib.json +1 -1
  196. package/providers/fake/tsconfig.spec.json +2 -12
  197. package/providers/fake/vitest.config.ts +14 -0
  198. package/providers/medusa/README.md +30 -0
  199. package/providers/medusa/TESTING.md +98 -0
  200. package/providers/medusa/eslint.config.mjs +19 -0
  201. package/providers/medusa/package.json +22 -0
  202. package/providers/medusa/project.json +34 -0
  203. package/providers/medusa/src/core/client.ts +370 -0
  204. package/providers/medusa/src/core/initialize.ts +78 -0
  205. package/providers/medusa/src/index.ts +13 -0
  206. package/providers/medusa/src/providers/cart.provider.ts +575 -0
  207. package/providers/medusa/src/providers/category.provider.ts +247 -0
  208. package/providers/medusa/src/providers/checkout.provider.ts +636 -0
  209. package/providers/medusa/src/providers/identity.provider.ts +137 -0
  210. package/providers/medusa/src/providers/inventory.provider.ts +173 -0
  211. package/providers/medusa/src/providers/order-search.provider.ts +202 -0
  212. package/providers/medusa/src/providers/order.provider.ts +226 -0
  213. package/providers/medusa/src/providers/price.provider.ts +140 -0
  214. package/providers/medusa/src/providers/product-search.provider.ts +243 -0
  215. package/providers/medusa/src/providers/product.provider.ts +261 -0
  216. package/providers/medusa/src/providers/profile.provider.ts +392 -0
  217. package/providers/medusa/src/schema/capabilities.schema.ts +18 -0
  218. package/providers/medusa/src/schema/configuration.schema.ts +11 -0
  219. package/providers/medusa/src/schema/medusa.schema.ts +31 -0
  220. package/providers/medusa/src/test/cart.provider.spec.ts +240 -0
  221. package/providers/medusa/src/test/category.provider.spec.ts +231 -0
  222. package/providers/medusa/src/test/checkout.spec.ts +349 -0
  223. package/providers/medusa/src/test/identity.provider.spec.ts +122 -0
  224. package/providers/medusa/src/test/inventory.provider.spec.ts +88 -0
  225. package/providers/medusa/src/test/large-cart.provider.spec.ts +103 -0
  226. package/providers/medusa/src/test/price.provider.spec.ts +104 -0
  227. package/providers/medusa/src/test/product.provider.spec.ts +146 -0
  228. package/providers/medusa/src/test/search.provider.spec.ts +203 -0
  229. package/providers/medusa/src/test/test-utils.ts +13 -0
  230. package/providers/medusa/src/utils/medusa-helpers.ts +89 -0
  231. package/providers/medusa/tsconfig.json +21 -0
  232. package/providers/medusa/tsconfig.lib.json +9 -0
  233. package/providers/medusa/tsconfig.spec.json +4 -0
  234. package/providers/medusa/vitest.config.ts +15 -0
  235. package/providers/meilisearch/README.md +48 -0
  236. package/providers/meilisearch/eslint.config.mjs +22 -0
  237. package/providers/meilisearch/package.json +13 -0
  238. package/providers/meilisearch/project.json +34 -0
  239. package/providers/meilisearch/src/core/initialize.ts +21 -0
  240. package/providers/meilisearch/src/index.ts +6 -0
  241. package/providers/meilisearch/src/providers/index.ts +1 -0
  242. package/providers/meilisearch/src/providers/order-search.provider.ts +222 -0
  243. package/providers/meilisearch/src/providers/product-search.provider.ts +251 -0
  244. package/providers/meilisearch/src/schema/capabilities.schema.ts +10 -0
  245. package/providers/meilisearch/src/schema/configuration.schema.ts +11 -0
  246. package/providers/meilisearch/src/schema/index.ts +3 -0
  247. package/providers/meilisearch/src/schema/search.schema.ts +14 -0
  248. package/providers/meilisearch/tsconfig.json +24 -0
  249. package/providers/meilisearch/tsconfig.lib.json +10 -0
  250. package/providers/meilisearch/tsconfig.spec.json +4 -0
  251. package/providers/meilisearch/vitest.config.ts +14 -0
  252. package/providers/posthog/package.json +2 -1
  253. package/providers/posthog/tsconfig.json +1 -1
  254. package/tsconfig.base.json +5 -0
  255. package/vitest.config.ts +10 -0
  256. package/core/src/providers/search.provider.ts +0 -18
  257. package/core/src/schemas/models/search.model.ts +0 -36
  258. package/core/src/schemas/queries/search.query.ts +0 -9
  259. package/examples/next/.swcrc +0 -30
  260. package/examples/next/eslint.config.mjs +0 -21
  261. package/examples/next/index.d.ts +0 -6
  262. package/examples/next/next-env.d.ts +0 -5
  263. package/examples/next/next.config.js +0 -31
  264. package/examples/next/project.json +0 -9
  265. package/examples/next/public/.gitkeep +0 -0
  266. package/examples/next/public/favicon.ico +0 -0
  267. package/examples/next/src/app/global.css +0 -0
  268. package/examples/next/src/app/layout.tsx +0 -18
  269. package/examples/next/src/app/page.module.scss +0 -2
  270. package/examples/next/src/app/page.tsx +0 -47
  271. package/examples/next/src/instrumentation.ts +0 -9
  272. package/examples/next/tsconfig.json +0 -44
  273. package/examples/node/jest.config.ts +0 -10
  274. package/jest.config.ts +0 -6
  275. package/jest.preset.js +0 -3
  276. package/providers/algolia/jest.config.ts +0 -10
  277. package/providers/algolia/src/providers/product.provider.ts +0 -66
  278. package/providers/algolia/src/providers/search.provider.ts +0 -106
  279. package/providers/algolia/src/test/search.provider.spec.ts +0 -91
  280. package/providers/commercetools/jest.config.cjs +0 -10
  281. package/providers/commercetools/src/providers/search.provider.ts +0 -96
  282. package/providers/commercetools/src/test/cart.provider.spec.ts +0 -199
  283. package/providers/commercetools/src/test/category.provider.spec.ts +0 -168
  284. package/providers/commercetools/src/test/checkout.provider.spec.ts +0 -312
  285. package/providers/commercetools/src/test/identity.provider.spec.ts +0 -88
  286. package/providers/commercetools/src/test/inventory.provider.spec.ts +0 -41
  287. package/providers/commercetools/src/test/price.provider.spec.ts +0 -81
  288. package/providers/commercetools/src/test/product.provider.spec.ts +0 -80
  289. package/providers/commercetools/src/test/profile.provider.spec.ts +0 -49
  290. package/providers/commercetools/src/test/search.provider.spec.ts +0 -61
  291. package/providers/commercetools/src/test/store.provider.spec.ts +0 -37
  292. package/providers/fake/jest.config.cjs +0 -10
  293. package/providers/fake/src/providers/search.provider.ts +0 -132
@@ -1,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,
@@ -6,5 +6,5 @@
6
6
  "types": ["node"]
7
7
  },
8
8
  "include": ["src/**/*.ts"],
9
- "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
9
+ "exclude": ["src/**/*.spec.ts", "vitest.config.ts"]
10
10
  }
@@ -1,16 +1,4 @@
1
1
  {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "module": "commonjs",
5
- "moduleResolution": "node",
6
- "verbatimModuleSyntax": false,
7
- "outDir": "../../dist/out-tsc",
8
- "types": ["jest", "node"]
9
- },
10
- "include": [
11
- "jest.config.ts",
12
- "src/**/*.test.ts",
13
- "src/**/*.spec.ts",
14
- "src/**/*.d.ts"
15
- ]
2
+ "extends": "./tsconfig.lib.json",
3
+ "exclude": []
16
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
+ });
@@ -1,6 +1,33 @@
1
- # provider-commercetools
1
+ # Commmercetools provider for Reactionary
2
+
3
+ ## Supports
4
+
5
+
6
+ | Feature | Support | Notes |
7
+ | ----------- | ----------- | --------- |
8
+ | product | Full | |
9
+ | productSearch | Full | |
10
+ | identity | Full | |
11
+ | cart | Full | |
12
+ | checkout | Full | |
13
+ | order | Full | |
14
+ | inventory | Full | |
15
+ | price | Full | |
16
+ | category | Full | |
17
+ | store | Full | |
18
+
19
+
20
+ ## Notes
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
2
30
 
3
- This library was generated with [Nx](https://nx.dev).
4
31
 
5
32
  ## Building
6
33
 
@@ -11,7 +38,7 @@ Run `nx build provider-commercetools` to build the library.
11
38
  Run `nx test provider-commercetools` to execute the unit tests via [Jest](https://jestjs.io).
12
39
 
13
40
 
14
- # ASSUMPTIONS for backend config
41
+ ## Assumptions for backend config
15
42
 
16
43
  - You will have 2 different channels for prices, one called `Offer Price` and one called `List Price`
17
44
  - ProductVariants will all have unique SKU values.
@@ -10,5 +10,6 @@
10
10
  "@commercetools/ts-client": "^4.2.1",
11
11
  "@commercetools/platform-sdk": "^8.8.0"
12
12
  },
13
- "type": "module"
13
+ "type": "module",
14
+ "sideEffects": false
14
15
  }
@@ -1,70 +1,92 @@
1
+ import { ClientBuilder } from '@commercetools/ts-client';
1
2
  import {
2
- ClientBuilder,
3
- type TokenCache,
4
- type TokenCacheOptions,
5
- type TokenStore,
6
- } from '@commercetools/ts-client';
7
- import { createApiBuilderFromCtpClient } from '@commercetools/platform-sdk';
3
+ createApiBuilderFromCtpClient,
4
+ type ApiRoot,
5
+ } from '@commercetools/platform-sdk';
8
6
  import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
9
7
  import { randomUUID } from 'crypto';
10
8
  import {
11
9
  AnonymousIdentitySchema,
12
- GuestIdentitySchema,
13
10
  RegisteredIdentitySchema,
11
+ type AnonymousIdentity,
12
+ type GuestIdentity,
13
+ type RegisteredIdentity,
14
14
  type RequestContext,
15
15
  } from '@reactionary/core';
16
16
  import * as crypto from 'crypto';
17
- import createDebug from 'debug'
18
- const debug = createDebug('commercetools:debug')
17
+ import createDebug from 'debug';
18
+ import { RequestContextTokenCache } from './token-cache.js';
19
+ const debug = createDebug('reactionary:commercetools');
19
20
 
21
+ export class CommercetoolsAPI {
22
+ protected config: CommercetoolsConfiguration;
23
+ protected context: RequestContext;
24
+ protected cache: RequestContextTokenCache;
25
+ protected client: Promise<ApiRoot> | undefined;
26
+ protected adminClient: Promise<ApiRoot> | undefined;
20
27
 
28
+ constructor(config: CommercetoolsConfiguration, context: RequestContext) {
29
+ this.config = config;
30
+ this.context = context;
31
+ this.cache = new RequestContextTokenCache(this.context);
32
+ }
21
33
 
34
+ public async getClient() {
35
+ if (!this.client) {
36
+ this.client = this.createClient();
37
+ }
22
38
 
23
- export class RequestContextTokenCache implements TokenCache {
24
- constructor(protected context: RequestContext) {}
39
+ return this.client;
40
+ }
25
41
 
26
- public async get(
27
- tokenCacheOptions?: TokenCacheOptions
28
- ): Promise<TokenStore | undefined> {
29
- const identity = this.context.identity;
42
+ public async getAdminClient() {
43
+ if (!this.adminClient) {
44
+ this.adminClient = this.createAdminClient();
45
+ }
30
46
 
31
- return {
32
- refreshToken: identity.refresh_token,
33
- token: identity.token || '',
34
- expirationTime: identity.expiry.getTime(),
35
- };
47
+ return this.adminClient;
36
48
  }
37
49
 
38
- public async set(
39
- cache: TokenStore,
40
- tokenCacheOptions?: TokenCacheOptions
41
- ): Promise<void> {
42
- const identity = this.context.identity;
50
+ protected async createAdminClient() {
51
+ let builder = this.createBaseClientBuilder();
52
+ builder = builder.withAnonymousSessionFlow({
53
+ credentials: {
54
+ clientId: this.config.clientId,
55
+ clientSecret: this.config.clientSecret,
56
+ },
57
+ host: this.config.authUrl,
58
+ projectKey: this.config.projectKey,
59
+ });
43
60
 
44
- identity.refresh_token = cache.refreshToken;
45
- identity.token = cache.token;
46
- identity.expiry = new Date(cache.expirationTime);
61
+ return createApiBuilderFromCtpClient(builder.build());
47
62
  }
48
- }
49
63
 
64
+ protected async createClient() {
65
+ let session = await this.cache.get();
66
+ const isNewSession = !session || !session.refreshToken;
50
67
 
68
+ if (isNewSession) {
69
+ await this.becomeGuest();
51
70
 
52
- export class CommercetoolsClient {
53
- protected config: CommercetoolsConfiguration;
71
+ session = await this.cache.get();
72
+ }
54
73
 
55
- constructor(config: CommercetoolsConfiguration) {
56
- this.config = config;
57
- }
74
+ let builder = this.createBaseClientBuilder();
75
+ builder = builder.withRefreshTokenFlow({
76
+ credentials: {
77
+ clientId: this.config.clientId,
78
+ clientSecret: this.config.clientSecret,
79
+ },
80
+ host: this.config.authUrl,
81
+ projectKey: this.config.projectKey,
82
+ refreshToken: session?.refreshToken || '',
83
+ tokenCache: this.cache,
84
+ });
58
85
 
59
- public async getClient(reqCtx: RequestContext) {
60
- return this.createClient(reqCtx);
86
+ return createApiBuilderFromCtpClient(builder.build());
61
87
  }
62
88
 
63
- public async register(
64
- username: string,
65
- password: string,
66
- reqCtx: RequestContext
67
- ) {
89
+ public async register(username: string, password: string) {
68
90
  const registrationBuilder =
69
91
  this.createBaseClientBuilder().withAnonymousSessionFlow({
70
92
  host: this.config.authUrl,
@@ -92,19 +114,13 @@ export class CommercetoolsClient {
92
114
  })
93
115
  .execute();
94
116
 
95
- const login = await this.login(username, password, reqCtx);
117
+ const login = await this.login(username, password);
96
118
 
97
119
  return login;
98
120
  }
99
121
 
100
- public async login(
101
- username: string,
102
- password: string,
103
- reqCtx: RequestContext
104
- ) {
105
- const cache = new RequestContextTokenCache(reqCtx);
106
-
107
- const loginBuilder = this.createBaseClientBuilder().withPasswordFlow({
122
+ public async login(username: string, password: string) {
123
+ const loginBuilder = this.createBaseClientBuilder().withPasswordFlow({
108
124
  host: this.config.authUrl,
109
125
  projectKey: this.config.projectKey,
110
126
  credentials: {
@@ -112,7 +128,7 @@ export class CommercetoolsClient {
112
128
  clientSecret: this.config.clientSecret,
113
129
  user: { username, password },
114
130
  },
115
- tokenCache: cache,
131
+ tokenCache: this.cache,
116
132
  scopes: this.config.scopes,
117
133
  });
118
134
 
@@ -130,78 +146,136 @@ export class CommercetoolsClient {
130
146
  })
131
147
  .execute();
132
148
 
133
- reqCtx.identity = RegisteredIdentitySchema.parse({
134
- ...reqCtx.identity,
149
+ const self = await loginClient
150
+ .withProjectKey({ projectKey: this.config.projectKey })
151
+ .me()
152
+ .get({})
153
+ .execute();
154
+
155
+ return RegisteredIdentitySchema.parse({
135
156
  type: 'Registered',
136
- logonId: username,
137
157
  id: {
138
- userId: login.body.customer.id,
158
+ userId: self.body.id,
139
159
  },
140
160
  });
161
+ }
141
162
 
142
- return reqCtx.identity;
163
+ public async logout() {
164
+ await this.cache.set({ token: '', refreshToken: '', expirationTime: 0 });
165
+
166
+ // TODO: We could do token revocation here, if we wanted to. The above simply whacks the session.
167
+ const identity = {
168
+ type: 'Anonymous'
169
+ } satisfies AnonymousIdentity;
170
+ return identity;
143
171
  }
144
172
 
145
- public async logout(reqCtx: RequestContext) {
146
- const cache = new RequestContextTokenCache(reqCtx);
147
- await cache.set({ token: '', refreshToken: '', expirationTime: 0 });
173
+ // FIXME: This can fail if the short-lived access token has expired. In other words, probably missing a token refresh.
174
+ public async introspect(): Promise<
175
+ AnonymousIdentity | GuestIdentity | RegisteredIdentity
176
+ > {
177
+ const session = await this.cache.get();
148
178
 
149
- reqCtx.identity = AnonymousIdentitySchema.parse({});
179
+ if (!session || !session.token) {
180
+ const identity = {
181
+ type: 'Anonymous'
182
+ } satisfies AnonymousIdentity;
150
183
 
151
- // TODO: We could do token revocation here, if we wanted to. The above simply whacks the session.
184
+ return identity;
185
+ }
152
186
 
153
- return reqCtx.identity;
154
- }
187
+ const authHeader =
188
+ 'Basic ' +
189
+ Buffer.from(
190
+ `${this.config.clientId}:${this.config.clientSecret}`
191
+ ).toString('base64');
192
+ const introspectionUrl = `${this.config.authUrl}/oauth/introspect`;
193
+
194
+ const response = await fetch(introspectionUrl, {
195
+ method: 'POST',
196
+ headers: {
197
+ Authorization: authHeader,
198
+ 'Content-Type': 'application/x-www-form-urlencoded',
199
+ },
200
+ body: new URLSearchParams({
201
+ token: session.token,
202
+ }),
203
+ });
155
204
 
156
- protected createClient(reqCtx: RequestContext) {
157
- const cache = new RequestContextTokenCache(reqCtx);
158
205
 
159
- if (reqCtx.identity.type === 'Anonymous') {
160
- reqCtx.identity = GuestIdentitySchema.parse({
206
+ const body = await response.json();
207
+ if (!body) {
208
+ return AnonymousIdentitySchema.parse({});
209
+ }
210
+
211
+ const scopes: string = body.scope + '';
212
+
213
+ // FIXME: Map unmapped user_id...
214
+ if (scopes.indexOf('anonymous_id') > -1) {
215
+ const s = scopes.split(' ');
216
+ const idScope = s.find((x) => x.startsWith('anonymous_id'));
217
+ const id = idScope?.split(':')[1] || '';
218
+ const identity = {
161
219
  id: {
162
- userId: crypto.randomUUID().toString(),
220
+ userId: id,
163
221
  },
164
222
  type: 'Guest',
165
- });
166
- }
223
+ } satisfies GuestIdentity;
167
224
 
168
- const identity = reqCtx.identity;
169
- let builder = this.createBaseClientBuilder(reqCtx);
225
+ return identity;
226
+ }
170
227
 
171
- if (!identity.token || !identity.refresh_token) {
172
- builder = builder.withAnonymousSessionFlow({
173
- host: this.config.authUrl,
174
- projectKey: this.config.projectKey,
175
- credentials: {
176
- clientId: this.config.clientId,
177
- clientSecret: this.config.clientSecret,
178
- anonymousId: identity.id.userId,
179
- },
180
- tokenCache: cache,
181
- });
182
- } else {
183
- builder = builder.withRefreshTokenFlow({
184
- credentials: {
185
- clientId: this.config.clientId,
186
- clientSecret: this.config.clientSecret,
228
+ // FIXME: Map unmapped user_id...
229
+ if (scopes.indexOf('customer_id') > -1) {
230
+ const s = scopes.split(' ');
231
+ const idScope = s.find((x: any) => x.startsWith('customer_id'));
232
+ const id = idScope?.split(':')[1] || '';
233
+ const identity = {
234
+ id: {
235
+ userId: id,
187
236
  },
188
- host: this.config.authUrl,
189
- projectKey: this.config.projectKey,
190
- refreshToken: identity.refresh_token || '',
191
- tokenCache: cache,
192
- });
237
+ type: 'Registered',
238
+ } satisfies RegisteredIdentity;
239
+
240
+ return identity;
193
241
  }
194
242
 
195
- return createApiBuilderFromCtpClient(builder.build());
243
+ return {
244
+ type: 'Anonymous',
245
+ } satisfies AnonymousIdentity;
196
246
  }
197
247
 
248
+ protected async becomeGuest() {
249
+ const credentials = Buffer.from(
250
+ `${this.config.clientId}:${this.config.clientSecret}`
251
+ ).toString('base64');
252
+
253
+ // FIXME: Missing scope-down from .env scopes list
254
+ const response = await fetch(
255
+ `${this.config.authUrl}/oauth/${this.config.projectKey}/anonymous/token?grant_type=client_credentials`,
256
+ {
257
+ method: 'POST',
258
+ headers: {
259
+ Authorization: `Basic ${credentials}`,
260
+ 'Content-Type': 'application/x-www-form-urlencoded',
261
+ },
262
+ body: new URLSearchParams({
263
+ grant_type: 'client_credentials',
264
+ }),
265
+ }
266
+ );
198
267
 
268
+ const result = await response.json();
199
269
 
270
+ this.cache.set({
271
+ expirationTime:
272
+ Date.now() + Number(result.expires_in) * 1000 - 5 * 60 * 1000,
273
+ token: result.access_token,
274
+ refreshToken: result.refresh_token,
275
+ });
276
+ }
200
277
 
201
-
202
-
203
-
204
- protected createBaseClientBuilder(reqCtx?: RequestContext) {
278
+ protected createBaseClientBuilder() {
205
279
  let builder = new ClientBuilder()
206
280
  .withProjectKey(this.config.projectKey)
207
281
  .withQueueMiddleware({
@@ -237,7 +311,12 @@ export class CommercetoolsClient {
237
311
  httpClient: fetch,
238
312
  });
239
313
 
240
- const correlationId = reqCtx?.correlationId || 'REACTIONARY-' + (typeof crypto !== 'undefined' && 'randomUUID' in crypto ? crypto.randomUUID() : randomUUID());
314
+ const correlationId =
315
+ this.context.correlationId ||
316
+ 'REACTIONARY-' +
317
+ (typeof crypto !== 'undefined' && 'randomUUID' in crypto
318
+ ? crypto.randomUUID()
319
+ : randomUUID());
241
320
  builder = builder.withCorrelationIdMiddleware({
242
321
  generate: () => correlationId,
243
322
  });