@reactionary/source 0.0.31 → 0.0.33

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 (110) hide show
  1. package/core/package.json +3 -1
  2. package/core/src/client/client-builder.ts +63 -0
  3. package/core/src/decorators/trpc.decorators.ts +144 -0
  4. package/core/src/index.ts +3 -0
  5. package/core/src/providers/analytics.provider.ts +2 -28
  6. package/core/src/providers/base.provider.ts +7 -135
  7. package/core/src/providers/cart.provider.ts +9 -22
  8. package/core/src/providers/identity.provider.ts +7 -21
  9. package/core/src/providers/inventory.provider.ts +4 -18
  10. package/core/src/providers/price.provider.ts +5 -22
  11. package/core/src/providers/product.provider.ts +5 -28
  12. package/core/src/providers/search.provider.ts +5 -22
  13. package/core/src/schemas/mutations/base.mutation.ts +0 -1
  14. package/core/src/schemas/mutations/cart.mutation.ts +0 -6
  15. package/core/src/schemas/mutations/identity.mutation.ts +0 -5
  16. package/core/src/schemas/queries/base.query.ts +0 -1
  17. package/core/src/schemas/queries/cart.query.ts +1 -3
  18. package/core/src/schemas/queries/identity.query.ts +1 -3
  19. package/core/src/schemas/queries/inventory.query.ts +0 -1
  20. package/core/src/schemas/queries/price.query.ts +0 -3
  21. package/core/src/schemas/queries/product.query.ts +2 -7
  22. package/core/src/schemas/queries/search.query.ts +0 -3
  23. package/examples/node/package.json +1 -5
  24. package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +97 -0
  25. package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +84 -0
  26. package/examples/node/src/basic/basic-node-setup.spec.ts +40 -0
  27. package/otel/src/index.ts +3 -0
  28. package/otel/src/trace-decorator.ts +246 -0
  29. package/package.json +2 -1
  30. package/providers/algolia/src/core/initialize.ts +3 -3
  31. package/providers/algolia/src/providers/product.provider.ts +44 -11
  32. package/providers/algolia/src/providers/search.provider.ts +47 -66
  33. package/providers/commercetools/src/core/client.ts +0 -1
  34. package/providers/commercetools/src/core/initialize.ts +8 -9
  35. package/providers/commercetools/src/providers/cart.provider.ts +58 -90
  36. package/providers/commercetools/src/providers/identity.provider.ts +34 -51
  37. package/providers/commercetools/src/providers/inventory.provider.ts +16 -29
  38. package/providers/commercetools/src/providers/price.provider.ts +30 -35
  39. package/providers/commercetools/src/providers/product.provider.ts +48 -38
  40. package/providers/commercetools/src/providers/search.provider.ts +32 -47
  41. package/providers/commercetools/src/schema/capabilities.schema.ts +1 -1
  42. package/providers/fake/package.json +3 -0
  43. package/providers/fake/src/core/initialize.ts +12 -11
  44. package/providers/fake/src/index.ts +4 -0
  45. package/providers/fake/src/providers/analytics.provider.ts +19 -0
  46. package/providers/fake/src/providers/cart.provider.ts +107 -0
  47. package/providers/fake/src/providers/identity.provider.ts +78 -68
  48. package/providers/fake/src/providers/inventory.provider.ts +54 -0
  49. package/providers/fake/src/providers/price.provider.ts +60 -0
  50. package/providers/fake/src/providers/product.provider.ts +53 -49
  51. package/providers/fake/src/providers/search.provider.ts +15 -33
  52. package/trpc/__mocks__/superjson.js +25 -0
  53. package/trpc/jest.config.ts +14 -0
  54. package/trpc/package.json +2 -1
  55. package/trpc/src/client.ts +176 -0
  56. package/trpc/src/index.ts +35 -62
  57. package/trpc/src/integration.spec.ts +216 -0
  58. package/trpc/src/server.ts +123 -0
  59. package/trpc/src/transparent-client.spec.ts +160 -0
  60. package/trpc/src/types.ts +142 -0
  61. package/trpc/tsconfig.json +3 -0
  62. package/trpc/tsconfig.lib.json +2 -1
  63. package/trpc/tsconfig.spec.json +15 -0
  64. package/tsconfig.base.json +0 -2
  65. package/examples/angular/e2e/example.spec.ts +0 -9
  66. package/examples/angular/eslint.config.mjs +0 -41
  67. package/examples/angular/playwright.config.ts +0 -38
  68. package/examples/angular/project.json +0 -86
  69. package/examples/angular/public/favicon.ico +0 -0
  70. package/examples/angular/src/app/app.component.html +0 -6
  71. package/examples/angular/src/app/app.component.scss +0 -22
  72. package/examples/angular/src/app/app.component.ts +0 -14
  73. package/examples/angular/src/app/app.config.ts +0 -16
  74. package/examples/angular/src/app/app.routes.ts +0 -25
  75. package/examples/angular/src/app/cart/cart.component.html +0 -4
  76. package/examples/angular/src/app/cart/cart.component.scss +0 -14
  77. package/examples/angular/src/app/cart/cart.component.ts +0 -73
  78. package/examples/angular/src/app/identity/identity.component.html +0 -6
  79. package/examples/angular/src/app/identity/identity.component.scss +0 -18
  80. package/examples/angular/src/app/identity/identity.component.ts +0 -49
  81. package/examples/angular/src/app/product/product.component.html +0 -14
  82. package/examples/angular/src/app/product/product.component.scss +0 -11
  83. package/examples/angular/src/app/product/product.component.ts +0 -42
  84. package/examples/angular/src/app/search/search.component.html +0 -35
  85. package/examples/angular/src/app/search/search.component.scss +0 -129
  86. package/examples/angular/src/app/search/search.component.ts +0 -50
  87. package/examples/angular/src/app/services/product.service.ts +0 -35
  88. package/examples/angular/src/app/services/search.service.ts +0 -48
  89. package/examples/angular/src/app/services/trpc.client.ts +0 -27
  90. package/examples/angular/src/index.html +0 -13
  91. package/examples/angular/src/main.ts +0 -7
  92. package/examples/angular/src/styles.scss +0 -17
  93. package/examples/angular/src/test-setup.ts +0 -6
  94. package/examples/angular/tsconfig.app.json +0 -10
  95. package/examples/angular/tsconfig.editor.json +0 -6
  96. package/examples/angular/tsconfig.json +0 -32
  97. package/examples/node/src/initialize-algolia.spec.ts +0 -29
  98. package/examples/node/src/initialize-commercetools.spec.ts +0 -31
  99. package/examples/node/src/initialize-extended-providers.spec.ts +0 -38
  100. package/examples/node/src/initialize-mixed-providers.spec.ts +0 -36
  101. package/examples/node/src/providers/custom-algolia-product.provider.ts +0 -18
  102. package/examples/node/src/schemas/custom-product.schema.ts +0 -8
  103. package/examples/trpc-node/.env.example +0 -52
  104. package/examples/trpc-node/eslint.config.mjs +0 -3
  105. package/examples/trpc-node/project.json +0 -61
  106. package/examples/trpc-node/src/assets/.gitkeep +0 -0
  107. package/examples/trpc-node/src/main.ts +0 -59
  108. package/examples/trpc-node/src/router-instance.ts +0 -54
  109. package/examples/trpc-node/tsconfig.app.json +0 -9
  110. package/examples/trpc-node/tsconfig.json +0 -13
package/core/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@reactionary/core",
3
3
  "version": "0.0.1",
4
+ "main": "./index.js",
5
+ "types": "./src/index.d.ts",
4
6
  "dependencies": {
5
7
  "zod": "4.0.0-beta.20250430T185432",
6
8
  "@upstash/redis": "^1.34.9",
7
- "@reactionary/otel": "0.0.1"
9
+ "reflect-metadata": "0.2.2"
8
10
  }
9
11
  }
@@ -0,0 +1,63 @@
1
+ import { Cache } from "../cache/cache.interface";
2
+ import { NoOpCache } from "../cache/noop-cache";
3
+ import { Client } from "./client";
4
+ import { AnalyticsProvider } from "../providers/analytics.provider";
5
+
6
+ type CapabilityFactory<T> = (cache: Cache) => T;
7
+
8
+ type MergeCapabilities<Acc, New> = Omit<Acc, keyof New> & New;
9
+
10
+ export class ClientBuilder<TClient = object> {
11
+ private factories: Array<CapabilityFactory<Partial<Client>>> = [];
12
+ private cache: Cache | undefined;
13
+
14
+ withCapability<TNew extends Partial<Client>>(
15
+ factory: CapabilityFactory<TNew>
16
+ ): ClientBuilder<MergeCapabilities<TClient, TNew>> {
17
+ const newBuilder = new ClientBuilder<MergeCapabilities<TClient, TNew>>();
18
+ newBuilder.factories = [...this.factories, factory];
19
+ newBuilder.cache = this.cache;
20
+ return newBuilder;
21
+ }
22
+
23
+ withCache(cache: Cache): ClientBuilder<TClient> {
24
+ const newBuilder = new ClientBuilder<TClient>();
25
+ newBuilder.factories = [...this.factories];
26
+ newBuilder.cache = cache;
27
+ return newBuilder;
28
+ }
29
+
30
+ build(): TClient & { cache: Cache } {
31
+ let client = {} as TClient;
32
+
33
+ // Use provided cache or default to NoOpCache
34
+ const sharedCache = this.cache || new NoOpCache();
35
+
36
+ const mergedAnalytics: AnalyticsProvider[] = [];
37
+
38
+ for (const factory of this.factories) {
39
+ const provider = factory(sharedCache);
40
+ client = {
41
+ ...client,
42
+ ...provider
43
+ };
44
+
45
+ if (provider.analytics) {
46
+ mergedAnalytics.push(...provider.analytics);
47
+ }
48
+ }
49
+
50
+ // Add merged analytics if any were collected
51
+ if (mergedAnalytics.length > 0) {
52
+ (client as Record<string, unknown>)['analytics'] = mergedAnalytics;
53
+ }
54
+
55
+ // Add cache to complete the client
56
+ const completeClient = {
57
+ ...client,
58
+ cache: sharedCache
59
+ } as TClient & { cache: Cache };
60
+
61
+ return completeClient;
62
+ }
63
+ }
@@ -0,0 +1,144 @@
1
+ import 'reflect-metadata';
2
+
3
+ /**
4
+ * Metadata keys for TRPC method decorators
5
+ */
6
+ export const TRPC_QUERY_METADATA_KEY = Symbol('trpc:query');
7
+ export const TRPC_MUTATION_METADATA_KEY = Symbol('trpc:mutation');
8
+
9
+
10
+
11
+ /**
12
+ * Options for TRPC method decorators
13
+ */
14
+ export interface TRPCMethodOptions {
15
+ /** Custom name for the TRPC procedure (defaults to method name) */
16
+ name?: string;
17
+ /** Description for documentation purposes */
18
+ description?: string;
19
+ }
20
+
21
+ /**
22
+ * Decorator to mark a provider method as a TRPC query procedure
23
+ * Query procedures are read-only operations (GET-like)
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * class ProductProvider extends BaseProvider {
28
+ * @trpcQuery()
29
+ * async getById(payload: ProductQueryById, session: Session): Promise<Product> {
30
+ * // implementation
31
+ * }
32
+ *
33
+ * @trpcQuery({ name: 'findBySlug', description: 'Find product by URL slug' })
34
+ * async getBySlug(payload: ProductQueryBySlug, session: Session): Promise<Product> {
35
+ * // implementation
36
+ * }
37
+ * }
38
+ * ```
39
+ */
40
+ export function trpcQuery(options: TRPCMethodOptions = {}): MethodDecorator {
41
+ return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
42
+ // Store metadata about this method being a TRPC query
43
+ const metadata = {
44
+ methodName: String(propertyKey),
45
+ isQuery: true,
46
+ isMutation: false,
47
+ options
48
+ };
49
+
50
+ // Store on the prototype so it's inherited
51
+ Reflect.defineMetadata(TRPC_QUERY_METADATA_KEY, metadata, target, propertyKey);
52
+
53
+ // Also store a list of all TRPC methods on the class
54
+ const existingMethods = Reflect.getMetadata(TRPC_QUERY_METADATA_KEY, target.constructor) || [];
55
+ existingMethods.push({ propertyKey, metadata });
56
+ Reflect.defineMetadata(TRPC_QUERY_METADATA_KEY, existingMethods, target.constructor);
57
+ };
58
+ }
59
+
60
+ /**
61
+ * Decorator to mark a provider method as a TRPC mutation procedure
62
+ * Mutation procedures are write operations that modify state (POST/PUT/DELETE-like)
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * class CartProvider extends BaseProvider {
67
+ * @trpcMutation()
68
+ * async add(payload: CartAddMutation, session: Session): Promise<Cart> {
69
+ * // implementation
70
+ * }
71
+ *
72
+ * @trpcMutation({ name: 'removeItem' })
73
+ * async remove(payload: CartRemoveMutation, session: Session): Promise<Cart> {
74
+ * // implementation
75
+ * }
76
+ * }
77
+ * ```
78
+ */
79
+ export function trpcMutation(options: TRPCMethodOptions = {}): MethodDecorator {
80
+ return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
81
+ // Store metadata about this method being a TRPC mutation
82
+ const metadata = {
83
+ methodName: String(propertyKey),
84
+ isQuery: false,
85
+ isMutation: true,
86
+ options
87
+ };
88
+
89
+ // Store on the prototype so it's inherited
90
+ Reflect.defineMetadata(TRPC_MUTATION_METADATA_KEY, metadata, target, propertyKey);
91
+
92
+ // Also store a list of all TRPC methods on the class
93
+ const existingMethods = Reflect.getMetadata(TRPC_MUTATION_METADATA_KEY, target.constructor) || [];
94
+ existingMethods.push({ propertyKey, metadata });
95
+ Reflect.defineMetadata(TRPC_MUTATION_METADATA_KEY, existingMethods, target.constructor);
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Get all TRPC query methods from a class or instance
101
+ */
102
+ export function getTRPCQueryMethods(target: any): Array<{ propertyKey: string | symbol; metadata: any }> {
103
+ const constructor = typeof target === 'function' ? target : target.constructor;
104
+ return Reflect.getMetadata(TRPC_QUERY_METADATA_KEY, constructor) || [];
105
+ }
106
+
107
+ /**
108
+ * Get all TRPC mutation methods from a class or instance
109
+ */
110
+ export function getTRPCMutationMethods(target: any): Array<{ propertyKey: string | symbol; metadata: any }> {
111
+ const constructor = typeof target === 'function' ? target : target.constructor;
112
+ return Reflect.getMetadata(TRPC_MUTATION_METADATA_KEY, constructor) || [];
113
+ }
114
+
115
+ /**
116
+ * Get all TRPC methods (both queries and mutations) from a class or instance
117
+ */
118
+ export function getAllTRPCMethods(target: any): Array<{ propertyKey: string | symbol; metadata: any }> {
119
+ return [
120
+ ...getTRPCQueryMethods(target),
121
+ ...getTRPCMutationMethods(target)
122
+ ];
123
+ }
124
+
125
+ /**
126
+ * Check if a method is marked as a TRPC query
127
+ */
128
+ export function isTRPCQuery(target: any, methodName: string | symbol): boolean {
129
+ return !!Reflect.getMetadata(TRPC_QUERY_METADATA_KEY, target, methodName);
130
+ }
131
+
132
+ /**
133
+ * Check if a method is marked as a TRPC mutation
134
+ */
135
+ export function isTRPCMutation(target: any, methodName: string | symbol): boolean {
136
+ return !!Reflect.getMetadata(TRPC_MUTATION_METADATA_KEY, target, methodName);
137
+ }
138
+
139
+ /**
140
+ * Check if a method is marked for TRPC exposure (query or mutation)
141
+ */
142
+ export function isTRPCMethod(target: any, methodName: string | symbol): boolean {
143
+ return isTRPCQuery(target, methodName) || isTRPCMutation(target, methodName);
144
+ }
package/core/src/index.ts CHANGED
@@ -4,6 +4,9 @@ export * from './cache/redis-cache';
4
4
  export * from './cache/noop-cache';
5
5
 
6
6
  export * from './client/client';
7
+ export * from './client/client-builder';
8
+
9
+ export * from './decorators/trpc.decorators';
7
10
 
8
11
  export * from './providers/analytics.provider';
9
12
  export * from './providers/base.provider';
@@ -1,33 +1,7 @@
1
1
  import { AnalyticsEvent } from '../schemas/models/analytics.model';
2
- import { AnalyticsMutation } from '../schemas/mutations/analytics.mutation';
3
- import { AnalyticsQuery } from '../schemas/queries/analytics.query';
4
2
  import { BaseProvider } from './base.provider';
5
- import { CacheEvaluation } from '../cache/cache-evaluation.interface';
6
- import { Session } from '../schemas/session.schema';
7
- import * as crypto from 'crypto';
8
3
 
9
4
  export abstract class AnalyticsProvider<
10
- T extends AnalyticsEvent = AnalyticsEvent,
11
- Q extends AnalyticsQuery = AnalyticsQuery,
12
- M extends AnalyticsMutation = AnalyticsMutation
13
- > extends BaseProvider<T, Q, M> {
14
-
15
- protected override getCacheEvaluation(query: Q, _session: Session): CacheEvaluation {
16
- const providerName = this.constructor.name.toLowerCase();
17
-
18
- // Hash analytics query parameters
19
- const relevantFields = {
20
- type: typeof query === 'object' && query !== null && 'type' in query ? (query as any).type : undefined,
21
- dateRange: typeof query === 'object' && query !== null && 'dateRange' in query ? (query as any).dateRange : undefined,
22
- filters: typeof query === 'object' && query !== null && 'filters' in query ? (query as any).filters : undefined
23
- };
24
- const analyticsHash = crypto.createHash('md5').update(JSON.stringify(relevantFields)).digest('hex').substring(0, 12);
25
- const key = `${providerName}:analytics:${analyticsHash}`;
26
-
27
- return {
28
- key,
29
- cacheDurationInSeconds: 0,
30
- canCache: false
31
- };
32
- }
5
+ T extends AnalyticsEvent = AnalyticsEvent
6
+ > extends BaseProvider<T> {
33
7
  }
@@ -1,32 +1,20 @@
1
1
  import { z } from 'zod';
2
- import { Session } from '../schemas/session.schema';
3
- import { BaseQuery } from '../schemas/queries/base.query';
4
- import { BaseMutation } from '../schemas/mutations/base.mutation';
5
2
  import { BaseModel } from '../schemas/models/base.model';
6
- import { createProviderInstrumentation } from '@reactionary/otel';
7
3
  import { Cache } from '../cache/cache.interface';
8
- import { CacheEvaluation } from '../cache/cache-evaluation.interface';
9
- import * as crypto from 'crypto';
10
4
 
11
5
  /**
12
6
  * Base capability provider, responsible for mutations (changes) and queries (fetches)
13
7
  * for a given business object domain.
14
8
  */
15
9
  export abstract class BaseProvider<
16
- T extends BaseModel = BaseModel,
17
- Q extends BaseQuery = BaseQuery,
18
- M extends BaseMutation = BaseMutation
10
+ T extends BaseModel = BaseModel
19
11
  > {
20
- protected instrumentation: ReturnType<typeof createProviderInstrumentation>;
21
12
  protected cache: Cache;
22
13
 
23
14
  constructor(
24
- public readonly schema: z.ZodType<T>,
25
- public readonly querySchema: z.ZodType<Q, Q>,
26
- public readonly mutationSchema: z.ZodType<M, M>,
15
+ public readonly schema: z.ZodType<T>,
27
16
  cache: Cache
28
17
  ) {
29
- this.instrumentation = createProviderInstrumentation(this.constructor.name);
30
18
  this.cache = cache;
31
19
  }
32
20
 
@@ -46,128 +34,12 @@ export abstract class BaseProvider<
46
34
  }
47
35
 
48
36
  /**
49
- * Retrieves a set of entities matching the list of queries. The size of
50
- * the resulting list WILL always match the size of the query list. The
51
- * result list will never contain nulls or undefined. The order
52
- * of the results will match the order of the queries.
37
+ * Handler for parsing a response from a remote provider and converting it
38
+ * into the typed domain model.
53
39
  */
54
- public async query(queries: Q[], session: Session): Promise<T[]> {
55
- return this.instrumentation.traceQuery(
56
- 'query',
57
- async (span) => {
58
- span.setAttribute('provider.query.count', queries.length);
59
-
60
- let cacheHits = 0;
61
- let cacheMisses = 0;
62
- const results: T[] = [];
40
+ protected parseSingle(_body: unknown): T {
41
+ const model = this.newModel();
63
42
 
64
- // Process each query individually for cache efficiency
65
- for (const query of queries) {
66
- let result: T | null = null;
67
-
68
- // Get cache evaluation from provider
69
- const cacheInfo = this.getCacheEvaluation(query, session);
70
-
71
- // Try cache first if caching is enabled
72
- if (cacheInfo.canCache && cacheInfo.key) {
73
- try {
74
- result = await this.cache.get(cacheInfo.key, this.schema);
75
- if (result) {
76
- cacheHits++;
77
- span.setAttribute('provider.cache.hit', true);
78
- }
79
- } catch (error) {
80
- // Cache error shouldn't break the query - log and continue
81
- console.warn(`Cache get error for ${this.constructor.name}:`, error);
82
- }
83
- }
84
-
85
- // If not in cache, fetch from source
86
- if (!result) {
87
- const singleResult = await this.fetch([query], session);
88
- result = singleResult[0];
89
- cacheMisses++;
90
-
91
- // Store in cache if caching is enabled
92
- if (result && cacheInfo.canCache && cacheInfo.key) {
93
- try {
94
- await this.cache.put(cacheInfo.key, result, cacheInfo.cacheDurationInSeconds);
95
- } catch (error) {
96
- // Cache put error shouldn't break the query - log and continue
97
- console.warn(`Cache put error for ${this.constructor.name}:`, error);
98
- }
99
- }
100
- }
101
-
102
- if (result) {
103
- this.assert(result);
104
- results.push(result);
105
- }
106
- }
107
-
108
- span.setAttribute('provider.result.count', results.length);
109
- span.setAttribute('provider.cache.hits', cacheHits);
110
- span.setAttribute('provider.cache.misses', cacheMisses);
111
- span.setAttribute('provider.cache.hit_ratio', cacheHits / (cacheHits + cacheMisses));
112
-
113
- return results;
114
- },
115
- { queryCount: queries.length }
116
- );
117
- }
118
-
119
- /**
120
- * Executes the listed mutations in order and returns the final state
121
- * resulting from that set of operations.
122
- */
123
- public async mutate(mutations: M[], session: Session): Promise<T> {
124
- return this.instrumentation.traceMutation(
125
- 'mutate',
126
- async (span) => {
127
- span.setAttribute('provider.mutation.count', mutations.length);
128
-
129
- // Perform the mutation
130
- const result = await this.process(mutations, session);
131
- this.assert(result);
132
-
133
- return result;
134
- },
135
- { mutationCount: mutations.length }
136
- );
137
- }
138
-
139
- /**
140
- * The internal extension point for providers implementating query
141
- * capabilities.
142
- */
143
- protected abstract fetch(queries: Q[], session: Session): Promise<T[]>;
144
-
145
- /**
146
- * The internal extension point for providers implementing mutation
147
- * capabilities.
148
- */
149
- protected abstract process(
150
- mutations: M[],
151
- session: Session
152
- ): Promise<T>;
153
-
154
- /**
155
- * Provider-specific cache evaluation logic.
156
- * Returns information about how this query should be cached.
157
- * Override this method to enable caching with custom keys and TTL.
158
- * Default implementation returns no caching.
159
- */
160
- protected getCacheEvaluation(query: Q, session: Session): CacheEvaluation {
161
- // Default implementation: generate a cache key but don't cache
162
- const providerName = this.constructor.name.toLowerCase();
163
- const userId = session.identity?.id || 'anonymous';
164
- const queryHash = crypto.createHash('md5').update(JSON.stringify(query)).digest('hex').substring(0, 12);
165
- const key = `${providerName}:${userId}:${queryHash}`;
166
-
167
- return {
168
- key,
169
- cacheDurationInSeconds: 0,
170
- canCache: false
171
- };
43
+ return this.assert(model);
172
44
  }
173
45
  }
@@ -1,27 +1,14 @@
1
- import { CartQuery } from "../schemas/queries/cart.query";
2
- import { CartMutation } from "../schemas/mutations/cart.mutation";
3
1
  import { BaseProvider } from "./base.provider";
4
2
  import { Cart } from "../schemas/models/cart.model";
5
- import { CacheEvaluation } from '../cache/cache-evaluation.interface';
6
- import { Session } from '../schemas/session.schema';
7
- import * as crypto from 'crypto';
3
+ import { CartQueryById } from "../schemas/queries/cart.query";
4
+ import { Session } from "../schemas/session.schema";
5
+ import { CartMutationItemAdd, CartMutationItemQuantityChange, CartMutationItemRemove } from "../schemas/mutations/cart.mutation";
8
6
 
9
7
  export abstract class CartProvider<
10
- T extends Cart = Cart,
11
- Q extends CartQuery = CartQuery,
12
- M extends CartMutation = CartMutation
13
- > extends BaseProvider<T, Q, M> {
14
-
15
- protected override getCacheEvaluation(query: Q, session: Session): CacheEvaluation {
16
- const providerName = this.constructor.name.toLowerCase();
17
- const userId = session.identity?.id || 'anonymous';
18
- const queryHash = crypto.createHash('md5').update(JSON.stringify(query)).digest('hex').substring(0, 12);
19
- const key = `${providerName}:cart:${userId}:${queryHash}`;
20
-
21
- return {
22
- key,
23
- cacheDurationInSeconds: 0,
24
- canCache: false
25
- };
26
- }
8
+ T extends Cart = Cart
9
+ > extends BaseProvider<T> {
10
+ public abstract getById(payload: CartQueryById, session: Session): Promise<T>;
11
+ public abstract add(payload: CartMutationItemAdd, session: Session): Promise<T>;
12
+ public abstract remove(payload: CartMutationItemRemove, session: Session): Promise<T>;
13
+ public abstract changeQuantity(payload: CartMutationItemQuantityChange, session: Session): Promise<T>;
27
14
  }
@@ -1,27 +1,13 @@
1
1
  import { Identity } from "../schemas/models/identity.model";
2
- import { IdentityQuery } from "../schemas/queries/identity.query";
3
- import { IdentityMutation } from "../schemas/mutations/identity.mutation";
2
+ import { IdentityMutationLogin, IdentityMutationLogout } from "../schemas/mutations/identity.mutation";
3
+ import { IdentityQuerySelf } from "../schemas/queries/identity.query";
4
+ import { Session } from "../schemas/session.schema";
4
5
  import { BaseProvider } from "./base.provider";
5
- import { CacheEvaluation } from '../cache/cache-evaluation.interface';
6
- import { Session } from '../schemas/session.schema';
7
6
 
8
7
  export abstract class IdentityProvider<
9
8
  T extends Identity = Identity,
10
- Q extends IdentityQuery = IdentityQuery,
11
- M extends IdentityMutation = IdentityMutation
12
- > extends BaseProvider<T, Q, M> {
13
-
14
- protected override getCacheEvaluation(_query: Q, session: Session): CacheEvaluation {
15
- const providerName = this.constructor.name.toLowerCase();
16
- const userId = session.identity?.id;
17
- const key = userId
18
- ? `${providerName}:identity:${userId}`
19
- : `${providerName}:identity:anonymous`;
20
-
21
- return {
22
- key,
23
- cacheDurationInSeconds: 0,
24
- canCache: false
25
- };
26
- }
9
+ > extends BaseProvider<T> {
10
+ public abstract getSelf(payload: IdentityQuerySelf, session: Session): Promise<T>;
11
+ public abstract login(payload: IdentityMutationLogin, session: Session): Promise<T>;
12
+ public abstract logout(payload: IdentityMutationLogout, session: Session): Promise<T>;
27
13
  }
@@ -1,24 +1,10 @@
1
1
  import { Inventory } from '../schemas/models/inventory.model';
2
2
  import { InventoryQuery } from '../schemas/queries/inventory.query';
3
- import { InventoryMutation } from '../schemas/mutations/inventory.mutation';
4
- import { BaseProvider } from './base.provider';
5
- import { CacheEvaluation } from '../cache/cache-evaluation.interface';
6
3
  import { Session } from '../schemas/session.schema';
4
+ import { BaseProvider } from './base.provider';
7
5
 
8
6
  export abstract class InventoryProvider<
9
- T extends Inventory = Inventory,
10
- Q extends InventoryQuery = InventoryQuery,
11
- M extends InventoryMutation = InventoryMutation
12
- > extends BaseProvider<T, Q, M> {
13
-
14
- protected override getCacheEvaluation(query: Q, _session: Session): CacheEvaluation {
15
- const providerName = this.constructor.name.toLowerCase();
16
- const key = `${providerName}:inventory:${query.sku}`;
17
-
18
- return {
19
- key,
20
- cacheDurationInSeconds: 0,
21
- canCache: false
22
- };
23
- }
7
+ T extends Inventory = Inventory
8
+ > extends BaseProvider<T> {
9
+ public abstract getBySKU(payload: InventoryQuery, session: Session): Promise<T>;
24
10
  }
@@ -1,27 +1,10 @@
1
1
  import { Price } from '../schemas/models/price.model';
2
- import { PriceMutation } from '../schemas/mutations/price.mutation';
3
- import { PriceQuery } from '../schemas/queries/price.query';
4
- import { BaseProvider } from './base.provider';
5
- import { CacheEvaluation } from '../cache/cache-evaluation.interface';
2
+ import { PriceQueryBySku } from '../schemas/queries/price.query';
6
3
  import { Session } from '../schemas/session.schema';
7
- import * as crypto from 'crypto';
4
+ import { BaseProvider } from './base.provider';
8
5
 
9
6
  export abstract class PriceProvider<
10
- T extends Price = Price,
11
- Q extends PriceQuery = PriceQuery,
12
- M extends PriceMutation = PriceMutation
13
- > extends BaseProvider<T, Q, M> {
14
-
15
- protected override getCacheEvaluation(query: Q, _session: Session): CacheEvaluation {
16
- const providerName = this.constructor.name.toLowerCase();
17
- // Hash complex SKU objects (price queries with currency, customer group, etc.)
18
- const skuHash = crypto.createHash('md5').update(JSON.stringify(query.sku)).digest('hex').substring(0, 12);
19
- const key = `${providerName}:price:${skuHash}`;
20
-
21
- return {
22
- key,
23
- cacheDurationInSeconds: 0,
24
- canCache: false
25
- };
26
- }
7
+ T extends Price = Price
8
+ > extends BaseProvider<T> {
9
+ public abstract getBySKU(payload: PriceQueryBySku, session: Session): Promise<T>;
27
10
  }
@@ -1,34 +1,11 @@
1
1
  import { Product } from '../schemas/models/product.model';
2
- import { ProductMutation } from '../schemas/mutations/product.mutation';
3
- import { ProductQuery, ProductQueryById, ProductQueryBySlug } from '../schemas/queries/product.query';
4
2
  import { BaseProvider } from './base.provider';
5
3
  import { Session } from '../schemas/session.schema';
6
- import { CacheEvaluation } from '../cache/cache-evaluation.interface';
7
- import * as crypto from 'crypto';
4
+ import { ProductQueryById, ProductQueryBySlug } from '../schemas/queries/product.query';
8
5
 
9
6
  export abstract class ProductProvider<
10
- T extends Product = Product,
11
- Q extends ProductQuery = ProductQuery,
12
- M extends ProductMutation = ProductMutation
13
- > extends BaseProvider<T, Q, M> {
14
-
15
- protected override getCacheEvaluation(query: Q, _session: Session): CacheEvaluation {
16
- const providerName = this.constructor.name.toLowerCase();
17
-
18
- let key: string;
19
- if (query.query === 'slug') {
20
- key = `${providerName}:product:slug:${(query as ProductQueryBySlug).slug}`;
21
- } else if (query.query === 'id') {
22
- key = `${providerName}:product:id:${(query as ProductQueryById).id}`;
23
- } else {
24
- const queryHash = crypto.createHash('md5').update(JSON.stringify(query)).digest('hex').substring(0, 12);
25
- key = `${providerName}:product:${queryHash}`;
26
- }
27
-
28
- return {
29
- key,
30
- cacheDurationInSeconds: 300, // Products are moderately stable - 5 minutes
31
- canCache: true
32
- };
33
- }
7
+ T extends Product = Product
8
+ > extends BaseProvider<T> {
9
+ public abstract getById(payload: ProductQueryById, session: Session): Promise<T>;
10
+ public abstract getBySlug(payload: ProductQueryBySlug, session: Session): Promise<T>;
34
11
  }
@@ -1,29 +1,12 @@
1
1
  import { SearchResult } from '../schemas/models/search.model';
2
- import { SearchQuery } from '../schemas/queries/search.query';
3
- import { SearchMutation } from '../schemas/mutations/search.mutation';
4
- import { BaseProvider } from './base.provider';
5
- import { CacheEvaluation } from '../cache/cache-evaluation.interface';
2
+ import { SearchQueryByTerm } from '../schemas/queries/search.query';
6
3
  import { Session } from '../schemas/session.schema';
7
- import * as crypto from 'crypto';
4
+ import { BaseProvider } from './base.provider';
8
5
 
9
6
  export abstract class SearchProvider<
10
- T extends SearchResult = SearchResult,
11
- Q extends SearchQuery = SearchQuery,
12
- M extends SearchMutation = SearchMutation
13
- > extends BaseProvider<T, Q, M> {
14
-
15
- protected override getCacheEvaluation(query: Q, _session: Session): CacheEvaluation {
16
- const providerName = this.constructor.name.toLowerCase();
17
- // Hash search parameters (term, filters, pagination, etc.)
18
- const searchHash = crypto.createHash('md5').update(JSON.stringify(query.search)).digest('hex').substring(0, 12);
19
- const key = `${providerName}:search:${searchHash}`;
20
-
21
- return {
22
- key,
23
- cacheDurationInSeconds: 0,
24
- canCache: false
25
- };
26
- }
7
+ T extends SearchResult = SearchResult
8
+ > extends BaseProvider<T> {
9
+ public abstract queryByTerm(payload: SearchQueryByTerm, session: Session): Promise<SearchResult>;
27
10
  }
28
11
 
29
12
 
@@ -1,7 +1,6 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  export const BaseMutationSchema = z.looseInterface({
4
- mutation: z.ZodLiteral
5
4
  });
6
5
 
7
6
  export type BaseMutation = z.infer<typeof BaseMutationSchema>;