@reactionary/source 0.0.27

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 (197) hide show
  1. package/.editorconfig +13 -0
  2. package/.github/workflows/pull-request.yml +37 -0
  3. package/.github/workflows/release.yml +49 -0
  4. package/.nvmrc +1 -0
  5. package/.prettierignore +6 -0
  6. package/.prettierrc +3 -0
  7. package/.verdaccio/config.yml +28 -0
  8. package/.vscode/extensions.json +9 -0
  9. package/README.md +39 -0
  10. package/core/README.md +11 -0
  11. package/core/eslint.config.mjs +23 -0
  12. package/core/package.json +8 -0
  13. package/core/project.json +33 -0
  14. package/core/src/cache/caching-strategy.ts +25 -0
  15. package/core/src/cache/redis-cache.ts +41 -0
  16. package/core/src/client/client.ts +39 -0
  17. package/core/src/index.ts +42 -0
  18. package/core/src/providers/analytics.provider.ts +10 -0
  19. package/core/src/providers/base.provider.ts +75 -0
  20. package/core/src/providers/cart.provider.ts +10 -0
  21. package/core/src/providers/identity.provider.ts +10 -0
  22. package/core/src/providers/inventory.provider.ts +11 -0
  23. package/core/src/providers/price.provider.ts +10 -0
  24. package/core/src/providers/product.provider.ts +11 -0
  25. package/core/src/providers/search.provider.ts +12 -0
  26. package/core/src/schemas/capabilities.schema.ts +13 -0
  27. package/core/src/schemas/models/analytics.model.ts +7 -0
  28. package/core/src/schemas/models/base.model.ts +19 -0
  29. package/core/src/schemas/models/cart.model.ts +17 -0
  30. package/core/src/schemas/models/currency.model.ts +187 -0
  31. package/core/src/schemas/models/identifiers.model.ts +45 -0
  32. package/core/src/schemas/models/identity.model.ts +15 -0
  33. package/core/src/schemas/models/inventory.model.ts +8 -0
  34. package/core/src/schemas/models/price.model.ts +17 -0
  35. package/core/src/schemas/models/product.model.ts +28 -0
  36. package/core/src/schemas/models/search.model.ts +35 -0
  37. package/core/src/schemas/mutations/analytics.mutation.ts +22 -0
  38. package/core/src/schemas/mutations/base.mutation.ts +7 -0
  39. package/core/src/schemas/mutations/cart.mutation.ts +30 -0
  40. package/core/src/schemas/mutations/identity.mutation.ts +18 -0
  41. package/core/src/schemas/mutations/inventory.mutation.ts +5 -0
  42. package/core/src/schemas/mutations/price.mutation.ts +5 -0
  43. package/core/src/schemas/mutations/product.mutation.ts +6 -0
  44. package/core/src/schemas/mutations/search.mutation.ts +5 -0
  45. package/core/src/schemas/queries/analytics.query.ts +5 -0
  46. package/core/src/schemas/queries/base.query.ts +7 -0
  47. package/core/src/schemas/queries/cart.query.ts +12 -0
  48. package/core/src/schemas/queries/identity.query.ts +10 -0
  49. package/core/src/schemas/queries/inventory.query.ts +9 -0
  50. package/core/src/schemas/queries/price.query.ts +12 -0
  51. package/core/src/schemas/queries/product.query.ts +18 -0
  52. package/core/src/schemas/queries/search.query.ts +12 -0
  53. package/core/src/schemas/session.schema.ts +9 -0
  54. package/core/tsconfig.json +23 -0
  55. package/core/tsconfig.lib.json +23 -0
  56. package/core/tsconfig.spec.json +28 -0
  57. package/eslint.config.mjs +46 -0
  58. package/examples/angular/e2e/example.spec.ts +9 -0
  59. package/examples/angular/eslint.config.mjs +41 -0
  60. package/examples/angular/playwright.config.ts +38 -0
  61. package/examples/angular/project.json +86 -0
  62. package/examples/angular/public/favicon.ico +0 -0
  63. package/examples/angular/src/app/app.component.html +6 -0
  64. package/examples/angular/src/app/app.component.scss +22 -0
  65. package/examples/angular/src/app/app.component.ts +14 -0
  66. package/examples/angular/src/app/app.config.ts +16 -0
  67. package/examples/angular/src/app/app.routes.ts +25 -0
  68. package/examples/angular/src/app/cart/cart.component.html +4 -0
  69. package/examples/angular/src/app/cart/cart.component.scss +14 -0
  70. package/examples/angular/src/app/cart/cart.component.ts +73 -0
  71. package/examples/angular/src/app/identity/identity.component.html +6 -0
  72. package/examples/angular/src/app/identity/identity.component.scss +18 -0
  73. package/examples/angular/src/app/identity/identity.component.ts +49 -0
  74. package/examples/angular/src/app/product/product.component.html +14 -0
  75. package/examples/angular/src/app/product/product.component.scss +11 -0
  76. package/examples/angular/src/app/product/product.component.ts +42 -0
  77. package/examples/angular/src/app/search/search.component.html +35 -0
  78. package/examples/angular/src/app/search/search.component.scss +129 -0
  79. package/examples/angular/src/app/search/search.component.ts +50 -0
  80. package/examples/angular/src/app/services/product.service.ts +35 -0
  81. package/examples/angular/src/app/services/search.service.ts +48 -0
  82. package/examples/angular/src/app/services/trpc.client.ts +27 -0
  83. package/examples/angular/src/index.html +13 -0
  84. package/examples/angular/src/main.ts +7 -0
  85. package/examples/angular/src/styles.scss +17 -0
  86. package/examples/angular/src/test-setup.ts +6 -0
  87. package/examples/angular/tsconfig.app.json +10 -0
  88. package/examples/angular/tsconfig.editor.json +6 -0
  89. package/examples/angular/tsconfig.json +32 -0
  90. package/examples/node/README.md +11 -0
  91. package/examples/node/eslint.config.mjs +22 -0
  92. package/examples/node/jest.config.ts +10 -0
  93. package/examples/node/package.json +10 -0
  94. package/examples/node/project.json +20 -0
  95. package/examples/node/src/index.ts +2 -0
  96. package/examples/node/src/initialize-algolia.spec.ts +29 -0
  97. package/examples/node/src/initialize-commercetools.spec.ts +31 -0
  98. package/examples/node/src/initialize-extended-providers.spec.ts +38 -0
  99. package/examples/node/src/initialize-mixed-providers.spec.ts +36 -0
  100. package/examples/node/src/providers/custom-algolia-product.provider.ts +18 -0
  101. package/examples/node/src/schemas/custom-product.schema.ts +8 -0
  102. package/examples/node/tsconfig.json +23 -0
  103. package/examples/node/tsconfig.lib.json +10 -0
  104. package/examples/node/tsconfig.spec.json +15 -0
  105. package/examples/trpc-node/eslint.config.mjs +3 -0
  106. package/examples/trpc-node/project.json +61 -0
  107. package/examples/trpc-node/src/assets/.gitkeep +0 -0
  108. package/examples/trpc-node/src/main.ts +55 -0
  109. package/examples/trpc-node/src/router-instance.ts +52 -0
  110. package/examples/trpc-node/tsconfig.app.json +9 -0
  111. package/examples/trpc-node/tsconfig.json +13 -0
  112. package/examples/vue/eslint.config.mjs +24 -0
  113. package/examples/vue/index.html +13 -0
  114. package/examples/vue/project.json +8 -0
  115. package/examples/vue/src/app/App.vue +275 -0
  116. package/examples/vue/src/main.ts +6 -0
  117. package/examples/vue/src/styles.scss +9 -0
  118. package/examples/vue/src/vue-shims.d.ts +5 -0
  119. package/examples/vue/tsconfig.app.json +14 -0
  120. package/examples/vue/tsconfig.json +20 -0
  121. package/examples/vue/vite.config.ts +31 -0
  122. package/jest.config.ts +6 -0
  123. package/jest.preset.js +3 -0
  124. package/migrations.json +11 -0
  125. package/nx.json +130 -0
  126. package/package.json +118 -0
  127. package/project.json +14 -0
  128. package/providers/algolia/README.md +11 -0
  129. package/providers/algolia/eslint.config.mjs +22 -0
  130. package/providers/algolia/jest.config.ts +10 -0
  131. package/providers/algolia/package.json +9 -0
  132. package/providers/algolia/project.json +33 -0
  133. package/providers/algolia/src/core/initialize.ts +20 -0
  134. package/providers/algolia/src/index.ts +7 -0
  135. package/providers/algolia/src/providers/product.provider.ts +25 -0
  136. package/providers/algolia/src/providers/search.provider.ts +125 -0
  137. package/providers/algolia/src/schema/capabilities.schema.ts +10 -0
  138. package/providers/algolia/src/schema/configuration.schema.ts +9 -0
  139. package/providers/algolia/src/schema/search.schema.ts +14 -0
  140. package/providers/algolia/src/test/product.provider.spec.ts +18 -0
  141. package/providers/algolia/src/test/search.provider.spec.ts +82 -0
  142. package/providers/algolia/tsconfig.json +23 -0
  143. package/providers/algolia/tsconfig.lib.json +10 -0
  144. package/providers/algolia/tsconfig.spec.json +15 -0
  145. package/providers/commercetools/README.md +11 -0
  146. package/providers/commercetools/eslint.config.mjs +22 -0
  147. package/providers/commercetools/jest.config.ts +10 -0
  148. package/providers/commercetools/package.json +10 -0
  149. package/providers/commercetools/project.json +33 -0
  150. package/providers/commercetools/src/core/client.ts +152 -0
  151. package/providers/commercetools/src/core/initialize.ts +40 -0
  152. package/providers/commercetools/src/index.ts +12 -0
  153. package/providers/commercetools/src/providers/cart.provider.ts +223 -0
  154. package/providers/commercetools/src/providers/identity.provider.ts +130 -0
  155. package/providers/commercetools/src/providers/inventory.provider.ts +82 -0
  156. package/providers/commercetools/src/providers/price.provider.ts +66 -0
  157. package/providers/commercetools/src/providers/product.provider.ts +90 -0
  158. package/providers/commercetools/src/providers/search.provider.ts +86 -0
  159. package/providers/commercetools/src/schema/capabilities.schema.ts +13 -0
  160. package/providers/commercetools/src/schema/configuration.schema.ts +11 -0
  161. package/providers/commercetools/src/test/product.provider.spec.ts +20 -0
  162. package/providers/commercetools/src/test/search.provider.spec.ts +18 -0
  163. package/providers/commercetools/tsconfig.json +23 -0
  164. package/providers/commercetools/tsconfig.lib.json +10 -0
  165. package/providers/commercetools/tsconfig.spec.json +15 -0
  166. package/providers/fake/README.md +7 -0
  167. package/providers/fake/eslint.config.mjs +22 -0
  168. package/providers/fake/package.json +9 -0
  169. package/providers/fake/project.json +33 -0
  170. package/providers/fake/src/core/initialize.ts +24 -0
  171. package/providers/fake/src/index.ts +8 -0
  172. package/providers/fake/src/providers/identity.provider.ts +91 -0
  173. package/providers/fake/src/providers/product.provider.ts +73 -0
  174. package/providers/fake/src/providers/search.provider.ts +142 -0
  175. package/providers/fake/src/schema/capabilities.schema.ts +10 -0
  176. package/providers/fake/src/schema/configuration.schema.ts +15 -0
  177. package/providers/fake/src/utilities/jitter.ts +14 -0
  178. package/providers/fake/tsconfig.json +20 -0
  179. package/providers/fake/tsconfig.lib.json +9 -0
  180. package/providers/posthog/README.md +7 -0
  181. package/providers/posthog/eslint.config.mjs +22 -0
  182. package/providers/posthog/package.json +11 -0
  183. package/providers/posthog/project.json +33 -0
  184. package/providers/posthog/src/core/initialize.ts +9 -0
  185. package/providers/posthog/src/index.ts +4 -0
  186. package/providers/posthog/src/schema/capabilities.schema.ts +8 -0
  187. package/providers/posthog/src/schema/configuration.schema.ts +8 -0
  188. package/providers/posthog/tsconfig.json +20 -0
  189. package/providers/posthog/tsconfig.lib.json +9 -0
  190. package/trpc/README.md +7 -0
  191. package/trpc/eslint.config.mjs +19 -0
  192. package/trpc/package.json +13 -0
  193. package/trpc/project.json +31 -0
  194. package/trpc/src/index.ts +64 -0
  195. package/trpc/tsconfig.json +13 -0
  196. package/trpc/tsconfig.lib.json +9 -0
  197. package/tsconfig.base.json +30 -0
@@ -0,0 +1,130 @@
1
+ import {
2
+ Identity,
3
+ IdentityMutation,
4
+ IdentityMutationLogin,
5
+ IdentityProvider,
6
+ IdentityQuery,
7
+ Session,
8
+ } from '@reactionary/core';
9
+ import { CommercetoolsConfiguration } from '../schema/configuration.schema';
10
+ import z from 'zod';
11
+ import { CommercetoolsClient } from '../core/client';
12
+
13
+ export class CommercetoolsIdentityProvider<
14
+ T extends Identity = Identity,
15
+ Q extends IdentityQuery = IdentityQuery,
16
+ M extends IdentityMutation = IdentityMutation
17
+ > extends IdentityProvider<T, Q, M> {
18
+ protected config: CommercetoolsConfiguration;
19
+
20
+ constructor(
21
+ config: CommercetoolsConfiguration,
22
+ schema: z.ZodType<T>,
23
+ querySchema: z.ZodType<Q, Q>,
24
+ mutationSchema: z.ZodType<M, M>
25
+ ) {
26
+ super(schema, querySchema, mutationSchema);
27
+
28
+ this.config = config;
29
+ }
30
+
31
+ protected override async fetch(queries: Q[], session: Session): Promise<T[]> {
32
+ const results = [];
33
+
34
+ for (const query of queries) {
35
+ const result = await this.get(session);
36
+
37
+ results.push(result);
38
+ }
39
+
40
+ return results;
41
+ }
42
+
43
+ protected override async process(
44
+ mutations: M[],
45
+ session: Session
46
+ ): Promise<T> {
47
+ let result = this.newModel();
48
+
49
+ for (const mutation of mutations) {
50
+ switch (mutation.mutation) {
51
+ case 'login':
52
+ result = await this.login(mutation, session);
53
+ break;
54
+ case 'logout':
55
+ result = await this.logout(session);
56
+ break;
57
+ }
58
+ }
59
+
60
+ return result;
61
+ }
62
+
63
+ protected async get(session: Session): Promise<T> {
64
+ const client = new CommercetoolsClient(this.config);
65
+ const base = this.newModel();
66
+
67
+ if (session.identity.token) {
68
+ const remote = await client.introspect(session.identity.token);
69
+
70
+ if (remote.active) {
71
+ const current = this.schema.safeParse(session.identity);
72
+
73
+ if (current.success) {
74
+ return current.data;
75
+ }
76
+ }
77
+ }
78
+
79
+ session.identity = base;
80
+
81
+ return base;
82
+ }
83
+
84
+ protected async login(
85
+ payload: IdentityMutationLogin,
86
+ session: Session
87
+ ): Promise<T> {
88
+ const client = new CommercetoolsClient(this.config);
89
+ const remote = await client.login(payload.username, payload.password);
90
+ const base = this.newModel();
91
+
92
+ if (remote && remote.access_token) {
93
+ base.issued = new Date();
94
+ base.expiry = new Date();
95
+ base.expiry.setSeconds(base.expiry.getSeconds() + remote.expires_in);
96
+ base.id = this.extractCustomerIdFromScopes(remote.scope);
97
+ base.token = remote.access_token;
98
+ base.type = 'Registered';
99
+ }
100
+
101
+ // TODO: error handling
102
+
103
+ session.identity = base;
104
+
105
+ return base;
106
+ }
107
+
108
+ protected async logout(session: Session): Promise<T> {
109
+ const client = new CommercetoolsClient(this.config);
110
+ const base = this.newModel();
111
+
112
+ if (session.identity.token) {
113
+ const remote = await client.logout(session.identity.token);
114
+
115
+ // TODO: error handling
116
+ }
117
+
118
+ session.identity = base;
119
+
120
+ return base;
121
+ }
122
+
123
+ protected extractCustomerIdFromScopes(scopes: string) {
124
+ const scopeList = scopes.split(' ');
125
+ const customerScope = scopeList.find((x) => x.startsWith('customer_id'));
126
+ const id = customerScope?.split(':')[1];
127
+
128
+ return id || '';
129
+ }
130
+ }
@@ -0,0 +1,82 @@
1
+ import {
2
+ BaseCachingStrategy,
3
+ Inventory,
4
+ InventoryProvider,
5
+ InventoryQuery,
6
+ RedisCache,
7
+ Session,
8
+ } from '@reactionary/core';
9
+ import z from 'zod';
10
+ import { CommercetoolsConfiguration } from '../schema/configuration.schema';
11
+ import { CommercetoolsClient } from '../core/client';
12
+ import { InventoryMutation } from 'core/src/schemas/mutations/inventory.mutation';
13
+
14
+ export class CommercetoolsInventoryProvider<
15
+ T extends Inventory = Inventory,
16
+ Q extends InventoryQuery = InventoryQuery,
17
+ M extends InventoryMutation = InventoryMutation
18
+ > extends InventoryProvider<T, Q, M> {
19
+ protected config: CommercetoolsConfiguration;
20
+ protected cache = new RedisCache(new BaseCachingStrategy());
21
+
22
+ constructor(
23
+ config: CommercetoolsConfiguration,
24
+ schema: z.ZodType<T>,
25
+ querySchema: z.ZodType<Q, Q>,
26
+ mutationSchema: z.ZodType<M, M>
27
+ ) {
28
+ super(schema, querySchema, mutationSchema);
29
+
30
+ this.config = config;
31
+ }
32
+
33
+ protected override async fetch(queries: Q[], session: Session): Promise<T[]> {
34
+ const results = [];
35
+
36
+ for (const query of queries) {
37
+ let result = await this.cache.get(query, session, this.schema);
38
+ console.log('from cache: ', result);
39
+
40
+ if (!result) {
41
+ result = await this.get(query, session);
42
+
43
+ this.cache.put(query, session, result);
44
+ }
45
+
46
+ results.push(result);
47
+ }
48
+
49
+ return results;
50
+ }
51
+
52
+ protected override process(mutations: M[], session: Session): Promise<T> {
53
+ throw new Error('Method not implemented.');
54
+ }
55
+
56
+ protected async get(query: Q, session: Session): Promise<T> {
57
+ const client = new CommercetoolsClient(this.config).getClient(
58
+ session.identity?.token
59
+ );
60
+
61
+ const remote = await client
62
+ .withProjectKey({ projectKey: this.config.projectKey })
63
+ .inventory()
64
+ .get({
65
+ queryArgs: {
66
+ where: 'sku=:sku',
67
+ 'var.sku': query.sku,
68
+ },
69
+ })
70
+ .execute();
71
+
72
+ const base = this.newModel();
73
+
74
+ if (remote.body.results.length > 0) {
75
+ const inv = remote.body.results[0];
76
+
77
+ base.quantity = inv.availableQuantity;
78
+ }
79
+
80
+ return base;
81
+ }
82
+ }
@@ -0,0 +1,66 @@
1
+ import { BaseCachingStrategy, BaseMutation, Price, PriceProvider, PriceQuery, RedisCache, Session } from '@reactionary/core';
2
+ import z from 'zod';
3
+ import { CommercetoolsConfiguration } from '../schema/configuration.schema';
4
+ import { CommercetoolsClient } from '../core/client';
5
+ import { PriceMutation } from 'core/src/schemas/mutations/price.mutation';
6
+
7
+ export class CommercetoolsPriceProvider<
8
+ T extends Price = Price,
9
+ Q extends PriceQuery = PriceQuery,
10
+ M extends PriceMutation = PriceMutation
11
+ > extends PriceProvider<T, Q, M> {
12
+ protected config: CommercetoolsConfiguration;
13
+
14
+ constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>) {
15
+ super(schema, querySchema, mutationSchema);
16
+
17
+ this.config = config;
18
+ }
19
+
20
+ protected override async fetch(queries: Q[], session: Session): Promise<T[]> {
21
+ const client = new CommercetoolsClient(this.config).getClient(
22
+ session.identity?.token
23
+ );
24
+
25
+ const queryArgs = {
26
+ where: 'sku in :skus',
27
+ 'var.skus': queries.map(x => x.sku.key),
28
+ };
29
+
30
+ const remote = await client
31
+ .withProjectKey({ projectKey: this.config.projectKey })
32
+ .standalonePrices()
33
+ .get({
34
+ queryArgs,
35
+ })
36
+ .execute();
37
+
38
+ const results = new Array<T>();
39
+
40
+ for (const query of queries) {
41
+ const base = this.newModel();
42
+ const matched = remote.body.results.find(x => x.sku === query.sku.key);
43
+
44
+ if (matched) {
45
+ base.value = {
46
+ cents: matched.value.centAmount,
47
+ currency: 'USD',
48
+ };
49
+ }
50
+
51
+ base.identifier = {
52
+ sku: {
53
+ key: query.sku.key
54
+ }
55
+ };
56
+
57
+ results.push(base);
58
+ }
59
+
60
+ return results;
61
+ }
62
+
63
+ protected override process(mutation: BaseMutation[], session: Session): Promise<T> {
64
+ throw new Error('Method not implemented.');
65
+ }
66
+ }
@@ -0,0 +1,90 @@
1
+ import {
2
+ ProductProvider,
3
+ ProductQuery,
4
+ Product,
5
+ Session,
6
+ BaseMutation,
7
+ ProductMutation,
8
+ } from '@reactionary/core';
9
+ import { CommercetoolsClient } from '../core/client';
10
+ import { z } from 'zod';
11
+ import { CommercetoolsConfiguration } from '../schema/configuration.schema';
12
+ import { ProductProjection } from '@commercetools/platform-sdk';
13
+
14
+ export class CommercetoolsProductProvider<
15
+ T extends Product = Product,
16
+ Q extends ProductQuery = ProductQuery,
17
+ M extends ProductMutation = ProductMutation
18
+ > extends ProductProvider<T, Q, M> {
19
+ protected config: CommercetoolsConfiguration;
20
+
21
+ constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>) {
22
+ super(schema, querySchema, mutationSchema);
23
+
24
+ this.config = config;
25
+ }
26
+
27
+ protected override async fetch(queries: ProductQuery[], session: Session): Promise<T[]> {
28
+ const ids = queries.filter((x) => x.query === 'id').map((x) => x.id);
29
+ const slugs = queries.filter((x) => x.query === 'slug').map((x) => x.slug);
30
+
31
+ const client = new CommercetoolsClient(this.config).createAnonymousClient();
32
+
33
+ const remote = await client
34
+ .withProjectKey({ projectKey: this.config.projectKey })
35
+ .productProjections()
36
+ .get({
37
+ queryArgs: {
38
+ where: 'slug(en-US in :slugs)',
39
+ 'var.slugs': slugs
40
+ }
41
+ })
42
+ .execute();
43
+
44
+ console.log('remote: ', remote);
45
+
46
+ const results = new Array<T>;
47
+
48
+ for (const r of remote.body.results) {
49
+ const result = this.parse(r);
50
+
51
+ results.push(result);
52
+ }
53
+
54
+ return results;
55
+ }
56
+
57
+ protected override process(
58
+ mutation: BaseMutation[],
59
+ session: Session
60
+ ): Promise<T> {
61
+ throw new Error('Method not implemented.');
62
+ }
63
+
64
+ protected parse(data: ProductProjection): T {
65
+ const base = this.newModel();
66
+
67
+ base.identifier.key = data.id;
68
+ base.name = data.name['en-US'];
69
+ base.slug = data.slug['en-US'];
70
+
71
+ if (data.description) {
72
+ base.description = data.description['en-US'];
73
+ }
74
+
75
+ if (data.masterVariant.images) {
76
+ base.image = data.masterVariant.images[0].url;
77
+ }
78
+
79
+ const variants = [data.masterVariant, ...data.variants];
80
+ for (const variant of variants) {
81
+ base.skus.push({
82
+ identifier: {
83
+ key: variant.sku || '',
84
+ },
85
+ });
86
+ }
87
+
88
+ return base;
89
+ }
90
+ }
@@ -0,0 +1,86 @@
1
+ import {
2
+ SearchIdentifier,
3
+ SearchMutation,
4
+ SearchProvider,
5
+ SearchQuery,
6
+ SearchResult,
7
+ SearchResultProductSchema,
8
+ Session,
9
+ } from '@reactionary/core';
10
+ import { CommercetoolsClient } from '../core/client';
11
+ import z from 'zod';
12
+ import { CommercetoolsConfiguration } from '../schema/configuration.schema';
13
+
14
+ export class CommercetoolsSearchProvider<
15
+ T extends SearchResult = SearchResult,
16
+ Q extends SearchQuery = SearchQuery,
17
+ M extends SearchMutation = SearchMutation
18
+ > extends SearchProvider<T, Q, M> {
19
+ protected config: CommercetoolsConfiguration;
20
+
21
+ constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>) {
22
+ super(schema, querySchema, mutationSchema);
23
+
24
+ this.config = config;
25
+ }
26
+
27
+ protected override async fetch(queries: Q[], session: Session): Promise<T[]> {
28
+ const results = [];
29
+
30
+ for (const query of queries) {
31
+ const result = await this.get(query.search);
32
+
33
+ results.push(result);
34
+ }
35
+
36
+ return results;
37
+ }
38
+
39
+ protected override process(mutations: M[], session: Session): Promise<T> {
40
+ throw new Error('Method not implemented.');
41
+ }
42
+
43
+ public async get(identifier: SearchIdentifier): Promise<T> {
44
+ const client = new CommercetoolsClient(this.config).createAnonymousClient();
45
+
46
+ const remote = await client
47
+ .withProjectKey({ projectKey: this.config.projectKey })
48
+ .productProjections()
49
+ .search()
50
+ .get({
51
+ queryArgs: {
52
+ limit: identifier.pageSize,
53
+ offset: identifier.pageSize * identifier.page,
54
+ ['text.en-US']: identifier.term,
55
+ },
56
+ })
57
+ .execute();
58
+
59
+ const parsed = this.parse(remote, identifier);
60
+
61
+ return parsed;
62
+ }
63
+
64
+ public parse(remote: any, query: SearchIdentifier): T {
65
+ const result = super.newModel();
66
+
67
+ result.identifier = query;
68
+
69
+ for (const p of remote.body.results) {
70
+ const product = SearchResultProductSchema.parse({});
71
+
72
+ product.identifier.key = p.id;
73
+ product.name = p.name['en-US'];
74
+
75
+ if (p.masterVariant.images) {
76
+ product.image = p.masterVariant.images[0].url;
77
+ }
78
+
79
+ result.products.push(product);
80
+ }
81
+
82
+ result.pages = Math.ceil((remote.body.total || 0) / query.pageSize);
83
+
84
+ return result;
85
+ }
86
+ }
@@ -0,0 +1,13 @@
1
+ import { CapabilitiesSchema, Price, PriceQuery } from "@reactionary/core";
2
+ import { z } from 'zod';
3
+
4
+ export const CommercetoolsCapabilitiesSchema = CapabilitiesSchema.pick({
5
+ product: true,
6
+ search: true,
7
+ identity: true,
8
+ cart: true,
9
+ inventory: true,
10
+ price: true
11
+ }).partial();
12
+
13
+ export type CommercetoolsCapabilities = z.infer<typeof CommercetoolsCapabilitiesSchema>;
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod';
2
+
3
+ export const CommercetoolsConfigurationSchema = z.looseInterface({
4
+ projectKey: z.string(),
5
+ authUrl: z.string(),
6
+ apiUrl: z.string(),
7
+ clientId: z.string(),
8
+ clientSecret: z.string()
9
+ });
10
+
11
+ export type CommercetoolsConfiguration = z.infer<typeof CommercetoolsConfigurationSchema>;
@@ -0,0 +1,20 @@
1
+ import { ProductSchema } from '@reactionary/core';
2
+ import { CommercetoolsProductProvider } from '../providers/product.provider';
3
+
4
+ describe('Commercetools Product Provider', () => {
5
+ it('should be able to get a product by id', async () => {
6
+ const provider = new CommercetoolsProductProvider({
7
+ apiUrl: process.env['COMMERCETOOLS_API_URL'] || '',
8
+ authUrl: process.env['COMMERCETOOLS_AUTH_URL'] || '',
9
+ clientId: process.env['COMMERCETOOLS_CLIENT_ID'] || '',
10
+ clientSecret: process.env['COMMERCETOOLS_CLIENT_SECRET'] || '',
11
+ projectKey: process.env['COMMERCETOOLS_PROJECT_KEY'] || ''
12
+ }, ProductSchema);
13
+
14
+ const result = await provider.get({ id: '4d28f98d-c446-446e-b59a-d9f718e5b98a'});
15
+
16
+ expect(result.identifier.key).toBe('4d28f98d-c446-446e-b59a-d9f718e5b98a');
17
+ expect(result.name).toBe('Sunnai Glass Bowl');
18
+ expect(result.image).toBe('https://storage.googleapis.com/merchant-center-europe/sample-data/goodstore/Sunnai_Glass_Bowl-1.1.jpeg');
19
+ });
20
+ });
@@ -0,0 +1,18 @@
1
+ import { SearchResultSchema } from '@reactionary/core';
2
+ import { CommercetoolsSearchProvider } from '../providers/search.provider';
3
+
4
+ describe('Commercetools Search Provider', () => {
5
+ it('should be able to get a result by term', async () => {
6
+ const provider = new CommercetoolsSearchProvider({
7
+ apiUrl: process.env['COMMERCETOOLS_API_URL'] || '',
8
+ authUrl: process.env['COMMERCETOOLS_AUTH_URL'] || '',
9
+ clientId: process.env['COMMERCETOOLS_CLIENT_ID'] || '',
10
+ clientSecret: process.env['COMMERCETOOLS_CLIENT_SECRET'] || '',
11
+ projectKey: process.env['COMMERCETOOLS_PROJECT_KEY'] || '',
12
+ }, SearchResultSchema);
13
+
14
+ const result = await provider.get({ term: 'glass', page: 0, pageSize: 20, facets: [] });
15
+
16
+ expect(result.products.length).toBeGreaterThan(0);
17
+ });
18
+ });
@@ -0,0 +1,23 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "module": "commonjs",
5
+ "forceConsistentCasingInFileNames": true,
6
+ "strict": true,
7
+ "importHelpers": true,
8
+ "noImplicitOverride": true,
9
+ "noImplicitReturns": true,
10
+ "noFallthroughCasesInSwitch": true,
11
+ "noPropertyAccessFromIndexSignature": true
12
+ },
13
+ "files": [],
14
+ "include": [],
15
+ "references": [
16
+ {
17
+ "path": "./tsconfig.lib.json"
18
+ },
19
+ {
20
+ "path": "./tsconfig.spec.json"
21
+ }
22
+ ]
23
+ }
@@ -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": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
10
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "module": "commonjs",
6
+ "moduleResolution": "node10",
7
+ "types": ["jest", "node"]
8
+ },
9
+ "include": [
10
+ "jest.config.ts",
11
+ "src/**/*.test.ts",
12
+ "src/**/*.spec.ts",
13
+ "src/**/*.d.ts"
14
+ ]
15
+ }
@@ -0,0 +1,7 @@
1
+ # Fake
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build provider-fake` to build the library.
@@ -0,0 +1,22 @@
1
+ import baseConfig from '../../eslint.config.mjs';
2
+
3
+ export default [
4
+ ...baseConfig,
5
+ {
6
+ files: ['**/*.json'],
7
+ rules: {
8
+ '@nx/dependency-checks': [
9
+ 'error',
10
+ {
11
+ ignoredFiles: [
12
+ '{projectRoot}/eslint.config.{js,cjs,mjs}',
13
+ '{projectRoot}/esbuild.config.{js,ts,mjs,mts}',
14
+ ],
15
+ },
16
+ ],
17
+ },
18
+ languageOptions: {
19
+ parser: await import('jsonc-eslint-parser'),
20
+ },
21
+ },
22
+ ];
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@reactionary/provider-fake",
3
+ "version": "0.0.1",
4
+ "dependencies": {
5
+ "@reactionary/core": "0.0.1",
6
+ "zod": "4.0.0-beta.20250430T185432",
7
+ "@faker-js/faker": "^9.8.0"
8
+ }
9
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "provider-fake",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "providers/fake/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/fake",
21
+ "main": "providers/fake/src/index.ts",
22
+ "tsConfig": "providers/fake/tsconfig.lib.json",
23
+ "assets": ["providers/fake/*.md"],
24
+ "format": ["esm"]
25
+ }
26
+ },
27
+ "nx-release-publish": {
28
+ "options": {
29
+ "packageRoot": "dist/{projectRoot}"
30
+ }
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,24 @@
1
+ import { Client, IdentityMutationSchema, IdentityQuerySchema, IdentitySchema, ProductMutationSchema, ProductQuerySchema, ProductSchema, SearchMutationSchema, SearchQuerySchema, SearchResultSchema } from "@reactionary/core";
2
+ import { FakeProductProvider } from "../providers/product.provider";
3
+ import { FakeSearchProvider } from "../providers/search.provider";
4
+ import { FakeConfiguration } from "../schema/configuration.schema";
5
+ import { FakeCapabilities } from "../schema/capabilities.schema";
6
+ import { FakeIdentityProvider } from "../providers/identity.provider";
7
+
8
+ export function withFakeCapabilities(configuration: FakeConfiguration, capabilities: FakeCapabilities) {
9
+ const client = {} as Partial<Client>;
10
+
11
+ if (capabilities.product) {
12
+ client.product = new FakeProductProvider(configuration, ProductSchema, ProductQuerySchema, ProductMutationSchema);
13
+ }
14
+
15
+ if (capabilities.search) {
16
+ client.search = new FakeSearchProvider(configuration, SearchResultSchema, SearchQuerySchema, SearchMutationSchema);
17
+ }
18
+
19
+ if (capabilities.identity) {
20
+ client.identity = new FakeIdentityProvider(configuration, IdentitySchema, IdentityQuerySchema, IdentityMutationSchema);
21
+ }
22
+
23
+ return client;
24
+ }
@@ -0,0 +1,8 @@
1
+ export * from './core/initialize';
2
+
3
+ export * from './providers/identity.provider';
4
+ export * from './providers/product.provider';
5
+ export * from './providers/search.provider';
6
+
7
+ export * from './schema/capabilities.schema';
8
+ export * from './schema/configuration.schema';