@reactionary/source 0.0.41 → 0.0.48

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 (125) hide show
  1. package/.claude/settings.local.json +28 -0
  2. package/.env-template +8 -5
  3. package/.vscode/settings.json +5 -0
  4. package/README.md +41 -0
  5. package/core/package.json +3 -1
  6. package/core/src/cache/cache.interface.ts +14 -18
  7. package/core/src/cache/memory-cache.ts +56 -0
  8. package/core/src/cache/noop-cache.ts +5 -23
  9. package/core/src/cache/redis-cache.ts +28 -38
  10. package/core/src/client/client-builder.ts +3 -3
  11. package/core/src/client/client.ts +11 -9
  12. package/core/src/decorators/reactionary.decorator.ts +80 -8
  13. package/core/src/index.ts +5 -29
  14. package/core/src/initialization.ts +43 -0
  15. package/core/src/providers/analytics.provider.ts +1 -1
  16. package/core/src/providers/base.provider.ts +61 -25
  17. package/core/src/providers/cart-payment.provider.ts +57 -0
  18. package/core/src/providers/cart.provider.ts +131 -8
  19. package/core/src/providers/category.provider.ts +9 -9
  20. package/core/src/providers/identity.provider.ts +8 -7
  21. package/core/src/providers/index.ts +12 -0
  22. package/core/src/providers/inventory.provider.ts +4 -4
  23. package/core/src/providers/price.provider.ts +7 -7
  24. package/core/src/providers/product.provider.ts +17 -5
  25. package/core/src/providers/profile.provider.ts +22 -0
  26. package/core/src/providers/search.provider.ts +4 -4
  27. package/core/src/providers/store.provider.ts +14 -0
  28. package/core/src/schemas/capabilities.schema.ts +3 -1
  29. package/core/src/schemas/models/analytics.model.ts +1 -1
  30. package/core/src/schemas/models/cart.model.ts +16 -3
  31. package/core/src/schemas/models/identifiers.model.ts +90 -22
  32. package/core/src/schemas/models/identity.model.ts +23 -7
  33. package/core/src/schemas/models/index.ts +15 -0
  34. package/core/src/schemas/models/payment.model.ts +41 -0
  35. package/core/src/schemas/models/profile.model.ts +35 -0
  36. package/core/src/schemas/models/shipping-method.model.ts +14 -0
  37. package/core/src/schemas/models/store.model.ts +11 -0
  38. package/core/src/schemas/mutations/cart-payment.mutation.ts +21 -0
  39. package/core/src/schemas/mutations/cart.mutation.ts +62 -3
  40. package/core/src/schemas/mutations/identity.mutation.ts +8 -1
  41. package/core/src/schemas/mutations/index.ts +10 -0
  42. package/core/src/schemas/mutations/profile.mutation.ts +9 -0
  43. package/core/src/schemas/queries/cart-payment.query.ts +12 -0
  44. package/core/src/schemas/queries/cart.query.ts +1 -1
  45. package/core/src/schemas/queries/identity.query.ts +1 -1
  46. package/core/src/schemas/queries/index.ts +3 -0
  47. package/core/src/schemas/queries/inventory.query.ts +4 -12
  48. package/core/src/schemas/queries/price.query.ts +1 -1
  49. package/core/src/schemas/queries/profile.query.ts +7 -0
  50. package/core/src/schemas/queries/search.query.ts +1 -1
  51. package/core/src/schemas/queries/store.query.ts +11 -0
  52. package/core/src/schemas/session.schema.ts +31 -6
  53. package/eslint.config.mjs +7 -0
  54. package/examples/next/src/app/page.tsx +4 -12
  55. package/examples/node/package.json +1 -3
  56. package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +9 -8
  57. package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +4 -3
  58. package/examples/node/src/basic/basic-node-setup.spec.ts +4 -5
  59. package/nx.json +1 -0
  60. package/otel/src/metrics.ts +2 -1
  61. package/otel/src/provider-instrumentation.ts +2 -1
  62. package/otel/src/tracer.ts +7 -6
  63. package/otel/src/trpc-middleware.ts +3 -2
  64. package/package.json +2 -1
  65. package/providers/algolia/src/core/initialize.ts +4 -3
  66. package/providers/algolia/src/providers/product.provider.ts +15 -13
  67. package/providers/algolia/src/providers/search.provider.ts +9 -9
  68. package/providers/algolia/src/schema/capabilities.schema.ts +1 -1
  69. package/providers/algolia/src/test/search.provider.spec.ts +10 -10
  70. package/providers/algolia/src/test/test-utils.ts +9 -4
  71. package/providers/commercetools/README.md +27 -0
  72. package/providers/commercetools/src/core/client.ts +164 -117
  73. package/providers/commercetools/src/core/initialize.ts +24 -14
  74. package/providers/commercetools/src/providers/cart-payment.provider.ts +193 -0
  75. package/providers/commercetools/src/providers/cart.provider.ts +402 -125
  76. package/providers/commercetools/src/providers/category.provider.ts +35 -35
  77. package/providers/commercetools/src/providers/identity.provider.ts +23 -75
  78. package/providers/commercetools/src/providers/index.ts +2 -0
  79. package/providers/commercetools/src/providers/inventory.provider.ts +69 -40
  80. package/providers/commercetools/src/providers/price.provider.ts +79 -47
  81. package/providers/commercetools/src/providers/product.provider.ts +36 -30
  82. package/providers/commercetools/src/providers/profile.provider.ts +61 -0
  83. package/providers/commercetools/src/providers/search.provider.ts +16 -12
  84. package/providers/commercetools/src/providers/store.provider.ts +78 -0
  85. package/providers/commercetools/src/schema/capabilities.schema.ts +3 -1
  86. package/providers/commercetools/src/schema/commercetools.schema.ts +18 -0
  87. package/providers/commercetools/src/schema/configuration.schema.ts +2 -1
  88. package/providers/commercetools/src/test/cart-payment.provider.spec.ts +145 -0
  89. package/providers/commercetools/src/test/cart.provider.spec.ts +82 -22
  90. package/providers/commercetools/src/test/category.provider.spec.ts +18 -17
  91. package/providers/commercetools/src/test/identity.provider.spec.ts +88 -0
  92. package/providers/commercetools/src/test/inventory.provider.spec.ts +41 -0
  93. package/providers/commercetools/src/test/price.provider.spec.ts +9 -8
  94. package/providers/commercetools/src/test/product.provider.spec.ts +33 -5
  95. package/providers/commercetools/src/test/profile.provider.spec.ts +49 -0
  96. package/providers/commercetools/src/test/search.provider.spec.ts +8 -7
  97. package/providers/commercetools/src/test/store.provider.spec.ts +37 -0
  98. package/providers/commercetools/src/test/test-utils.ts +7 -31
  99. package/providers/fake/src/core/initialize.ts +96 -38
  100. package/providers/fake/src/providers/analytics.provider.ts +6 -5
  101. package/providers/fake/src/providers/cart.provider.ts +66 -19
  102. package/providers/fake/src/providers/category.provider.ts +12 -12
  103. package/providers/fake/src/providers/identity.provider.ts +22 -14
  104. package/providers/fake/src/providers/index.ts +1 -0
  105. package/providers/fake/src/providers/inventory.provider.ts +13 -13
  106. package/providers/fake/src/providers/price.provider.ts +13 -13
  107. package/providers/fake/src/providers/product.provider.ts +13 -10
  108. package/providers/fake/src/providers/search.provider.ts +7 -5
  109. package/providers/fake/src/providers/store.provider.ts +47 -0
  110. package/providers/fake/src/schema/capabilities.schema.ts +4 -1
  111. package/providers/fake/src/test/cart.provider.spec.ts +18 -18
  112. package/providers/fake/src/test/category.provider.spec.ts +55 -37
  113. package/providers/fake/src/test/price.provider.spec.ts +9 -14
  114. package/providers/fake/src/test/product.provider.spec.ts +27 -0
  115. package/providers/fake/src/test/test-utils.ts +2 -28
  116. package/providers/posthog/src/core/initialize.ts +3 -3
  117. package/providers/posthog/src/schema/capabilities.schema.ts +1 -1
  118. package/trpc/src/client.ts +42 -41
  119. package/trpc/src/index.ts +4 -3
  120. package/trpc/src/integration.spec.ts +11 -11
  121. package/trpc/src/server.ts +26 -24
  122. package/trpc/src/test-utils.ts +9 -4
  123. package/trpc/src/types.ts +24 -22
  124. package/core/src/cache/cache-evaluation.interface.ts +0 -19
  125. package/examples/node/src/test-utils.ts +0 -26
@@ -1,22 +1,22 @@
1
- import { z } from 'zod';
2
- import { BaseModel, createPaginatedResponseSchema } from '../schemas/models/base.model';
3
- import { Cache } from '../cache/cache.interface';
4
- import { Session } from '../schemas/session.schema';
5
- import { IdentifierType } from '../schemas/models/identifiers.model';
1
+ import type { z } from 'zod';
2
+ import type {
3
+ BaseModel} from '../schemas/models/base.model';
4
+ import {
5
+ createPaginatedResponseSchema,
6
+ } from '../schemas/models/base.model';
7
+ import type { Cache } from '../cache/cache.interface';
8
+ import type { RequestContext, Session } from '../schemas/session.schema';
9
+ import type { IdentifierType } from '../schemas/models/identifiers.model';
10
+ import { hasher } from "node-object-hash";
6
11
 
7
12
  /**
8
13
  * Base capability provider, responsible for mutations (changes) and queries (fetches)
9
14
  * for a given business object domain.
10
15
  */
11
- export abstract class BaseProvider<
12
- T extends BaseModel = BaseModel
13
- > {
16
+ export abstract class BaseProvider<T extends BaseModel = BaseModel> {
14
17
  protected cache: Cache;
15
18
 
16
- constructor(
17
- public readonly schema: z.ZodType<T>,
18
- cache: Cache
19
- ) {
19
+ constructor(public readonly schema: z.ZodType<T>, cache: Cache) {
20
20
  this.cache = cache;
21
21
  }
22
22
 
@@ -39,37 +39,73 @@ export abstract class BaseProvider<
39
39
  * Handler for parsing a response from a remote provider and converting it
40
40
  * into the typed domain model.
41
41
  */
42
- protected parseSingle(_body: unknown, session: Session): T {
42
+ protected parseSingle(_body: unknown, reqCtx: RequestContext): T {
43
43
  const model = this.newModel();
44
44
 
45
45
  return this.assert(model);
46
46
  }
47
47
 
48
48
 
49
- protected parsePaginatedResult(_body: unknown, session: Session): z.infer<ReturnType<typeof createPaginatedResponseSchema<typeof this.schema>>> {
49
+ protected parsePaginatedResult(_body: unknown, reqCtx: RequestContext): z.infer<ReturnType<typeof createPaginatedResponseSchema<typeof this.schema>>> {
50
50
  return createPaginatedResponseSchema(this.schema).parse({});
51
51
  }
52
+
53
+ public generateDependencyIdsForModel(model: unknown): Array<string> {
54
+ // TODO: Messy because we can't guarantee that a model has an identifier (type-wise)
55
+ const identifier = (model as any)?.identifier;
52
56
 
53
- protected generateCacheKeyPaginatedResult(resultSetName: string, res: ReturnType<typeof this.parsePaginatedResult>, session: Session): string {
57
+ if (!identifier) {
58
+ return [];
59
+ }
60
+
61
+ const h = hasher({ sort: true, coerce: false });
62
+ const hash = h.hash(identifier);
63
+
64
+ return [hash];
65
+ }
66
+
67
+ protected generateCacheKeyForQuery(scope: string, query: object): string {
68
+ const h = hasher({ sort: true, coerce: false });
69
+
70
+ const queryHash = h.hash(query);
71
+
72
+ // TODO: This really should include the internationalization parts as well (locale, currency, etc), or at least provide the option
73
+ // for specifying in the decorator whether they do (eg categories don't really seem to depend on currency...)
74
+
75
+ return `${scope}:${queryHash}`;
76
+ }
77
+
78
+ protected generateCacheKeyPaginatedResult(
79
+ resultSetName: string,
80
+ res: ReturnType<typeof this.parsePaginatedResult>,
81
+ reqCtx: RequestContext
82
+ ): string {
54
83
  const type = this.getResourceName();
55
- const langPart = session.languageContext.locale;
56
- const currencyPart = session.languageContext.currencyCode || 'default';
57
- const storePart = session.storeIdentifier?.key || 'default';
84
+ const langPart = reqCtx.languageContext.locale;
85
+ const currencyPart = reqCtx.languageContext.currencyCode || 'default';
86
+ const storePart = reqCtx.storeIdentifier?.key || 'default';
58
87
  return `${type}-${resultSetName}-paginated|pageNumber:${res.pageNumber}|pageSize:${res.pageSize}|store:${storePart}|lang:${langPart}|currency:${currencyPart}`;
59
88
  }
60
89
 
61
-
62
- protected generateCacheKeySingle(identifier: IdentifierType, session: Session): string {
90
+ protected generateCacheKeySingle(
91
+ identifier: IdentifierType,
92
+ reqCtx: RequestContext
93
+ ): string {
63
94
  const type = this.getResourceName();
64
- const idPart = Object.entries(identifier).map(([k, v]) => `${k}:${(v as any).key}`).join('#');
65
- const langPart = session.languageContext.locale;
66
- const currencyPart = session.languageContext.currencyCode || 'default';
67
- const storePart = session.storeIdentifier?.key || 'default';
95
+
96
+ const idPart = Object.entries(identifier)
97
+ .map(([k, v]) => `${k}:${v}`)
98
+ .join('#');
99
+
100
+ const langPart = reqCtx.languageContext.locale;
101
+ const currencyPart = reqCtx.languageContext.currencyCode || 'default';
102
+ const storePart = reqCtx.storeIdentifier?.key || 'default';
103
+
68
104
  return `${type}-${idPart}|store:${storePart}|lang:${langPart}|currency:${currencyPart}`;
69
105
  }
70
106
 
71
107
  /**
72
108
  * Returns the abstract resource name provided by the remote system.
73
109
  */
74
- protected abstract getResourceName(): string ;
110
+ protected abstract getResourceName(): string;
75
111
  }
@@ -0,0 +1,57 @@
1
+ import type { CartPaymentInstruction } from '../schemas/models/payment.model';
2
+ import type { CartPaymentMutationAddPayment, CartPaymentMutationCancelPayment } from '../schemas/mutations/cart-payment.mutation';
3
+ import type { CartPaymentQueryByCart } from '../schemas/queries/cart-payment.query';
4
+ import type { RequestContext} from '../schemas/session.schema';
5
+ import { Session } from '../schemas/session.schema';
6
+ import { BaseProvider } from './base.provider';
7
+
8
+ export abstract class CartPaymentProvider<
9
+ T extends CartPaymentInstruction = CartPaymentInstruction
10
+ > extends BaseProvider<T> {
11
+
12
+
13
+
14
+ /**
15
+ * Returns all payment instructions associated with a given cart, optionally filtered by status
16
+ *
17
+ * Usecase: Fetch all registered payment instructions to show on checkout page, in case you support multiple payments in your storefront,
18
+ * and need to show how far the user has come in the payment process. Also useful if user reloads page, or goes back to site or otherwise breaks the flow.
19
+ *
20
+ * Only returns payment instructions in status 'pending' or 'authorized'
21
+ * @param cartIdentifier
22
+ * @param session
23
+ */
24
+ public abstract getByCartIdentifier(payload: CartPaymentQueryByCart, reqCtx: RequestContext): Promise<T[]>;
25
+
26
+
27
+ /**
28
+ * Calls payment provider to set up a new payment intent. Returns whatever is needed to continue the payment process, e.g. a client secret for Stripe, or a redirect URL for PayPal.
29
+ *
30
+ * Usecase: User has filled out checkout form, and is ready to pay. You call this to get the payment process started.
31
+ *
32
+ * Note: The payment provider MAY change the cart during the payment process, so be sure to reload the cart object after this call.
33
+ *
34
+ * @param payload
35
+ * @param session
36
+ */
37
+ public abstract initiatePaymentForCart(payload: CartPaymentMutationAddPayment, reqCtx: RequestContext): Promise<T>;
38
+
39
+
40
+
41
+ /**
42
+ * Cancel a payment instruction. This will typically void the payment intent in the payment provider, and set the status of the payment instruction to 'canceled'.
43
+ *
44
+ * Usecase: User has decided to cancel the payment, e.g. by going back in the checkout process, or by closing the browser window. You call this to clean up the payment instruction.
45
+ *
46
+ * Note: The payment provider MAY change the cart during the cancellation process, so be sure to reload the cart object after this call.
47
+ *
48
+ * @param payload
49
+ * @param session
50
+ * @returns
51
+ */
52
+ public abstract cancelPaymentInstruction(payload: CartPaymentMutationCancelPayment, reqCtx: RequestContext): Promise<T>;
53
+
54
+ protected override getResourceName(): string {
55
+ return 'cart-payment-instruction';
56
+ }
57
+ }
@@ -1,17 +1,140 @@
1
1
  import { BaseProvider } from "./base.provider";
2
- import { Cart } from "../schemas/models/cart.model";
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";
2
+ import type { Cart } from "../schemas/models/cart.model";
3
+ import type { CartQueryById } from "../schemas/queries/cart.query";
4
+ import type { RequestContext } from "../schemas/session.schema";
5
+ import type { CartMutationApplyCoupon, CartMutationChangeCurrency, CartMutationCheckout, CartMutationDeleteCart, CartMutationItemAdd, CartMutationItemQuantityChange, CartMutationItemRemove, CartMutationRemoveCoupon, CartMutationSetBillingAddress, CartMutationSetShippingInfo } from "../schemas/mutations/cart.mutation";
6
+ import type { CartIdentifier, OrderIdentifier } from "../schemas/models/identifiers.model";
6
7
 
7
8
  export abstract class CartProvider<
8
9
  T extends Cart = Cart
9
10
  > 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>;
14
11
 
12
+ /**
13
+ * Get cart by ID.
14
+ *
15
+ * Usecase: Unclear, until we support multiple carts per user.
16
+ * @param payload
17
+ * @param session
18
+ */
19
+ public abstract getById(payload: CartQueryById, reqCtx: RequestContext): Promise<T>;
20
+
21
+
22
+ /**
23
+ * Get the active cart id for the user.
24
+ *
25
+ * Usecase: Most common usecase during site load, or after login. You want to get the active cart for the user, so you can display it in the minicart.
26
+ * @param session
27
+ */
28
+ public abstract getActiveCartId(reqCtx: RequestContext): Promise<CartIdentifier>;
29
+
30
+
31
+ /**
32
+ * Add item to cart. If no cart exists, create a new one. Returns the updated and recalculated cart.
33
+ * Does not automatically consolidate items, so if you want to have second add of same item to increase quantity,
34
+ * you need to handle that in your logic or on the server.
35
+ *
36
+ * Usecase: Add item to cart, create cart if none exists.
37
+ * @param payload
38
+ * @param session
39
+ */
40
+ public abstract add(payload: CartMutationItemAdd, reqCtx: RequestContext): Promise<T>;
41
+
42
+ /**
43
+ * Remove item from cart. If the cart is empty after removal, delete the cart. Returns the updated and recalculated cart.
44
+ *
45
+ * Usecase: Remove item from cart, delete cart if empty.
46
+ * @param payload
47
+ * @param session
48
+ */
49
+ public abstract remove(payload: CartMutationItemRemove, reqCtx: RequestContext): Promise<T>;
50
+
51
+ /**
52
+ * Change quantity of item in cart. If the cart is empty after change, delete the cart. Returns the updated and recalculated cart.
53
+ * Changing quantity to 0 is not allowed. Use the remove call instead. This is done to avoid accidental removal of item.
54
+ * Calls with quantity 0 will just be ignored.
55
+ *
56
+ * Usecase: Change quantity of item in cart, like in a minicart, or in the full cart view.
57
+ * @param payload
58
+ * @param session
59
+ */
60
+ public abstract changeQuantity(payload: CartMutationItemQuantityChange, reqCtx: RequestContext): Promise<T>;
61
+
62
+
63
+ /**
64
+ * Deletes the entire cart.
65
+ *
66
+ * Usecase: User wants to empty the cart or something is wrong with the current cart, and you want to clear it out and start fresh.
67
+ * @param payload
68
+ * @param session
69
+ */
70
+ public abstract deleteCart(payload: CartMutationDeleteCart, reqCtx: RequestContext): Promise<T>;
71
+
72
+ /**
73
+ * Sets shipping method and address on the cart. Returns the updated and recalculated cart.
74
+ *
75
+ * Usecase: User selects shipping method during checkout.
76
+ * @param payload
77
+ * @param session
78
+ */
79
+ public abstract setShippingInfo(payload: CartMutationSetShippingInfo, reqCtx: RequestContext): Promise<T>;
80
+
81
+ /**
82
+ * Sets billing address on the cart. Returns the updated and recalculated cart.
83
+ *
84
+ * Usecase: User enters billing address during checkout.
85
+ *
86
+ * @param payload
87
+ * @param session
88
+ */
89
+ public abstract setBillingAddress(payload: CartMutationSetBillingAddress, reqCtx: RequestContext): Promise<T>;
90
+
91
+ /**
92
+ * Applies a coupon code to the cart. Returns the updated and recalculated cart.
93
+ *
94
+ * Usecase: User applies a coupon code during checkout.
95
+ * @param payload
96
+ * @param session
97
+ */
98
+ public abstract applyCouponCode(payload: CartMutationApplyCoupon, reqCtx: RequestContext): Promise<T>;
99
+
100
+
101
+ /**
102
+ * Removes a coupon code from the cart. Returns the updated and recalculated cart.
103
+ *
104
+ * Usecase: User removes a coupon code during checkout.
105
+ * @param payload
106
+ * @param session
107
+ */
108
+ public abstract removeCouponCode(payload: CartMutationRemoveCoupon, reqCtx: RequestContext): Promise<T>;
109
+
110
+
111
+ /**
112
+ * Checks out the cart. Returns the order identifier of the newly created order.
113
+ *
114
+ * Usecase: User proceeds to checkout.
115
+ *
116
+ * @param payload
117
+ * @param session
118
+ */
119
+ public abstract checkout(payload: CartMutationCheckout, reqCtx: RequestContext): Promise<OrderIdentifier>;
120
+
121
+ /**
122
+ * Changes the currency of the cart.
123
+ *
124
+ * Usecase: User wants to change the currency for his session. This will change the currency of the cart, and recalculate prices.
125
+ * @param newCurrency
126
+ * @param session
127
+ */
128
+ public abstract changeCurrency(payload: CartMutationChangeCurrency, reqCtx: RequestContext): Promise<T>;
129
+
130
+
131
+
132
+ protected createEmptyCart(): T {
133
+ const cart = this.newModel();
134
+ cart.meta = { placeholder: true, cache: { hit: true, key: 'empty-cart' } };
135
+ cart.identifier = { key: '' };
136
+ return cart;
137
+ }
15
138
 
16
139
  protected override getResourceName(): string {
17
140
  return 'cart';
@@ -1,10 +1,10 @@
1
1
 
2
2
 
3
3
  import { PaginationOptions } from "../schemas/models/base.model";
4
- import { Category } from "../schemas/models/category.model";
4
+ import type { Category } from "../schemas/models/category.model";
5
5
  import { CategoryIdentifier } from "../schemas/models/identifiers.model";
6
- import { CategoryQueryById, CategoryQueryBySlug, CategoryQueryForBreadcrumb, CategoryQueryForChildCategories, CategoryQueryForTopCategories } from "../schemas/queries/category.query";
7
-
6
+ import type { CategoryQueryById, CategoryQueryBySlug, CategoryQueryForBreadcrumb, CategoryQueryForChildCategories, CategoryQueryForTopCategories } from "../schemas/queries/category.query";
7
+ import type { RequestContext} from "../schemas/session.schema";
8
8
  import { Session } from "../schemas/session.schema";
9
9
  import { BaseProvider } from "./base.provider";
10
10
 
@@ -32,11 +32,11 @@ export abstract class CategoryProvider<
32
32
  * For now, the result will be en empty category, but we should probably throw an error instead.
33
33
  *
34
34
  * Use case: You have received a list of category ids from a recommendation engine, and you need to show a tile of this.
35
- * Future optimization: getByIds(ids: CategoryIdentifier[], session: Session): Promise<T[]>
35
+ * Future optimization: getByIds(ids: CategoryIdentifier[], reqCtx: RequestContext): Promise<T[]>
36
36
  * @param id
37
37
  * @param session
38
38
  */
39
- public abstract getById(payload: CategoryQueryById, session: Session): Promise<T>;
39
+ public abstract getById(payload: CategoryQueryById, reqCtx: RequestContext): Promise<T>;
40
40
 
41
41
  /**
42
42
  * Gets a single category by its seo slug
@@ -45,7 +45,7 @@ export abstract class CategoryProvider<
45
45
  * @param slug the slug
46
46
  * @param session
47
47
  */
48
- public abstract getBySlug(payload: CategoryQueryBySlug, session: Session): Promise<T | null>;
48
+ public abstract getBySlug(payload: CategoryQueryBySlug, reqCtx: RequestContext): Promise<T | null>;
49
49
 
50
50
 
51
51
  /**
@@ -56,7 +56,7 @@ export abstract class CategoryProvider<
56
56
  * @param id
57
57
  * @param session
58
58
  */
59
- public abstract getBreadcrumbPathToCategory(payload: CategoryQueryForBreadcrumb, session: Session): Promise<T[]>;
59
+ public abstract getBreadcrumbPathToCategory(payload: CategoryQueryForBreadcrumb, reqCtx: RequestContext): Promise<T[]>;
60
60
 
61
61
  // hmm, this is not really good enough.... We need a type we can pass in that will allow us to specify the precise return type, but otoh we also need
62
62
  // to be able to verify and assert the output type. FIXME
@@ -71,7 +71,7 @@ export abstract class CategoryProvider<
71
71
  * @param id The ID of the parent category.
72
72
  * @param session The session information.
73
73
  */
74
- public abstract findChildCategories(payload: CategoryQueryForChildCategories, session: Session): Promise< ReturnType<typeof this.parsePaginatedResult>>;
74
+ public abstract findChildCategories(payload: CategoryQueryForChildCategories, reqCtx: RequestContext): Promise< ReturnType<typeof this.parsePaginatedResult>>;
75
75
 
76
76
  /**
77
77
  * Returns all top categories, i.e. categories without a parent.
@@ -80,7 +80,7 @@ export abstract class CategoryProvider<
80
80
  * @param paginationOptions
81
81
  * @param session
82
82
  */
83
- public abstract findTopCategories( payload: CategoryQueryForTopCategories, session: Session): Promise<ReturnType<typeof this.parsePaginatedResult>>;
83
+ public abstract findTopCategories( payload: CategoryQueryForTopCategories, reqCtx: RequestContext): Promise<ReturnType<typeof this.parsePaginatedResult>>;
84
84
 
85
85
 
86
86
  protected override getResourceName(): string {
@@ -1,15 +1,16 @@
1
- import { Identity } from "../schemas/models/identity.model";
2
- import { IdentityMutationLogin, IdentityMutationLogout } from "../schemas/mutations/identity.mutation";
3
- import { IdentityQuerySelf } from "../schemas/queries/identity.query";
4
- import { Session } from "../schemas/session.schema";
1
+ import type { Identity } from "../schemas/models/identity.model";
2
+ import type { IdentityMutationLogin, IdentityMutationLogout, IdentityMutationRegister } from "../schemas/mutations/identity.mutation";
3
+ import type { IdentityQuerySelf } from "../schemas/queries/identity.query";
4
+ import type { RequestContext} from "../schemas/session.schema";
5
5
  import { BaseProvider } from "./base.provider";
6
6
 
7
7
  export abstract class IdentityProvider<
8
8
  T extends Identity = Identity,
9
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>;
10
+ public abstract getSelf(payload: IdentityQuerySelf, reqCtx: RequestContext): Promise<T>;
11
+ public abstract login(payload: IdentityMutationLogin, reqCtx: RequestContext): Promise<T>;
12
+ public abstract logout(payload: IdentityMutationLogout, reqCtx: RequestContext): Promise<T>;
13
+ public abstract register(payload: IdentityMutationRegister, reqCtx: RequestContext): Promise<T>;
13
14
 
14
15
  protected override getResourceName(): string {
15
16
  return 'identity';
@@ -0,0 +1,12 @@
1
+ export * from './analytics.provider';
2
+ export * from './base.provider';
3
+ export * from './cart-payment.provider';
4
+ export * from './cart.provider';
5
+ export * from './category.provider';
6
+ export * from './identity.provider';
7
+ export * from './inventory.provider';
8
+ export * from './price.provider';
9
+ export * from './product.provider';
10
+ export * from './profile.provider';
11
+ export * from './search.provider';
12
+ export * from './store.provider';
@@ -1,12 +1,12 @@
1
- import { Inventory } from '../schemas/models/inventory.model';
2
- import { InventoryQuery } from '../schemas/queries/inventory.query';
3
- import { Session } from '../schemas/session.schema';
1
+ import type { Inventory } from '../schemas/models/inventory.model';
2
+ import type { InventoryQueryBySKU } from '../schemas/queries/inventory.query';
3
+ import type { RequestContext } from '../schemas/session.schema';
4
4
  import { BaseProvider } from './base.provider';
5
5
 
6
6
  export abstract class InventoryProvider<
7
7
  T extends Inventory = Inventory
8
8
  > extends BaseProvider<T> {
9
- public abstract getBySKU(payload: InventoryQuery, session: Session): Promise<T>;
9
+ public abstract getBySKU(payload: InventoryQueryBySKU, reqCtx: RequestContext): Promise<T>;
10
10
 
11
11
  protected override getResourceName(): string {
12
12
  return 'inventory';
@@ -1,7 +1,7 @@
1
- import { Currency } from '../schemas/models/currency.model';
2
- import { Price } from '../schemas/models/price.model';
3
- import { PriceQueryBySku } from '../schemas/queries/price.query';
4
- import { Session } from '../schemas/session.schema';
1
+ import type { Currency } from '../schemas/models/currency.model';
2
+ import type { Price } from '../schemas/models/price.model';
3
+ import type { PriceQueryBySku } from '../schemas/queries/price.query';
4
+ import type { RequestContext } from '../schemas/session.schema';
5
5
  import { BaseProvider } from './base.provider';
6
6
 
7
7
  export abstract class PriceProvider<
@@ -19,7 +19,7 @@ export abstract class PriceProvider<
19
19
  * @param payload The SKU to query
20
20
  * @param session The session information
21
21
  */
22
- public abstract getBySKU(payload: PriceQueryBySku, session: Session): Promise<T>;
22
+ public abstract getBySKU(payload: PriceQueryBySku, reqCtx: RequestContext): Promise<T>;
23
23
 
24
24
 
25
25
  /**
@@ -29,7 +29,7 @@ export abstract class PriceProvider<
29
29
  * @param payload The SKUs to query
30
30
  * @param session The session information
31
31
  */
32
- public abstract getBySKUs(payload: PriceQueryBySku[], session: Session): Promise<T[]>;
32
+ public abstract getBySKUs(payload: PriceQueryBySku[], reqCtx: RequestContext): Promise<T[]>;
33
33
 
34
34
 
35
35
  /**
@@ -40,7 +40,7 @@ export abstract class PriceProvider<
40
40
  * @param currency
41
41
  * @returns
42
42
  */
43
- protected getEmptyPriceResult(sku: string, currency: Currency): T {
43
+ protected createEmptyPriceResult(sku: string, currency: Currency): T {
44
44
  const base = this.newModel();
45
45
  base.identifier = {
46
46
  sku: { key: sku }
@@ -1,14 +1,26 @@
1
- import { Product } from '../schemas/models/product.model';
1
+ import type { Product } from '../schemas/models/product.model';
2
2
  import { BaseProvider } from './base.provider';
3
- import { Session } from '../schemas/session.schema';
4
- import { ProductQueryById, ProductQueryBySlug } from '../schemas/queries/product.query';
3
+ import type { RequestContext } from '../schemas/session.schema';
4
+ import type { ProductQueryById, ProductQueryBySlug } from '../schemas/queries/product.query';
5
5
 
6
6
  export abstract class ProductProvider<
7
7
  T extends Product = Product
8
8
  > extends BaseProvider<T> {
9
- public abstract getById(payload: ProductQueryById, session: Session): Promise<T>;
10
- public abstract getBySlug(payload: ProductQueryBySlug, session: Session): Promise<T>;
9
+ public abstract getById(payload: ProductQueryById, reqCtx: RequestContext): Promise<T>;
10
+ public abstract getBySlug(payload: ProductQueryBySlug, reqCtx: RequestContext): Promise<T | null>;
11
11
 
12
+
13
+ protected createEmptyProduct(id: string): T {
14
+ const product = this.newModel();
15
+ product.identifier = { key: id };
16
+ product.meta.placeholder = true;
17
+ return product;
18
+ }
19
+
20
+ /**
21
+ * The resource name, used for caching and logging.
22
+ * @returns
23
+ */
12
24
  protected override getResourceName(): string {
13
25
  return 'product';
14
26
  }
@@ -0,0 +1,22 @@
1
+ import type { Profile } from '../schemas/models';
2
+ import type { ProfileMutationUpdate } from '../schemas/mutations';
3
+ import type { ProfileQuerySelf } from '../schemas/queries';
4
+ import type { RequestContext } from '../schemas/session.schema';
5
+ import { BaseProvider } from './base.provider';
6
+
7
+ export abstract class ProfileProvider<
8
+ T extends Profile = Profile
9
+ > extends BaseProvider<T> {
10
+ public abstract getSelf(
11
+ payload: ProfileQuerySelf,
12
+ reqCtx: RequestContext
13
+ ): Promise<T>;
14
+ public abstract update(
15
+ payload: ProfileMutationUpdate,
16
+ reqCtx: RequestContext
17
+ ): Promise<T>;
18
+
19
+ protected override getResourceName(): string {
20
+ return 'profile';
21
+ }
22
+ }
@@ -1,12 +1,12 @@
1
- import { SearchResult } from '../schemas/models/search.model';
2
- import { SearchQueryByTerm } from '../schemas/queries/search.query';
3
- import { Session } from '../schemas/session.schema';
1
+ import type { SearchResult } from '../schemas/models/search.model';
2
+ import type { SearchQueryByTerm } from '../schemas/queries/search.query';
3
+ import type { RequestContext } from '../schemas/session.schema';
4
4
  import { BaseProvider } from './base.provider';
5
5
 
6
6
  export abstract class SearchProvider<
7
7
  T extends SearchResult = SearchResult
8
8
  > extends BaseProvider<T> {
9
- public abstract queryByTerm(payload: SearchQueryByTerm, session: Session): Promise<SearchResult>;
9
+ public abstract queryByTerm(payload: SearchQueryByTerm, reqCtx: RequestContext): Promise<SearchResult>;
10
10
 
11
11
 
12
12
  protected override getResourceName(): string {
@@ -0,0 +1,14 @@
1
+ import type { Store } from '../schemas/models/store.model';
2
+ import type { StoreQueryByProximity } from '../schemas/queries';
3
+ import type { RequestContext } from '../schemas/session.schema';
4
+ import { BaseProvider } from './base.provider';
5
+
6
+ export abstract class StoreProvider<
7
+ T extends Store = Store
8
+ > extends BaseProvider<T> {
9
+ public abstract queryByProximity(payload: StoreQueryByProximity, reqCtx: RequestContext): Promise<Array<T>>;
10
+
11
+ protected override getResourceName(): string {
12
+ return 'store';
13
+ }
14
+ }
@@ -6,9 +6,11 @@ export const CapabilitiesSchema = z.looseObject({
6
6
  analytics: z.boolean(),
7
7
  identity: z.boolean(),
8
8
  cart: z.boolean(),
9
+ cartPayment: z.boolean(),
9
10
  inventory: z.boolean(),
10
11
  price: z.boolean(),
11
- category: z.boolean()
12
+ category: z.boolean(),
13
+ store: z.boolean()
12
14
  });
13
15
 
14
16
  export type Capabilities = z.infer<typeof CapabilitiesSchema>;
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import type { z } from 'zod';
2
2
  import { BaseModelSchema } from './base.model';
3
3
 
4
4
  export const AnalyticsEventSchema = BaseModelSchema.extend({
@@ -1,7 +1,9 @@
1
1
  import { z } from 'zod';
2
- import { CartIdentifierSchema, CartItemIdentifierSchema, ProductIdentifierSchema } from '../models/identifiers.model';
2
+ import { CartIdentifierSchema, CartItemIdentifierSchema, IdentityIdentifierSchema, ProductIdentifierSchema, SKUIdentifierSchema } from '../models/identifiers.model';
3
3
  import { BaseModelSchema } from './base.model';
4
4
  import { MonetaryAmountSchema } from './price.model';
5
+ import { AddressSchema } from './profile.model';
6
+ import { ShippingMethodSchema } from './shipping-method.model';
5
7
 
6
8
  export const CostBreakDownSchema = z.looseObject({
7
9
  totalTax: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe('The amount of tax paid on the cart. This may include VAT, GST, sales tax, etc.'),
@@ -11,7 +13,6 @@ export const CostBreakDownSchema = z.looseObject({
11
13
  totalProductPrice: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe('The total price of products in the cart.'),
12
14
  grandTotal: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe('The total price for the cart including all taxes, discounts, and shipping.'),
13
15
  });
14
- export type CostBreakDown = z.infer<typeof CostBreakDownSchema>;
15
16
 
16
17
  export const ItemCostBreakdownSchema = z.looseObject({
17
18
  unitPrice: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe('The price per single unit of the item.'),
@@ -20,22 +21,34 @@ export const ItemCostBreakdownSchema = z.looseObject({
20
21
  totalDiscount: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe('The total discount applied to all units of the item.'),
21
22
  });
22
23
 
23
- export type ItemCostBreakdown = z.infer<typeof ItemCostBreakdownSchema>;
24
24
 
25
25
  export const CartItemSchema = z.looseObject({
26
26
  identifier: CartItemIdentifierSchema.default(() => CartItemIdentifierSchema.parse({})),
27
27
  product: ProductIdentifierSchema.default(() => ProductIdentifierSchema.parse({})),
28
+ sku: SKUIdentifierSchema.default(() => SKUIdentifierSchema.parse({})),
28
29
  quantity: z.number().default(0),
29
30
  price: ItemCostBreakdownSchema.default(() => ItemCostBreakdownSchema.parse({})),
30
31
  });
31
32
 
32
33
  export const CartSchema = BaseModelSchema.extend({
33
34
  identifier: CartIdentifierSchema.default(() => CartIdentifierSchema.parse({})),
35
+
36
+ userId: IdentityIdentifierSchema.default(() => IdentityIdentifierSchema.parse({})),
37
+
34
38
  items: z.array(CartItemSchema).default(() => []),
35
39
  price: CostBreakDownSchema.default(() => CostBreakDownSchema.parse({})),
36
40
  name: z.string().default(''),
37
41
  description: z.string().default(''),
42
+
43
+
44
+ shippingAddress: AddressSchema.optional(),
45
+ billingAddress: AddressSchema.optional(),
46
+ shippingMethod: ShippingMethodSchema.optional(),
38
47
  });
39
48
 
49
+
50
+
51
+ export type CostBreakDown = z.infer<typeof CostBreakDownSchema>;
52
+ export type ItemCostBreakdown = z.infer<typeof ItemCostBreakdownSchema>;
40
53
  export type CartItem = z.infer<typeof CartItemSchema>;
41
54
  export type Cart = z.infer<typeof CartSchema>;