@faststore/core 4.6.0-dev.2 → 4.6.0-dev.4
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.
- package/.turbo/turbo-generate.log +4 -4
- package/.turbo/turbo-test.log +80 -77
- package/@generated/cached-operations.json +1 -1
- package/@generated/gql.ts +6 -6
- package/@generated/graphql.ts +10 -10
- package/@generated/persisted-documents.json +1 -1
- package/CHANGELOG.md +8 -0
- package/package.json +6 -6
- package/src/components/sections/RecommendationShelf/README.md +4 -1
- package/src/components/sections/RecommendationShelf/RecommendationShelf.tsx +11 -92
- package/src/components/sections/RecommendationShelf/RecommendationShelf.types.ts +3 -18
- package/src/sdk/recommendations/index.ts +17 -0
- package/src/sdk/recommendations/types.ts +17 -0
- package/src/sdk/recommendations/useRecommendationShelf.ts +141 -0
- package/src/{components/sections/RecommendationShelf → sdk/recommendations}/vrn.ts +1 -1
- package/test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx +6 -12
- package/test/sdk/recommendations/useRecommendationShelf.browser.test.tsx +199 -0
- package/test/{components/RecommendationShelf → sdk/recommendations}/useRecommendationUserId.test.ts +1 -1
- package/test/{components/RecommendationShelf → sdk/recommendations}/useRecommendations.test.ts +1 -1
- package/test/{components/RecommendationShelf → sdk/recommendations}/vrn.test.ts +1 -4
- /package/src/{components/sections/RecommendationShelf → sdk/recommendations}/useRecommendationUserId.ts +0 -0
- /package/src/{components/sections/RecommendationShelf → sdk/recommendations}/useRecommendations.ts +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
> @faststore/core@4.6.0-dev.
|
|
2
|
+
> @faststore/core@4.6.0-dev.3 generate /home/runner/work/faststore/faststore/packages/core
|
|
3
3
|
> pnpm run gen-types && pnpm run cache-graphql
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> @faststore/core@4.6.0-dev.
|
|
6
|
+
> @faststore/core@4.6.0-dev.3 gen-types /home/runner/work/faststore/faststore/packages/core
|
|
7
7
|
> node ../cli/bin/run generate-types .
|
|
8
8
|
|
|
9
9
|
[33m[STARTED][39m Parse Configuration
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
[32m[COMPLETED][39m Generate to /home/runner/work/faststore/faststore/packages/core/@generated/
|
|
20
20
|
[32m[COMPLETED][39m Generate outputs
|
|
21
21
|
|
|
22
|
-
> @faststore/core@4.6.0-dev.
|
|
22
|
+
> @faststore/core@4.6.0-dev.3 cache-graphql /home/runner/work/faststore/faststore/packages/core
|
|
23
23
|
> node ../cli/bin/run cache-graphql --config=./discovery.config.default.js --queries=./@generated/persisted-documents.json
|
|
24
24
|
|
|
25
25
|
[Info] - Config file location: /home/runner/work/faststore/faststore/packages/core/discovery.config.default.js
|
|
26
26
|
[Info] - Persisted documents at: /home/runner/work/faststore/faststore/packages/core/@generated/persisted-documents.json
|
|
27
27
|
[Info] - Cached operations output: /home/runner/work/faststore/faststore/packages/core/@generated/cached-operations.json
|
|
28
28
|
[Success] - GraphQL queries cached with success: 🎉
|
|
29
|
-
Queries:
|
|
29
|
+
Queries: ServerAccountPageQuery, ServerCollectionPageQuery, ServerProductQuery, ServerOrderDetailsQuery, ServerListOrdersQuery, ServerProfileQuery, ServerListQuotesQuery, ServerQuotesOrganizationMemberQuery, ServerSecurity, ServerUserDetailsQuery, AvailableContractsQuery, ValidateUser, ClientPickupPointsQuery, OrderEntryOperationQuery, OrderFormItemsQuery, ClientProductCountQuery, ClientAllVariantProductsQuery, ClientManyProductsQueryWithSearchId, ClientProductGalleryQuery, ClientProductQuery, ClientManyProductsQuery, ClientManyProductsSelectedQuery, ClientProfileQuery, ClientRecommendationsQuery, ClientSearchSuggestionsQuery, ClientTopSearchSuggestionsQuery, ClientShippingSimulationQuery, ServerManyProductsQuery
|
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
|
|
2
|
-
> @faststore/core@4.6.0-dev.
|
|
2
|
+
> @faststore/core@4.6.0-dev.3 test /home/runner/work/faststore/faststore/packages/core
|
|
3
3
|
> vitest run
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
[1m[46m RUN [49m[22m [36mv4.0.18 [39m[90m/home/runner/work/faststore/faststore/packages/core[39m
|
|
7
7
|
|
|
8
|
+
[32m✓[39m [30m[46m browser [49m[39m test/components/ui/ProductGallery/GalleryPageHeightLock.browser.test.tsx [2m([22m[2m8 tests[22m[2m)[22m[32m 83[2mms[22m[39m
|
|
8
9
|
Not implemented: navigation to another Document
|
|
9
10
|
Not implemented: navigation to another Document
|
|
10
|
-
[32m✓[39m [30m[46m browser [49m[39m test/sdk/ui/useScrollRestoration.browser.test.ts [2m([22m[2m43 tests[22m[2m)[22m[33m
|
|
11
|
+
[32m✓[39m [30m[46m browser [49m[39m test/sdk/ui/useScrollRestoration.browser.test.ts [2m([22m[2m43 tests[22m[2m)[22m[33m 526[2mms[22m[39m
|
|
12
|
+
[32m✓[39m [30m[46m browser [49m[39m test/sdk/session/reconcileSessionLocale.browser.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m 23[2mms[22m[39m
|
|
13
|
+
[32m✓[39m [30m[46m browser [49m[39m test/components/account/ContractSwitcher.browser.test.tsx [2m([22m[2m13 tests[22m[2m)[22m[33m 1701[2mms[22m[39m
|
|
14
|
+
[33m[2m✓[22m[39m shows an error state when the list fails to load [33m 532[2mms[22m[39m
|
|
15
|
+
[33m[2m✓[22m[39m requires selecting a contract before Confirm is enabled, then switches (REQ-03) [33m 302[2mms[22m[39m
|
|
11
16
|
[90mstderr[2m | test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx
|
|
12
17
|
[22m[39mError in optimistic validation: TypeError: Failed to parse URL from /api/graphql?operationName=ValidateCartMutation&operationHash=3c71b0d3fc2bade14df68abfa271858beb361b12
|
|
13
18
|
[90m at node:internal/deps/undici/undici:16416:13[39m
|
|
@@ -37,14 +42,11 @@ Not implemented: navigation to another Document
|
|
|
37
42
|
Browserslist: browsers data (caniuse-lite) is 6 months old. Please run:
|
|
38
43
|
npx update-browserslist-db@latest
|
|
39
44
|
Why you should do it regularly: https://github.com/browserslist/update-db#readme
|
|
40
|
-
[32m✓[39m [30m[46m browser [49m[39m test/components/account/ContractSwitcher.browser.test.tsx [2m([22m[2m13 tests[22m[2m)[22m[33m 1389[2mms[22m[39m
|
|
41
45
|
[90mstderr[2m | test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx[2m > [22m[2mRecommendationShelf[2m > [22m[2mrenders nothing when there is an error
|
|
42
46
|
[22m[39mError fetching recommendations [90mundefined[39m boom for campaign vrn:recommendations:acc:rec-top-items-v2:campaign-1
|
|
43
47
|
|
|
44
|
-
[32m✓[39m [30m[46m browser [49m[39m test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx [2m([22m[2m8 tests[22m[2m)[22m[32m
|
|
45
|
-
[32m✓[39m [30m[46m browser [49m[39m test/
|
|
46
|
-
[32m✓[39m [30m[46m browser [49m[39m test/sdk/session/reconcileSessionLocale.browser.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m 14[2mms[22m[39m
|
|
47
|
-
[32m✓[39m [30m[46m browser [49m[39m test/sdk/session/getInitialSession.browser.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 19[2mms[22m[39m
|
|
48
|
+
[32m✓[39m [30m[46m browser [49m[39m test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx [2m([22m[2m8 tests[22m[2m)[22m[32m 211[2mms[22m[39m
|
|
49
|
+
[32m✓[39m [30m[46m browser [49m[39m test/sdk/recommendations/useRecommendationShelf.browser.test.tsx [2m([22m[2m10 tests[22m[2m)[22m[32m 166[2mms[22m[39m
|
|
48
50
|
[90mstderr[2m | test/pages/password-protection.browser.test.tsx[2m > [22m[2mPasswordProtectionLogin[2m > [22m[2mposts the password and redirects to the requested return path
|
|
49
51
|
[22m[39mWarning: An update to PasswordProtectionLogin inside a test was not wrapped in act(...).
|
|
50
52
|
|
|
@@ -274,11 +276,12 @@ act(() => {
|
|
|
274
276
|
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
|
|
275
277
|
at PasswordProtectionLogin (/home/runner/work/faststore/faststore/packages/core/src/pages/password-protection/index.tsx:19:69)
|
|
276
278
|
|
|
277
|
-
[32m✓[39m [30m[46m browser [49m[39m test/pages/password-protection.browser.test.tsx [2m([22m[2m6 tests[22m[2m)[22m[33m
|
|
278
|
-
[33m[2m✓[22m[39m renders the password protection form [33m
|
|
279
|
+
[32m✓[39m [30m[46m browser [49m[39m test/pages/password-protection.browser.test.tsx [2m([22m[2m6 tests[22m[2m)[22m[33m 1264[2mms[22m[39m
|
|
280
|
+
[33m[2m✓[22m[39m renders the password protection form [33m 472[2mms[22m[39m
|
|
281
|
+
[32m✓[39m [30m[46m browser [49m[39m test/sdk/session/getInitialSession.browser.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 12[2mms[22m[39m
|
|
282
|
+
[32m✓[39m [30m[46m browser [49m[39m test/components/account/OrganizationDrawer.browser.test.tsx [2m([22m[2m5 tests[22m[2m)[22m[33m 1016[2mms[22m[39m
|
|
283
|
+
[33m[2m✓[22m[39m renders the menu view with contract details and manage footer [33m 617[2mms[22m[39m
|
|
279
284
|
[32m✓[39m [30m[46m browser [49m[39m test/sdk/account/changeContractToken.browser.test.ts [2m([22m[2m9 tests[22m[2m)[22m[32m 31[2mms[22m[39m
|
|
280
|
-
[32m✓[39m [30m[46m browser [49m[39m test/components/account/OrganizationDrawer.browser.test.tsx [2m([22m[2m5 tests[22m[2m)[22m[33m 1172[2mms[22m[39m
|
|
281
|
-
[33m[2m✓[22m[39m renders the menu view with contract details and manage footer [33m 744[2mms[22m[39m
|
|
282
285
|
[90mstderr[2m | test/sdk/localization/store-url.browser.test.ts
|
|
283
286
|
[22m[39mError in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
|
|
284
287
|
at waitForSessionValidated [90m(/home/runner/work/faststore/faststore/packages/core/[39msrc/sdk/cart/waitForSessionValidated.ts:16:32[90m)[39m
|
|
@@ -287,10 +290,10 @@ This ensures that you're testing the behavior the user would see in the browser.
|
|
|
287
290
|
at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
|
|
288
291
|
[90m at processTicksAndRejections (node:internal/process/task_queues:103:5)[39m
|
|
289
292
|
|
|
290
|
-
[32m✓[39m [30m[46m browser [49m[39m test/sdk/localization/store-url.browser.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m
|
|
291
|
-
[32m✓[39m [30m[46m browser [49m[39m test/sdk/account/useAvailableContracts.browser.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m
|
|
292
|
-
[32m✓[39m [30m[46m browser [49m[39m test/components/templates/SearchPage/SearchWrapper.browser.test.tsx [2m([22m[2m3 tests[22m[2m)[22m[32m
|
|
293
|
-
[32m✓[39m [30m[46m browser [49m[39m test/sdk/account/useSwitchContract.browser.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m
|
|
293
|
+
[32m✓[39m [30m[46m browser [49m[39m test/sdk/localization/store-url.browser.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 9[2mms[22m[39m
|
|
294
|
+
[32m✓[39m [30m[46m browser [49m[39m test/sdk/account/useAvailableContracts.browser.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 32[2mms[22m[39m
|
|
295
|
+
[32m✓[39m [30m[46m browser [49m[39m test/components/templates/SearchPage/SearchWrapper.browser.test.tsx [2m([22m[2m3 tests[22m[2m)[22m[32m 26[2mms[22m[39m
|
|
296
|
+
[32m✓[39m [30m[46m browser [49m[39m test/sdk/account/useSwitchContract.browser.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m 21[2mms[22m[39m
|
|
294
297
|
[90mstderr[2m | test/components/account/doLogout.browser.test.ts
|
|
295
298
|
[22m[39mError in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
|
|
296
299
|
at waitForSessionValidated [90m(/home/runner/work/faststore/faststore/packages/core/[39msrc/sdk/cart/waitForSessionValidated.ts:16:32[90m)[39m
|
|
@@ -299,8 +302,9 @@ This ensures that you're testing the behavior the user would see in the browser.
|
|
|
299
302
|
at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
|
|
300
303
|
[90m at processTicksAndRejections (node:internal/process/task_queues:103:5)[39m
|
|
301
304
|
|
|
302
|
-
[32m✓[39m [30m[46m browser [49m[39m test/components/account/doLogout.browser.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m
|
|
303
|
-
[32m✓[39m [30m[46m browser [49m[39m test/components/account/OrganizationDrawerHeader.browser.test.tsx [2m([22m[2m3 tests[22m[2m)[22m[32m
|
|
305
|
+
[32m✓[39m [30m[46m browser [49m[39m test/components/account/doLogout.browser.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 16[2mms[22m[39m
|
|
306
|
+
[32m✓[39m [30m[46m browser [49m[39m test/components/account/OrganizationDrawerHeader.browser.test.tsx [2m([22m[2m3 tests[22m[2m)[22m[32m 166[2mms[22m[39m
|
|
307
|
+
[32m✓[39m [30m[46m browser [49m[39m test/components/account/OrganizationDrawerFooter.browser.test.tsx [2m([22m[2m3 tests[22m[2m)[22m[32m 178[2mms[22m[39m
|
|
304
308
|
[90mstderr[2m | test/sdk/account/clearPersistedSessionState.browser.test.ts
|
|
305
309
|
[22m[39mError in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
|
|
306
310
|
at waitForSessionValidated [90m(/home/runner/work/faststore/faststore/packages/core/[39msrc/sdk/cart/waitForSessionValidated.ts:16:32[90m)[39m
|
|
@@ -309,13 +313,12 @@ This ensures that you're testing the behavior the user would see in the browser.
|
|
|
309
313
|
at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
|
|
310
314
|
[90m at processTicksAndRejections (node:internal/process/task_queues:103:5)[39m
|
|
311
315
|
|
|
312
|
-
[32m✓[39m [30m[46m browser [49m[39m test/sdk/account/clearPersistedSessionState.browser.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m
|
|
313
|
-
[32m✓[39m [30m[46m browser [49m[39m test/
|
|
314
|
-
[32m✓[39m [30m[46m browser [49m[39m test/utils/isLocalHost.browser.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 4[2mms[22m[39m
|
|
316
|
+
[32m✓[39m [30m[46m browser [49m[39m test/sdk/account/clearPersistedSessionState.browser.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 10[2mms[22m[39m
|
|
317
|
+
[32m✓[39m [30m[46m browser [49m[39m test/utils/isLocalHost.browser.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 2[2mms[22m[39m
|
|
315
318
|
[32m✓[39m [30m[46m browser [49m[39m test/sdk/session/reconcileSessionLocale.disabled.browser.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 2[2mms[22m[39m
|
|
316
|
-
[32m✓[39m [30m[43m node [49m[39m test/utils/localization/bindingPaths.test.ts [2m([22m[2m71 tests[22m[2m)[22m[32m
|
|
317
|
-
[32m✓[39m [30m[46m browser [49m[39m test/components/skeletons/ProductGridSkeleton.browser.test.tsx [2m([22m[2m2 tests[22m[2m)[22m[32m
|
|
318
|
-
[32m✓[39m [30m[43m node [49m[39m test/server/password-protection-service.test.ts [2m([22m[2m22 tests[22m[2m)[22m[32m
|
|
319
|
+
[32m✓[39m [30m[43m node [49m[39m test/utils/localization/bindingPaths.test.ts [2m([22m[2m71 tests[22m[2m)[22m[32m 16[2mms[22m[39m
|
|
320
|
+
[32m✓[39m [30m[46m browser [49m[39m test/components/skeletons/ProductGridSkeleton.browser.test.tsx [2m([22m[2m2 tests[22m[2m)[22m[32m 23[2mms[22m[39m
|
|
321
|
+
[32m✓[39m [30m[43m node [49m[39m test/server/password-protection-service.test.ts [2m([22m[2m22 tests[22m[2m)[22m[32m 23[2mms[22m[39m
|
|
319
322
|
[90mstderr[2m | test/sdk/account/refreshToken.browser.test.ts
|
|
320
323
|
[22m[39mError in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
|
|
321
324
|
at waitForSessionValidated [90m(/home/runner/work/faststore/faststore/packages/core/[39msrc/sdk/cart/waitForSessionValidated.ts:16:32[90m)[39m
|
|
@@ -325,10 +328,10 @@ This ensures that you're testing the behavior the user would see in the browser.
|
|
|
325
328
|
[90m at processTicksAndRejections (node:internal/process/task_queues:103:5)[39m
|
|
326
329
|
|
|
327
330
|
[32m✓[39m [30m[43m node [49m[39m test/utils/match-url.test.ts [2m([22m[2m10 tests[22m[2m)[22m[32m 5[2mms[22m[39m
|
|
328
|
-
[32m✓[39m [30m[46m browser [49m[39m test/sdk/account/refreshToken.browser.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m
|
|
329
|
-
[32m✓[39m [30m[43m node [49m[39m test/sdk/localization/useBindingSelector.test.tsx [2m([22m[2m22 tests[22m[2m)[22m[32m
|
|
330
|
-
[32m✓[39m [30m[43m node [49m[39m test/pages/api/unlock.test.ts [2m([22m[2m15 tests[22m[2m)[22m[32m
|
|
331
|
-
[32m✓[39m [30m[43m node [49m[39m test/sdk/localization/bindingSelector.test.ts [2m([22m[2m18 tests[22m[2m)[22m[32m
|
|
331
|
+
[32m✓[39m [30m[46m browser [49m[39m test/sdk/account/refreshToken.browser.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 3[2mms[22m[39m
|
|
332
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/localization/useBindingSelector.test.tsx [2m([22m[2m22 tests[22m[2m)[22m[32m 9[2mms[22m[39m
|
|
333
|
+
[32m✓[39m [30m[43m node [49m[39m test/pages/api/unlock.test.ts [2m([22m[2m15 tests[22m[2m)[22m[32m 100[2mms[22m[39m
|
|
334
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/localization/bindingSelector.test.ts [2m([22m[2m18 tests[22m[2m)[22m[32m 5[2mms[22m[39m
|
|
332
335
|
[90mstderr[2m | test/components/search/SearchInputComponent.test.tsx
|
|
333
336
|
[22m[39mWarning: forwardRef render functions accept exactly two parameters: props and ref. Did you forget to use the ref parameter?
|
|
334
337
|
|
|
@@ -392,11 +395,12 @@ Warning: React does not recognize the `providerProps` prop on a DOM element. If
|
|
|
392
395
|
at Suspense
|
|
393
396
|
at Wrapper (/home/runner/work/faststore/faststore/packages/core/test/components/search/SearchInputComponent.test.tsx:106:20)
|
|
394
397
|
|
|
395
|
-
[32m✓[39m [30m[43m node [49m[39m test/components/search/SearchInputComponent.test.tsx [2m([22m[2m6 tests[22m[2m)[22m[32m
|
|
396
|
-
[32m✓[39m [30m[43m node [49m[39m test/utils/cookieCacheBusting.test.ts [2m([22m[2m10 tests[22m[2m)[22m[32m
|
|
397
|
-
[32m✓[39m [30m[43m node [49m[39m test/sdk/search/useSearchHistory.test.ts [2m([22m[2m13 tests[22m[2m)[22m[32m
|
|
398
|
-
[32m✓[39m [30m[43m node [49m[39m test/experimental/myAccountCmsServerSideProps.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m
|
|
398
|
+
[32m✓[39m [30m[43m node [49m[39m test/components/search/SearchInputComponent.test.tsx [2m([22m[2m6 tests[22m[2m)[22m[32m 54[2mms[22m[39m
|
|
399
|
+
[32m✓[39m [30m[43m node [49m[39m test/utils/cookieCacheBusting.test.ts [2m([22m[2m10 tests[22m[2m)[22m[32m 11[2mms[22m[39m
|
|
400
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/search/useSearchHistory.test.ts [2m([22m[2m13 tests[22m[2m)[22m[32m 38[2mms[22m[39m
|
|
401
|
+
[32m✓[39m [30m[43m node [49m[39m test/experimental/myAccountCmsServerSideProps.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m 6[2mms[22m[39m
|
|
399
402
|
[32m✓[39m [30m[43m node [49m[39m test/utils/clearCookies.test.ts [2m([22m[2m20 tests[22m[2m)[22m[32m 19[2mms[22m[39m
|
|
403
|
+
[32m✓[39m [30m[43m node [49m[39m test/components/quotes/MyAccountListQuotesTable.test.tsx [2m([22m[2m12 tests[22m[2m)[22m[32m 261[2mms[22m[39m
|
|
400
404
|
[90mstderr[2m | test/pages/api/graphql.test.ts[2m > [22m[2m/api/graphql error status propagation[2m > [22m[2mpropagates the upstream 400 from a wrapped BadRequestError instead of 500
|
|
401
405
|
[22m[39mGraphql execution returned with error: [
|
|
402
406
|
{
|
|
@@ -489,64 +493,63 @@ Warning: React does not recognize the `providerProps` prop on a DOM element. If
|
|
|
489
493
|
at file:///home/runner/work/faststore/faststore/node_modules/[4m.pnpm[24m/@vitest+runner@4.0.18/node_modules/[4m@vitest/runner[24m/dist/index.js:1653:37
|
|
490
494
|
at Traces.$ (file:///home/runner/work/faststore/faststore/node_modules/[4m.pnpm[24m/vitest@4.0.18_@opentelemetry+api@1.9.0_@types+node@20.19.13_jiti@2.6.1_jsdom@27.4.0_sas_13be941c57d55f08f06eeaf1c180ddfe/node_modules/[4mvitest[24m/dist/chunks/traces.CCmnQaNT.js:142:27)
|
|
491
495
|
|
|
492
|
-
[32m✓[39m [30m[43m node [49m[39m test/pages/api/graphql.test.ts [2m([22m[2m17 tests[22m[2m)[22m[32m
|
|
493
|
-
[32m✓[39m [30m[43m node [49m[39m test/components/quotes/MyAccountListQuotesTable.test.tsx [2m([22m[2m12 tests[22m[2m)[22m[33m 323[2mms[22m[39m
|
|
496
|
+
[32m✓[39m [30m[43m node [49m[39m test/pages/api/graphql.test.ts [2m([22m[2m17 tests[22m[2m)[22m[32m 16[2mms[22m[39m
|
|
494
497
|
[90mstderr[2m | test/sdk/account/useSetPassword.test.ts[2m > [22m[2museSetPassword[2m > [22m[2mmaps wrongcredentials error responses to the existing message
|
|
495
498
|
[22m[39mSet password request failed: Bad Request
|
|
496
499
|
|
|
497
|
-
[32m✓[39m [30m[43m node [49m[39m test/sdk/account/useSetPassword.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m
|
|
498
|
-
[32m✓[39m [30m[43m node [49m[39m test/sdk/orderEntry/useOrderEntryOperation.test.ts [2m([22m[2m8 tests[22m[2m)[22m[32m
|
|
499
|
-
[32m✓[39m [30m[43m node [49m[39m test/components/quotes/
|
|
500
|
-
|
|
501
|
-
[33m[2m✓[22m[39m navigates
|
|
502
|
-
|
|
503
|
-
[32m✓[39m [30m[43m node [49m[39m test/utils/multipleTemplates.test.ts [2m([22m[2m8 tests[22m[2m)[22m[32m
|
|
500
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/account/useSetPassword.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 17[2mms[22m[39m
|
|
501
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/orderEntry/useOrderEntryOperation.test.ts [2m([22m[2m8 tests[22m[2m)[22m[32m 22[2mms[22m[39m
|
|
502
|
+
[32m✓[39m [30m[43m node [49m[39m test/components/quotes/quoteFilters.test.ts [2m([22m[2m23 tests[22m[2m)[22m[32m 14[2mms[22m[39m
|
|
503
|
+
[32m✓[39m [30m[43m node [49m[39m test/components/quotes/MyAccountListQuotes.test.tsx [2m([22m[2m9 tests[22m[2m)[22m[33m 794[2mms[22m[39m
|
|
504
|
+
[33m[2m✓[22m[39m navigates with the search term on blur [33m 359[2mms[22m[39m
|
|
505
|
+
[33m[2m✓[22m[39m navigates without a label param when the search is cleared [33m 357[2mms[22m[39m
|
|
506
|
+
[32m✓[39m [30m[43m node [49m[39m test/utils/multipleTemplates.test.ts [2m([22m[2m8 tests[22m[2m)[22m[32m 4[2mms[22m[39m
|
|
504
507
|
[32m✓[39m [30m[43m node [49m[39m test/utils/localization/localizeRedirectDestination.test.ts [2m([22m[2m11 tests[22m[2m)[22m[32m 4[2mms[22m[39m
|
|
505
508
|
[32m✓[39m [30m[43m node [49m[39m test/sdk/orderEntry/useOrderEntryUpload.test.ts [2m([22m[2m8 tests[22m[2m)[22m[32m 37[2mms[22m[39m
|
|
506
|
-
[32m✓[39m [30m[43m node [49m[39m test/components/quotes/MyAccountQuotesFilterSlider.test.tsx [2m([22m[2m5 tests[22m[2m)[22m[32m
|
|
507
|
-
[32m✓[39m [30m[43m node [49m[39m test/sdk/analytics/useStartRecommendationSession.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m
|
|
508
|
-
[32m✓[39m [30m[43m node [49m[39m test/sdk/orderEntry/useOrderEntry.test.ts [2m([22m[2m8 tests[22m[2m)[22m[32m
|
|
509
|
-
[32m✓[39m [30m[43m node [49m[39m test/proxy.localization.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m
|
|
510
|
-
[32m✓[39m [30m[43m node [49m[39m test/sdk/account/getMyAccountRoutes.test.ts [2m([22m[2m10 tests[22m[2m)[22m[32m
|
|
511
|
-
[32m✓[39m [30m[43m node [49m[39m test/sdk/session/syncSalesChannelFromOrderForm.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m
|
|
512
|
-
[32m✓[39m [30m[43m node [49m[39m test/server/index.test.ts [2m([22m[2m7 tests[22m[2m)[22m[33m
|
|
513
|
-
[33m[2m✓[22m[39m should handle options and execute [33m
|
|
514
|
-
[32m✓[39m [30m[43m node [49m[39m test/
|
|
515
|
-
[32m✓[39m [30m[43m node [49m[39m test/
|
|
516
|
-
[32m✓[39m [30m[43m node [49m[39m test/
|
|
517
|
-
[32m✓[39m [30m[43m node [49m[39m test/
|
|
518
|
-
[32m✓[39m [30m[43m node [49m[39m test/
|
|
519
|
-
[32m✓[39m [30m[43m node [49m[39m test/sdk/
|
|
520
|
-
[32m✓[39m [30m[43m node [49m[39m test/
|
|
521
|
-
[32m✓[39m [30m[43m node [49m[39m test/server/
|
|
509
|
+
[32m✓[39m [30m[43m node [49m[39m test/components/quotes/MyAccountQuotesFilterSlider.test.tsx [2m([22m[2m5 tests[22m[2m)[22m[32m 87[2mms[22m[39m
|
|
510
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/analytics/useStartRecommendationSession.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 44[2mms[22m[39m
|
|
511
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/orderEntry/useOrderEntry.test.ts [2m([22m[2m8 tests[22m[2m)[22m[32m 20[2mms[22m[39m
|
|
512
|
+
[32m✓[39m [30m[43m node [49m[39m test/proxy.localization.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m 22[2mms[22m[39m
|
|
513
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/account/getMyAccountRoutes.test.ts [2m([22m[2m10 tests[22m[2m)[22m[32m 4[2mms[22m[39m
|
|
514
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/session/syncSalesChannelFromOrderForm.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 4[2mms[22m[39m
|
|
515
|
+
[32m✓[39m [30m[43m node [49m[39m test/server/index.test.ts [2m([22m[2m7 tests[22m[2m)[22m[33m 970[2mms[22m[39m
|
|
516
|
+
[33m[2m✓[22m[39m should handle options and execute [33m 632[2mms[22m[39m
|
|
517
|
+
[32m✓[39m [30m[43m node [49m[39m test/components/quotes/MyAccountQuotesSelectedTags.test.tsx [2m([22m[2m8 tests[22m[2m)[22m[32m 65[2mms[22m[39m
|
|
518
|
+
[32m✓[39m [30m[43m node [49m[39m test/proxy.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m 11[2mms[22m[39m
|
|
519
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/orderEntry/useOrderFormItems.test.ts [2m([22m[2m7 tests[22m[2m)[22m[32m 25[2mms[22m[39m
|
|
520
|
+
[32m✓[39m [30m[43m node [49m[39m test/server/webops-api.test.ts [2m([22m[2m9 tests[22m[2m)[22m[32m 12[2mms[22m[39m
|
|
521
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/cart/waitForSessionValidated.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m 7[2mms[22m[39m
|
|
522
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/account/refreshToken.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m 5[2mms[22m[39m
|
|
523
|
+
[32m✓[39m [30m[43m node [49m[39m test/server/cms/global.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 5[2mms[22m[39m
|
|
524
|
+
[32m✓[39m [30m[43m node [49m[39m test/server/content/service.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m 3[2mms[22m[39m
|
|
522
525
|
[32m✓[39m [30m[43m node [49m[39m test/sdk/localization/store-url.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 2[2mms[22m[39m
|
|
523
|
-
[32m✓[39m [30m[43m node [49m[39m test/sdk/auth/useAuth.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m
|
|
526
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/auth/useAuth.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m 18[2mms[22m[39m
|
|
524
527
|
[32m✓[39m [30m[43m node [49m[39m test/sdk/analytics/hasEnabledRecommendationShelf.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 4[2mms[22m[39m
|
|
525
|
-
[32m✓[39m [30m[43m node [49m[39m test/components/quotes/MyAccountQuotesStatusSelector.test.tsx [2m([22m[2m6 tests[22m[2m)[22m[32m
|
|
526
|
-
[32m✓[39m [30m[43m node [49m[39m test/utils/getRequestHostname.test.ts [2m([22m[2m12 tests[22m[2m)[22m[32m
|
|
528
|
+
[32m✓[39m [30m[43m node [49m[39m test/components/quotes/MyAccountQuotesStatusSelector.test.tsx [2m([22m[2m6 tests[22m[2m)[22m[32m 83[2mms[22m[39m
|
|
529
|
+
[32m✓[39m [30m[43m node [49m[39m test/utils/getRequestHostname.test.ts [2m([22m[2m12 tests[22m[2m)[22m[32m 4[2mms[22m[39m
|
|
527
530
|
[32m✓[39m [30m[43m node [49m[39m test/pages/api/preview.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 5[2mms[22m[39m
|
|
528
|
-
[32m✓[39m [30m[43m node [49m[39m test/utils/validateSessionRefreshToken.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m
|
|
529
|
-
[32m✓[39m [30m[43m node [49m[39m test/server/rootSpan.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m
|
|
531
|
+
[32m✓[39m [30m[43m node [49m[39m test/utils/validateSessionRefreshToken.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 3[2mms[22m[39m
|
|
532
|
+
[32m✓[39m [30m[43m node [49m[39m test/server/rootSpan.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m 23[2mms[22m[39m
|
|
530
533
|
[32m✓[39m [30m[43m node [49m[39m test/components/search/SearchInput.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 6[2mms[22m[39m
|
|
531
|
-
[32m✓[39m [30m[43m node [49m[39m test/
|
|
534
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/recommendations/vrn.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m 3[2mms[22m[39m
|
|
532
535
|
[32m✓[39m [30m[43m node [49m[39m test/server/cms/index.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 4[2mms[22m[39m
|
|
533
|
-
[32m✓[39m [30m[43m node [49m[39m test/utils/retry.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m
|
|
536
|
+
[32m✓[39m [30m[43m node [49m[39m test/utils/retry.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m 10[2mms[22m[39m
|
|
534
537
|
[32m✓[39m [30m[43m node [49m[39m test/components/account/Layout.test.tsx [2m([22m[2m4 tests[22m[2m)[22m[32m 29[2mms[22m[39m
|
|
535
|
-
[32m✓[39m [30m[43m node [49m[39m test/
|
|
536
|
-
[32m✓[39m [30m[43m node [49m[39m test/utils/unlockResponse.test.ts [2m([22m[2m11 tests[22m[2m)[22m[32m
|
|
537
|
-
[32m✓[39m [30m[43m node [49m[39m test/server/cms/myAccountDefaultSections.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m
|
|
538
|
-
[32m✓[39m [30m[43m node [49m[39m test/
|
|
539
|
-
[32m✓[39m [30m[43m node [49m[39m test/utils/isLocalHost.test.ts [2m([22m[2m7 tests[22m[2m)[22m[32m
|
|
540
|
-
[32m✓[39m [30m[43m node [49m[39m test/
|
|
541
|
-
[32m✓[39m [30m[43m node [49m[39m test/
|
|
538
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/recommendations/useRecommendationUserId.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m 181[2mms[22m[39m
|
|
539
|
+
[32m✓[39m [30m[43m node [49m[39m test/utils/unlockResponse.test.ts [2m([22m[2m11 tests[22m[2m)[22m[32m 6[2mms[22m[39m
|
|
540
|
+
[32m✓[39m [30m[43m node [49m[39m test/server/cms/myAccountDefaultSections.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m 3[2mms[22m[39m
|
|
541
|
+
[32m✓[39m [30m[43m node [49m[39m test/sdk/recommendations/useRecommendations.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 2[2mms[22m[39m
|
|
542
|
+
[32m✓[39m [30m[43m node [49m[39m test/utils/isLocalHost.test.ts [2m([22m[2m7 tests[22m[2m)[22m[32m 3[2mms[22m[39m
|
|
543
|
+
[32m✓[39m [30m[43m node [49m[39m test/components/account/OrganizationDrawerBody.test.tsx [2m([22m[2m3 tests[22m[2m)[22m[32m 31[2mms[22m[39m
|
|
544
|
+
[32m✓[39m [30m[43m node [49m[39m test/utils/userOrderStatus.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 2[2mms[22m[39m
|
|
542
545
|
[32m✓[39m [30m[43m node [49m[39m test/sdk/analytics/utils.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 3[2mms[22m[39m
|
|
543
|
-
[32m✓[39m [30m[43m node [49m[39m test/
|
|
544
|
-
[32m✓[39m [30m[43m node [49m[39m test/
|
|
546
|
+
[32m✓[39m [30m[43m node [49m[39m test/components/auth/ProfileChallenge.test.tsx [2m([22m[2m3 tests[22m[2m)[22m[32m 21[2mms[22m[39m
|
|
547
|
+
[32m✓[39m [30m[43m node [49m[39m test/instrumentation.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 3[2mms[22m[39m
|
|
545
548
|
[32m✓[39m [30m[43m node [49m[39m test/sdk/account/clearPersistedSessionState.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 13[2mms[22m[39m
|
|
546
|
-
[32m✓[39m [30m[43m node [49m[39m test/components/quotes/MyAccountQuoteStatusBadge.test.tsx [2m([22m[2m2 tests[22m[2m)[22m[32m
|
|
549
|
+
[32m✓[39m [30m[43m node [49m[39m test/components/quotes/MyAccountQuoteStatusBadge.test.tsx [2m([22m[2m2 tests[22m[2m)[22m[32m 15[2mms[22m[39m
|
|
547
550
|
|
|
548
|
-
[2m Test Files [22m [1m[
|
|
549
|
-
[2m Tests [22m [1m[
|
|
550
|
-
[2m Start at [22m
|
|
551
|
-
[2m Duration [22m
|
|
551
|
+
[2m Test Files [22m [1m[32m82 passed[39m[22m[90m (82)[39m
|
|
552
|
+
[2m Tests [22m [1m[32m661 passed[39m[22m[90m (661)[39m
|
|
553
|
+
[2m Start at [22m 17:43:08
|
|
554
|
+
[2m Duration [22m 26.15s[2m (transform 6.03s, setup 0ms, import 21.23s, tests 8.74s, environment 34.27s)[22m
|
|
552
555
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
[
|
|
2
|
-
"ClientRecommendationsQuery",
|
|
3
2
|
"ServerAccountPageQuery",
|
|
4
3
|
"ServerCollectionPageQuery",
|
|
5
4
|
"ServerProductQuery",
|
|
@@ -23,6 +22,7 @@
|
|
|
23
22
|
"ClientManyProductsQuery",
|
|
24
23
|
"ClientManyProductsSelectedQuery",
|
|
25
24
|
"ClientProfileQuery",
|
|
25
|
+
"ClientRecommendationsQuery",
|
|
26
26
|
"ClientSearchSuggestionsQuery",
|
|
27
27
|
"ClientTopSearchSuggestionsQuery",
|
|
28
28
|
"ClientShippingSimulationQuery",
|
package/@generated/gql.ts
CHANGED
|
@@ -18,7 +18,6 @@ type Documents = {
|
|
|
18
18
|
"\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\t\tunitMultiplier\n\n isVariantOf {\n productGroupID\n name\n\t\t\tskuVariants {\n\t\t\t\tallVariantsByName\n\t\t\t\tactiveVariations\n\t\t\t\tslugsMap\n\t\t\t\tavailableVariations\n\t\t\t}\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n priceWithTaxes\n quantity\n priceToken\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n hasSpecifications\n\n unitMultiplier\n\n isVariantOf {\n productGroupID\n name\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n allVariantProducts {\n name\n productID\n }\n }\n }\n\n advertisement {\n adId\n adResponseId\n }\n\n deliveryPromiseBadges {\n typeName\n }\n }\n": typeof types.ProductSummary_ProductFragmentDoc,
|
|
19
19
|
"\n fragment Filter_facets on StoreFacet {\n ... on StoreFacetRange {\n key\n label\n\n min {\n selected\n absolute\n }\n\n max {\n selected\n absolute\n }\n\n __typename\n }\n ... on StoreFacetBoolean {\n key\n label\n values {\n label\n value\n selected\n quantity\n }\n\n __typename\n }\n }\n": typeof types.Filter_FacetsFragmentDoc,
|
|
20
20
|
"\n fragment ProductDetailsFragment_product on StoreProduct {\n id: productID\n sku\n name\n gtin\n description\n unitMultiplier\n isVariantOf {\n name\n productGroupID\n\t\t\tskuVariants {\n activeVariations\n slugsMap\n availableVariations\n allVariantProducts {\n name\n productID\n }\n }\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n priceWithTaxes\n listPrice\n listPriceWithTaxes\n quantity\n priceToken\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n # Contains necessary info to add this item to cart\n ...CartProductItem\n }\n": typeof types.ProductDetailsFragment_ProductFragmentDoc,
|
|
21
|
-
"query ClientRecommendationsQuery(\n $campaignVrn: String!\n $userId: String\n $products: [String!]\n) {\n recommendations(\n userId: $userId\n campaignVrn: $campaignVrn\n products: $products\n ) {\n products {\n ...ProductSummary_product\n }\n correlationId\n campaign {\n id\n title\n type\n }\n }\n}\n": typeof types.ClientRecommendationsQueryDocument,
|
|
22
21
|
"\n fragment ProductComparisonFragment_product on StoreProduct {\n id: productID\n sku\n slug\n name\n gtin\n description\n unitMultiplier\n isVariantOf {\n name\n productGroupID\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n allVariantProducts {\n name\n productID\n }\n }\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n priceWithTaxes\n listPrice\n quantity\n listPriceWithTaxes\n priceToken\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n advertisement {\n adId\n adResponseId\n }\n\n hasSpecifications\n\n skuSpecifications {\n field {\n id\n name\n originalName\n }\n values {\n name\n id\n fieldId\n originalName\n }\n }\n\n specificationGroups {\n name\n originalName\n specifications {\n name\n originalName\n values\n }\n }\n }\n": typeof types.ProductComparisonFragment_ProductFragmentDoc,
|
|
23
22
|
"\n fragment ProductSKUMatrixSidebarFragment_product on StoreProduct {\n id: productID\n isVariantOf {\n name\n productGroupID\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n allVariantProducts {\n\t\t\t\t\tsku\n name\n image {\n url\n alternateName\n }\n offers {\n highPrice\n lowPrice\n lowPriceWithTaxes\n offerCount\n priceCurrency\n offers {\n listPrice\n listPriceWithTaxes\n sellingPrice\n priceCurrency\n price\n priceWithTaxes\n priceValidUntil\n itemCondition\n availability\n quantity\n priceToken\n }\n }\n additionalProperty {\n propertyID\n value\n name\n valueReference\n }\n }\n }\n }\n }\n": typeof types.ProductSkuMatrixSidebarFragment_ProductFragmentDoc,
|
|
24
23
|
"\n fragment ClientManyProducts on Query {\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n sponsoredCount: $sponsoredCount\n\n ) {\n products {\n pageInfo {\n totalCount\n }\n }\n }\n }\n": typeof types.ClientManyProductsFragmentDoc,
|
|
@@ -60,6 +59,7 @@ type Documents = {
|
|
|
60
59
|
"\n query ClientManyProductsQuery(\n $first: Int!\n $after: String\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n $sponsoredCount: Int\n ) {\n ...ClientManyProducts\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n sponsoredCount: $sponsoredCount\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n ...ProductSummary_product\n }\n }\n }\n }\n }\n": typeof types.ClientManyProductsQueryDocument,
|
|
61
60
|
"\n query ClientManyProductsSelectedQuery(\n $productIds: [String!]!\n ) {\n products(productIds: $productIds) {\n ...ProductComparisonFragment_product\n }\n }\n": typeof types.ClientManyProductsSelectedQueryDocument,
|
|
62
61
|
"\n query ClientProfileQuery($id: String!) {\n profile(id: $id) {\n addresses {\n country\n postalCode\n geoCoordinate\n city\n }\n }\n }\n": typeof types.ClientProfileQueryDocument,
|
|
62
|
+
"query ClientRecommendationsQuery(\n $campaignVrn: String!\n $userId: String\n $products: [String!]\n) {\n recommendations(\n userId: $userId\n campaignVrn: $campaignVrn\n products: $products\n ) {\n products {\n ...ProductSummary_product\n }\n correlationId\n campaign {\n id\n title\n type\n }\n }\n}\n": typeof types.ClientRecommendationsQueryDocument,
|
|
63
63
|
"\n query ClientSearchSuggestionsQuery(\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]\n ) {\n ...ClientSearchSuggestions\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n products {\n ...ProductSummary_product\n }\n }\n products {\n pageInfo {\n totalCount\n }\n }\n metadata {\n ...SearchEvent_metadata\n }\n searchId\n }\n }\n": typeof types.ClientSearchSuggestionsQueryDocument,
|
|
64
64
|
"\n query ClientTopSearchSuggestionsQuery(\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]\n ) {\n ...ClientTopSearchSuggestions\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n }\n }\n }\n": typeof types.ClientTopSearchSuggestionsQueryDocument,
|
|
65
65
|
"\n mutation ValidateSession($session: IStoreSession!, $search: String!) {\n validateSession(session: $session, search: $search) {\n locale\n channel\n country\n addressType\n postalCode\n city\n deliveryMode {\n deliveryChannel\n deliveryMethod\n deliveryWindow {\n startDate\n endDate\n }\n }\n geoCoordinates {\n latitude\n longitude\n }\n currency {\n code\n symbol\n }\n person {\n id\n email\n givenName\n familyName\n }\n b2b {\n customerId\n isRepresentative\n unitName\n unitId\n firstName\n lastName\n userName\n userEmail\n savedPostalCode\n contractName\n organizationManager\n }\n marketingData {\n utmCampaign\n utmMedium\n utmSource\n utmiCampaign\n utmiPage\n utmiPart\n }\n refreshAfter\n }\n }\n": typeof types.ValidateSessionDocument,
|
|
@@ -70,7 +70,6 @@ const documents: Documents = {
|
|
|
70
70
|
"\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\t\tunitMultiplier\n\n isVariantOf {\n productGroupID\n name\n\t\t\tskuVariants {\n\t\t\t\tallVariantsByName\n\t\t\t\tactiveVariations\n\t\t\t\tslugsMap\n\t\t\t\tavailableVariations\n\t\t\t}\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n priceWithTaxes\n quantity\n priceToken\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n hasSpecifications\n\n unitMultiplier\n\n isVariantOf {\n productGroupID\n name\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n allVariantProducts {\n name\n productID\n }\n }\n }\n\n advertisement {\n adId\n adResponseId\n }\n\n deliveryPromiseBadges {\n typeName\n }\n }\n": types.ProductSummary_ProductFragmentDoc,
|
|
71
71
|
"\n fragment Filter_facets on StoreFacet {\n ... on StoreFacetRange {\n key\n label\n\n min {\n selected\n absolute\n }\n\n max {\n selected\n absolute\n }\n\n __typename\n }\n ... on StoreFacetBoolean {\n key\n label\n values {\n label\n value\n selected\n quantity\n }\n\n __typename\n }\n }\n": types.Filter_FacetsFragmentDoc,
|
|
72
72
|
"\n fragment ProductDetailsFragment_product on StoreProduct {\n id: productID\n sku\n name\n gtin\n description\n unitMultiplier\n isVariantOf {\n name\n productGroupID\n\t\t\tskuVariants {\n activeVariations\n slugsMap\n availableVariations\n allVariantProducts {\n name\n productID\n }\n }\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n priceWithTaxes\n listPrice\n listPriceWithTaxes\n quantity\n priceToken\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n # Contains necessary info to add this item to cart\n ...CartProductItem\n }\n": types.ProductDetailsFragment_ProductFragmentDoc,
|
|
73
|
-
"query ClientRecommendationsQuery(\n $campaignVrn: String!\n $userId: String\n $products: [String!]\n) {\n recommendations(\n userId: $userId\n campaignVrn: $campaignVrn\n products: $products\n ) {\n products {\n ...ProductSummary_product\n }\n correlationId\n campaign {\n id\n title\n type\n }\n }\n}\n": types.ClientRecommendationsQueryDocument,
|
|
74
73
|
"\n fragment ProductComparisonFragment_product on StoreProduct {\n id: productID\n sku\n slug\n name\n gtin\n description\n unitMultiplier\n isVariantOf {\n name\n productGroupID\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n allVariantProducts {\n name\n productID\n }\n }\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n priceWithTaxes\n listPrice\n quantity\n listPriceWithTaxes\n priceToken\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n advertisement {\n adId\n adResponseId\n }\n\n hasSpecifications\n\n skuSpecifications {\n field {\n id\n name\n originalName\n }\n values {\n name\n id\n fieldId\n originalName\n }\n }\n\n specificationGroups {\n name\n originalName\n specifications {\n name\n originalName\n values\n }\n }\n }\n": types.ProductComparisonFragment_ProductFragmentDoc,
|
|
75
74
|
"\n fragment ProductSKUMatrixSidebarFragment_product on StoreProduct {\n id: productID\n isVariantOf {\n name\n productGroupID\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n allVariantProducts {\n\t\t\t\t\tsku\n name\n image {\n url\n alternateName\n }\n offers {\n highPrice\n lowPrice\n lowPriceWithTaxes\n offerCount\n priceCurrency\n offers {\n listPrice\n listPriceWithTaxes\n sellingPrice\n priceCurrency\n price\n priceWithTaxes\n priceValidUntil\n itemCondition\n availability\n quantity\n priceToken\n }\n }\n additionalProperty {\n propertyID\n value\n name\n valueReference\n }\n }\n }\n }\n }\n": types.ProductSkuMatrixSidebarFragment_ProductFragmentDoc,
|
|
76
75
|
"\n fragment ClientManyProducts on Query {\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n sponsoredCount: $sponsoredCount\n\n ) {\n products {\n pageInfo {\n totalCount\n }\n }\n }\n }\n": types.ClientManyProductsFragmentDoc,
|
|
@@ -112,6 +111,7 @@ const documents: Documents = {
|
|
|
112
111
|
"\n query ClientManyProductsQuery(\n $first: Int!\n $after: String\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n $sponsoredCount: Int\n ) {\n ...ClientManyProducts\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n sponsoredCount: $sponsoredCount\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n ...ProductSummary_product\n }\n }\n }\n }\n }\n": types.ClientManyProductsQueryDocument,
|
|
113
112
|
"\n query ClientManyProductsSelectedQuery(\n $productIds: [String!]!\n ) {\n products(productIds: $productIds) {\n ...ProductComparisonFragment_product\n }\n }\n": types.ClientManyProductsSelectedQueryDocument,
|
|
114
113
|
"\n query ClientProfileQuery($id: String!) {\n profile(id: $id) {\n addresses {\n country\n postalCode\n geoCoordinate\n city\n }\n }\n }\n": types.ClientProfileQueryDocument,
|
|
114
|
+
"query ClientRecommendationsQuery(\n $campaignVrn: String!\n $userId: String\n $products: [String!]\n) {\n recommendations(\n userId: $userId\n campaignVrn: $campaignVrn\n products: $products\n ) {\n products {\n ...ProductSummary_product\n }\n correlationId\n campaign {\n id\n title\n type\n }\n }\n}\n": types.ClientRecommendationsQueryDocument,
|
|
115
115
|
"\n query ClientSearchSuggestionsQuery(\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]\n ) {\n ...ClientSearchSuggestions\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n products {\n ...ProductSummary_product\n }\n }\n products {\n pageInfo {\n totalCount\n }\n }\n metadata {\n ...SearchEvent_metadata\n }\n searchId\n }\n }\n": types.ClientSearchSuggestionsQueryDocument,
|
|
116
116
|
"\n query ClientTopSearchSuggestionsQuery(\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]\n ) {\n ...ClientTopSearchSuggestions\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n }\n }\n }\n": types.ClientTopSearchSuggestionsQueryDocument,
|
|
117
117
|
"\n mutation ValidateSession($session: IStoreSession!, $search: String!) {\n validateSession(session: $session, search: $search) {\n locale\n channel\n country\n addressType\n postalCode\n city\n deliveryMode {\n deliveryChannel\n deliveryMethod\n deliveryWindow {\n startDate\n endDate\n }\n }\n geoCoordinates {\n latitude\n longitude\n }\n currency {\n code\n symbol\n }\n person {\n id\n email\n givenName\n familyName\n }\n b2b {\n customerId\n isRepresentative\n unitName\n unitId\n firstName\n lastName\n userName\n userEmail\n savedPostalCode\n contractName\n organizationManager\n }\n marketingData {\n utmCampaign\n utmMedium\n utmSource\n utmiCampaign\n utmiPage\n utmiPart\n }\n refreshAfter\n }\n }\n": types.ValidateSessionDocument,
|
|
@@ -131,10 +131,6 @@ export function gql(source: "\n fragment Filter_facets on StoreFacet {\n ...
|
|
|
131
131
|
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
132
132
|
*/
|
|
133
133
|
export function gql(source: "\n fragment ProductDetailsFragment_product on StoreProduct {\n id: productID\n sku\n name\n gtin\n description\n unitMultiplier\n isVariantOf {\n name\n productGroupID\n\t\t\tskuVariants {\n activeVariations\n slugsMap\n availableVariations\n allVariantProducts {\n name\n productID\n }\n }\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n priceWithTaxes\n listPrice\n listPriceWithTaxes\n quantity\n priceToken\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n # Contains necessary info to add this item to cart\n ...CartProductItem\n }\n"): typeof import('./graphql').ProductDetailsFragment_ProductFragmentDoc;
|
|
134
|
-
/**
|
|
135
|
-
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
136
|
-
*/
|
|
137
|
-
export function gql(source: "query ClientRecommendationsQuery(\n $campaignVrn: String!\n $userId: String\n $products: [String!]\n) {\n recommendations(\n userId: $userId\n campaignVrn: $campaignVrn\n products: $products\n ) {\n products {\n ...ProductSummary_product\n }\n correlationId\n campaign {\n id\n title\n type\n }\n }\n}\n"): typeof import('./graphql').ClientRecommendationsQueryDocument;
|
|
138
134
|
/**
|
|
139
135
|
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
140
136
|
*/
|
|
@@ -299,6 +295,10 @@ export function gql(source: "\n query ClientManyProductsSelectedQuery(\n $pr
|
|
|
299
295
|
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
300
296
|
*/
|
|
301
297
|
export function gql(source: "\n query ClientProfileQuery($id: String!) {\n profile(id: $id) {\n addresses {\n country\n postalCode\n geoCoordinate\n city\n }\n }\n }\n"): typeof import('./graphql').ClientProfileQueryDocument;
|
|
298
|
+
/**
|
|
299
|
+
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
300
|
+
*/
|
|
301
|
+
export function gql(source: "query ClientRecommendationsQuery(\n $campaignVrn: String!\n $userId: String\n $products: [String!]\n) {\n recommendations(\n userId: $userId\n campaignVrn: $campaignVrn\n products: $products\n ) {\n products {\n ...ProductSummary_product\n }\n correlationId\n campaign {\n id\n title\n type\n }\n }\n}\n"): typeof import('./graphql').ClientRecommendationsQueryDocument;
|
|
302
302
|
/**
|
|
303
303
|
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
304
304
|
*/
|
package/@generated/graphql.ts
CHANGED
|
@@ -2727,15 +2727,6 @@ export type Filter_FacetsFragment =
|
|
|
2727
2727
|
|
|
2728
2728
|
export type ProductDetailsFragment_ProductFragment = { sku: string, name: string, gtin: string, description: string, unitMultiplier: number | null, id: string, isVariantOf: { name: string, productGroupID: string, skuVariants: { activeVariations: any | null, slugsMap: any | null, availableVariations: any | null, allVariantProducts: Array<{ name: string, productID: string }> | null } | null }, image: Array<{ url: string, alternateName: string }>, brand: { name: string }, offers: { lowPrice: number, lowPriceWithTaxes: number, offers: Array<{ availability: string, price: number, priceWithTaxes: number, listPrice: number, listPriceWithTaxes: number, quantity: number, priceToken: string | null, seller: { identifier: string } }> }, additionalProperty: Array<{ propertyID: string, name: string, value: any, valueReference: any }> };
|
|
2729
2729
|
|
|
2730
|
-
export type ClientRecommendationsQueryQueryVariables = Exact<{
|
|
2731
|
-
campaignVrn: Scalars['String']['input'];
|
|
2732
|
-
userId: InputMaybe<Scalars['String']['input']>;
|
|
2733
|
-
products: InputMaybe<Array<Scalars['String']['input']> | Scalars['String']['input']>;
|
|
2734
|
-
}>;
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
export type ClientRecommendationsQueryQuery = { recommendations: { correlationId: string, products: Array<{ slug: string, sku: string, name: string, gtin: string, unitMultiplier: number | null, hasSpecifications: boolean | null, id: string, brand: { name: string, brandName: string }, isVariantOf: { productGroupID: string, name: string, skuVariants: { allVariantsByName: any | null, activeVariations: any | null, slugsMap: any | null, availableVariations: any | null, allVariantProducts: Array<{ name: string, productID: string }> | null } | null }, image: Array<{ url: string, alternateName: string }>, offers: { lowPrice: number, lowPriceWithTaxes: number, offers: Array<{ availability: string, price: number, listPrice: number, listPriceWithTaxes: number, priceWithTaxes: number, quantity: number, priceToken: string | null, seller: { identifier: string } }> }, additionalProperty: Array<{ propertyID: string, name: string, value: any, valueReference: any }>, advertisement: { adId: string, adResponseId: string } | null, deliveryPromiseBadges: Array<{ typeName: string | null } | null> | null }>, campaign: { id: string, title: string | null, type: string } } };
|
|
2738
|
-
|
|
2739
2730
|
export type ProductComparisonFragment_ProductFragment = { sku: string, slug: string, name: string, gtin: string, description: string, unitMultiplier: number | null, hasSpecifications: boolean | null, id: string, isVariantOf: { name: string, productGroupID: string, skuVariants: { activeVariations: any | null, slugsMap: any | null, availableVariations: any | null, allVariantProducts: Array<{ name: string, productID: string }> | null } | null }, image: Array<{ url: string, alternateName: string }>, brand: { name: string }, offers: { lowPrice: number, lowPriceWithTaxes: number, offers: Array<{ availability: string, price: number, priceWithTaxes: number, listPrice: number, quantity: number, listPriceWithTaxes: number, priceToken: string | null, seller: { identifier: string } }> }, additionalProperty: Array<{ propertyID: string, name: string, value: any, valueReference: any }>, advertisement: { adId: string, adResponseId: string } | null, skuSpecifications: Array<{ field: { id: string | null, name: string, originalName: string | null }, values: Array<{ name: string, id: string | null, fieldId: string | null, originalName: string | null }> }>, specificationGroups: Array<{ name: string, originalName: string, specifications: Array<{ name: string, originalName: string, values: Array<string> }> }> };
|
|
2740
2731
|
|
|
2741
2732
|
export type ProductSkuMatrixSidebarFragment_ProductFragment = { id: string, isVariantOf: { name: string, productGroupID: string, skuVariants: { activeVariations: any | null, slugsMap: any | null, availableVariations: any | null, allVariantProducts: Array<{ sku: string, name: string, image: Array<{ url: string, alternateName: string }>, offers: { highPrice: number, lowPrice: number, lowPriceWithTaxes: number, offerCount: number, priceCurrency: string, offers: Array<{ listPrice: number, listPriceWithTaxes: number, sellingPrice: number, priceCurrency: string, price: number, priceWithTaxes: number, priceValidUntil: string, itemCondition: string, availability: string, quantity: number, priceToken: string | null }> }, additionalProperty: Array<{ propertyID: string, value: any, name: string, valueReference: any }> }> | null } | null } };
|
|
@@ -2994,6 +2985,15 @@ export type ClientProfileQueryQueryVariables = Exact<{
|
|
|
2994
2985
|
|
|
2995
2986
|
export type ClientProfileQueryQuery = { profile: { addresses: Array<{ country: string | null, postalCode: string | null, geoCoordinate: Array<number | null> | null, city: string | null } | null> | null } | null };
|
|
2996
2987
|
|
|
2988
|
+
export type ClientRecommendationsQueryQueryVariables = Exact<{
|
|
2989
|
+
campaignVrn: Scalars['String']['input'];
|
|
2990
|
+
userId: InputMaybe<Scalars['String']['input']>;
|
|
2991
|
+
products: InputMaybe<Array<Scalars['String']['input']> | Scalars['String']['input']>;
|
|
2992
|
+
}>;
|
|
2993
|
+
|
|
2994
|
+
|
|
2995
|
+
export type ClientRecommendationsQueryQuery = { recommendations: { correlationId: string, products: Array<{ slug: string, sku: string, name: string, gtin: string, unitMultiplier: number | null, hasSpecifications: boolean | null, id: string, brand: { name: string, brandName: string }, isVariantOf: { productGroupID: string, name: string, skuVariants: { allVariantsByName: any | null, activeVariations: any | null, slugsMap: any | null, availableVariations: any | null, allVariantProducts: Array<{ name: string, productID: string }> | null } | null }, image: Array<{ url: string, alternateName: string }>, offers: { lowPrice: number, lowPriceWithTaxes: number, offers: Array<{ availability: string, price: number, listPrice: number, listPriceWithTaxes: number, priceWithTaxes: number, quantity: number, priceToken: string | null, seller: { identifier: string } }> }, additionalProperty: Array<{ propertyID: string, name: string, value: any, valueReference: any }>, advertisement: { adId: string, adResponseId: string } | null, deliveryPromiseBadges: Array<{ typeName: string | null } | null> | null }>, campaign: { id: string, title: string | null, type: string } } };
|
|
2996
|
+
|
|
2997
2997
|
export type ClientSearchSuggestionsQueryQueryVariables = Exact<{
|
|
2998
2998
|
term: Scalars['String']['input'];
|
|
2999
2999
|
selectedFacets: InputMaybe<Array<IStoreSelectedFacet> | IStoreSelectedFacet>;
|
|
@@ -3555,7 +3555,6 @@ export const SearchEvent_MetadataFragmentDoc = new TypedDocumentString(`
|
|
|
3555
3555
|
fuzzy
|
|
3556
3556
|
}
|
|
3557
3557
|
`, {"fragmentName":"SearchEvent_metadata"}) as unknown as TypedDocumentString<SearchEvent_MetadataFragment, unknown>;
|
|
3558
|
-
export const ClientRecommendationsQueryDocument = {"__meta__":{"operationName":"ClientRecommendationsQuery","operationHash":"b227071b0388469d7b90d04cff4cbc11f8f134d4"}} as unknown as TypedDocumentString<ClientRecommendationsQueryQuery, ClientRecommendationsQueryQueryVariables>;
|
|
3559
3558
|
export const ServerAccountPageQueryDocument = {"__meta__":{"operationName":"ServerAccountPageQuery","operationHash":"9baae331b75848a310fecb457e8c971ae27897ff"}} as unknown as TypedDocumentString<ServerAccountPageQueryQuery, ServerAccountPageQueryQueryVariables>;
|
|
3560
3559
|
export const ServerCollectionPageQueryDocument = {"__meta__":{"operationName":"ServerCollectionPageQuery","operationHash":"d46841b30ae1f6350021b5cf02f253d56c848664"}} as unknown as TypedDocumentString<ServerCollectionPageQueryQuery, ServerCollectionPageQueryQueryVariables>;
|
|
3561
3560
|
export const ServerProductQueryDocument = {"__meta__":{"operationName":"ServerProductQuery","operationHash":"b89e93519be01aebc01c402489a0ae640d38675a"}} as unknown as TypedDocumentString<ServerProductQueryQuery, ServerProductQueryQueryVariables>;
|
|
@@ -3586,6 +3585,7 @@ export const ClientProductQueryDocument = {"__meta__":{"operationName":"ClientPr
|
|
|
3586
3585
|
export const ClientManyProductsQueryDocument = {"__meta__":{"operationName":"ClientManyProductsQuery","operationHash":"ee14fd92b4a04fc3751efcbfcb60b3886d912253"}} as unknown as TypedDocumentString<ClientManyProductsQueryQuery, ClientManyProductsQueryQueryVariables>;
|
|
3587
3586
|
export const ClientManyProductsSelectedQueryDocument = {"__meta__":{"operationName":"ClientManyProductsSelectedQuery","operationHash":"7e1b06c167e411905ceac9db11c1a6a7892cfdd6"}} as unknown as TypedDocumentString<ClientManyProductsSelectedQueryQuery, ClientManyProductsSelectedQueryQueryVariables>;
|
|
3588
3587
|
export const ClientProfileQueryDocument = {"__meta__":{"operationName":"ClientProfileQuery","operationHash":"34ea14c0d4a57ddf9bc11e4be0cd2b5a6506d3d4"}} as unknown as TypedDocumentString<ClientProfileQueryQuery, ClientProfileQueryQueryVariables>;
|
|
3588
|
+
export const ClientRecommendationsQueryDocument = {"__meta__":{"operationName":"ClientRecommendationsQuery","operationHash":"b227071b0388469d7b90d04cff4cbc11f8f134d4"}} as unknown as TypedDocumentString<ClientRecommendationsQueryQuery, ClientRecommendationsQueryQueryVariables>;
|
|
3589
3589
|
export const ClientSearchSuggestionsQueryDocument = {"__meta__":{"operationName":"ClientSearchSuggestionsQuery","operationHash":"ee57cd392fdfde90620787c9954e2ecae47881f9"}} as unknown as TypedDocumentString<ClientSearchSuggestionsQueryQuery, ClientSearchSuggestionsQueryQueryVariables>;
|
|
3590
3590
|
export const ClientTopSearchSuggestionsQueryDocument = {"__meta__":{"operationName":"ClientTopSearchSuggestionsQuery","operationHash":"e2385b0f11726d0068f96548f57a8dd441c064e3"}} as unknown as TypedDocumentString<ClientTopSearchSuggestionsQueryQuery, ClientTopSearchSuggestionsQueryQueryVariables>;
|
|
3591
3591
|
export const ValidateSessionDocument = {"__meta__":{"operationName":"ValidateSession","operationHash":"8c3a5999496e227f167e9dc79697e6c478d48a9e"}} as unknown as TypedDocumentString<ValidateSessionMutation, ValidateSessionMutationVariables>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"b227071b0388469d7b90d04cff4cbc11f8f134d4": "fragment ProductSummary_product on StoreProduct { additionalProperty { name propertyID value valueReference } advertisement { adId adResponseId } brand { brandName: name } brand { name } deliveryPromiseBadges { typeName } gtin hasSpecifications image { alternateName url } isVariantOf { name productGroupID skuVariants { activeVariations allVariantsByName availableVariations slugsMap } } isVariantOf { name productGroupID skuVariants { activeVariations allVariantProducts { name productID } availableVariations slugsMap } } name offers { lowPrice lowPriceWithTaxes offers { availability listPrice listPriceWithTaxes price priceToken priceWithTaxes quantity seller { identifier } } } id: productID sku slug unitMultiplier unitMultiplier } query ClientRecommendationsQuery($campaignVrn: String!, $products: [String!], $userId: String) { recommendations(userId: $userId, campaignVrn: $campaignVrn, products: $products) { campaign { id title type } correlationId products { ...ProductSummary_product } } }",
|
|
3
2
|
"9baae331b75848a310fecb457e8c971ae27897ff": "query ServerAccountPageQuery { accountProfile { name } }",
|
|
4
3
|
"d46841b30ae1f6350021b5cf02f253d56c848664": "fragment ServerCollectionPage on Query { collection(slug: $slug) { id } } query ServerCollectionPageQuery($slug: String!) { collection(slug: $slug) { breadcrumbList { itemListElement { item name position } } meta { selectedFacets { key value } } otherLocales { locale slug } seo { description title } } ...ServerCollectionPage }",
|
|
5
4
|
"b89e93519be01aebc01c402489a0ae640d38675a": "fragment CartProductItem on StoreProduct { additionalProperty { name propertyID value valueReference } brand { name } gtin image { alternateName url } isVariantOf { name productGroupID skuVariants { activeVariations availableVariations slugsMap } } name sku unitMultiplier } fragment ProductDetailsFragment_product on StoreProduct { additionalProperty { name propertyID value valueReference } brand { name } description gtin image { alternateName url } isVariantOf { name productGroupID skuVariants { activeVariations allVariantProducts { name productID } availableVariations slugsMap } } name offers { lowPrice lowPriceWithTaxes offers { availability listPrice listPriceWithTaxes price priceToken priceWithTaxes quantity seller { identifier } } } id: productID sku unitMultiplier ...CartProductItem } fragment ServerProduct on Query { product(locator: $locator) { id: productID } } query ServerProductQuery($locator: [IStoreSelectedFacet!]!) { product(locator: $locator) { brand { name } breadcrumbList { itemListElement { item name position } } description gtin image { alternateName url } isVariantOf { productGroupID } mpn name offers { highPrice lowPrice lowPriceWithTaxes offers { availability itemCondition price priceCurrency priceToken priceValidUntil seller { identifier } } priceCurrency } otherLocales { locale slug } id: productID releaseDate seo { canonical description title } sku ...ProductDetailsFragment_product } ...ServerProduct }",
|
|
@@ -30,6 +29,7 @@
|
|
|
30
29
|
"ee14fd92b4a04fc3751efcbfcb60b3886d912253": "fragment ClientManyProducts on Query { search( first: $first after: $after sort: $sort term: $term selectedFacets: $selectedFacets sponsoredCount: $sponsoredCount ) { products { pageInfo { totalCount } } } } fragment ProductSummary_product on StoreProduct { additionalProperty { name propertyID value valueReference } advertisement { adId adResponseId } brand { brandName: name } brand { name } deliveryPromiseBadges { typeName } gtin hasSpecifications image { alternateName url } isVariantOf { name productGroupID skuVariants { activeVariations allVariantsByName availableVariations slugsMap } } isVariantOf { name productGroupID skuVariants { activeVariations allVariantProducts { name productID } availableVariations slugsMap } } name offers { lowPrice lowPriceWithTaxes offers { availability listPrice listPriceWithTaxes price priceToken priceWithTaxes quantity seller { identifier } } } id: productID sku slug unitMultiplier unitMultiplier } query ClientManyProductsQuery($after: String, $first: Int!, $selectedFacets: [IStoreSelectedFacet!]!, $sort: StoreSort!, $sponsoredCount: Int, $term: String!) { search( first: $first after: $after sort: $sort term: $term selectedFacets: $selectedFacets sponsoredCount: $sponsoredCount ) { products { edges { node { ...ProductSummary_product } } pageInfo { totalCount } } } ...ClientManyProducts }",
|
|
31
30
|
"7e1b06c167e411905ceac9db11c1a6a7892cfdd6": "fragment ProductComparisonFragment_product on StoreProduct { additionalProperty { name propertyID value valueReference } advertisement { adId adResponseId } brand { name } description gtin hasSpecifications image { alternateName url } isVariantOf { name productGroupID skuVariants { activeVariations allVariantProducts { name productID } availableVariations slugsMap } } name offers { lowPrice lowPriceWithTaxes offers { availability listPrice listPriceWithTaxes price priceToken priceWithTaxes quantity seller { identifier } } } id: productID sku skuSpecifications { field { id name originalName } values { fieldId id name originalName } } slug specificationGroups { name originalName specifications { name originalName values } } unitMultiplier } query ClientManyProductsSelectedQuery($productIds: [String!]!) { products(productIds: $productIds) { ...ProductComparisonFragment_product } }",
|
|
32
31
|
"34ea14c0d4a57ddf9bc11e4be0cd2b5a6506d3d4": "query ClientProfileQuery($id: String!) { profile(id: $id) { addresses { city country geoCoordinate postalCode } } }",
|
|
32
|
+
"b227071b0388469d7b90d04cff4cbc11f8f134d4": "fragment ProductSummary_product on StoreProduct { additionalProperty { name propertyID value valueReference } advertisement { adId adResponseId } brand { brandName: name } brand { name } deliveryPromiseBadges { typeName } gtin hasSpecifications image { alternateName url } isVariantOf { name productGroupID skuVariants { activeVariations allVariantsByName availableVariations slugsMap } } isVariantOf { name productGroupID skuVariants { activeVariations allVariantProducts { name productID } availableVariations slugsMap } } name offers { lowPrice lowPriceWithTaxes offers { availability listPrice listPriceWithTaxes price priceToken priceWithTaxes quantity seller { identifier } } } id: productID sku slug unitMultiplier unitMultiplier } query ClientRecommendationsQuery($campaignVrn: String!, $products: [String!], $userId: String) { recommendations(userId: $userId, campaignVrn: $campaignVrn, products: $products) { campaign { id title type } correlationId products { ...ProductSummary_product } } }",
|
|
33
33
|
"ee57cd392fdfde90620787c9954e2ecae47881f9": "fragment ClientSearchSuggestions on Query { search(first: 5, term: $term, selectedFacets: $selectedFacets) { suggestions { terms { value } } } } fragment ProductSummary_product on StoreProduct { additionalProperty { name propertyID value valueReference } advertisement { adId adResponseId } brand { brandName: name } brand { name } deliveryPromiseBadges { typeName } gtin hasSpecifications image { alternateName url } isVariantOf { name productGroupID skuVariants { activeVariations allVariantsByName availableVariations slugsMap } } isVariantOf { name productGroupID skuVariants { activeVariations allVariantProducts { name productID } availableVariations slugsMap } } name offers { lowPrice lowPriceWithTaxes offers { availability listPrice listPriceWithTaxes price priceToken priceWithTaxes quantity seller { identifier } } } id: productID sku slug unitMultiplier unitMultiplier } fragment SearchEvent_metadata on SearchMetadata { fuzzy isTermMisspelled logicalOperator } query ClientSearchSuggestionsQuery($selectedFacets: [IStoreSelectedFacet!], $term: String!) { search(first: 5, term: $term, selectedFacets: $selectedFacets) { metadata { ...SearchEvent_metadata } products { pageInfo { totalCount } } searchId suggestions { products { ...ProductSummary_product } terms { value } } } ...ClientSearchSuggestions }",
|
|
34
34
|
"e2385b0f11726d0068f96548f57a8dd441c064e3": "fragment ClientTopSearchSuggestions on Query { search(first: 5, term: $term, selectedFacets: $selectedFacets) { suggestions { terms { value } } } } query ClientTopSearchSuggestionsQuery($selectedFacets: [IStoreSelectedFacet!], $term: String!) { search(first: 5, term: $term, selectedFacets: $selectedFacets) { suggestions { terms { value } } } ...ClientTopSearchSuggestions }",
|
|
35
35
|
"8c3a5999496e227f167e9dc79697e6c478d48a9e": "mutation ValidateSession($search: String!, $session: IStoreSession!) { validateSession(session: $session, search: $search) { addressType b2b { contractName customerId firstName isRepresentative lastName organizationManager savedPostalCode unitId unitName userEmail userName } channel city country currency { code symbol } deliveryMode { deliveryChannel deliveryMethod deliveryWindow { endDate startDate } } geoCoordinates { latitude longitude } locale marketingData { utmCampaign utmMedium utmSource utmiCampaign utmiPage utmiPart } person { email familyName givenName id } postalCode refreshAfter } }",
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [4.6.0-dev.4](https://github.com/vtex/faststore/compare/v4.6.0-dev.3...v4.6.0-dev.4) (2026-08-12)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @faststore/core
|
|
9
|
+
|
|
10
|
+
# [4.6.0-dev.3](https://github.com/vtex/faststore/compare/v4.6.0-dev.2...v4.6.0-dev.3) (2026-08-12)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @faststore/core
|
|
13
|
+
|
|
6
14
|
# [4.6.0-dev.2](https://github.com/vtex/faststore/compare/v4.6.0-dev.1...v4.6.0-dev.2) (2026-08-12)
|
|
7
15
|
|
|
8
16
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faststore/core",
|
|
3
|
-
"version": "4.6.0-dev.
|
|
3
|
+
"version": "4.6.0-dev.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -71,11 +71,11 @@
|
|
|
71
71
|
"style-loader": "^3.3.1",
|
|
72
72
|
"swr": "^2.2.5",
|
|
73
73
|
"use-sync-external-store": "^1.6.0",
|
|
74
|
-
"@faststore/
|
|
75
|
-
"@faststore/
|
|
76
|
-
"@faststore/
|
|
77
|
-
"@faststore/
|
|
78
|
-
"@faststore/
|
|
74
|
+
"@faststore/api": "4.6.0-dev.4",
|
|
75
|
+
"@faststore/lighthouse": "4.6.0-dev.4",
|
|
76
|
+
"@faststore/diagnostics": "4.6.0-dev.4",
|
|
77
|
+
"@faststore/ui": "4.6.0-dev.4",
|
|
78
|
+
"@faststore/sdk": "4.6.0-dev.4"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@cypress/code-coverage": "^3.12.1",
|
|
@@ -99,8 +99,11 @@ All recommendation work runs client-side after hydration:
|
|
|
99
99
|
5. **Render** — shows `ProductShelfSkeleton` while loading; returns `null` on error or empty results; otherwise renders the carousel and optional heading.
|
|
100
100
|
6. **Tracking** — when correlation and campaign identifiers are present, the shelf emits `data-af-*` attributes for Activity Flow. PDP product views use `product:*` meta tags from `pages/[slug]/p.tsx`.
|
|
101
101
|
|
|
102
|
+
Steps 1 to 4 carry no presentation: they live in `src/sdk/recommendations` and are orchestrated by `useRecommendationShelf()`, which the shelf consumes.
|
|
103
|
+
|
|
102
104
|
Related files:
|
|
103
105
|
|
|
106
|
+
- Shared data layer: `src/sdk/recommendations/`
|
|
104
107
|
- Shelf: `src/components/sections/RecommendationShelf/`
|
|
105
108
|
- Hook: `src/sdk/analytics/hooks/useStartRecommendationSession.ts`
|
|
106
109
|
- Layout: `src/Layout.tsx`
|
|
@@ -129,7 +132,7 @@ import { RecommendationShelf } from 'src/components/sections/RecommendationShelf
|
|
|
129
132
|
- **`ProductCard`**: custom card component (defaults to the core `ProductCard`).
|
|
130
133
|
- **`mapProductToProductCard`**: maps each recommended product (normalized `StoreProduct`) into card props. When provided, it fully owns the card props and the default `productCardConfiguration` merge no longer applies.
|
|
131
134
|
|
|
132
|
-
Campaign types and VRN validation are defined in `vrn.ts` (`VRN_TYPE_TO_RECOMMENDATION`). Updates should be made there and mirrored in the CMS schema `pattern`.
|
|
135
|
+
Campaign types and VRN validation are defined in `src/sdk/recommendations/vrn.ts` (`VRN_TYPE_TO_RECOMMENDATION`). Updates should be made there and mirrored in the CMS schema `pattern`.
|
|
133
136
|
|
|
134
137
|
## Placement guidelines
|
|
135
138
|
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { type ComponentType, useId, useMemo } from 'react'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { ProductShelf, Carousel } from '@faststore/ui'
|
|
3
|
+
import { Carousel, ProductShelf } from '@faststore/ui'
|
|
5
4
|
import type { ProductSummary_ProductFragment } from '@generated/graphql'
|
|
6
5
|
|
|
7
6
|
import DefaultProductCard, {
|
|
8
7
|
type ProductCardProps,
|
|
9
8
|
} from 'src/components/product/ProductCard'
|
|
10
9
|
import ProductShelfSkeleton from 'src/components/skeletons/ProductShelfSkeleton'
|
|
11
|
-
import {
|
|
10
|
+
import { useRecommendationShelf } from 'src/sdk/recommendations'
|
|
12
11
|
import useScreenResize from 'src/sdk/ui/useScreenResize'
|
|
13
12
|
|
|
14
13
|
import type {
|
|
@@ -16,48 +15,6 @@ import type {
|
|
|
16
15
|
RecommendationShelfProps,
|
|
17
16
|
} from './RecommendationShelf.types'
|
|
18
17
|
import styles from './section.module.scss'
|
|
19
|
-
import {
|
|
20
|
-
useRecommendations,
|
|
21
|
-
type RecommendationInput,
|
|
22
|
-
} from './useRecommendations'
|
|
23
|
-
import { useRecommendationUserId } from './useRecommendationUserId'
|
|
24
|
-
import { getTypeFromVrn } from './vrn'
|
|
25
|
-
|
|
26
|
-
function getRecommendationArguments(
|
|
27
|
-
campaignVrn: string,
|
|
28
|
-
context: { userId?: string | null; contextProducts: string[] }
|
|
29
|
-
): RecommendationInput | null {
|
|
30
|
-
const { userId, contextProducts } = context
|
|
31
|
-
const type = getTypeFromVrn(campaignVrn)
|
|
32
|
-
|
|
33
|
-
// `type` is null for malformed/unknown VRNs; bail out so we never fetch (and
|
|
34
|
-
// never throw) on an invalid campaign coming from the CMS.
|
|
35
|
-
if (!type || !userId) return null
|
|
36
|
-
|
|
37
|
-
switch (type) {
|
|
38
|
-
case 'NEXT_INTERACTION':
|
|
39
|
-
case 'VISUAL_SIMILARITY':
|
|
40
|
-
case 'CROSS_SELL':
|
|
41
|
-
case 'SIMILAR_ITEMS':
|
|
42
|
-
// These campaigns need product context. Without any (e.g. `CART` context
|
|
43
|
-
// on an empty cart, or `PDP` context outside a product page), skip the
|
|
44
|
-
// fetch instead of requesting recommendations we can't anchor.
|
|
45
|
-
if (contextProducts.length === 0) {
|
|
46
|
-
return null
|
|
47
|
-
}
|
|
48
|
-
return {
|
|
49
|
-
userId,
|
|
50
|
-
campaignVrn,
|
|
51
|
-
products: contextProducts,
|
|
52
|
-
}
|
|
53
|
-
default:
|
|
54
|
-
return {
|
|
55
|
-
userId,
|
|
56
|
-
campaignVrn,
|
|
57
|
-
products: [],
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
18
|
|
|
62
19
|
export function RecommendationShelf<
|
|
63
20
|
TCardProps extends object = ProductCardProps,
|
|
@@ -96,43 +53,14 @@ export function RecommendationShelf<
|
|
|
96
53
|
const itemsPerPage =
|
|
97
54
|
isMobile || isTablet ? itemsPerPageMobile : itemsPerPageDesktop
|
|
98
55
|
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// source: the current PDP product, or the (deduplicated) cart items.
|
|
106
|
-
const contextProducts = useMemo(() => {
|
|
107
|
-
if (itemsContext === 'CART') {
|
|
108
|
-
return Array.from(
|
|
109
|
-
new Set(
|
|
110
|
-
cartItems
|
|
111
|
-
.map((item) => item.itemOffered.isVariantOf.productGroupID)
|
|
112
|
-
.filter(Boolean)
|
|
113
|
-
)
|
|
114
|
-
)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const pdpProduct = productDetailPage?.product?.isVariantOf?.productGroupID
|
|
118
|
-
|
|
119
|
-
return pdpProduct ? [pdpProduct] : []
|
|
120
|
-
}, [itemsContext, cartItems, productDetailPage])
|
|
121
|
-
|
|
122
|
-
// Gate the whole feature behind the CMS opt-in: when Recommendations is
|
|
123
|
-
// disabled the shelf never requests recommendations (nor renders).
|
|
124
|
-
const recommendationArgs = enableRecommendations
|
|
125
|
-
? getRecommendationArguments(campaignVrn, {
|
|
126
|
-
userId,
|
|
127
|
-
contextProducts,
|
|
128
|
-
})
|
|
129
|
-
: null
|
|
130
|
-
|
|
131
|
-
const { data, isLoading, error } = useRecommendations(recommendationArgs)
|
|
56
|
+
const { items, isLoading, error, campaign, correlationId } =
|
|
57
|
+
useRecommendationShelf({
|
|
58
|
+
campaignVrn,
|
|
59
|
+
enableRecommendations,
|
|
60
|
+
itemsContext,
|
|
61
|
+
})
|
|
132
62
|
|
|
133
|
-
const
|
|
134
|
-
const correlationId = data?.correlationId
|
|
135
|
-
const campaignId = data?.campaign.id
|
|
63
|
+
const campaignId = campaign?.id
|
|
136
64
|
|
|
137
65
|
const productIds = useMemo(
|
|
138
66
|
() => items.map((p) => p.isVariantOf.productGroupID).join(', '),
|
|
@@ -147,15 +75,6 @@ export function RecommendationShelf<
|
|
|
147
75
|
)
|
|
148
76
|
|
|
149
77
|
if (error) {
|
|
150
|
-
// Don't log `recommendationArgs`: it carries the userId. Log only
|
|
151
|
-
// non-identifying context.
|
|
152
|
-
console.error(
|
|
153
|
-
'Error fetching recommendations',
|
|
154
|
-
error.cause,
|
|
155
|
-
error.message,
|
|
156
|
-
'for campaign',
|
|
157
|
-
campaignVrn
|
|
158
|
-
)
|
|
159
78
|
return null
|
|
160
79
|
}
|
|
161
80
|
|
|
@@ -178,9 +97,9 @@ export function RecommendationShelf<
|
|
|
178
97
|
: {})}
|
|
179
98
|
>
|
|
180
99
|
<ProductShelfSkeleton loading={isLoading} itemsPerPage={itemsPerPage}>
|
|
181
|
-
{(title ||
|
|
100
|
+
{(title || campaign?.title) && (
|
|
182
101
|
<h2 className="text__title-section layout__content">
|
|
183
|
-
{title ||
|
|
102
|
+
{title || campaign?.title}
|
|
184
103
|
</h2>
|
|
185
104
|
)}
|
|
186
105
|
|
|
@@ -4,6 +4,9 @@ import type { CarouselProps } from '@faststore/ui'
|
|
|
4
4
|
import type { ProductSummary_ProductFragment } from '@generated/graphql'
|
|
5
5
|
|
|
6
6
|
import type { ProductCardProps } from 'src/components/product/ProductCard'
|
|
7
|
+
import type { ItemContext } from 'src/sdk/recommendations'
|
|
8
|
+
|
|
9
|
+
export type { ItemContext, RecommendationType } from 'src/sdk/recommendations'
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* Maps a recommendation product (a normalized `StoreProduct`, identical to the
|
|
@@ -17,14 +20,6 @@ export type RecommendationProductCardMapper<TCardProps = ProductCardProps> = (
|
|
|
17
20
|
index: number
|
|
18
21
|
) => TCardProps
|
|
19
22
|
|
|
20
|
-
/**
|
|
21
|
-
* Source of the products used as context for the recommendation request:
|
|
22
|
-
* - `'PDP'`: the current product detail page product.
|
|
23
|
-
* - `'CART'`: the products currently in the cart (useful for cross-sell on the
|
|
24
|
-
* cart page).
|
|
25
|
-
*/
|
|
26
|
-
export type ItemContext = 'PDP' | 'CART'
|
|
27
|
-
|
|
28
23
|
export type RecommendationShelfProps<
|
|
29
24
|
TCardProps extends object = ProductCardProps,
|
|
30
25
|
> = {
|
|
@@ -106,13 +101,3 @@ export type RecommendationShelfProps<
|
|
|
106
101
|
readonly bordered?: boolean
|
|
107
102
|
}
|
|
108
103
|
}
|
|
109
|
-
|
|
110
|
-
export type RecommendationType =
|
|
111
|
-
| 'CROSS_SELL'
|
|
112
|
-
| 'SIMILAR_ITEMS'
|
|
113
|
-
| 'PERSONALIZED'
|
|
114
|
-
| 'TOP_ITEMS'
|
|
115
|
-
| 'LAST_SEEN'
|
|
116
|
-
| 'SEARCH_BASED'
|
|
117
|
-
| 'VISUAL_SIMILARITY'
|
|
118
|
-
| 'NEXT_INTERACTION'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type { ItemContext, RecommendationType } from './types'
|
|
2
|
+
export type {
|
|
3
|
+
RecommendationInput,
|
|
4
|
+
RecommendationProduct,
|
|
5
|
+
RecommendationResponse,
|
|
6
|
+
} from './useRecommendations'
|
|
7
|
+
export { useRecommendations } from './useRecommendations'
|
|
8
|
+
export { useRecommendationUserId } from './useRecommendationUserId'
|
|
9
|
+
export type {
|
|
10
|
+
UseRecommendationShelfArgs,
|
|
11
|
+
UseRecommendationShelfResult,
|
|
12
|
+
} from './useRecommendationShelf'
|
|
13
|
+
export {
|
|
14
|
+
getRecommendationArguments,
|
|
15
|
+
useRecommendationShelf,
|
|
16
|
+
} from './useRecommendationShelf'
|
|
17
|
+
export { getTypeFromVrn, isValidVrn } from './vrn'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source of the products used as context for the recommendation request:
|
|
3
|
+
* - `'PDP'`: the current product detail page product.
|
|
4
|
+
* - `'CART'`: the products currently in the cart (useful for cross-sell on the
|
|
5
|
+
* cart page and inside the cart drawer).
|
|
6
|
+
*/
|
|
7
|
+
export type ItemContext = 'PDP' | 'CART'
|
|
8
|
+
|
|
9
|
+
export type RecommendationType =
|
|
10
|
+
| 'CROSS_SELL'
|
|
11
|
+
| 'SIMILAR_ITEMS'
|
|
12
|
+
| 'PERSONALIZED'
|
|
13
|
+
| 'TOP_ITEMS'
|
|
14
|
+
| 'LAST_SEEN'
|
|
15
|
+
| 'SEARCH_BASED'
|
|
16
|
+
| 'VISUAL_SIMILARITY'
|
|
17
|
+
| 'NEXT_INTERACTION'
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { usePDP } from '@faststore/core'
|
|
2
|
+
import { useMemo } from 'react'
|
|
3
|
+
|
|
4
|
+
import { useCart } from 'src/sdk/cart'
|
|
5
|
+
|
|
6
|
+
import type { ItemContext } from './types'
|
|
7
|
+
import {
|
|
8
|
+
type RecommendationInput,
|
|
9
|
+
type RecommendationResponse,
|
|
10
|
+
useRecommendations,
|
|
11
|
+
} from './useRecommendations'
|
|
12
|
+
import { useRecommendationUserId } from './useRecommendationUserId'
|
|
13
|
+
import { getTypeFromVrn } from './vrn'
|
|
14
|
+
|
|
15
|
+
export function getRecommendationArguments(
|
|
16
|
+
campaignVrn: string,
|
|
17
|
+
context: { userId?: string | null; contextProducts: string[] }
|
|
18
|
+
): RecommendationInput | null {
|
|
19
|
+
const { userId, contextProducts } = context
|
|
20
|
+
const type = getTypeFromVrn(campaignVrn)
|
|
21
|
+
|
|
22
|
+
// `type` is null for malformed/unknown VRNs; bail out so we never fetch (and
|
|
23
|
+
// never throw) on an invalid campaign coming from the CMS.
|
|
24
|
+
if (!type || !userId) return null
|
|
25
|
+
|
|
26
|
+
switch (type) {
|
|
27
|
+
case 'NEXT_INTERACTION':
|
|
28
|
+
case 'VISUAL_SIMILARITY':
|
|
29
|
+
case 'CROSS_SELL':
|
|
30
|
+
case 'SIMILAR_ITEMS':
|
|
31
|
+
// These campaigns need product context. Without any (e.g. `CART` context
|
|
32
|
+
// on an empty cart, or `PDP` context outside a product page), skip the
|
|
33
|
+
// fetch instead of requesting recommendations we can't anchor.
|
|
34
|
+
if (contextProducts.length === 0) {
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
userId,
|
|
39
|
+
campaignVrn,
|
|
40
|
+
products: contextProducts,
|
|
41
|
+
}
|
|
42
|
+
default:
|
|
43
|
+
return {
|
|
44
|
+
userId,
|
|
45
|
+
campaignVrn,
|
|
46
|
+
products: [],
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type UseRecommendationShelfArgs = {
|
|
52
|
+
readonly campaignVrn: string
|
|
53
|
+
/**
|
|
54
|
+
* Opt-in for VTEX Recommendations. When `false` (default) the campaign is
|
|
55
|
+
* never requested. The anonymous user id is still resolved, but it goes
|
|
56
|
+
* unused and never leaves the browser.
|
|
57
|
+
*/
|
|
58
|
+
readonly enableRecommendations?: boolean
|
|
59
|
+
/**
|
|
60
|
+
* Where to read the products used as context for the recommendation request.
|
|
61
|
+
* @default 'PDP'
|
|
62
|
+
*/
|
|
63
|
+
readonly itemsContext?: ItemContext
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type UseRecommendationShelfResult = {
|
|
67
|
+
readonly items: RecommendationResponse['products']
|
|
68
|
+
readonly isLoading: boolean
|
|
69
|
+
readonly error: unknown
|
|
70
|
+
readonly campaign?: RecommendationResponse['campaign']
|
|
71
|
+
readonly correlationId?: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const EMPTY_ITEMS: RecommendationResponse['products'] = []
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Headless data layer behind every recommendation shelf: resolves the campaign
|
|
78
|
+
* type, the anonymous user id and the product context, then fetches the
|
|
79
|
+
* campaign. It renders nothing — presentation lives in the shelf components
|
|
80
|
+
* that consume it, so a new surface can reuse the campaign rules instead of
|
|
81
|
+
* reimplementing them.
|
|
82
|
+
*/
|
|
83
|
+
export function useRecommendationShelf({
|
|
84
|
+
campaignVrn,
|
|
85
|
+
enableRecommendations = false,
|
|
86
|
+
itemsContext = 'PDP',
|
|
87
|
+
}: UseRecommendationShelfArgs): UseRecommendationShelfResult {
|
|
88
|
+
const userId = useRecommendationUserId(campaignVrn)
|
|
89
|
+
|
|
90
|
+
const { data: productDetailPage } = usePDP()
|
|
91
|
+
const { items: cartItems } = useCart()
|
|
92
|
+
|
|
93
|
+
// Resolve the products used as context for the request from the configured
|
|
94
|
+
// source: the current PDP product, or the (deduplicated) cart items.
|
|
95
|
+
const contextProducts = useMemo(() => {
|
|
96
|
+
if (itemsContext === 'CART') {
|
|
97
|
+
return Array.from(
|
|
98
|
+
new Set(
|
|
99
|
+
cartItems
|
|
100
|
+
.map((item) => item.itemOffered.isVariantOf.productGroupID)
|
|
101
|
+
.filter(Boolean)
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const pdpProduct = productDetailPage?.product?.isVariantOf?.productGroupID
|
|
107
|
+
|
|
108
|
+
return pdpProduct ? [pdpProduct] : []
|
|
109
|
+
}, [itemsContext, cartItems, productDetailPage])
|
|
110
|
+
|
|
111
|
+
// Gate the whole feature behind the CMS opt-in: when Recommendations is
|
|
112
|
+
// disabled the shelf never requests recommendations (nor renders).
|
|
113
|
+
const recommendationArgs = enableRecommendations
|
|
114
|
+
? getRecommendationArguments(campaignVrn, {
|
|
115
|
+
userId,
|
|
116
|
+
contextProducts,
|
|
117
|
+
})
|
|
118
|
+
: null
|
|
119
|
+
|
|
120
|
+
const { data, isLoading, error } = useRecommendations(recommendationArgs)
|
|
121
|
+
|
|
122
|
+
if (error) {
|
|
123
|
+
// Don't log `recommendationArgs`: it carries the userId. Log only
|
|
124
|
+
// non-identifying context.
|
|
125
|
+
console.error(
|
|
126
|
+
'Error fetching recommendations',
|
|
127
|
+
(error as { cause?: unknown })?.cause,
|
|
128
|
+
(error as { message?: string })?.message,
|
|
129
|
+
'for campaign',
|
|
130
|
+
campaignVrn
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
items: error ? EMPTY_ITEMS : (data?.products ?? EMPTY_ITEMS),
|
|
136
|
+
isLoading,
|
|
137
|
+
error,
|
|
138
|
+
campaign: data?.campaign,
|
|
139
|
+
correlationId: data?.correlationId,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RecommendationType } from './
|
|
1
|
+
import type { RecommendationType } from './types'
|
|
2
2
|
|
|
3
3
|
// Single source of truth for the campaign taxonomy: maps each Intelligent Search
|
|
4
4
|
// campaign VRN type to its `RecommendationType`. The supported VRN types, the
|
|
@@ -29,23 +29,17 @@ vi.mock('src/components/skeletons/ProductShelfSkeleton', () => ({
|
|
|
29
29
|
}))
|
|
30
30
|
|
|
31
31
|
const useRecommendations = vi.hoisted(() => vi.fn())
|
|
32
|
-
vi.mock(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
useRecommendations,
|
|
36
|
-
})
|
|
37
|
-
)
|
|
32
|
+
vi.mock('src/sdk/recommendations/useRecommendations', () => ({
|
|
33
|
+
useRecommendations,
|
|
34
|
+
}))
|
|
38
35
|
|
|
39
36
|
const useScreenResize = vi.hoisted(() => vi.fn())
|
|
40
37
|
vi.mock('src/sdk/ui/useScreenResize', () => ({ default: useScreenResize }))
|
|
41
38
|
|
|
42
39
|
const useRecommendationUserId = vi.hoisted(() => vi.fn())
|
|
43
|
-
vi.mock(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
useRecommendationUserId,
|
|
47
|
-
})
|
|
48
|
-
)
|
|
40
|
+
vi.mock('src/sdk/recommendations/useRecommendationUserId', () => ({
|
|
41
|
+
useRecommendationUserId,
|
|
42
|
+
}))
|
|
49
43
|
|
|
50
44
|
import { RecommendationShelf } from 'src/components/sections/RecommendationShelf/RecommendationShelf'
|
|
51
45
|
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react'
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
const usePDP = vi.hoisted(() => vi.fn())
|
|
5
|
+
vi.mock('@faststore/core', () => ({ usePDP }))
|
|
6
|
+
|
|
7
|
+
const useCart = vi.hoisted(() => vi.fn())
|
|
8
|
+
vi.mock('src/sdk/cart', () => ({ useCart }))
|
|
9
|
+
|
|
10
|
+
const useRecommendations = vi.hoisted(() => vi.fn())
|
|
11
|
+
vi.mock('src/sdk/recommendations/useRecommendations', () => ({
|
|
12
|
+
useRecommendations,
|
|
13
|
+
}))
|
|
14
|
+
|
|
15
|
+
const useRecommendationUserId = vi.hoisted(() => vi.fn())
|
|
16
|
+
vi.mock('src/sdk/recommendations/useRecommendationUserId', () => ({
|
|
17
|
+
useRecommendationUserId,
|
|
18
|
+
}))
|
|
19
|
+
|
|
20
|
+
import { useRecommendationShelf } from 'src/sdk/recommendations/useRecommendationShelf'
|
|
21
|
+
|
|
22
|
+
const TOP_ITEMS_VRN = 'vrn:recommendations:acc:rec-top-items-v2:campaign-1'
|
|
23
|
+
const CROSS_SELL_VRN = 'vrn:recommendations:acc:rec-cross-v2:campaign-1'
|
|
24
|
+
|
|
25
|
+
const cartItem = (productGroupID: string) => ({
|
|
26
|
+
itemOffered: { isVariantOf: { productGroupID } },
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const campaignData = {
|
|
30
|
+
products: [
|
|
31
|
+
{ id: 'p-1', isVariantOf: { productGroupID: 'p-1' } },
|
|
32
|
+
{ id: 'p-2', isVariantOf: { productGroupID: 'p-2' } },
|
|
33
|
+
],
|
|
34
|
+
correlationId: 'corr-1',
|
|
35
|
+
campaign: { id: 'camp-1', title: 'Recommended for you', type: 'TOP_ITEMS' },
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const lastArgs = () => useRecommendations.mock.calls.at(-1)?.[0]
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
usePDP.mockReturnValue({ data: undefined })
|
|
42
|
+
useCart.mockReturnValue({ items: [] })
|
|
43
|
+
useRecommendationUserId.mockReturnValue('user-1')
|
|
44
|
+
useRecommendations.mockReturnValue({
|
|
45
|
+
data: campaignData,
|
|
46
|
+
isLoading: false,
|
|
47
|
+
error: null,
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
vi.clearAllMocks()
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
describe('useRecommendationShelf', () => {
|
|
56
|
+
it('exposes the campaign payload when enabled', () => {
|
|
57
|
+
const { result } = renderHook(() =>
|
|
58
|
+
useRecommendationShelf({
|
|
59
|
+
campaignVrn: TOP_ITEMS_VRN,
|
|
60
|
+
enableRecommendations: true,
|
|
61
|
+
})
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
expect(result.current.items).toHaveLength(2)
|
|
65
|
+
expect(result.current.campaign?.title).toBe('Recommended for you')
|
|
66
|
+
expect(result.current.correlationId).toBe('corr-1')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('never fetches while recommendations are disabled', () => {
|
|
70
|
+
renderHook(() => useRecommendationShelf({ campaignVrn: TOP_ITEMS_VRN }))
|
|
71
|
+
|
|
72
|
+
expect(lastArgs()).toBeNull()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('never fetches before a userId resolves', () => {
|
|
76
|
+
useRecommendationUserId.mockReturnValue(null)
|
|
77
|
+
|
|
78
|
+
renderHook(() =>
|
|
79
|
+
useRecommendationShelf({
|
|
80
|
+
campaignVrn: TOP_ITEMS_VRN,
|
|
81
|
+
enableRecommendations: true,
|
|
82
|
+
})
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
expect(lastArgs()).toBeNull()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('never fetches on a malformed campaign vrn', () => {
|
|
89
|
+
renderHook(() =>
|
|
90
|
+
useRecommendationShelf({
|
|
91
|
+
campaignVrn: 'not-a-vrn',
|
|
92
|
+
enableRecommendations: true,
|
|
93
|
+
})
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
expect(lastArgs()).toBeNull()
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('anchors cart-context campaigns on the deduplicated cart products', () => {
|
|
100
|
+
useCart.mockReturnValue({
|
|
101
|
+
items: [cartItem('pg-1'), cartItem('pg-2'), cartItem('pg-1')],
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
renderHook(() =>
|
|
105
|
+
useRecommendationShelf({
|
|
106
|
+
campaignVrn: CROSS_SELL_VRN,
|
|
107
|
+
enableRecommendations: true,
|
|
108
|
+
itemsContext: 'CART',
|
|
109
|
+
})
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
expect(lastArgs()).toEqual({
|
|
113
|
+
userId: 'user-1',
|
|
114
|
+
campaignVrn: CROSS_SELL_VRN,
|
|
115
|
+
products: ['pg-1', 'pg-2'],
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('skips cart-context campaigns while the cart is empty', () => {
|
|
120
|
+
renderHook(() =>
|
|
121
|
+
useRecommendationShelf({
|
|
122
|
+
campaignVrn: CROSS_SELL_VRN,
|
|
123
|
+
enableRecommendations: true,
|
|
124
|
+
itemsContext: 'CART',
|
|
125
|
+
})
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
expect(lastArgs()).toBeNull()
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it('still fetches context-agnostic campaigns on an empty cart', () => {
|
|
132
|
+
renderHook(() =>
|
|
133
|
+
useRecommendationShelf({
|
|
134
|
+
campaignVrn: TOP_ITEMS_VRN,
|
|
135
|
+
enableRecommendations: true,
|
|
136
|
+
itemsContext: 'CART',
|
|
137
|
+
})
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
expect(lastArgs()).toEqual({
|
|
141
|
+
userId: 'user-1',
|
|
142
|
+
campaignVrn: TOP_ITEMS_VRN,
|
|
143
|
+
products: [],
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('follows the live cart context when not frozen', () => {
|
|
148
|
+
useCart.mockReturnValue({ items: [cartItem('pg-1')] })
|
|
149
|
+
|
|
150
|
+
const { rerender } = renderHook(() =>
|
|
151
|
+
useRecommendationShelf({
|
|
152
|
+
campaignVrn: CROSS_SELL_VRN,
|
|
153
|
+
enableRecommendations: true,
|
|
154
|
+
itemsContext: 'CART',
|
|
155
|
+
})
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
useCart.mockReturnValue({ items: [cartItem('pg-1'), cartItem('pg-2')] })
|
|
159
|
+
rerender()
|
|
160
|
+
|
|
161
|
+
expect(lastArgs()?.products).toEqual(['pg-1', 'pg-2'])
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('anchors PDP-context campaigns on the current product', () => {
|
|
165
|
+
usePDP.mockReturnValue({
|
|
166
|
+
data: { product: { isVariantOf: { productGroupID: 'pg-pdp' } } },
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
renderHook(() =>
|
|
170
|
+
useRecommendationShelf({
|
|
171
|
+
campaignVrn: CROSS_SELL_VRN,
|
|
172
|
+
enableRecommendations: true,
|
|
173
|
+
})
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
expect(lastArgs()?.products).toEqual(['pg-pdp'])
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('returns no items and logs without the userId on error', () => {
|
|
180
|
+
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
181
|
+
useRecommendations.mockReturnValue({
|
|
182
|
+
data: undefined,
|
|
183
|
+
isLoading: false,
|
|
184
|
+
error: new Error('boom'),
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
const { result } = renderHook(() =>
|
|
188
|
+
useRecommendationShelf({
|
|
189
|
+
campaignVrn: TOP_ITEMS_VRN,
|
|
190
|
+
enableRecommendations: true,
|
|
191
|
+
})
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
expect(result.current.items).toEqual([])
|
|
195
|
+
expect(result.current.error).toBeInstanceOf(Error)
|
|
196
|
+
expect(consoleError).toHaveBeenCalled()
|
|
197
|
+
expect(JSON.stringify(consoleError.mock.calls)).not.toContain('user-1')
|
|
198
|
+
})
|
|
199
|
+
})
|
package/test/{components/RecommendationShelf → sdk/recommendations}/useRecommendationUserId.test.ts
RENAMED
|
@@ -14,7 +14,7 @@ vi.mock('src/utils/retry', () => ({
|
|
|
14
14
|
retry: (fn: () => unknown) => Promise.resolve().then(fn),
|
|
15
15
|
}))
|
|
16
16
|
|
|
17
|
-
import { useRecommendationUserId } from 'src/
|
|
17
|
+
import { useRecommendationUserId } from 'src/sdk/recommendations/useRecommendationUserId'
|
|
18
18
|
|
|
19
19
|
const CAMPAIGN_VRN = 'vrn:recommendations:acc:rec-top-items-v2:campaign-1'
|
|
20
20
|
|
package/test/{components/RecommendationShelf → sdk/recommendations}/useRecommendations.test.ts
RENAMED
|
@@ -7,7 +7,7 @@ vi.mock('src/sdk/graphql/useQuery', () => ({
|
|
|
7
7
|
useQuery: useQueryMock,
|
|
8
8
|
}))
|
|
9
9
|
|
|
10
|
-
import { useRecommendations } from 'src/
|
|
10
|
+
import { useRecommendations } from 'src/sdk/recommendations/useRecommendations'
|
|
11
11
|
|
|
12
12
|
afterEach(() => {
|
|
13
13
|
vi.clearAllMocks()
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
getTypeFromVrn,
|
|
5
|
-
isValidVrn,
|
|
6
|
-
} from 'src/components/sections/RecommendationShelf/vrn'
|
|
3
|
+
import { getTypeFromVrn, isValidVrn } from 'src/sdk/recommendations/vrn'
|
|
7
4
|
|
|
8
5
|
const vrn = (type: string) => `vrn:recommendations:account:${type}:campaign-1`
|
|
9
6
|
|
|
File without changes
|
/package/src/{components/sections/RecommendationShelf → sdk/recommendations}/useRecommendations.ts
RENAMED
|
File without changes
|