@reactionary/source 0.0.30 → 0.0.32

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 (119) hide show
  1. package/CLAUDE.md +11 -0
  2. package/core/package.json +1 -1
  3. package/core/src/cache/cache-evaluation.interface.ts +19 -0
  4. package/core/src/cache/cache.interface.ts +38 -0
  5. package/core/src/cache/noop-cache.ts +42 -0
  6. package/core/src/cache/redis-cache.ts +55 -22
  7. package/core/src/client/client-builder.ts +63 -0
  8. package/core/src/client/client.ts +27 -3
  9. package/core/src/decorators/trpc.decorators.ts +144 -0
  10. package/core/src/index.ts +6 -1
  11. package/core/src/providers/analytics.provider.ts +3 -6
  12. package/core/src/providers/base.provider.ts +13 -63
  13. package/core/src/providers/cart.provider.ts +10 -6
  14. package/core/src/providers/identity.provider.ts +8 -5
  15. package/core/src/providers/inventory.provider.ts +5 -6
  16. package/core/src/providers/price.provider.ts +6 -6
  17. package/core/src/providers/product.provider.ts +6 -6
  18. package/core/src/providers/search.provider.ts +6 -6
  19. package/core/src/schemas/mutations/base.mutation.ts +0 -1
  20. package/core/src/schemas/mutations/cart.mutation.ts +0 -6
  21. package/core/src/schemas/mutations/identity.mutation.ts +0 -5
  22. package/core/src/schemas/mutations/product.mutation.ts +0 -1
  23. package/core/src/schemas/queries/base.query.ts +0 -1
  24. package/core/src/schemas/queries/cart.query.ts +1 -3
  25. package/core/src/schemas/queries/identity.query.ts +1 -3
  26. package/core/src/schemas/queries/inventory.query.ts +0 -1
  27. package/core/src/schemas/queries/price.query.ts +0 -3
  28. package/core/src/schemas/queries/product.query.ts +2 -7
  29. package/core/src/schemas/queries/search.query.ts +0 -3
  30. package/examples/node/package.json +1 -5
  31. package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +97 -0
  32. package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +84 -0
  33. package/examples/node/src/basic/basic-node-setup.spec.ts +40 -0
  34. package/otel/src/index.ts +3 -0
  35. package/otel/src/trace-decorator.ts +246 -0
  36. package/package.json +2 -1
  37. package/providers/algolia/src/core/initialize.ts +11 -9
  38. package/providers/algolia/src/providers/product.provider.ts +44 -11
  39. package/providers/algolia/src/providers/search.provider.ts +47 -66
  40. package/providers/commercetools/src/core/client.ts +0 -1
  41. package/providers/commercetools/src/core/initialize.ts +28 -24
  42. package/providers/commercetools/src/providers/cart.provider.ts +58 -89
  43. package/providers/commercetools/src/providers/identity.provider.ts +34 -50
  44. package/providers/commercetools/src/providers/inventory.provider.ts +16 -38
  45. package/providers/commercetools/src/providers/price.provider.ts +30 -35
  46. package/providers/commercetools/src/providers/product.provider.ts +48 -38
  47. package/providers/commercetools/src/providers/search.provider.ts +32 -47
  48. package/providers/commercetools/src/schema/capabilities.schema.ts +1 -1
  49. package/providers/fake/package.json +1 -0
  50. package/providers/fake/src/core/initialize.ts +17 -14
  51. package/providers/fake/src/index.ts +4 -0
  52. package/providers/fake/src/providers/analytics.provider.ts +19 -0
  53. package/providers/fake/src/providers/cart.provider.ts +107 -0
  54. package/providers/fake/src/providers/identity.provider.ts +78 -67
  55. package/providers/fake/src/providers/inventory.provider.ts +54 -0
  56. package/providers/fake/src/providers/price.provider.ts +60 -0
  57. package/providers/fake/src/providers/product.provider.ts +53 -49
  58. package/providers/fake/src/providers/search.provider.ts +15 -33
  59. package/providers/posthog/src/core/initialize.ts +6 -4
  60. package/trpc/__mocks__/superjson.js +25 -0
  61. package/trpc/jest.config.ts +14 -0
  62. package/trpc/package.json +2 -1
  63. package/trpc/src/client.ts +176 -0
  64. package/trpc/src/index.ts +35 -62
  65. package/trpc/src/integration.spec.ts +216 -0
  66. package/trpc/src/server.ts +123 -0
  67. package/trpc/src/transparent-client.spec.ts +160 -0
  68. package/trpc/src/types.ts +142 -0
  69. package/trpc/tsconfig.json +3 -0
  70. package/trpc/tsconfig.lib.json +2 -1
  71. package/trpc/tsconfig.spec.json +15 -0
  72. package/tsconfig.base.json +0 -2
  73. package/core/src/cache/caching-strategy.ts +0 -25
  74. package/examples/angular/e2e/example.spec.ts +0 -9
  75. package/examples/angular/eslint.config.mjs +0 -41
  76. package/examples/angular/playwright.config.ts +0 -38
  77. package/examples/angular/project.json +0 -86
  78. package/examples/angular/public/favicon.ico +0 -0
  79. package/examples/angular/src/app/app.component.html +0 -6
  80. package/examples/angular/src/app/app.component.scss +0 -22
  81. package/examples/angular/src/app/app.component.ts +0 -14
  82. package/examples/angular/src/app/app.config.ts +0 -16
  83. package/examples/angular/src/app/app.routes.ts +0 -25
  84. package/examples/angular/src/app/cart/cart.component.html +0 -4
  85. package/examples/angular/src/app/cart/cart.component.scss +0 -14
  86. package/examples/angular/src/app/cart/cart.component.ts +0 -73
  87. package/examples/angular/src/app/identity/identity.component.html +0 -6
  88. package/examples/angular/src/app/identity/identity.component.scss +0 -18
  89. package/examples/angular/src/app/identity/identity.component.ts +0 -49
  90. package/examples/angular/src/app/product/product.component.html +0 -14
  91. package/examples/angular/src/app/product/product.component.scss +0 -11
  92. package/examples/angular/src/app/product/product.component.ts +0 -42
  93. package/examples/angular/src/app/search/search.component.html +0 -35
  94. package/examples/angular/src/app/search/search.component.scss +0 -129
  95. package/examples/angular/src/app/search/search.component.ts +0 -50
  96. package/examples/angular/src/app/services/product.service.ts +0 -35
  97. package/examples/angular/src/app/services/search.service.ts +0 -48
  98. package/examples/angular/src/app/services/trpc.client.ts +0 -27
  99. package/examples/angular/src/index.html +0 -13
  100. package/examples/angular/src/main.ts +0 -7
  101. package/examples/angular/src/styles.scss +0 -17
  102. package/examples/angular/src/test-setup.ts +0 -6
  103. package/examples/angular/tsconfig.app.json +0 -10
  104. package/examples/angular/tsconfig.editor.json +0 -6
  105. package/examples/angular/tsconfig.json +0 -32
  106. package/examples/node/src/initialize-algolia.spec.ts +0 -29
  107. package/examples/node/src/initialize-commercetools.spec.ts +0 -31
  108. package/examples/node/src/initialize-extended-providers.spec.ts +0 -38
  109. package/examples/node/src/initialize-mixed-providers.spec.ts +0 -36
  110. package/examples/node/src/providers/custom-algolia-product.provider.ts +0 -18
  111. package/examples/node/src/schemas/custom-product.schema.ts +0 -8
  112. package/examples/trpc-node/.env.example +0 -52
  113. package/examples/trpc-node/eslint.config.mjs +0 -3
  114. package/examples/trpc-node/project.json +0 -61
  115. package/examples/trpc-node/src/assets/.gitkeep +0 -0
  116. package/examples/trpc-node/src/main.ts +0 -59
  117. package/examples/trpc-node/src/router-instance.ts +0 -52
  118. package/examples/trpc-node/tsconfig.app.json +0 -9
  119. package/examples/trpc-node/tsconfig.json +0 -13
@@ -0,0 +1,246 @@
1
+ import { SpanKind, SpanStatusCode } from '@opentelemetry/api';
2
+ import { getTracer } from './tracer';
3
+
4
+ /**
5
+ * Options for the @traced decorator
6
+ */
7
+ export interface TracedOptions {
8
+ /** Whether to capture function arguments as span attributes (default: true) */
9
+ captureArgs?: boolean;
10
+ /** Whether to capture the return value as a span attribute (default: true) */
11
+ captureResult?: boolean;
12
+ /** Custom span name to use instead of the function name */
13
+ spanName?: string;
14
+ /** OpenTelemetry SpanKind (default: INTERNAL) */
15
+ spanKind?: SpanKind;
16
+ }
17
+
18
+ /**
19
+ * Safely serializes a value for use as a span attribute
20
+ * Handles circular references and large objects
21
+ */
22
+ function safeSerialize(value: unknown, maxDepth = 3, currentDepth = 0): string {
23
+ if (currentDepth >= maxDepth) {
24
+ return '[Max depth reached]';
25
+ }
26
+
27
+ if (value === null) return 'null';
28
+ if (value === undefined) return 'undefined';
29
+
30
+ const type = typeof value;
31
+
32
+ if (type === 'string' || type === 'number' || type === 'boolean') {
33
+ return String(value);
34
+ }
35
+
36
+ if (type === 'function') {
37
+ return `[Function: ${(value as { name?: string }).name || 'anonymous'}]`;
38
+ }
39
+
40
+ if (value instanceof Date) {
41
+ return value.toISOString();
42
+ }
43
+
44
+ if (value instanceof Error) {
45
+ return `[Error: ${value.message}]`;
46
+ }
47
+
48
+ if (Array.isArray(value)) {
49
+ if (value.length > 10) {
50
+ return `[Array(${value.length})]`;
51
+ }
52
+ try {
53
+ return JSON.stringify(value.map(item =>
54
+ safeSerialize(item, maxDepth, currentDepth + 1)
55
+ ));
56
+ } catch {
57
+ return '[Array - circular reference]';
58
+ }
59
+ }
60
+
61
+ if (type === 'object') {
62
+ try {
63
+ const keys = Object.keys(value as object);
64
+ if (keys.length > 20) {
65
+ return `[Object with ${keys.length} keys]`;
66
+ }
67
+ const simplified: Record<string, unknown> = {};
68
+ for (const key of keys.slice(0, 10)) {
69
+ simplified[key] = safeSerialize(
70
+ (value as Record<string, unknown>)[key],
71
+ maxDepth,
72
+ currentDepth + 1
73
+ );
74
+ }
75
+ return JSON.stringify(simplified);
76
+ } catch {
77
+ return '[Object - circular reference]';
78
+ }
79
+ }
80
+
81
+ return String(value);
82
+ }
83
+
84
+ /**
85
+ * TypeScript decorator for tracing function execution
86
+ * Automatically creates OpenTelemetry spans for decorated methods
87
+ * Supports both Stage 2 (legacy) and Stage 3 decorator syntax
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * class MyService {
92
+ * @traced()
93
+ * async fetchData(id: string): Promise<Data> {
94
+ * // method implementation
95
+ * }
96
+ *
97
+ * @traced({ spanName: 'custom-operation', captureResult: false })
98
+ * processData(data: Data): void {
99
+ * // method implementation
100
+ * }
101
+ * }
102
+ * ```
103
+ */
104
+ export function traced(options: TracedOptions = {}): any {
105
+ const {
106
+ captureArgs = true,
107
+ captureResult = true,
108
+ spanName,
109
+ spanKind = SpanKind.INTERNAL
110
+ } = options;
111
+
112
+ // Stage 2 (legacy) decorator
113
+ return function (
114
+ target: any,
115
+ propertyKey?: string | symbol,
116
+ descriptor?: PropertyDescriptor
117
+ ): any {
118
+ // Handle Stage 3 decorator (when called with context)
119
+ if (typeof propertyKey === 'object' && propertyKey && 'kind' in propertyKey) {
120
+ const context = propertyKey as any;
121
+ const originalMethod = target;
122
+ const methodName = String(context.name);
123
+
124
+ return createTracedMethod(originalMethod, methodName, {
125
+ captureArgs,
126
+ captureResult,
127
+ spanName,
128
+ spanKind
129
+ });
130
+ }
131
+
132
+ // Handle Stage 2 decorator
133
+ if (descriptor && typeof descriptor.value === 'function') {
134
+ const originalMethod = descriptor.value;
135
+ const methodName = String(propertyKey);
136
+
137
+ descriptor.value = createTracedMethod(originalMethod, methodName, {
138
+ captureArgs,
139
+ captureResult,
140
+ spanName,
141
+ spanKind
142
+ });
143
+
144
+ return descriptor;
145
+ }
146
+
147
+ return target;
148
+ };
149
+ }
150
+
151
+ function createTracedMethod(
152
+ originalMethod: (...args: any[]) => any,
153
+ methodName: string,
154
+ options: {
155
+ captureArgs: boolean;
156
+ captureResult: boolean;
157
+ spanName?: string;
158
+ spanKind: SpanKind;
159
+ }
160
+ ): any {
161
+ const { captureArgs, captureResult, spanName, spanKind } = options;
162
+
163
+ function tracedMethod(this: any, ...args: any[]): any {
164
+ const tracer = getTracer();
165
+ const className = this?.constructor?.name || 'Unknown';
166
+ const effectiveSpanName = spanName || `${className}.${methodName}`;
167
+
168
+ // Start the span
169
+ const span = tracer.startSpan(effectiveSpanName, {
170
+ kind: spanKind,
171
+ attributes: {
172
+ 'function.name': methodName,
173
+ 'function.class': className,
174
+ }
175
+ });
176
+
177
+ // Capture arguments if enabled
178
+ if (captureArgs && args.length > 0) {
179
+ args.forEach((arg, index) => {
180
+ try {
181
+ span.setAttribute(`function.args.${index}`, safeSerialize(arg));
182
+ } catch {
183
+ span.setAttribute(`function.args.${index}`, '[Serialization error]');
184
+ }
185
+ });
186
+ span.setAttribute('function.args.count', args.length);
187
+ }
188
+
189
+ // Helper function to finalize span with result
190
+ const finalizeSpan = (result: unknown, isError = false) => {
191
+ if (!isError && captureResult && result !== undefined) {
192
+ try {
193
+ span.setAttribute('function.result', safeSerialize(result));
194
+ } catch {
195
+ span.setAttribute('function.result', '[Serialization error]');
196
+ }
197
+ }
198
+
199
+ if (isError) {
200
+ span.setStatus({
201
+ code: SpanStatusCode.ERROR,
202
+ message: result instanceof Error ? result.message : String(result)
203
+ });
204
+ if (result instanceof Error) {
205
+ span.recordException(result);
206
+ }
207
+ } else {
208
+ span.setStatus({ code: SpanStatusCode.OK });
209
+ }
210
+
211
+ span.end();
212
+ };
213
+
214
+ try {
215
+ const result = originalMethod.apply(this, args);
216
+
217
+ // Handle async functions
218
+ if (result instanceof Promise) {
219
+ return result
220
+ .then((value) => {
221
+ finalizeSpan(value);
222
+ return value;
223
+ })
224
+ .catch((error) => {
225
+ finalizeSpan(error, true);
226
+ throw error;
227
+ });
228
+ }
229
+
230
+ // Handle sync functions
231
+ finalizeSpan(result);
232
+ return result;
233
+ } catch (error) {
234
+ finalizeSpan(error, true);
235
+ throw error;
236
+ }
237
+ }
238
+
239
+ // Preserve the original function's name and properties
240
+ Object.defineProperty(tracedMethod, 'name', {
241
+ value: methodName,
242
+ configurable: true
243
+ });
244
+
245
+ return tracedMethod;
246
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactionary/source",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "license": "MIT",
5
5
  "private": false,
6
6
  "dependencies": {
@@ -40,6 +40,7 @@
40
40
  "posthog-node": "^4.18.0",
41
41
  "react": "19.0.0",
42
42
  "react-dom": "19.0.0",
43
+ "reflect-metadata": "^0.2.2",
43
44
  "rxjs": "~7.8.0",
44
45
  "search-insights": "^2.17.3",
45
46
  "superjson": "^2.2.2",
@@ -1,4 +1,4 @@
1
- import { Client, ProductMutationSchema, ProductQuerySchema, ProductSchema, SearchMutationSchema, SearchQuerySchema } from "@reactionary/core";
1
+ import { Client, ProductSchema, Cache } from "@reactionary/core";
2
2
  import { AlgoliaProductProvider } from "../providers/product.provider";
3
3
  import { AlgoliaSearchProvider } from "../providers/search.provider";
4
4
  import { AlgoliaCapabilities } from "../schema/capabilities.schema";
@@ -6,15 +6,17 @@ import { AlgoliaConfiguration } from "../schema/configuration.schema";
6
6
  import { AlgoliaSearchResultSchema } from "../schema/search.schema";
7
7
 
8
8
  export function withAlgoliaCapabilities(configuration: AlgoliaConfiguration, capabilities: AlgoliaCapabilities) {
9
- const client: Partial<Client> = {};
9
+ return (cache: Cache) => {
10
+ const client: Partial<Client> = {};
10
11
 
11
- if (capabilities.product) {
12
- client.product = new AlgoliaProductProvider(configuration, ProductSchema, ProductQuerySchema, ProductMutationSchema);
13
- }
12
+ if (capabilities.product) {
13
+ client.product = new AlgoliaProductProvider(configuration, ProductSchema, cache);
14
+ }
14
15
 
15
- if (capabilities.search) {
16
- client.search = new AlgoliaSearchProvider(configuration, AlgoliaSearchResultSchema, SearchQuerySchema, SearchMutationSchema);
17
- }
16
+ if (capabilities.search) {
17
+ client.search = new AlgoliaSearchProvider(configuration, AlgoliaSearchResultSchema, cache);
18
+ }
18
19
 
19
- return client;
20
+ return client;
21
+ };
20
22
  }
@@ -1,25 +1,58 @@
1
- import { BaseMutation, Product, ProductMutation, ProductProvider, ProductQuery, Session } from '@reactionary/core';
1
+ import {
2
+ Product,
3
+ ProductProvider,
4
+ ProductQueryById,
5
+ ProductQueryBySlug,
6
+ Session,
7
+ Cache
8
+ } from '@reactionary/core';
2
9
  import { z } from 'zod';
3
10
  import { AlgoliaConfiguration } from '../schema/configuration.schema';
4
11
 
5
12
  export class AlgoliaProductProvider<
6
- T extends Product = Product,
7
- Q extends ProductQuery = ProductQuery,
8
- M extends ProductMutation = ProductMutation
9
- > extends ProductProvider<T, Q, M> {
13
+ T extends Product = Product
14
+ > extends ProductProvider<T> {
10
15
  protected config: AlgoliaConfiguration;
11
16
 
12
- constructor(config: AlgoliaConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>) {
13
- super(schema, querySchema, mutationSchema);
17
+ constructor(config: AlgoliaConfiguration, schema: z.ZodType<T>, cache: Cache) {
18
+ super(schema, cache);
14
19
 
15
20
  this.config = config;
16
21
  }
17
22
 
18
- protected override async fetch(queries: Q[], session: Session): Promise<T[]> {
19
- return [];
23
+ public override async getById(
24
+ payload: ProductQueryById,
25
+ _session: Session
26
+ ): Promise<T> {
27
+ // TODO: Implement Algolia product fetch by ID
28
+ const result = this.newModel();
29
+ result.identifier = { key: payload.id };
30
+ result.name = `Algolia Product ${payload.id}`;
31
+ result.slug = payload.id;
32
+ result.description = 'Product from Algolia';
33
+ result.meta = {
34
+ cache: { hit: false, key: payload.id },
35
+ placeholder: true
36
+ };
37
+
38
+ return this.assert(result);
20
39
  }
21
40
 
22
- protected override process(mutation: BaseMutation[], session: Session): Promise<T> {
23
- throw new Error('Method not implemented.');
41
+ public override async getBySlug(
42
+ payload: ProductQueryBySlug,
43
+ _session: Session
44
+ ): Promise<T> {
45
+ // TODO: Implement Algolia product fetch by slug
46
+ const result = this.newModel();
47
+ result.identifier = { key: payload.slug };
48
+ result.name = `Algolia Product ${payload.slug}`;
49
+ result.slug = payload.slug;
50
+ result.description = 'Product from Algolia';
51
+ result.meta = {
52
+ cache: { hit: false, key: payload.slug },
53
+ placeholder: true
54
+ };
55
+
56
+ return this.assert(result);
24
57
  }
25
58
  }
@@ -1,125 +1,106 @@
1
1
  import {
2
- SearchIdentifier,
3
- SearchMutation,
4
2
  SearchProvider,
5
- SearchQuery,
3
+ SearchQueryByTerm,
6
4
  SearchResult,
7
- SearchResultFacetSchema,
8
- SearchResultFacetValueSchema,
5
+ SearchResultFacet,
6
+ SearchResultProduct,
9
7
  Session,
8
+ Cache,
10
9
  } from '@reactionary/core';
11
10
  import { algoliasearch } from 'algoliasearch';
12
11
  import { z } from 'zod';
13
12
  import { AlgoliaConfiguration } from '../schema/configuration.schema';
14
13
 
15
14
  export class AlgoliaSearchProvider<
16
- T extends SearchResult = SearchResult,
17
- Q extends SearchQuery = SearchQuery,
18
- M extends SearchMutation = SearchMutation
19
- > extends SearchProvider<T, Q, M> {
15
+ T extends SearchResult = SearchResult
16
+ > extends SearchProvider<T> {
20
17
  protected config: AlgoliaConfiguration;
21
18
 
22
- constructor(config: AlgoliaConfiguration, schema: z.ZodType<T>, querySchema: z.ZodType<Q, Q>, mutationSchema: z.ZodType<M, M>) {
23
- super(schema, querySchema, mutationSchema);
19
+ constructor(config: AlgoliaConfiguration, schema: z.ZodType<T>, cache: Cache) {
20
+ super(schema, cache);
24
21
 
25
22
  this.config = config;
26
23
  }
27
24
 
28
- protected override async fetch(queries: Q[], session: Session): Promise<T[]> {
29
- const results = [];
30
-
31
- for (const query of queries) {
32
- const result = await this.get(query.search);
33
-
34
- results.push(result);
35
- }
36
-
37
- return results;
38
- }
39
-
40
- protected override process(mutations: M[], session: Session): Promise<T> {
41
- throw new Error('Method not implemented.');
42
- }
43
-
44
- protected async get(identifier: SearchIdentifier): Promise<T> {
25
+ public override async queryByTerm(
26
+ payload: SearchQueryByTerm,
27
+ _session: Session
28
+ ): Promise<SearchResult> {
45
29
  const client = algoliasearch(this.config.appId, this.config.apiKey);
46
30
  const remote = await client.search<unknown>({
47
31
  requests: [
48
32
  {
49
33
  indexName: this.config.indexName,
50
- query: identifier.term,
51
- page: identifier.page,
52
- hitsPerPage: identifier.pageSize,
34
+ query: payload.search.term,
35
+ page: payload.search.page,
36
+ hitsPerPage: payload.search.pageSize,
53
37
  facets: ['*'],
54
38
  analytics: true,
55
39
  clickAnalytics: true,
56
- facetFilters: identifier.facets.map(
40
+ facetFilters: payload.search.facets.map(
57
41
  (x) => `${encodeURIComponent(x.facet.key)}:${x.key}`
58
42
  ),
59
43
  },
60
44
  ],
61
45
  });
62
46
 
63
- const parsed = this.parse(remote, identifier);
64
-
65
- return parsed;
47
+ return this.parseSearchResult(remote, payload);
66
48
  }
67
49
 
68
- protected parse(remote: any, query: SearchIdentifier): T {
50
+ protected parseSearchResult(remote: unknown, payload: SearchQueryByTerm): T {
69
51
  const result = this.newModel();
52
+ const remoteData = remote as { results: Array<{ facets: Record<string, Record<string, number>>; hits: Array<{ objectID: string; slug?: string; name?: string; image?: string }>; index: string; queryID: string; nbPages: number }> };
53
+ const remoteProducts = remoteData.results[0];
70
54
 
71
- const remoteProducts = remote.results[0];
72
-
55
+ // Parse facets
73
56
  for (const id in remoteProducts.facets) {
74
57
  const f = remoteProducts.facets[id];
75
58
 
76
- const facet = SearchResultFacetSchema.parse({});
77
- facet.identifier.key = id;
78
- facet.name = id;
59
+ const facet = {
60
+ identifier: { key: id },
61
+ name: id,
62
+ values: []
63
+ } as SearchResultFacet;
79
64
 
80
65
  for (const vid in f) {
81
66
  const fv = f[vid];
82
-
83
- const facetValue = SearchResultFacetValueSchema.parse({});
84
- facetValue.count = fv;
85
- facetValue.name = vid;
86
- facetValue.identifier.key = vid;
87
- facetValue.identifier.facet = facet.identifier;
88
-
89
- if (
90
- query.facets.find(
91
- (x) =>
92
- x.facet.key == facetValue.identifier.facet.key &&
93
- x.key == facetValue.identifier.key
94
- )
95
- ) {
96
- facetValue.active = true;
97
- }
98
-
99
- facet.values.push(facetValue);
67
+ const isActive = payload.search.facets.find(
68
+ (x) => x.facet.key === id && x.key === vid
69
+ );
70
+
71
+ facet.values.push({
72
+ identifier: { key: vid, facet: { key: id } },
73
+ count: fv,
74
+ name: vid,
75
+ active: !!isActive
76
+ });
100
77
  }
101
78
 
102
79
  result.facets.push(facet);
103
80
  }
104
81
 
82
+ // Parse products
105
83
  for (const p of remoteProducts.hits) {
106
84
  result.products.push({
107
- identifier: {
108
- key: p.objectID,
109
- },
85
+ identifier: { key: p.objectID },
110
86
  slug: p.slug,
111
87
  name: p.name,
112
- image: p.image,
113
- });
88
+ image: p.image
89
+ } as SearchResultProduct);
114
90
  }
115
91
 
92
+ // Set result metadata
116
93
  result.identifier = {
117
- ...query,
94
+ ...payload.search,
118
95
  index: remoteProducts.index,
119
96
  key: remoteProducts.queryID
120
97
  };
121
98
  result.pages = remoteProducts.nbPages;
99
+ result.meta = {
100
+ cache: { hit: false, key: payload.search.term },
101
+ placeholder: false
102
+ };
122
103
 
123
- return result;
104
+ return this.assert(result);
124
105
  }
125
106
  }
@@ -1,7 +1,6 @@
1
1
  import { ClientBuilder } from '@commercetools/ts-client';
2
2
  import { createApiBuilderFromCtpClient } from '@commercetools/platform-sdk';
3
3
  import { CommercetoolsConfiguration } from '../schema/configuration.schema';
4
- import { Session } from '@reactionary/core';
5
4
 
6
5
  const ANONYMOUS_SCOPES = ['view_published_products', 'manage_shopping_lists', 'view_shipping_methods', 'manage_customers', 'view_product_selections', 'view_categories', 'view_project_settings', 'manage_order_edits', 'view_sessions', 'view_standalone_prices', 'manage_orders', 'view_tax_categories', 'view_cart_discounts', 'view_discount_codes', 'create_anonymous_token', 'manage_sessions', 'view_products', 'view_types'];
7
6
  const GUEST_SCOPES = [...ANONYMOUS_SCOPES];
@@ -1,4 +1,4 @@
1
- import { CartMutationSchema, CartQuerySchema, CartSchema, Client, IdentityMutationSchema, IdentityQuerySchema, IdentitySchema, InventoryQuerySchema, InventorySchema, PriceMutationSchema, PriceQuerySchema, PriceSchema, ProductMutationSchema, ProductQuerySchema, ProductSchema, SearchMutationSchema, SearchQuerySchema, SearchResultSchema } from "@reactionary/core";
1
+ import { CartSchema, Client, IdentitySchema, InventorySchema, PriceSchema, ProductSchema, SearchResultSchema, Cache } from "@reactionary/core";
2
2
  import { CommercetoolsCapabilities } from "../schema/capabilities.schema";
3
3
  import { CommercetoolsSearchProvider } from "../providers/search.provider";
4
4
  import { CommercetoolsProductProvider } from '../providers/product.provider';
@@ -7,34 +7,38 @@ import { CommercetoolsIdentityProvider } from "../providers/identity.provider";
7
7
  import { CommercetoolsCartProvider } from "../providers/cart.provider";
8
8
  import { CommercetoolsInventoryProvider } from "../providers/inventory.provider";
9
9
  import { CommercetoolsPriceProvider } from "../providers/price.provider";
10
- import { InventoryMutationSchema } from "core/src/schemas/mutations/inventory.mutation";
11
10
 
12
- export function withCommercetoolsCapabilities(configuration: CommercetoolsConfiguration, capabilities: CommercetoolsCapabilities) {
13
- const client: Partial<Client> = {};
11
+ export function withCommercetoolsCapabilities(
12
+ configuration: CommercetoolsConfiguration,
13
+ capabilities: CommercetoolsCapabilities
14
+ ) {
15
+ return (cache: Cache) => {
16
+ const client: Partial<Client> = {};
14
17
 
15
- if (capabilities.product) {
16
- client.product = new CommercetoolsProductProvider(configuration, ProductSchema, ProductQuerySchema, ProductMutationSchema);
17
- }
18
+ if (capabilities.product) {
19
+ client.product = new CommercetoolsProductProvider(configuration, ProductSchema, cache);
20
+ }
18
21
 
19
- if (capabilities.search) {
20
- client.search = new CommercetoolsSearchProvider(configuration, SearchResultSchema, SearchQuerySchema, SearchMutationSchema);
21
- }
22
+ if (capabilities.search) {
23
+ client.search = new CommercetoolsSearchProvider(configuration, SearchResultSchema, cache);
24
+ }
22
25
 
23
- if (capabilities.identity) {
24
- client.identity = new CommercetoolsIdentityProvider(configuration, IdentitySchema, IdentityQuerySchema, IdentityMutationSchema);
25
- }
26
+ if (capabilities.identity) {
27
+ client.identity = new CommercetoolsIdentityProvider(configuration, IdentitySchema, cache);
28
+ }
26
29
 
27
- if (capabilities.cart) {
28
- client.cart = new CommercetoolsCartProvider(configuration, CartSchema, CartQuerySchema, CartMutationSchema);
29
- }
30
+ if (capabilities.cart) {
31
+ client.cart = new CommercetoolsCartProvider(configuration, CartSchema, cache);
32
+ }
30
33
 
31
- if (capabilities.inventory) {
32
- client.inventory = new CommercetoolsInventoryProvider(configuration, InventorySchema, InventoryQuerySchema, InventoryMutationSchema);
33
- }
34
+ if (capabilities.inventory) {
35
+ client.inventory = new CommercetoolsInventoryProvider(configuration, InventorySchema, cache);
36
+ }
34
37
 
35
- if (capabilities.price) {
36
- client.price = new CommercetoolsPriceProvider(configuration, PriceSchema, PriceQuerySchema, PriceMutationSchema);
37
- }
38
+ if (capabilities.price) {
39
+ client.price = new CommercetoolsPriceProvider(configuration, PriceSchema, cache);
40
+ }
38
41
 
39
- return client;
40
- }
42
+ return client;
43
+ };
44
+ }