@hartl-services/medusa-storefront 0.0.0-stage → 0.2.0

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 (194) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -2
  3. package/dist/browser/account-verification-channel.d.ts +11 -0
  4. package/dist/browser/account-verification-channel.js +32 -0
  5. package/dist/browser/actions.d.ts +53 -0
  6. package/dist/browser/actions.js +1 -0
  7. package/dist/browser/affiliate-referral.d.ts +152 -0
  8. package/dist/browser/affiliate-referral.js +85 -0
  9. package/dist/browser/browser-session.d.ts +3 -0
  10. package/dist/browser/browser-session.js +27 -0
  11. package/dist/browser/cart-mutations.d.ts +36 -0
  12. package/dist/browser/cart-mutations.js +137 -0
  13. package/dist/browser/cart-refresh.d.ts +11 -0
  14. package/dist/browser/cart-refresh.js +33 -0
  15. package/dist/browser/client.d.ts +15 -0
  16. package/dist/browser/client.js +20 -0
  17. package/dist/browser/fresh-order-marker.d.ts +11 -0
  18. package/dist/browser/fresh-order-marker.js +30 -0
  19. package/dist/browser/index.d.ts +946 -0
  20. package/dist/browser/index.js +70 -0
  21. package/dist/browser/query-invalidation.d.ts +4 -0
  22. package/dist/browser/query-invalidation.js +39 -0
  23. package/dist/browser/query-keys.d.ts +43 -0
  24. package/dist/browser/query-keys.js +28 -0
  25. package/dist/browser/query-options.d.ts +781 -0
  26. package/dist/browser/query-options.js +165 -0
  27. package/dist/browser/region.d.ts +5 -0
  28. package/dist/browser/region.js +15 -0
  29. package/dist/browser/unwrap-medusa-action.d.ts +7 -0
  30. package/dist/browser/unwrap-medusa-action.js +25 -0
  31. package/dist/core/action-result.d.ts +17 -0
  32. package/dist/core/action-result.js +12 -0
  33. package/dist/core/addresses.d.ts +46 -0
  34. package/dist/core/addresses.js +137 -0
  35. package/dist/core/affiliate/affiliate-application-errors.d.ts +2 -0
  36. package/dist/core/affiliate/affiliate-application-errors.js +14 -0
  37. package/dist/core/affiliate/affiliate-commission-presentation.d.ts +15 -0
  38. package/dist/core/affiliate/affiliate-commission-presentation.js +28 -0
  39. package/dist/core/affiliate/affiliate-coupon-code-presentation.d.ts +10 -0
  40. package/dist/core/affiliate/affiliate-coupon-code-presentation.js +22 -0
  41. package/dist/core/affiliate/affiliate-coupon-label.d.ts +3 -0
  42. package/dist/core/affiliate/affiliate-coupon-label.js +4 -0
  43. package/dist/core/affiliate/affiliate-date-range.d.ts +6 -0
  44. package/dist/core/affiliate/affiliate-date-range.js +16 -0
  45. package/dist/core/affiliate/affiliate-rate-presentation.d.ts +1 -0
  46. package/dist/core/affiliate/affiliate-rate-presentation.js +8 -0
  47. package/dist/core/affiliate/partner-company-address.d.ts +24 -0
  48. package/dist/core/affiliate/partner-company-address.js +31 -0
  49. package/dist/core/auth/customer-sign-in.d.ts +36 -0
  50. package/dist/core/auth/customer-sign-in.js +55 -0
  51. package/dist/core/auth/link-token.d.ts +8 -0
  52. package/dist/core/auth/link-token.js +11 -0
  53. package/dist/core/auth/login-code-auto-send.d.ts +7 -0
  54. package/dist/core/auth/login-code-auto-send.js +11 -0
  55. package/dist/core/auth/password-rule-label.d.ts +4 -0
  56. package/dist/core/auth/password-rule-label.js +13 -0
  57. package/dist/core/cart/cart-line-item-image-url.d.ts +9 -0
  58. package/dist/core/cart/cart-line-item-image-url.js +14 -0
  59. package/dist/core/cart/cart-normalization.d.ts +2 -0
  60. package/dist/core/cart/cart-normalization.js +11 -0
  61. package/dist/core/cart/cart-price-breakdown.d.ts +30 -0
  62. package/dist/core/cart/cart-price-breakdown.js +45 -0
  63. package/dist/core/cart/cart-tax-breakdown.d.ts +17 -0
  64. package/dist/core/cart/cart-tax-breakdown.js +18 -0
  65. package/dist/core/cart/coupon-promotions.d.ts +8 -0
  66. package/dist/core/cart/coupon-promotions.js +9 -0
  67. package/dist/core/cart/payment-session.d.ts +3 -0
  68. package/dist/core/cart/payment-session.js +12 -0
  69. package/dist/core/cart/refunded-cart.d.ts +27 -0
  70. package/dist/core/cart/refunded-cart.js +46 -0
  71. package/dist/core/catalog/category-filter.d.ts +16 -0
  72. package/dist/core/catalog/category-filter.js +33 -0
  73. package/dist/core/catalog/inventory.d.ts +10 -0
  74. package/dist/core/catalog/inventory.js +9 -0
  75. package/dist/core/catalog/medusa-product-image.d.ts +8 -0
  76. package/dist/core/catalog/medusa-product-image.js +13 -0
  77. package/dist/core/catalog/product-price.d.ts +8 -0
  78. package/dist/core/catalog/product-price.js +21 -0
  79. package/dist/core/catalog/selected-variant.d.ts +7 -0
  80. package/dist/core/catalog/selected-variant.js +9 -0
  81. package/dist/core/checkout/checkout-account-offer-state.d.ts +41 -0
  82. package/dist/core/checkout/checkout-account-offer-state.js +32 -0
  83. package/dist/core/checkout/checkout-address-draft.d.ts +39 -0
  84. package/dist/core/checkout/checkout-address-draft.js +78 -0
  85. package/dist/core/checkout/checkout-address-form.d.ts +24 -0
  86. package/dist/core/checkout/checkout-address-form.js +99 -0
  87. package/dist/core/checkout/checkout-coupon.d.ts +9 -0
  88. package/dist/core/checkout/checkout-coupon.js +13 -0
  89. package/dist/core/checkout/checkout-steps.d.ts +20 -0
  90. package/dist/core/checkout/checkout-steps.js +44 -0
  91. package/dist/core/checkout/customer-login-required.d.ts +12 -0
  92. package/dist/core/checkout/customer-login-required.js +14 -0
  93. package/dist/core/checkout/payment-authorization.d.ts +8 -0
  94. package/dist/core/checkout/payment-authorization.js +9 -0
  95. package/dist/core/checkout/stripe-redirect-return.d.ts +13 -0
  96. package/dist/core/checkout/stripe-redirect-return.js +29 -0
  97. package/dist/core/errors.d.ts +12 -0
  98. package/dist/core/errors.js +18 -0
  99. package/dist/core/fields.d.ts +18 -0
  100. package/dist/core/fields.js +20 -0
  101. package/dist/core/form-values.d.ts +162 -0
  102. package/dist/core/form-values.js +234 -0
  103. package/dist/core/format/money.d.ts +7 -0
  104. package/dist/core/format/money.js +6 -0
  105. package/dist/core/i18n.d.ts +24 -0
  106. package/dist/core/i18n.js +15 -0
  107. package/dist/core/image-url.d.ts +18 -0
  108. package/dist/core/image-url.js +34 -0
  109. package/dist/core/index.d.ts +48 -0
  110. package/dist/core/index.js +49 -0
  111. package/dist/core/order/order-payment-summary.d.ts +15 -0
  112. package/dist/core/order/order-payment-summary.js +48 -0
  113. package/dist/core/plugin-features.d.ts +21 -0
  114. package/dist/core/plugin-features.js +30 -0
  115. package/dist/core/query-string.d.ts +6 -0
  116. package/dist/core/query-string.js +16 -0
  117. package/dist/core/region.d.ts +11 -0
  118. package/dist/core/region.js +13 -0
  119. package/dist/core/report-error.d.ts +12 -0
  120. package/dist/core/report-error.js +1 -0
  121. package/dist/core/routing-locale.d.ts +1 -0
  122. package/dist/core/routing-locale.js +25 -0
  123. package/dist/core/settle-within.d.ts +8 -0
  124. package/dist/core/settle-within.js +14 -0
  125. package/dist/core/tax-compliance/address-tax-identification.d.ts +37 -0
  126. package/dist/core/tax-compliance/address-tax-identification.js +51 -0
  127. package/dist/core/tax-compliance/tax-identification.d.ts +18 -0
  128. package/dist/core/tax-compliance/tax-identification.js +58 -0
  129. package/dist/core/texts-de.d.ts +136 -0
  130. package/dist/core/texts-de.js +152 -0
  131. package/dist/react/cart-error-boundary.d.ts +24 -0
  132. package/dist/react/cart-error-boundary.js +57 -0
  133. package/dist/react/context.d.ts +17 -0
  134. package/dist/react/context.js +46 -0
  135. package/dist/react/index.d.ts +12 -0
  136. package/dist/react/index.js +14 -0
  137. package/dist/react/use-account.d.ts +214 -0
  138. package/dist/react/use-account.js +294 -0
  139. package/dist/react/use-affiliate.d.ts +566 -0
  140. package/dist/react/use-affiliate.js +573 -0
  141. package/dist/react/use-auth.d.ts +136 -0
  142. package/dist/react/use-auth.js +402 -0
  143. package/dist/react/use-cart.d.ts +57 -0
  144. package/dist/react/use-cart.js +137 -0
  145. package/dist/react/use-checkout-address-form.d.ts +156 -0
  146. package/dist/react/use-checkout-address-form.js +95 -0
  147. package/dist/react/use-checkout.d.ts +268 -0
  148. package/dist/react/use-checkout.js +873 -0
  149. package/dist/react/use-countdown.d.ts +5 -0
  150. package/dist/react/use-countdown.js +23 -0
  151. package/dist/react/use-link-token.d.ts +12 -0
  152. package/dist/react/use-link-token.js +17 -0
  153. package/dist/react/use-plugin-feature.d.ts +16 -0
  154. package/dist/react/use-plugin-feature.js +31 -0
  155. package/dist/react/use-verification-watch.d.ts +7 -0
  156. package/dist/react/use-verification-watch.js +49 -0
  157. package/dist/registry/account.json +248 -0
  158. package/dist/registry/affiliate.json +302 -0
  159. package/dist/registry/cart.json +127 -0
  160. package/dist/registry/checkout.json +169 -0
  161. package/dist/registry/form.json +145 -0
  162. package/dist/registry/registry.json +917 -0
  163. package/dist/registry/setup.json +76 -0
  164. package/dist/server/affiliate.d.ts +124 -0
  165. package/dist/server/affiliate.js +145 -0
  166. package/dist/server/cart.d.ts +10 -0
  167. package/dist/server/cart.js +113 -0
  168. package/dist/server/catalog.d.ts +28 -0
  169. package/dist/server/catalog.js +82 -0
  170. package/dist/server/checkout.d.ts +6 -0
  171. package/dist/server/checkout.js +12 -0
  172. package/dist/server/context.d.ts +38 -0
  173. package/dist/server/context.js +34 -0
  174. package/dist/server/customer.d.ts +84 -0
  175. package/dist/server/customer.js +362 -0
  176. package/dist/server/documents.d.ts +29 -0
  177. package/dist/server/documents.js +55 -0
  178. package/dist/server/index.d.ts +258 -0
  179. package/dist/server/index.js +45 -0
  180. package/dist/server/orders.d.ts +9 -0
  181. package/dist/server/orders.js +31 -0
  182. package/dist/server/plugin-features.d.ts +13 -0
  183. package/dist/server/plugin-features.js +52 -0
  184. package/dist/server/region.d.ts +6 -0
  185. package/dist/server/region.js +17 -0
  186. package/dist/server/route-response.d.ts +2 -0
  187. package/dist/server/route-response.js +17 -0
  188. package/dist/server/run-medusa-action.d.ts +7 -0
  189. package/dist/server/run-medusa-action.js +40 -0
  190. package/dist/server/session.d.ts +29 -0
  191. package/dist/server/session.js +106 -0
  192. package/dist/server/tax-compliance.d.ts +26 -0
  193. package/dist/server/tax-compliance.js +85 -0
  194. package/package.json +86 -3
@@ -0,0 +1,70 @@
1
+ import { loadStripe } from "@stripe/stripe-js/pure";
2
+ import { createMedusaImageUrls } from "../core/image-url.js";
3
+ import { createAffiliateReferral } from "./affiliate-referral.js";
4
+ import { endMedusaBrowserSessionWithClient, startMedusaBrowserSessionWithClient } from "./browser-session.js";
5
+ import { createCartMutations } from "./cart-mutations.js";
6
+ import { createRefreshCartAfterMutation } from "./cart-refresh.js";
7
+ import { createCredentialedClientFactory } from "./client.js";
8
+ import { createFreshOrderMarker } from "./fresh-order-marker.js";
9
+ import { createAffiliateApiPaths, createQueryOptions } from "./query-options.js";
10
+ import { createBrowserRegion } from "./region.js";
11
+ import { createUnwrapMedusaAction } from "./unwrap-medusa-action.js";
12
+ export { affiliateQueryKeys, medusaQueryKeys } from "./query-keys.js";
13
+ export { invalidateAffiliateQueries, invalidateCommerceQueries, resetCustomerScopedQueries } from "./query-invalidation.js";
14
+ export { applyCheckoutCouponWithClient, captureAffiliateReferralAfterCartRefreshWithClient, captureAffiliateReferralWithClient, removeCapturedAffiliateReferral, resolveCouponLabelsWithClient, runAffiliateReferralCapture, waitForAffiliateReferralCapture, } from "./affiliate-referral.js";
15
+ export { endMedusaBrowserSessionWithClient, startMedusaBrowserSessionWithClient } from "./browser-session.js";
16
+ export { canReachOtherTabs, openAccountVerificationChannel, parseAccountVerificationMessage, postAccountVerificationMessage } from "./account-verification-channel.js";
17
+ /**
18
+ * The storefront's browser side: the credentialed Medusa client, cart and
19
+ * referral calls, react-query options and invalidation. Create it once at
20
+ * module level in the shop (`storefront.client.ts`); the module state it
21
+ * relies on (pending refunded-cart replacements, in-flight referral captures)
22
+ * is shared by the whole page.
23
+ */
24
+ export function createStorefrontBrowser(config) {
25
+ const { actions, t } = config;
26
+ const createCredentialedMedusaClient = createCredentialedClientFactory({ publicBackendUrl: config.publicBackendUrl, publishableKey: config.publishableKey, t });
27
+ const reportCheckoutError = (error, context = {}) => config.reportError(error, { area: "checkout", context });
28
+ const startMedusaBrowserSession = (token) => startMedusaBrowserSessionWithClient(createCredentialedMedusaClient(), token);
29
+ const endMedusaBrowserSession = () => endMedusaBrowserSessionWithClient(createCredentialedMedusaClient());
30
+ const referral = createAffiliateReferral(createCredentialedMedusaClient);
31
+ const cart = createCartMutations({ actions, createClient: createCredentialedMedusaClient, referral, reportCheckoutError });
32
+ const region = createBrowserRegion(createCredentialedMedusaClient);
33
+ const unwrapMedusaAction = createUnwrapMedusaAction(endMedusaBrowserSession);
34
+ const apiBasePath = (config.apiBasePath ?? "/api").replace(/\/+$/, "");
35
+ const affiliateApiPaths = createAffiliateApiPaths(apiBasePath);
36
+ const queries = createQueryOptions({ actions, affiliateApiPaths, cart, referral, region, t, unwrapMedusaAction });
37
+ // Loaded on the first call (the payment step), never on other pages: Stripe.js
38
+ // sets its own cookies. One promise per page, so Elements always gets the same one.
39
+ let stripePromise;
40
+ const getStripe = () => {
41
+ if (stripePromise === undefined) {
42
+ stripePromise = config.stripePublishableKey ? loadStripe(config.stripePublishableKey) : null;
43
+ }
44
+ return stripePromise;
45
+ };
46
+ return {
47
+ actions,
48
+ t,
49
+ reportError: config.reportError,
50
+ reportCheckoutError,
51
+ createCredentialedMedusaClient,
52
+ startMedusaBrowserSession,
53
+ endMedusaBrowserSession,
54
+ unwrapMedusaAction,
55
+ cart,
56
+ referral,
57
+ region,
58
+ queries,
59
+ affiliateApiPaths,
60
+ callbacks: config.callbacks ?? {},
61
+ /** Link to the shop's document download Route Handler (`api/documents/[id]/download`). */
62
+ documentDownloadHref: (documentId) => `${apiBasePath}/documents/${encodeURIComponent(documentId)}/download`,
63
+ getStripe,
64
+ hasStripe: Boolean(config.stripePublishableKey),
65
+ orderConfirmedPath: config.orderConfirmedPath ?? ((countryCode, orderId) => `/${countryCode}/order/${orderId}/confirmed`),
66
+ refreshCartAfterMutation: createRefreshCartAfterMutation({ cart, queries, reportCheckoutError }),
67
+ freshOrder: createFreshOrderMarker(config.freshOrderKeyPrefix),
68
+ imageUrls: createMedusaImageUrls(config.publicBackendUrl),
69
+ };
70
+ }
@@ -0,0 +1,4 @@
1
+ import type { QueryClient } from "@tanstack/react-query";
2
+ export declare function invalidateCommerceQueries(queryClient: QueryClient, cartId?: string): Promise<void>;
3
+ export declare function resetCustomerScopedQueries(queryClient: QueryClient): Promise<void>;
4
+ export declare function invalidateAffiliateQueries(queryClient: QueryClient): Promise<void>;
@@ -0,0 +1,39 @@
1
+ import { affiliateQueryKeys, medusaQueryKeys } from "./query-keys.js";
2
+ // An always-mounted cart widget keeps the active cart an observed query on
3
+ // every storefront page, so invalidateQueries already triggers its refetch -
4
+ // no separate refetchQueries call is needed on top.
5
+ export async function invalidateCommerceQueries(queryClient, cartId) {
6
+ await Promise.all([
7
+ queryClient.invalidateQueries({ queryKey: medusaQueryKeys.activeCart() }),
8
+ queryClient.invalidateQueries({ queryKey: medusaQueryKeys.customer() }),
9
+ ...(cartId ? [queryClient.invalidateQueries({ queryKey: medusaQueryKeys.shippingOptions(cartId) })] : []),
10
+ ]);
11
+ }
12
+ // Called on logout and after every successful login/registration. Orders,
13
+ // documents and affiliate portal data are customer-scoped and cache no
14
+ // customer id in their query key, so a second account on the same tab must
15
+ // never see the first account's cached data - they are removed outright
16
+ // rather than just invalidated. They are not observed at login/logout
17
+ // except the checkout coupon form's cart-referral/coupon-label queries, for
18
+ // which a transient refetch is harmless.
19
+ //
20
+ // Shipping options are cart-scoped, not customer-scoped, and are observed
21
+ // during a checkout login - they are only invalidated, same as the active
22
+ // cart and customer queries below.
23
+ //
24
+ // The active cart and customer queries are deliberately NOT removed here:
25
+ // queryCache.remove() would make their observers rebuild a fresh pending
26
+ // query, and the checkout (pending cart -> skeleton) and the account gate
27
+ // would unmount their subtree mid-flow and lose their login state. Those two
28
+ // keys are always observed by a mounted component, so invalidateCommerceQueries
29
+ // refetching them in place is the correct reset.
30
+ export async function resetCustomerScopedQueries(queryClient) {
31
+ queryClient.removeQueries({ queryKey: medusaQueryKeys.ordersAll() });
32
+ queryClient.removeQueries({ queryKey: medusaQueryKeys.documents() });
33
+ queryClient.removeQueries({ queryKey: medusaQueryKeys.taxIdentifications() });
34
+ queryClient.removeQueries({ queryKey: affiliateQueryKeys.all });
35
+ await Promise.all([queryClient.invalidateQueries({ queryKey: medusaQueryKeys.shippingOptionsAll() }), invalidateCommerceQueries(queryClient)]);
36
+ }
37
+ export async function invalidateAffiliateQueries(queryClient) {
38
+ await queryClient.invalidateQueries({ queryKey: affiliateQueryKeys.all });
39
+ }
@@ -0,0 +1,43 @@
1
+ import type { ListStoreAffiliateCouponCodesRequest, PartnerPortalCommissionsRequest } from "@hartl-services/medusa-contracts/types/affiliate-store";
2
+ export declare const medusaQueryKeys: {
3
+ activeCart: () => readonly ["medusa", "cart", "active"];
4
+ customer: () => readonly ["medusa", "customer"];
5
+ customerPasswordPolicy: () => readonly ["medusa", "customer-password-policy"];
6
+ documents: () => readonly ["medusa", "documents"];
7
+ orders: (page: {
8
+ limit: number;
9
+ offset: number;
10
+ }) => readonly ["medusa", "orders", {
11
+ limit: number;
12
+ offset: number;
13
+ }];
14
+ orderDetail: (orderId: string) => readonly ["medusa", "orders", "detail", string];
15
+ ordersAll: () => readonly ["medusa", "orders"];
16
+ paymentProviders: (regionId: string) => readonly ["medusa", "payment-providers", string];
17
+ pluginFeatures: () => readonly ["medusa", "plugin-features"];
18
+ region: (countryCode: string) => readonly ["medusa", "region", string];
19
+ regions: () => readonly ["medusa", "regions"];
20
+ shippingOptions: (cartId: string) => readonly ["medusa", "shipping-options", string];
21
+ shippingOptionsAll: () => readonly ["medusa", "shipping-options"];
22
+ taxIdentifications: () => readonly ["medusa", "tax-identifications"];
23
+ };
24
+ export declare const affiliateQueryKeys: {
25
+ all: readonly ["affiliate"];
26
+ cartReferral: (cartId: string) => readonly ["affiliate", "cart-referral", string];
27
+ commissions: (query: PartnerPortalCommissionsRequest) => readonly ["affiliate", "commissions", {
28
+ from?: string | undefined;
29
+ to?: string | undefined;
30
+ limit?: string | number | undefined;
31
+ offset?: string | number | undefined;
32
+ }];
33
+ consignmentInventory: () => readonly ["affiliate", "consignment", "inventory"];
34
+ consignmentReports: () => readonly ["affiliate", "consignment", "reports"];
35
+ context: () => readonly ["affiliate", "context"];
36
+ couponCodes: (query: ListStoreAffiliateCouponCodesRequest) => readonly ["affiliate", "coupon-codes", {
37
+ status?: "enabled" | "revoked" | undefined;
38
+ limit?: string | number | undefined;
39
+ offset?: string | number | undefined;
40
+ }];
41
+ couponLabels: (codes: string[]) => readonly ["affiliate", "coupon-labels", string[]];
42
+ slugs: () => readonly ["affiliate", "slugs"];
43
+ };
@@ -0,0 +1,28 @@
1
+ export const medusaQueryKeys = {
2
+ activeCart: () => ["medusa", "cart", "active"],
3
+ customer: () => ["medusa", "customer"],
4
+ customerPasswordPolicy: () => ["medusa", "customer-password-policy"],
5
+ documents: () => ["medusa", "documents"],
6
+ orders: (page) => ["medusa", "orders", page],
7
+ // Under ordersAll, so everything that resets the orders resets a detail too.
8
+ orderDetail: (orderId) => ["medusa", "orders", "detail", orderId],
9
+ ordersAll: () => ["medusa", "orders"],
10
+ paymentProviders: (regionId) => ["medusa", "payment-providers", regionId],
11
+ pluginFeatures: () => ["medusa", "plugin-features"],
12
+ region: (countryCode) => ["medusa", "region", countryCode.toLowerCase()],
13
+ regions: () => ["medusa", "regions"],
14
+ shippingOptions: (cartId) => ["medusa", "shipping-options", cartId],
15
+ shippingOptionsAll: () => ["medusa", "shipping-options"],
16
+ taxIdentifications: () => ["medusa", "tax-identifications"],
17
+ };
18
+ export const affiliateQueryKeys = {
19
+ all: ["affiliate"],
20
+ cartReferral: (cartId) => ["affiliate", "cart-referral", cartId],
21
+ commissions: (query) => ["affiliate", "commissions", query],
22
+ consignmentInventory: () => ["affiliate", "consignment", "inventory"],
23
+ consignmentReports: () => ["affiliate", "consignment", "reports"],
24
+ context: () => ["affiliate", "context"],
25
+ couponCodes: (query) => ["affiliate", "coupon-codes", query],
26
+ couponLabels: (codes) => ["affiliate", "coupon-labels", [...codes].sort()],
27
+ slugs: () => ["affiliate", "slugs"],
28
+ };