@faststore/core 4.5.1 → 4.6.0-dev.1

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.
@@ -1,9 +1,9 @@
1
1
 
2
- > @faststore/core@4.5.1-dev.2 generate /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@4.6.0-dev.0 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.5.1-dev.2 gen-types /home/runner/work/faststore/faststore/packages/core
6
+ > @faststore/core@4.6.0-dev.0 gen-types /home/runner/work/faststore/faststore/packages/core
7
7
  > node ../cli/bin/run generate-types .
8
8
 
9
9
  [STARTED] Parse Configuration
@@ -19,7 +19,7 @@
19
19
  [COMPLETED] Generate to /home/runner/work/faststore/faststore/packages/core/@generated/
20
20
  [COMPLETED] Generate outputs
21
21
 
22
- > @faststore/core@4.5.1-dev.2 cache-graphql /home/runner/work/faststore/faststore/packages/core
22
+ > @faststore/core@4.6.0-dev.0 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
@@ -1,9 +1,9 @@
1
1
 
2
- > @faststore/core@4.5.1-dev.2 test /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@4.6.0-dev.0 test /home/runner/work/faststore/faststore/packages/core
3
3
  > vitest run
4
4
 
5
5
 
6
-  RUN  v4.0.7 /home/runner/work/faststore/faststore/packages/core
6
+  RUN  v4.0.18 /home/runner/work/faststore/faststore/packages/core
7
7
 
8
8
  Not implemented: navigation to another Document
9
9
  Not implemented: navigation to another Document
@@ -19,7 +19,7 @@ Not implemented: navigation to another Document
19
19
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:17) {
20
20
  [cause]: TypeError: Invalid URL
21
21
   at new URL (node:internal/url:828:25)
22
- at new URL$1 (file:///home/runner/work/faststore/faststore/node_modules/.pnpm/vitest@4.0.7_@types+node@20.19.13_jiti@2.6.1_jsdom@27.4.0_sass@1.97.3_terser@5.46.0_tsx@4.21.0_yaml@2.8.2/node_modules/vitest/dist/chunks/index.CWIFvlX5.js:469:9)
22
+ at new URL$1 (file:///home/runner/work/faststore/faststore/node_modules/.pnpm/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/vitest/dist/chunks/index.CyBMJtT7.js:556:9)
23
23
   at new Request (node:internal/deps/undici/undici:10971:25)
24
24
   at fetch (node:internal/deps/undici/undici:11891:25)
25
25
   at fetch (node:internal/deps/undici/undici:16414:10)
@@ -36,16 +36,18 @@ Not implemented: navigation to another Document
36
36
  Browserslist: browsers data (caniuse-lite) is 6 months old. Please run:
37
37
  npx update-browserslist-db@latest
38
38
  Why you should do it regularly: https://github.com/browserslist/update-db#readme
39
- ✓  browser  test/sdk/ui/useScrollRestoration.browser.test.ts (43 tests) 1024ms
39
+ ✓  browser  test/sdk/ui/useScrollRestoration.browser.test.ts (43 tests) 1055ms
40
40
  stderr | test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx > RecommendationShelf > renders nothing when there is an error
41
41
  Error fetching recommendations undefined boom for campaign vrn:recommendations:acc:rec-top-items-v2:campaign-1
42
42
 
43
- ✓  browser  test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx (8 tests) 161ms
44
- ✓  browser  test/components/account/ContractSwitcher.browser.test.tsx (13 tests) 2109ms
45
- ✓ shows an error state when the list fails to load  511ms
46
- ✓ requires selecting a contract before Confirm is enabled, then switches (REQ-03)  334ms
47
- ✓  browser  test/components/ui/ProductGallery/GalleryPageHeightLock.browser.test.tsx (8 tests) 148ms
48
- ✓  browser  test/sdk/session/reconcileSessionLocale.browser.test.ts (5 tests) 32ms
43
+ ✓  browser  test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx (8 tests) 209ms
44
+ ✓  browser  test/components/account/ContractSwitcher.browser.test.tsx (13 tests) 2760ms
45
+ ✓ shows an error state when the list fails to load  743ms
46
+ ✓ requires selecting a contract before Confirm is enabled, then switches (REQ-03)  391ms
47
+ ✓ does not reload when the switch returns false  308ms
48
+ ✓  browser  test/sdk/session/reconcileSessionLocale.browser.test.ts (5 tests) 63ms
49
+ ✓  browser  test/components/ui/ProductGallery/GalleryPageHeightLock.browser.test.tsx (8 tests) 252ms
50
+ ✓  browser  test/sdk/session/getInitialSession.browser.test.ts (6 tests) 26ms
49
51
  stderr | test/pages/password-protection.browser.test.tsx > PasswordProtectionLogin > posts the password and redirects to the requested return path
50
52
  Warning: An update to PasswordProtectionLogin inside a test was not wrapped in act(...).
51
53
 
@@ -275,13 +277,11 @@ act(() => {
275
277
  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
276
278
  at PasswordProtectionLogin (/home/runner/work/faststore/faststore/packages/core/src/pages/password-protection/index.tsx:19:69)
277
279
 
278
- ✓  browser  test/pages/password-protection.browser.test.tsx (6 tests) 1545ms
279
- ✓ renders the password protection form  594ms
280
- ✓  browser  test/sdk/session/getInitialSession.browser.test.ts (6 tests) 27ms
281
- ✓  browser  test/components/account/OrganizationDrawer.browser.test.tsx (5 tests) 1942ms
282
- ✓ renders the menu view with contract details and manage footer  936ms
283
- ✓ opens the contract switcher and returns to the menu on back  452ms
284
- ✓  browser  test/sdk/account/changeContractToken.browser.test.ts (9 tests) 55ms
280
+ ✓  browser  test/pages/password-protection.browser.test.tsx (6 tests) 1700ms
281
+ ✓ renders the password protection form  845ms
282
+ ✓  browser  test/components/account/OrganizationDrawer.browser.test.tsx (5 tests) 1546ms
283
+ ✓ renders the menu view with contract details and manage footer  853ms
284
+ ✓  browser  test/sdk/account/changeContractToken.browser.test.ts (9 tests) 112ms
285
285
  stderr | test/sdk/localization/store-url.browser.test.ts
286
286
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
287
287
  at validateCart (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/index.ts:121:27)
@@ -289,10 +289,10 @@ This ensures that you're testing the behavior the user would see in the browser.
289
289
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
290
290
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
291
291
 
292
- ✓  browser  test/sdk/localization/store-url.browser.test.ts (3 tests) 8ms
293
- ✓  browser  test/sdk/account/useAvailableContracts.browser.test.ts (6 tests) 40ms
294
- ✓  browser  test/components/templates/SearchPage/SearchWrapper.browser.test.tsx (3 tests) 35ms
295
- ✓  browser  test/sdk/account/useSwitchContract.browser.test.ts (4 tests) 39ms
292
+ ✓  browser  test/sdk/localization/store-url.browser.test.ts (3 tests) 10ms
293
+ ✓  browser  test/sdk/account/useAvailableContracts.browser.test.ts (6 tests) 41ms
294
+ ✓  browser  test/components/templates/SearchPage/SearchWrapper.browser.test.tsx (3 tests) 38ms
295
+ ✓  browser  test/sdk/account/useSwitchContract.browser.test.ts (4 tests) 43ms
296
296
  stderr | test/components/account/doLogout.browser.test.ts
297
297
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
298
298
  at validateCart (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/index.ts:121:27)
@@ -300,8 +300,8 @@ This ensures that you're testing the behavior the user would see in the browser.
300
300
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
301
301
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
302
302
 
303
- ✓  browser  test/components/account/doLogout.browser.test.ts (1 test) 20ms
304
- ✓  browser  test/components/account/OrganizationDrawerHeader.browser.test.tsx (3 tests) 265ms
303
+ ✓  browser  test/components/account/doLogout.browser.test.ts (1 test) 18ms
304
+ ✓  browser  test/components/account/OrganizationDrawerHeader.browser.test.tsx (3 tests) 295ms
305
305
  stderr | test/sdk/account/clearPersistedSessionState.browser.test.ts
306
306
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
307
307
  at validateCart (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/index.ts:121:27)
@@ -309,13 +309,11 @@ This ensures that you're testing the behavior the user would see in the browser.
309
309
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
310
310
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
311
311
 
312
- ✓  browser  test/sdk/account/clearPersistedSessionState.browser.test.ts (1 test) 12ms
313
- ✓  browser  test/components/account/OrganizationDrawerFooter.browser.test.tsx (3 tests) 248ms
314
- ✓  browser  test/utils/isLocalHost.browser.test.ts (3 tests) 3ms
315
- ✓  browser  test/sdk/session/reconcileSessionLocale.disabled.browser.test.ts (1 test) 2ms
316
- ✓  node  test/utils/localization/bindingPaths.test.ts (71 tests) 27ms
317
- ✓  browser  test/components/skeletons/ProductGridSkeleton.browser.test.tsx (2 tests) 38ms
318
- ✓  node  test/server/password-protection-service.test.ts (22 tests) 30ms
312
+ ✓  browser  test/sdk/account/clearPersistedSessionState.browser.test.ts (1 test) 8ms
313
+ ✓  browser  test/components/account/OrganizationDrawerFooter.browser.test.tsx (3 tests) 255ms
314
+ ✓  browser  test/utils/isLocalHost.browser.test.ts (3 tests) 5ms
315
+ ✓  browser  test/sdk/session/reconcileSessionLocale.disabled.browser.test.ts (1 test) 3ms
316
+ ✓  node  test/utils/localization/bindingPaths.test.ts (71 tests) 29ms
319
317
  stderr | test/sdk/account/refreshToken.browser.test.ts
320
318
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
321
319
  at validateCart (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/index.ts:121:27)
@@ -323,11 +321,13 @@ This ensures that you're testing the behavior the user would see in the browser.
323
321
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
324
322
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
325
323
 
324
+ ✓  browser  test/sdk/account/refreshToken.browser.test.ts (1 test) 4ms
325
+ ✓  browser  test/components/skeletons/ProductGridSkeleton.browser.test.tsx (2 tests) 48ms
326
+ ✓  node  test/server/password-protection-service.test.ts (22 tests) 38ms
326
327
  ✓  node  test/utils/match-url.test.ts (10 tests) 10ms
327
- ✓  browser  test/sdk/account/refreshToken.browser.test.ts (1 test) 5ms
328
328
  ✓  node  test/sdk/localization/useBindingSelector.test.tsx (22 tests) 14ms
329
- ✓  node  test/pages/api/unlock.test.ts (15 tests) 206ms
330
- ✓  node  test/sdk/localization/bindingSelector.test.ts (18 tests) 9ms
329
+ ✓  node  test/pages/api/unlock.test.ts (15 tests) 284ms
330
+ ✓  node  test/sdk/localization/bindingSelector.test.ts (18 tests) 11ms
331
331
  stderr | test/components/search/SearchInputComponent.test.tsx
332
332
  Warning: forwardRef render functions accept exactly two parameters: props and ref. Did you forget to use the ref parameter?
333
333
 
@@ -391,24 +391,11 @@ Warning: React does not recognize the `providerProps` prop on a DOM element. If
391
391
  at Suspense
392
392
  at Wrapper (/home/runner/work/faststore/faststore/packages/core/test/components/search/SearchInputComponent.test.tsx:106:20)
393
393
 
394
- ✓  node  test/components/search/SearchInputComponent.test.tsx (6 tests) 71ms
395
- ✓  node  test/utils/cookieCacheBusting.test.ts (10 tests) 12ms
396
- ✓  node  test/sdk/search/useSearchHistory.test.ts (13 tests) 58ms
397
- ✓  node  test/experimental/myAccountCmsServerSideProps.test.ts (5 tests) 9ms
398
- ✓  node  test/utils/clearCookies.test.ts (20 tests) 27ms
399
- ✓  node  test/components/quotes/MyAccountListQuotesTable.test.tsx (12 tests) 419ms
400
- stderr | test/sdk/account/useSetPassword.test.ts > useSetPassword > maps wrongcredentials error responses to the existing message
401
- Set password request failed: Bad Request
402
-
403
- ✓  node  test/sdk/account/useSetPassword.test.ts (6 tests) 34ms
404
- ✓  node  test/components/quotes/quoteFilters.test.ts (23 tests) 24ms
405
- ✓  node  test/utils/multipleTemplates.test.ts (8 tests) 5ms
406
- ✓  node  test/utils/localization/localizeRedirectDestination.test.ts (11 tests) 5ms
407
- ✓  node  test/components/quotes/MyAccountListQuotes.test.tsx (9 tests) 804ms
408
- ✓ navigates with the search term on blur  359ms
409
- ✓ navigates without a label param when the search is cleared  358ms
410
- ✓  node  test/sdk/orderEntry/useOrderEntryOperation.test.ts (8 tests) 42ms
411
- ✓  node  test/sdk/orderEntry/useOrderEntryUpload.test.ts (8 tests) 56ms
394
+ ✓  node  test/components/search/SearchInputComponent.test.tsx (6 tests) 63ms
395
+ ✓  node  test/utils/cookieCacheBusting.test.ts (10 tests) 11ms
396
+ ✓  node  test/sdk/search/useSearchHistory.test.ts (13 tests) 63ms
397
+ ✓  node  test/experimental/myAccountCmsServerSideProps.test.ts (5 tests) 11ms
398
+ ✓  node  test/utils/clearCookies.test.ts (20 tests) 23ms
412
399
  stderr | test/pages/api/graphql.test.ts > /api/graphql error status propagation > propagates the upstream 400 from a wrapped BadRequestError instead of 500
413
400
  Graphql execution returned with error: [
414
401
  {
@@ -455,47 +442,108 @@ Warning: React does not recognize the `providerProps` prop on a DOM element. If
455
442
  { message: 'bad', status: 400, type: 'BadRequestError' }
456
443
  ]
457
444
 
458
- ✓  node  test/pages/api/graphql.test.ts (10 tests) 25ms
459
- ✓  node  test/sdk/analytics/useStartRecommendationSession.test.ts (6 tests) 59ms
460
- ✓  node  test/components/quotes/MyAccountQuotesFilterSlider.test.tsx (5 tests) 131ms
461
- ✓  node  test/server/index.test.ts (7 tests) 799ms
462
- ✓ should return a valid merged GraphQL schema  301ms
463
- ✓  node  test/proxy.localization.test.ts (5 tests) 71ms
464
- ✓  node  test/sdk/account/getMyAccountRoutes.test.ts (10 tests) 8ms
465
- ✓  node  test/sdk/orderEntry/useOrderEntry.test.ts (8 tests) 47ms
445
+ stderr | test/pages/api/graphql.test.ts > /api/graphql request handling > maps BadRequestError thrown by execute to its status
446
+ Something unexpected occurred querying Graphql endpoint:
447
+ _ [FastStoreError]: malformed
448
+ at /home/runner/work/faststore/faststore/packages/core/test/pages/api/graphql.test.ts:190:31
449
+ at /home/runner/work/faststore/faststore/packages/core/test/pages/api/graphql.test.ts:196:39
450
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1024:60
451
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:145:11
452
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:915:26
453
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1243:20
454
+ at new Promise (<anonymous>)
455
+ at runWithTimeout (file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1209:10)
456
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1653:37
457
+ at Traces.$ (file:///home/runner/work/faststore/faststore/node_modules/.pnpm/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/vitest/dist/chunks/traces.CCmnQaNT.js:142:27) {
458
+ extensions: { status: 400, type: 'BadRequestError' }
459
+ }
460
+
461
+ stderr | test/pages/api/graphql.test.ts > /api/graphql request handling > maps UnauthorizedError thrown by execute to its status
462
+ Something unexpected occurred querying Graphql endpoint:
463
+ te [FastStoreError]: expired
464
+ at /home/runner/work/faststore/faststore/packages/core/test/pages/api/graphql.test.ts:191:33
465
+ at /home/runner/work/faststore/faststore/packages/core/test/pages/api/graphql.test.ts:196:39
466
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1024:60
467
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:145:11
468
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:915:26
469
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1243:20
470
+ at new Promise (<anonymous>)
471
+ at runWithTimeout (file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1209:10)
472
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1653:37
473
+ at Traces.$ (file:///home/runner/work/faststore/faststore/node_modules/.pnpm/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/vitest/dist/chunks/traces.CCmnQaNT.js:142:27) {
474
+ extensions: { status: 401, type: 'UnauthorizedError' }
475
+ }
476
+
477
+ stderr | test/pages/api/graphql.test.ts > /api/graphql request handling > maps an unexpected failure thrown by execute to its status
478
+ Something unexpected occurred querying Graphql endpoint:
479
+ Error: socket hang up
480
+ at /home/runner/work/faststore/faststore/packages/core/test/pages/api/graphql.test.ts:192:37
481
+ at /home/runner/work/faststore/faststore/packages/core/test/pages/api/graphql.test.ts:196:39
482
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1024:60
483
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:145:11
484
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:915:26
485
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1243:20
486
+ at new Promise (<anonymous>)
487
+ at runWithTimeout (file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1209:10)
488
+ at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1653:37
489
+ at Traces.$ (file:///home/runner/work/faststore/faststore/node_modules/.pnpm/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/vitest/dist/chunks/traces.CCmnQaNT.js:142:27)
490
+
491
+ ✓  node  test/pages/api/graphql.test.ts (17 tests) 39ms
492
+ ✓  node  test/components/quotes/MyAccountListQuotesTable.test.tsx (12 tests) 424ms
493
+ ✓  node  test/components/quotes/MyAccountListQuotes.test.tsx (9 tests) 847ms
494
+ ✓ navigates with the search term on blur  363ms
495
+ ✓ navigates without a label param when the search is cleared  357ms
496
+ stderr | test/sdk/account/useSetPassword.test.ts > useSetPassword > maps wrongcredentials error responses to the existing message
497
+ Set password request failed: Bad Request
498
+
499
+ ✓  node  test/sdk/account/useSetPassword.test.ts (6 tests) 32ms
500
+ ✓  node  test/sdk/orderEntry/useOrderEntryOperation.test.ts (8 tests) 41ms
501
+ ✓  node  test/utils/multipleTemplates.test.ts (8 tests) 7ms
502
+ ✓  node  test/components/quotes/quoteFilters.test.ts (23 tests) 28ms
503
+ ✓  node  test/utils/localization/localizeRedirectDestination.test.ts (11 tests) 6ms
504
+ ✓  node  test/sdk/orderEntry/useOrderEntryUpload.test.ts (8 tests) 54ms
505
+ ✓  node  test/components/quotes/MyAccountQuotesFilterSlider.test.tsx (5 tests) 198ms
506
+ ✓  node  test/sdk/analytics/useStartRecommendationSession.test.ts (6 tests) 51ms
507
+ ✓  node  test/server/index.test.ts (7 tests) 809ms
508
+ ✓ should return a valid merged GraphQL schema  315ms
509
+ ✓  node  test/sdk/orderEntry/useOrderEntry.test.ts (8 tests) 42ms
510
+ ✓  node  test/sdk/account/getMyAccountRoutes.test.ts (10 tests) 7ms
511
+ ✓  node  test/proxy.localization.test.ts (5 tests) 49ms
466
512
  ✓  node  test/proxy.test.ts (4 tests) 21ms
467
- ✓  node  test/components/quotes/MyAccountQuotesSelectedTags.test.tsx (8 tests) 107ms
468
- ✓  node  test/server/webops-api.test.ts (9 tests) 23ms
469
- ✓  node  test/sdk/orderEntry/useOrderFormItems.test.ts (7 tests) 40ms
470
- ✓  node  test/sdk/account/refreshToken.test.ts (5 tests) 7ms
471
- ✓  node  test/server/cms/global.test.ts (3 tests) 5ms
472
- ✓  node  test/server/content/service.test.ts (5 tests) 4ms
513
+ ✓  node  test/server/webops-api.test.ts (9 tests) 14ms
514
+ ✓  node  test/server/content/service.test.ts (5 tests) 5ms
515
+ ✓  node  test/sdk/orderEntry/useOrderFormItems.test.ts (7 tests) 46ms
516
+ ✓  node  test/components/quotes/MyAccountQuotesSelectedTags.test.tsx (8 tests) 112ms
517
+ ✓  node  test/sdk/account/refreshToken.test.ts (5 tests) 9ms
518
+ ✓  node  test/server/cms/global.test.ts (3 tests) 6ms
473
519
  ✓  node  test/sdk/localization/store-url.test.ts (1 test) 4ms
520
+ ✓  node  test/sdk/auth/useAuth.test.ts (5 tests) 29ms
474
521
  ✓  node  test/sdk/analytics/hasEnabledRecommendationShelf.test.ts (6 tests) 4ms
475
- ✓  node  test/utils/getRequestHostname.test.ts (12 tests) 6ms
476
- ✓  node  test/sdk/auth/useAuth.test.ts (5 tests) 20ms
477
- ✓  node  test/components/quotes/MyAccountQuotesStatusSelector.test.tsx (6 tests) 144ms
478
- ✓  node  test/utils/validateSessionRefreshToken.test.ts (6 tests) 5ms
479
- ✓  node  test/pages/api/preview.test.ts (2 tests) 7ms
522
+ ✓  node  test/components/quotes/MyAccountQuotesStatusSelector.test.tsx (6 tests) 143ms
523
+ ✓  node  test/utils/getRequestHostname.test.ts (12 tests) 8ms
524
+ ✓  node  test/pages/api/preview.test.ts (2 tests) 11ms
480
525
  ✓  node  test/components/search/SearchInput.test.ts (6 tests) 6ms
481
- ✓  node  test/components/RecommendationShelf/vrn.test.ts (5 tests) 6ms
482
- ✓  node  test/server/cms/index.test.ts (2 tests) 7ms
483
- ✓  node  test/utils/retry.test.ts (5 tests) 14ms
484
- ✓  node  test/components/account/Layout.test.tsx (4 tests) 47ms
485
- ✓  node  test/components/RecommendationShelf/useRecommendationUserId.test.ts (4 tests) 191ms
526
+ ✓  node  test/utils/validateSessionRefreshToken.test.ts (6 tests) 6ms
527
+ ✓  node  test/server/rootSpan.test.ts (4 tests) 39ms
528
+ ✓  node  test/components/RecommendationShelf/vrn.test.ts (5 tests) 7ms
529
+ ✓  node  test/server/cms/index.test.ts (2 tests) 6ms
530
+ ✓  node  test/utils/retry.test.ts (5 tests) 19ms
531
+ ✓  node  test/components/account/Layout.test.tsx (4 tests) 42ms
532
+ ✓  node  test/components/RecommendationShelf/useRecommendationUserId.test.ts (4 tests) 183ms
486
533
  ✓  node  test/utils/unlockResponse.test.ts (11 tests) 6ms
487
- ✓  node  test/server/cms/myAccountDefaultSections.test.ts (5 tests) 6ms
488
- ✓  node  test/components/RecommendationShelf/useRecommendations.test.ts (2 tests) 3ms
489
- ✓  node  test/utils/isLocalHost.test.ts (7 tests) 5ms
534
+ ✓  node  test/server/cms/myAccountDefaultSections.test.ts (5 tests) 4ms
535
+ ✓  node  test/components/RecommendationShelf/useRecommendations.test.ts (2 tests) 4ms
536
+ ✓  node  test/utils/isLocalHost.test.ts (7 tests) 6ms
537
+ ✓  node  test/components/account/OrganizationDrawerBody.test.tsx (3 tests) 42ms
490
538
  ✓  node  test/utils/userOrderStatus.test.ts (3 tests) 4ms
491
- ✓  node  test/components/account/OrganizationDrawerBody.test.tsx (3 tests) 49ms
492
539
  ✓  node  test/sdk/analytics/utils.test.ts (2 tests) 4ms
493
- ✓  node  test/components/auth/ProfileChallenge.test.tsx (3 tests) 27ms
494
- ✓  node  test/sdk/account/clearPersistedSessionState.test.ts (1 test) 11ms
495
- ✓  node  test/components/quotes/MyAccountQuoteStatusBadge.test.tsx (2 tests) 24ms
496
-
497
-  Test Files  77 passed (77)
498
-  Tests  626 passed (626)
499
-  Start at  14:30:25
500
-  Duration  37.93s (transform 8.83s, setup 0ms, collect 30.97s, tests 11.57s, environment 49.14s, prepare 1.21s)
540
+ ✓  node  test/instrumentation.test.ts (3 tests) 7ms
541
+ ✓  node  test/components/auth/ProfileChallenge.test.tsx (3 tests) 33ms
542
+ ✓  node  test/sdk/account/clearPersistedSessionState.test.ts (1 test) 12ms
543
+ ✓  node  test/components/quotes/MyAccountQuoteStatusBadge.test.tsx (2 tests) 25ms
544
+
545
+  Test Files  79 passed (79)
546
+  Tests  640 passed (640)
547
+  Start at  23:16:25
548
+  Duration  41.03s (transform 8.89s, setup 0ms, import 34.03s, tests 12.55s, environment 53.93s)
501
549
 
package/CHANGELOG.md CHANGED
@@ -3,7 +3,9 @@
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.5.1](https://github.com/vtex/faststore/compare/v4.5.1-dev.2...v4.5.1) (2026-08-11)
6
+ # [4.6.0-dev.1](https://github.com/vtex/faststore/compare/v4.6.0-dev.0...v4.6.0-dev.1) (2026-08-11)
7
+
8
+ ## [4.5.1](https://github.com/vtex/faststore/compare/v4.5.1-dev.4...v4.5.1) (2026-08-11)
7
9
 
8
10
  ### Bug Fixes
9
11
 
@@ -21,6 +23,42 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
21
23
  - **core:** type scroll restore paint timer for DOM/Node setTimeout ([940c903](https://github.com/vtex/faststore/commit/940c90376f8d2eb77665e73a6fb0ea53c9786c8e))
22
24
  - **core:** use replaceAll for infinite-scroll session key ([4f52048](https://github.com/vtex/faststore/commit/4f520482a9ba9661782fcf25a286b8f2e6c6b922))
23
25
 
26
+ # [4.6.0-dev.0](https://github.com/vtex/faststore/compare/v4.5.1-dev.4...v4.6.0-dev.0) (2026-08-11)
27
+
28
+ ### Features
29
+
30
+ - removing vtex/diagnostics-nodejs ([#3389](https://github.com/vtex/faststore/issues/3389)) ([74e2d50](https://github.com/vtex/faststore/commit/74e2d503a44903929b90af7701956536690d4e41))
31
+
32
+ ## [4.5.1-dev.4](https://github.com/vtex/faststore/compare/v4.5.1-dev.3...v4.5.1-dev.4) (2026-08-10)
33
+
34
+ **Note:** Version bump only for package @faststore/core
35
+
36
+ ## [4.5.1-dev.3](https://github.com/vtex/faststore/compare/v4.5.1-dev.2...v4.5.1-dev.3) (2026-08-10)
37
+
38
+ ### Bug Fixes
39
+
40
+ - **core:** add twitter card meta tag to default next seo config ([720f3e6](https://github.com/vtex/faststore/commit/720f3e6d04ef523f2fcf0e146c3741b7e5af70fa))
41
+ - **core:** add twittermeta tags validaton for tests ([c8f5872](https://github.com/vtex/faststore/commit/c8f5872e42b69ddae5b2abfa0e8d3140e2bc7163))
42
+ - **core:** lint error ([d07460f](https://github.com/vtex/faststore/commit/d07460f890ba712c9aecbec25bda5de96bf3e4ef))
43
+ - **core:** remove duplicated twitter meta tag ([f388288](https://github.com/vtex/faststore/commit/f388288731ff75fceedba3cd7449ef1088b0b0c2))
44
+ - **metaTags:** add twitter meta tags to nextSeo ([9c2439b](https://github.com/vtex/faststore/commit/9c2439b4a668dd81b7bfe5bdc44df575725067ec))
45
+
46
+ ## [4.5.1](https://github.com/vtex/faststore/compare/v4.5.1-dev.2...v4.5.1) (2026-08-11)
47
+
48
+ - **core:** address scroll restoration review feedback ([a5fe6a3](https://github.com/vtex/faststore/commit/a5fe6a35c54e6095f81c7995a815ce6ef3b4dd4e))
49
+ - **core:** align beforePopState callback with NextHistoryState ([10719e5](https://github.com/vtex/faststore/commit/10719e5eca199385d9fe18bdd6d7da290716a1d5))
50
+ - **core:** compare window to undefined directly for Sonar ([bf05a54](https://github.com/vtex/faststore/commit/bf05a54bb014d120b7bdc95ff7c01a4e9b35b485))
51
+ - **core:** harden scroll restore path matching and hydration-safe heights ([34efa6b](https://github.com/vtex/faststore/commit/34efa6bda7549895a4531277c2fe55bd6bbe98a7))
52
+ - **core:** hash gallery height keys to avoid truncation collisions ([9e98043](https://github.com/vtex/faststore/commit/9e980438d74e457fa33aa77900438339463a078c))
53
+ - **core:** prevent IS redirect hang and improve scroll restoration ([5f30778](https://github.com/vtex/faststore/commit/5f30778161eec2e4381b3fe971479dd7cbe53b08))
54
+ - **core:** reset infinite scroll on forward listing navigations ([249226a](https://github.com/vtex/faststore/commit/249226a3acbe4cb26d687cf962125b2593d5949e))
55
+ - **core:** resolve Sonar new-code smells for scroll restoration ([59b41ed](https://github.com/vtex/faststore/commit/59b41ed1d2a30ba693726b19895a0279eb1ba464))
56
+ - **core:** restore PLP infinite-scroll pages on back navigation ([92b2efa](https://github.com/vtex/faststore/commit/92b2efa1d4d0895a10b5a0e71a4d9bf37cae1686))
57
+ - **core:** resync gallery height lock when children change ([5058089](https://github.com/vtex/faststore/commit/50580898d97baf800b97c8a876f7967529859c61))
58
+ - **core:** satisfy Sonar on gallery height key hash ([3a6ce47](https://github.com/vtex/faststore/commit/3a6ce47485c26be75d6d76906df3af64c9e34fd6))
59
+ - **core:** type scroll restore paint timer for DOM/Node setTimeout ([940c903](https://github.com/vtex/faststore/commit/940c90376f8d2eb77665e73a6fb0ea53c9786c8e))
60
+ - **core:** use replaceAll for infinite-scroll session key ([4f52048](https://github.com/vtex/faststore/commit/4f520482a9ba9661782fcf25a286b8f2e6c6b922))
61
+
24
62
  ## [4.5.1-dev.2](https://github.com/vtex/faststore/compare/v4.5.1-dev.1...v4.5.1-dev.2) (2026-08-04)
25
63
 
26
64
  **Note:** Version bump only for package @faststore/core
package/README.md CHANGED
@@ -31,7 +31,7 @@ packages/core/
31
31
  │ ├── server/ # Server-side utilities (CMS content fetching)
32
32
  │ ├── customizations/ # Store-level overrides: styles, fonts, components, fragments
33
33
  │ ├── styles/ # Global SCSS styles
34
- │ └── instrumentation.ts # Next.js instrumentation hook (boots @faststore/diagnostics when otelEnabled)
34
+ │ └── instrumentation.ts # Next.js instrumentation hook (boots @faststore/diagnostics — see docs/observability.md)
35
35
  ├── cms/faststore/ # CMS configuration: sections.json, content-types.json, schemas
36
36
  ├── @generated/ # Auto-generated GraphQL types — do not edit
37
37
  ├── discovery.config.default.js # Committed base store configuration (platform, sales channel, locale)
@@ -47,7 +47,7 @@ describe('Home Page Seo', () => {
47
47
  })
48
48
  })
49
49
 
50
- it('has OpenGraph tags', () => {
50
+ it('has OpenGraph and Twitter tags', () => {
51
51
  cy.visit(pages.home, options)
52
52
  cy.waitForHydration()
53
53
 
@@ -68,6 +68,12 @@ describe('Home Page Seo', () => {
68
68
  .should(($el) => {
69
69
  expect($el.attr('content')).to.be.a('string')
70
70
  })
71
+
72
+ cy.get('meta[name="twitter:card"]')
73
+ .should('exist')
74
+ .should(($el) => {
75
+ expect($el.attr('content')).to.eq('summary_large_image')
76
+ })
71
77
  })
72
78
  })
73
79
 
@@ -117,7 +123,7 @@ describe('Product Page Seo', () => {
117
123
  })
118
124
  })
119
125
 
120
- it('has OpenGraph tags', () => {
126
+ it('has OpenGraph and Twitter tags', () => {
121
127
  cy.visit(pages.pdp, options)
122
128
  cy.waitForHydration()
123
129
 
@@ -156,6 +162,12 @@ describe('Product Page Seo', () => {
156
162
  .should(($el) => {
157
163
  expect($el.attr('content')).to.be.a('string')
158
164
  })
165
+
166
+ cy.get('meta[name="twitter:card"]')
167
+ .should('exist')
168
+ .should(($el) => {
169
+ expect($el.attr('content')).to.eq('summary_large_image')
170
+ })
159
171
  })
160
172
  })
161
173
 
@@ -201,7 +213,7 @@ describe('Collection Page Seo', () => {
201
213
  })
202
214
  })
203
215
 
204
- it('has OpenGraph tags', () => {
216
+ it('has OpenGraph and Twitter tags', () => {
205
217
  cy.visit(pages.collection, options)
206
218
  cy.waitForHydration()
207
219
 
@@ -222,6 +234,12 @@ describe('Collection Page Seo', () => {
222
234
  .should(($el) => {
223
235
  expect($el.attr('content')).to.be.a('string')
224
236
  })
237
+
238
+ cy.get('meta[name="twitter:card"]')
239
+ .should('exist')
240
+ .should(($el) => {
241
+ expect($el.attr('content')).to.eq('summary_large_image')
242
+ })
225
243
  })
226
244
  })
227
245
 
@@ -256,7 +274,7 @@ describe('Filtered Collection Page Seo', () => {
256
274
  })
257
275
  })
258
276
 
259
- it('has OpenGraph tags', () => {
277
+ it('has OpenGraph and Twitter tags', () => {
260
278
  cy.visit(pages.collection_filtered, options)
261
279
  cy.waitForHydration()
262
280
 
@@ -277,6 +295,12 @@ describe('Filtered Collection Page Seo', () => {
277
295
  .should(($el) => {
278
296
  expect($el.attr('content')).to.be.a('string')
279
297
  })
298
+
299
+ cy.get('meta[name="twitter:card"]')
300
+ .should('exist')
301
+ .should(($el) => {
302
+ expect($el.attr('content')).to.eq('summary_large_image')
303
+ })
280
304
  })
281
305
  })
282
306
 
@@ -295,7 +319,7 @@ describe('Search Page Seo', () => {
295
319
  cy.get('link[rel="canonical"]').should('not.exist')
296
320
  })
297
321
 
298
- it('has OpenGraph tags', () => {
322
+ it('has OpenGraph and Twitter tags', () => {
299
323
  cy.visit(pages.search, options)
300
324
  cy.waitForHydration()
301
325
 
@@ -316,5 +340,11 @@ describe('Search Page Seo', () => {
316
340
  .should(($el) => {
317
341
  expect($el.attr('content')).to.be.a('string')
318
342
  })
343
+
344
+ cy.get('meta[name="twitter:card"]')
345
+ .should('exist')
346
+ .should(($el) => {
347
+ expect($el.attr('content')).to.eq('summary_large_image')
348
+ })
319
349
  })
320
350
  })
@@ -166,8 +166,7 @@ module.exports = {
166
166
  analytics: {
167
167
  // https://developers.google.com/tag-platform/tag-manager/web#standard_web_page_installation,
168
168
  gtmContainerId: 'GTM-PGHZ95N',
169
- serviceName: 'faststore-proxy',
170
- otelEnabled: true,
169
+ serviceName: 'faststore',
171
170
  },
172
171
 
173
172
  cms: {
@@ -8,6 +8,9 @@ const config: DefaultSeoProps = {
8
8
  norobots: storeConfig.experimental.noRobots,
9
9
  dangerouslySetAllPagesToNoFollow: storeConfig.experimental.nofollow,
10
10
  dangerouslySetAllPagesToNoIndex: storeConfig.experimental.noindex,
11
+ twitter: {
12
+ cardType: 'summary_large_image',
13
+ },
11
14
  additionalMetaTags: [
12
15
  {
13
16
  name: 'generated-at',
package/next.config.js CHANGED
@@ -14,7 +14,6 @@ const getRootFolder = () => {
14
14
  console.log(`
15
15
  Root folder at: ${getRootFolder()}
16
16
  AccountID: ${storeConfig.api?.storeId ?? 'Unknown'}
17
- Analytics Enabled: ${storeConfig.analytics?.otelEnabled ?? false}
18
17
  `)
19
18
 
20
19
  /**
@@ -22,8 +21,6 @@ console.log(`
22
21
  * */
23
22
  const nextConfig = {
24
23
  /* config options here */
25
- /* Replaces terser by swc for minifying. It's the default in NextJS 13 */
26
- swcMinify: true,
27
24
  ...(Array.isArray(storeConfig.experimental?.transpilePackages) &&
28
25
  storeConfig.experimental.transpilePackages.length > 0 && {
29
26
  transpilePackages: storeConfig.experimental.transpilePackages,
@@ -55,9 +52,7 @@ const nextConfig = {
55
52
  sassOptions: {
56
53
  silenceDeprecations: ['if-function', 'legacy-js-api'],
57
54
  },
58
- // TODO: We won't need to enable this experimental feature when migrating to Next.js 13
59
55
  experimental: {
60
- instrumentationHook: true,
61
56
  scrollRestoration: !storeConfig.experimental.scrollRestoration,
62
57
  },
63
58
  /*
@@ -68,6 +63,27 @@ const nextConfig = {
68
63
  * */
69
64
  outputFileTracingRoot: getRootFolder(),
70
65
  webpack: (config, { isServer, dev }) => {
66
+ // `@opentelemetry/sdk-node` (pulled in by @faststore/diagnostics) does an
67
+ // optional `require('@opentelemetry/shim-opencensus')` guarded by a try/catch.
68
+ // The module is an optional peer that isn't installed, so webpack reports a
69
+ // "Module not found" error it can't see is intentional. Aliasing it to `false`
70
+ // resolves it to an empty module, matching the graceful degradation the SDK
71
+ // already expects at runtime (Turbopack handles this case on its own).
72
+ config.resolve.alias = {
73
+ ...config.resolve.alias,
74
+ '@opentelemetry/shim-opencensus': false,
75
+ }
76
+
77
+ // `require-in-the-middle` (used by OpenTelemetry instrumentation) patches
78
+ // modules through a dynamic `require` that webpack can't statically analyze,
79
+ // emitting a benign "Critical dependency" warning. `serverExternalPackages`
80
+ // handles this for Turbopack, but the webpack builder still traces into it,
81
+ // so we silence the warning for this module specifically.
82
+ config.ignoreWarnings = [
83
+ ...(config.ignoreWarnings ?? []),
84
+ { module: /require-in-the-middle/ },
85
+ ]
86
+
71
87
  // https://github.com/vercel/next.js/discussions/11267#discussioncomment-2479112
72
88
  // camel-case style names from css modules
73
89
  config.module.rules
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/core",
3
- "version": "4.5.1",
3
+ "version": "4.6.0-dev.1",
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/lighthouse": "4.5.1",
75
- "@faststore/ui": "4.5.1",
76
- "@faststore/diagnostics": "4.5.1",
77
- "@faststore/sdk": "4.5.1",
78
- "@faststore/api": "4.5.1"
74
+ "@faststore/api": "4.6.0-dev.1",
75
+ "@faststore/ui": "4.6.0-dev.1",
76
+ "@faststore/lighthouse": "4.6.0-dev.1",
77
+ "@faststore/diagnostics": "4.6.0-dev.1",
78
+ "@faststore/sdk": "4.6.0-dev.1"
79
79
  },
80
80
  "devDependencies": {
81
81
  "@cypress/code-coverage": "^3.12.1",
@@ -88,7 +88,7 @@
88
88
  "@types/fs-extra": "^9.0.13",
89
89
  "@types/node": "20.19.13",
90
90
  "@vitejs/plugin-react": "^5.1.2",
91
- "@vitest/coverage-v8": "4.0.7",
91
+ "@vitest/coverage-v8": "4.0.18",
92
92
  "@vtex/prettier-config": "1.0.0",
93
93
  "axe-core": "^4.8.1",
94
94
  "cypress": "12.17.4",
@@ -99,7 +99,7 @@
99
99
  "is-ci": "^3.0.0",
100
100
  "jsdom": "^27.4.0",
101
101
  "typescript": "5.9.3",
102
- "vitest": "4.0.7"
102
+ "vitest": "4.0.18"
103
103
  },
104
104
  "peerDependencies": {
105
105
  "graphql": "16.11.0",
@@ -1,17 +1,25 @@
1
1
  import config from '../discovery.config'
2
2
  import pkgJSON from '../package.json'
3
3
 
4
+ /**
5
+ * Next.js instrumentation hook. This is the only place telemetry is booted, so
6
+ * the OTel SDK is registered before any request is served.
7
+ */
4
8
  export async function register() {
5
- if (
6
- process.env.NEXT_RUNTIME === 'nodejs' &&
7
- config.analytics.otelEnabled === true
8
- ) {
9
+ // Disabled feature until release date
10
+ const instrumentationEnabled = false
11
+
12
+ // The runtime check must wrap the import instead of guarding with an early
13
+ // return: bundlers replace `process.env.NEXT_RUNTIME` with a literal, so only
14
+ // a positive branch gets dropped from the edge bundle. Left reachable, the
15
+ // OTel SDK pulls in Node builtins (`fs`, `zlib`, `stream`) that fail to resolve.
16
+ if (process.env.NEXT_RUNTIME === 'nodejs' && instrumentationEnabled) {
9
17
  const { name, version } = pkgJSON
18
+
10
19
  try {
11
20
  const { getTelemetryClient } = await import('@faststore/diagnostics')
12
- console.log('Instrumemtation.ts: Getting telemetry client')
13
21
 
14
- return getTelemetryClient({
22
+ return await getTelemetryClient({
15
23
  serviceName: config.analytics?.serviceName ?? name,
16
24
  version,
17
25
  account: config.api.storeId,
@@ -19,7 +27,7 @@ export async function register() {
19
27
  packageName: name,
20
28
  })
21
29
  } catch (error) {
22
- console.error('Failed to initialize OTEL Instrumentation')
30
+ console.error('Failed to initialize OTEL Instrumentation', error)
23
31
  }
24
32
  }
25
33
  }
@@ -13,6 +13,9 @@ import { getRequestHostname } from 'src/utils/getRequestHostname'
13
13
  import { isLocalHost } from 'src/utils/isLocalHost'
14
14
  import { shouldForceRefreshTokenForValidateSession } from 'src/utils/validateSessionRefreshToken'
15
15
  import { execute } from '../../server'
16
+ import { logger } from '@faststore/diagnostics'
17
+
18
+ const OTELLogger = logger('@faststore/core')
16
19
 
17
20
  const DEFAULT_MAX_AGE = 5 * 60 // 5 minutes
18
21
  const DEFAULT_STALE_WHILE_REVALIDATE = 60 * 60 // 1 hour
@@ -150,6 +153,12 @@ const hasVtexIdclientAutCookie = (request: NextApiRequest): boolean => {
150
153
  const handler: NextApiHandler = async (request, response) => {
151
154
  if (request.method !== 'POST' && request.method !== 'GET') {
152
155
  response.status(405).end()
156
+ OTELLogger(
157
+ 'info',
158
+ 'Invalid request method: %s %s',
159
+ request.method,
160
+ request.url
161
+ )
153
162
 
154
163
  return
155
164
  }
@@ -158,6 +167,15 @@ const handler: NextApiHandler = async (request, response) => {
158
167
  // value is used to cache bust the request if there is a VtexIdclientAutCookie
159
168
  const { operation, variables, query, v: value } = parseRequest(request)
160
169
 
170
+ // Variables and the query body are deliberately not logged: they carry
171
+ // session and customer data.
172
+ OTELLogger(
173
+ 'debug',
174
+ 'operation: %s hash: %s',
175
+ operation?.__meta__?.operationName,
176
+ operation?.__meta__?.operationHash
177
+ )
178
+
161
179
  const isLocal = isLocalHost(getRequestHostname(request.headers.host))
162
180
 
163
181
  if (
@@ -209,18 +227,21 @@ const handler: NextApiHandler = async (request, response) => {
209
227
  // upstream status) is nested in `originalError`. Recover it so the BFF
210
228
  // propagates the real status instead of collapsing everything to 500.
211
229
  const fastStoreError = errors.map(recoverFastStoreError).find(Boolean)
230
+ const reportedErrors = errors.map((graphqlError) => {
231
+ const fsError = recoverFastStoreError(graphqlError)
232
+
233
+ return {
234
+ message: (graphqlError as { message?: string })?.message,
235
+ status: fsError?.extensions.status,
236
+ type: fsError?.extensions.type,
237
+ }
238
+ })
212
239
 
213
- console.error(
214
- 'Graphql execution returned with error: ',
215
- errors.map((graphqlError) => {
216
- const fsError = recoverFastStoreError(graphqlError)
217
-
218
- return {
219
- message: (graphqlError as { message?: string })?.message,
220
- status: fsError?.extensions.status,
221
- type: fsError?.extensions.type,
222
- }
223
- })
240
+ console.error('Graphql execution returned with error: ', reportedErrors)
241
+ OTELLogger(
242
+ 'error',
243
+ 'Graphql execution returned with error: %o',
244
+ reportedErrors
224
245
  )
225
246
 
226
247
  const status = fastStoreError?.extensions.status ?? 500
@@ -304,6 +325,11 @@ const handler: NextApiHandler = async (request, response) => {
304
325
  'Something unexpected occurred querying Graphql endpoint: \n',
305
326
  err
306
327
  )
328
+ OTELLogger(
329
+ 'error',
330
+ 'Something unexpected occurred querying Graphql endpoint: %o',
331
+ err
332
+ )
307
333
 
308
334
  if (err instanceof BadRequestError) {
309
335
  response.status(400).end()
@@ -16,6 +16,7 @@ import {
16
16
  GraphqlVtexSchema,
17
17
  isFastStoreError,
18
18
  } from '@faststore/api'
19
+ import { OTELAPI } from '@faststore/diagnostics'
19
20
  import { loadFilesSync } from '@graphql-tools/load-files'
20
21
  import { mergeTypeDefs } from '@graphql-tools/merge'
21
22
  import { makeExecutableSchema } from '@graphql-tools/schema'
@@ -28,7 +29,7 @@ import thirdPartyResolvers from '../customizations/src/graphql/thirdParty/resolv
28
29
  import vtexExtensionsResolvers from '../customizations/src/graphql/vtex/resolvers'
29
30
 
30
31
  import type { Operation } from '../sdk/graphql/request'
31
- import { apiOptions, withTraceClient } from './options'
32
+ import { apiOptions } from './options'
32
33
 
33
34
  interface ExecuteOptions<V = Record<string, unknown>> {
34
35
  operation: Operation
@@ -70,8 +71,7 @@ export function getFinalAPISchema() {
70
71
  }
71
72
 
72
73
  export const getEnvelop = async () => {
73
- const options = await withTraceClient(apiOptions)
74
- const apiContextFactory = await GraphqlVtexContextFactory(options)
74
+ const apiContextFactory = await GraphqlVtexContextFactory(apiOptions)
75
75
 
76
76
  return envelop({
77
77
  plugins: [
@@ -136,13 +136,17 @@ export const execute = async <V extends Maybe<{ [key: string]: unknown }>, D>(
136
136
  contextValue.storage.locale = locale
137
137
  }
138
138
 
139
- const { data, errors } = (await run({
140
- schema,
141
- document: parse(query),
142
- variableValues: variables,
143
- contextValue,
144
- operationName,
145
- })) as { data: D; errors: unknown[] }
139
+ const { data, errors } = await withRootSpan(
140
+ `graphql ${operationName ?? 'operation'}`,
141
+ () =>
142
+ run({
143
+ schema,
144
+ document: parse(query),
145
+ variableValues: variables,
146
+ contextValue,
147
+ operationName,
148
+ }) as Promise<{ data: D; errors: unknown[] }>
149
+ )
146
150
 
147
151
  return {
148
152
  data,
@@ -153,3 +157,27 @@ export const execute = async <V extends Maybe<{ [key: string]: unknown }>, D>(
153
157
  },
154
158
  }
155
159
  }
160
+
161
+ /**
162
+ * Runs an operation inside a per-request root span, made the active context so
163
+ * the resolver spans emitted by `@faststore/api` parent to it. Resolves to a
164
+ * plain call when telemetry is disabled.
165
+ */
166
+ async function withRootSpan<T>(name: string, run: () => Promise<T>) {
167
+ if (!apiOptions.OTEL_ENABLED) {
168
+ return run()
169
+ }
170
+
171
+ const span = OTELAPI.trace.getTracer('@faststore/core').startSpan(name)
172
+ const otelContext = OTELAPI.trace.setSpan(OTELAPI.context.active(), span)
173
+
174
+ try {
175
+ return await OTELAPI.context.with(otelContext, run)
176
+ } catch (error) {
177
+ span.setStatus({ code: OTELAPI.SpanStatusCode.ERROR })
178
+ span.recordException(error as Error)
179
+ throw error
180
+ } finally {
181
+ span.end()
182
+ }
183
+ }
@@ -1,9 +1,8 @@
1
1
  import type { APIOptions } from '@faststore/api'
2
- import { getTraceClient } from '@faststore/diagnostics'
3
2
  import storeConfig from '../../discovery.config'
4
3
  import pkgJSON from '../../package.json'
5
4
 
6
- const { name, version } = pkgJSON
5
+ const { version } = pkgJSON
7
6
 
8
7
  export const apiOptions: APIOptions = {
9
8
  platform: storeConfig.platform as APIOptions['platform'],
@@ -23,25 +22,7 @@ export const apiOptions: APIOptions = {
23
22
  storeConfig.api?.enableUnavailableItemsOnCart ?? false,
24
23
  },
25
24
  version,
26
- OTEL: {
27
- enabled: storeConfig.analytics.otelEnabled,
28
- },
25
+ // feature flag default to false as it should not emit traces/logs yet
26
+ OTEL_ENABLED: false,
29
27
  discoveryConfig: storeConfig,
30
28
  }
31
-
32
- export async function withTraceClient<T extends APIOptions = typeof apiOptions>(
33
- apiOptions: T
34
- ): Promise<T> {
35
- const OTEL = {}
36
- getTraceClient(
37
- apiOptions?.discoveryConfig?.analytics?.serviceName ?? name
38
- )?.inject(OTEL)
39
-
40
- return {
41
- ...apiOptions,
42
- OTEL: {
43
- ...OTEL,
44
- enabled: storeConfig.analytics?.otelEnabled?.toString() === 'true',
45
- },
46
- } as T
47
- }
@@ -0,0 +1,27 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2
+
3
+ import { register } from '../src/instrumentation'
4
+
5
+ const getTelemetryClient = vi.hoisted(() => vi.fn())
6
+
7
+ vi.mock('@faststore/diagnostics', () => ({ getTelemetryClient }))
8
+
9
+ beforeEach(() => {
10
+ vi.clearAllMocks()
11
+ })
12
+
13
+ afterEach(() => {
14
+ vi.unstubAllEnvs()
15
+ })
16
+
17
+ describe('register', () => {
18
+ it.each(['nodejs', 'edge', undefined])(
19
+ 'does not boot telemetry on the %s runtime while the feature is off',
20
+ async (runtime) => {
21
+ vi.stubEnv('NEXT_RUNTIME', runtime)
22
+
23
+ await expect(register()).resolves.toBeUndefined()
24
+ expect(getTelemetryClient).not.toHaveBeenCalled()
25
+ }
26
+ )
27
+ })
@@ -29,9 +29,13 @@ const createResponse = () => {
29
29
  return res as unknown as NextApiResponse & typeof res
30
30
  }
31
31
 
32
- const createRequest = (operationName = 'ClientShippingSimulationQuery') =>
32
+ const createRequest = (
33
+ operationName = 'ClientShippingSimulationQuery',
34
+ method = 'GET'
35
+ ) =>
33
36
  ({
34
- method: 'GET',
37
+ method,
38
+ url: '/api/graphql',
35
39
  headers: { host: 'localhost:3000' },
36
40
  query: {
37
41
  operationName,
@@ -165,3 +169,37 @@ describe('/api/graphql error status propagation', () => {
165
169
  expect(res.status).toHaveBeenCalledWith(404)
166
170
  })
167
171
  })
172
+
173
+ describe('/api/graphql request handling', () => {
174
+ beforeEach(() => {
175
+ vi.clearAllMocks()
176
+ })
177
+
178
+ it.each(['PUT', 'DELETE', 'PATCH', 'OPTIONS'])(
179
+ 'rejects %s with 405 without reaching the GraphQL layer',
180
+ async (method) => {
181
+ const res = createResponse()
182
+ await handler(createRequest(undefined, method), res)
183
+
184
+ expect(res.status).toHaveBeenCalledWith(405)
185
+ expect(mockedExecute).not.toHaveBeenCalled()
186
+ }
187
+ )
188
+
189
+ it.each([
190
+ ['BadRequestError', () => new BadRequestError('malformed'), 400],
191
+ ['UnauthorizedError', () => new UnauthorizedError('expired'), 401],
192
+ ['an unexpected failure', () => new Error('socket hang up'), 500],
193
+ ])(
194
+ 'maps %s thrown by execute to its status',
195
+ async (_name, makeError, status) => {
196
+ mockedExecute.mockRejectedValue(makeError())
197
+
198
+ const res = createResponse()
199
+ await handler(createRequest(), res)
200
+
201
+ expect(res.status).toHaveBeenCalledWith(status)
202
+ expect(res.end).toHaveBeenCalled()
203
+ }
204
+ )
205
+ })
@@ -0,0 +1,90 @@
1
+ import { OTELAPI } from '@faststore/diagnostics'
2
+ import {
3
+ afterAll,
4
+ afterEach,
5
+ beforeEach,
6
+ describe,
7
+ expect,
8
+ it,
9
+ vi,
10
+ } from 'vitest'
11
+
12
+ import { execute } from '../../src/server'
13
+ import { apiOptions } from '../../src/server/options'
14
+
15
+ const span = {
16
+ end: vi.fn(),
17
+ setStatus: vi.fn(),
18
+ recordException: vi.fn(),
19
+ }
20
+
21
+ const startSpan = vi.fn(() => span)
22
+
23
+ const otelEnabledByDefault = apiOptions.OTEL_ENABLED
24
+
25
+ // `__typename` resolves inside GraphQL itself, so these run the whole envelop
26
+ // pipeline without reaching the VTEX platform.
27
+ const runOperation = (query: string, operationName?: string) =>
28
+ execute({
29
+ operation: { __meta__: { operationName, operationHash: 'not-persisted' } },
30
+ variables: {},
31
+ query,
32
+ })
33
+
34
+ beforeEach(() => {
35
+ vi.clearAllMocks()
36
+ vi.spyOn(OTELAPI.trace, 'getTracer').mockReturnValue({ startSpan } as never)
37
+ apiOptions.OTEL_ENABLED = true
38
+ })
39
+
40
+ afterEach(() => {
41
+ vi.restoreAllMocks()
42
+ })
43
+
44
+ afterAll(() => {
45
+ apiOptions.OTEL_ENABLED = otelEnabledByDefault
46
+ })
47
+
48
+ describe('execute root span', () => {
49
+ it('wraps the operation in a span named after it', async () => {
50
+ const { data } = await runOperation(
51
+ 'query RootSpanQuery { __typename }',
52
+ 'RootSpanQuery'
53
+ )
54
+
55
+ expect(data).toEqual({ __typename: 'Query' })
56
+ expect(startSpan).toHaveBeenCalledWith('graphql RootSpanQuery')
57
+ expect(span.end).toHaveBeenCalledTimes(1)
58
+ expect(span.setStatus).not.toHaveBeenCalled()
59
+ })
60
+
61
+ it('names the span generically when the operation is anonymous', async () => {
62
+ await runOperation('{ __typename }')
63
+
64
+ expect(startSpan).toHaveBeenCalledWith('graphql operation')
65
+ })
66
+
67
+ it('marks the span as failed and ends it when the operation throws', async () => {
68
+ await expect(
69
+ runOperation('this is not graphql', 'BrokenQuery')
70
+ ).rejects.toThrow(/Syntax Error/)
71
+
72
+ expect(span.setStatus).toHaveBeenCalledWith({
73
+ code: OTELAPI.SpanStatusCode.ERROR,
74
+ })
75
+ expect(span.recordException).toHaveBeenCalledWith(expect.any(Error))
76
+ expect(span.end).toHaveBeenCalledTimes(1)
77
+ })
78
+
79
+ it('skips instrumentation entirely when telemetry is disabled', async () => {
80
+ apiOptions.OTEL_ENABLED = false
81
+
82
+ const { data } = await runOperation(
83
+ 'query RootSpanQuery { __typename }',
84
+ 'RootSpanQuery'
85
+ )
86
+
87
+ expect(data).toEqual({ __typename: 'Query' })
88
+ expect(startSpan).not.toHaveBeenCalled()
89
+ })
90
+ })