Skip to content

Commit 0a92667

Browse files
authored
fix: restore backward-compatible header constants (#616)
* fix: restore backward compatibility for header constants - Re-export legacy `*_HEADER` named exports (e.g., TENANT_HEADER, REQUEST_ID_HEADER, etc.) as deprecated aliases of `HeaderKeys.*` - Preserves public API without a major bump while guiding migration via @deprecated JSDoc * test: add back-compatible coverage for header constants - Assert deprecated `*_HEADER` exports exist and mirror `HeaderKeys.*` - Add checks for critical VTEX headers and expected string values * chore: bump version to 7.2.4-beta.0 * test: enhance coverage for header constants usage - Add tests to verify constants can be used as object keys without runtime errors - Ensure destructuring from module exports works correctly - Validate compatibility of constants with VaryHeaders type - Simulate realistic scenarios for header key usage in IO apps * test: specify type for incomingHeaders in constants tests - Update the type of incomingHeaders to Record<string, string> for better type safety * ci: remove dependency on lint job for test execution * chore: release v7.2.4
1 parent 3d43ad1 commit 0a92667

File tree

5 files changed

+332
-3
lines changed

5 files changed

+332
-3
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ jobs:
1919
- run: yarn run lint
2020

2121
test:
22-
needs: [lint]
2322
runs-on: ${{ matrix.os }}
2423
strategy:
2524
fail-fast: true

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
77

88
## [Unreleased]
99

10+
## [7.2.4] - 2025-10-15
11+
### Fix
12+
- Restores backward compatibility for individual header constant exports
13+
1014
## [7.2.3] - 2025-10-08
1115
### Fix
1216
- Separate console and diagnostics client logging formats

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@vtex/api",
3-
"version": "7.2.3",
3+
"version": "7.2.4",
44
"description": "VTEX I/O API client",
55
"main": "lib/index.js",
66
"typings": "lib/index.d.ts",

src/constants.test.ts

Lines changed: 243 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,36 @@ import {
2020
PRODUCTION,
2121
INSPECT_DEBUGGER_PORT,
2222
cancellableMethods,
23-
LOG_CLIENT_INIT_TIMEOUT_MS
23+
LOG_CLIENT_INIT_TIMEOUT_MS,
24+
// Backward-compatible individual header constants
25+
CACHE_CONTROL_HEADER,
26+
SEGMENT_HEADER,
27+
SESSION_HEADER,
28+
PRODUCT_HEADER,
29+
LOCALE_HEADER,
30+
FORWARDED_HOST_HEADER,
31+
TENANT_HEADER,
32+
BINDING_HEADER,
33+
META_HEADER,
34+
META_HEADER_BUCKET,
35+
ETAG_HEADER,
36+
ACCOUNT_HEADER,
37+
CREDENTIAL_HEADER,
38+
REQUEST_ID_HEADER,
39+
ROUTER_CACHE_HEADER,
40+
OPERATION_ID_HEADER,
41+
PLATFORM_HEADER,
42+
WORKSPACE_IS_PRODUCTION_HEADER,
43+
WORKSPACE_HEADER,
44+
EVENT_KEY_HEADER,
45+
EVENT_SENDER_HEADER,
46+
EVENT_SUBJECT_HEADER,
47+
EVENT_HANDLER_ID_HEADER,
48+
COLOSSUS_ROUTE_DECLARER_HEADER,
49+
COLOSSUS_ROUTE_ID_HEADER,
50+
COLOSSUS_PARAMS_HEADER,
51+
TRACE_ID_HEADER,
52+
PROVIDER_HEADER
2453
} from './constants'
2554

2655
describe('constants', () => {
@@ -426,5 +455,218 @@ describe('constants', () => {
426455
expect(APP).toHaveProperty(prop)
427456
})
428457
})
458+
459+
describe('Individual header constants (deprecated)', () => {
460+
test('all individual header constants should be defined', () => {
461+
expect(CACHE_CONTROL_HEADER).toBeDefined()
462+
expect(SEGMENT_HEADER).toBeDefined()
463+
expect(SESSION_HEADER).toBeDefined()
464+
expect(PRODUCT_HEADER).toBeDefined()
465+
expect(LOCALE_HEADER).toBeDefined()
466+
expect(FORWARDED_HOST_HEADER).toBeDefined()
467+
expect(TENANT_HEADER).toBeDefined()
468+
expect(BINDING_HEADER).toBeDefined()
469+
expect(META_HEADER).toBeDefined()
470+
expect(META_HEADER_BUCKET).toBeDefined()
471+
expect(ETAG_HEADER).toBeDefined()
472+
expect(ACCOUNT_HEADER).toBeDefined()
473+
expect(CREDENTIAL_HEADER).toBeDefined()
474+
expect(REQUEST_ID_HEADER).toBeDefined()
475+
expect(ROUTER_CACHE_HEADER).toBeDefined()
476+
expect(OPERATION_ID_HEADER).toBeDefined()
477+
expect(PLATFORM_HEADER).toBeDefined()
478+
expect(WORKSPACE_IS_PRODUCTION_HEADER).toBeDefined()
479+
expect(WORKSPACE_HEADER).toBeDefined()
480+
expect(EVENT_KEY_HEADER).toBeDefined()
481+
expect(EVENT_SENDER_HEADER).toBeDefined()
482+
expect(EVENT_SUBJECT_HEADER).toBeDefined()
483+
expect(EVENT_HANDLER_ID_HEADER).toBeDefined()
484+
expect(COLOSSUS_ROUTE_DECLARER_HEADER).toBeDefined()
485+
expect(COLOSSUS_ROUTE_ID_HEADER).toBeDefined()
486+
expect(COLOSSUS_PARAMS_HEADER).toBeDefined()
487+
expect(TRACE_ID_HEADER).toBeDefined()
488+
expect(PROVIDER_HEADER).toBeDefined()
489+
})
490+
491+
test('individual constants should equal HeaderKeys values', () => {
492+
expect(CACHE_CONTROL_HEADER).toBe(HeaderKeys.CACHE_CONTROL)
493+
expect(SEGMENT_HEADER).toBe(HeaderKeys.SEGMENT)
494+
expect(SESSION_HEADER).toBe(HeaderKeys.SESSION)
495+
expect(PRODUCT_HEADER).toBe(HeaderKeys.PRODUCT)
496+
expect(LOCALE_HEADER).toBe(HeaderKeys.LOCALE)
497+
expect(FORWARDED_HOST_HEADER).toBe(HeaderKeys.FORWARDED_HOST)
498+
expect(TENANT_HEADER).toBe(HeaderKeys.TENANT)
499+
expect(BINDING_HEADER).toBe(HeaderKeys.BINDING)
500+
expect(META_HEADER).toBe(HeaderKeys.META)
501+
expect(META_HEADER_BUCKET).toBe(HeaderKeys.META_BUCKET)
502+
expect(ETAG_HEADER).toBe(HeaderKeys.ETAG)
503+
expect(ACCOUNT_HEADER).toBe(HeaderKeys.ACCOUNT)
504+
expect(CREDENTIAL_HEADER).toBe(HeaderKeys.CREDENTIAL)
505+
expect(REQUEST_ID_HEADER).toBe(HeaderKeys.REQUEST_ID)
506+
expect(ROUTER_CACHE_HEADER).toBe(HeaderKeys.ROUTER_CACHE)
507+
expect(OPERATION_ID_HEADER).toBe(HeaderKeys.OPERATION_ID)
508+
expect(PLATFORM_HEADER).toBe(HeaderKeys.PLATFORM)
509+
expect(WORKSPACE_IS_PRODUCTION_HEADER).toBe(HeaderKeys.WORKSPACE_IS_PRODUCTION)
510+
expect(WORKSPACE_HEADER).toBe(HeaderKeys.WORKSPACE)
511+
expect(EVENT_KEY_HEADER).toBe(HeaderKeys.EVENT_KEY)
512+
expect(EVENT_SENDER_HEADER).toBe(HeaderKeys.EVENT_SENDER)
513+
expect(EVENT_SUBJECT_HEADER).toBe(HeaderKeys.EVENT_SUBJECT)
514+
expect(EVENT_HANDLER_ID_HEADER).toBe(HeaderKeys.EVENT_HANDLER_ID)
515+
expect(COLOSSUS_ROUTE_DECLARER_HEADER).toBe(HeaderKeys.COLOSSUS_ROUTE_DECLARER)
516+
expect(COLOSSUS_ROUTE_ID_HEADER).toBe(HeaderKeys.COLOSSUS_ROUTE_ID)
517+
expect(COLOSSUS_PARAMS_HEADER).toBe(HeaderKeys.COLOSSUS_PARAMS)
518+
expect(TRACE_ID_HEADER).toBe(HeaderKeys.TRACE_ID)
519+
expect(PROVIDER_HEADER).toBe(HeaderKeys.PROVIDER)
520+
})
521+
522+
test('critical individual constants should have expected string values', () => {
523+
expect(TENANT_HEADER).toBe('x-vtex-tenant')
524+
expect(BINDING_HEADER).toBe('x-vtex-binding')
525+
expect(LOCALE_HEADER).toBe('x-vtex-locale')
526+
expect(SEGMENT_HEADER).toBe('x-vtex-segment')
527+
expect(SESSION_HEADER).toBe('x-vtex-session')
528+
expect(ACCOUNT_HEADER).toBe('x-vtex-account')
529+
expect(WORKSPACE_HEADER).toBe('x-vtex-workspace')
530+
})
531+
532+
test('individual constants should be strings', () => {
533+
expect(typeof TENANT_HEADER).toBe('string')
534+
expect(typeof BINDING_HEADER).toBe('string')
535+
expect(typeof LOCALE_HEADER).toBe('string')
536+
expect(typeof SEGMENT_HEADER).toBe('string')
537+
})
538+
539+
test('constants can be used as object keys without runtime errors', () => {
540+
// This is how IO apps use them in practice
541+
const headers = {
542+
[TENANT_HEADER]: 'example-value',
543+
[BINDING_HEADER]: 'example-binding',
544+
[LOCALE_HEADER]: 'en-US',
545+
[SEGMENT_HEADER]: 'segment-token',
546+
[SESSION_HEADER]: 'session-token',
547+
[ACCOUNT_HEADER]: 'account-name',
548+
[WORKSPACE_HEADER]: 'master'
549+
}
550+
551+
expect(headers['x-vtex-tenant']).toBe('example-value')
552+
expect(headers['x-vtex-binding']).toBe('example-binding')
553+
expect(headers['x-vtex-locale']).toBe('en-US')
554+
expect(headers['x-vtex-segment']).toBe('segment-token')
555+
expect(Object.keys(headers)).toHaveLength(7)
556+
557+
// Verify no undefined keys were created
558+
Object.keys(headers).forEach(key => {
559+
expect(key).not.toBe('undefined')
560+
expect(headers[key]).toBeDefined()
561+
})
562+
})
563+
564+
test('constants can be destructured from module exports', () => {
565+
// Simulates: import { TENANT_HEADER, BINDING_HEADER } from '@vtex/api'
566+
const constants = require('./constants')
567+
const {
568+
TENANT_HEADER: tenant,
569+
BINDING_HEADER: binding,
570+
LOCALE_HEADER: locale,
571+
SEGMENT_HEADER: segment
572+
} = constants
573+
574+
expect(tenant).toBeDefined()
575+
expect(binding).toBeDefined()
576+
expect(locale).toBeDefined()
577+
expect(segment).toBeDefined()
578+
579+
expect(tenant).toBe('x-vtex-tenant')
580+
expect(binding).toBe('x-vtex-binding')
581+
expect(locale).toBe('x-vtex-locale')
582+
expect(segment).toBe('x-vtex-segment')
583+
584+
// Ensure they're not undefined
585+
expect(tenant).not.toBe(undefined)
586+
expect(binding).not.toBe(undefined)
587+
})
588+
589+
test('individual constants are compatible with VaryHeaders type', () => {
590+
// VaryHeaders type uses HeaderKeys internally, but should accept old constants
591+
const varyHeaderValues: string[] = [SEGMENT_HEADER, SESSION_HEADER, PRODUCT_HEADER, LOCALE_HEADER]
592+
593+
varyHeaderValues.forEach(header => {
594+
expect(typeof header).toBe('string')
595+
expect(header.length).toBeGreaterThan(0)
596+
// VTEX headers follow x-vtex- pattern, except standard headers like cache-control
597+
expect(header).toMatch(/^x-vtex-|^cache-control$|^etag$/)
598+
})
599+
600+
// Verify they match the type definition (HeaderKeys values)
601+
const expectedVaryHeaders = [
602+
HeaderKeys.SEGMENT,
603+
HeaderKeys.SESSION,
604+
HeaderKeys.PRODUCT,
605+
HeaderKeys.LOCALE
606+
]
607+
608+
expect(varyHeaderValues).toEqual(expectedVaryHeaders)
609+
610+
// Ensure VaryHeaders type inference works
611+
expect(SEGMENT_HEADER).toBe(HeaderKeys.SEGMENT)
612+
expect(SESSION_HEADER).toBe(HeaderKeys.SESSION)
613+
expect(PRODUCT_HEADER).toBe(HeaderKeys.PRODUCT)
614+
expect(LOCALE_HEADER).toBe(HeaderKeys.LOCALE)
615+
})
616+
617+
test('constants work correctly as header keys in realistic scenarios', () => {
618+
// Simulates IO apps usage patterns
619+
const mockBinding = { locale: 'en-US', currency: 'USD' }
620+
const mockTenant = { locale: 'pt-BR' }
621+
const mockSegmentToken = 'eyJjYW1wYWlnbnMiOm51bGx9'
622+
const mockSessionToken = 'session-abc-123'
623+
624+
// Pattern 1: Building headers object
625+
const requestHeaders = {
626+
[BINDING_HEADER]: JSON.stringify(mockBinding),
627+
[TENANT_HEADER]: mockTenant.locale,
628+
[LOCALE_HEADER]: 'en-US',
629+
[SEGMENT_HEADER]: mockSegmentToken,
630+
[SESSION_HEADER]: mockSessionToken,
631+
[ACCOUNT_HEADER]: 'vtexstore',
632+
[WORKSPACE_HEADER]: 'master'
633+
}
634+
635+
expect(requestHeaders['x-vtex-binding']).toBe(JSON.stringify(mockBinding))
636+
expect(requestHeaders['x-vtex-tenant']).toBe('pt-BR')
637+
expect(requestHeaders['x-vtex-locale']).toBe('en-US')
638+
expect(requestHeaders['x-vtex-segment']).toBe(mockSegmentToken)
639+
expect(requestHeaders['x-vtex-session']).toBe(mockSessionToken)
640+
641+
// Pattern 2: Conditional header setting
642+
const conditionalHeaders: Record<string, string> = {}
643+
if (mockSegmentToken) {
644+
conditionalHeaders[SEGMENT_HEADER] = mockSegmentToken
645+
}
646+
if (mockSessionToken) {
647+
conditionalHeaders[SESSION_HEADER] = mockSessionToken
648+
}
649+
650+
expect(conditionalHeaders['x-vtex-segment']).toBe(mockSegmentToken)
651+
expect(conditionalHeaders['x-vtex-session']).toBe(mockSessionToken)
652+
expect(Object.keys(conditionalHeaders)).toHaveLength(2)
653+
654+
// Pattern 3: Reading from headers object
655+
const incomingHeaders: Record<string, string> = {
656+
'x-vtex-tenant': 'es-AR',
657+
'x-vtex-binding': '{"locale":"es-AR"}',
658+
'x-vtex-account': 'mystore'
659+
}
660+
661+
expect(incomingHeaders[TENANT_HEADER]).toBe('es-AR')
662+
expect(incomingHeaders[BINDING_HEADER]).toBe('{"locale":"es-AR"}')
663+
expect(incomingHeaders[ACCOUNT_HEADER]).toBe('mystore')
664+
665+
// Verify no undefined keys in any pattern
666+
expect(TENANT_HEADER).not.toBe('undefined')
667+
expect(BINDING_HEADER).not.toBe('undefined')
668+
expect(SEGMENT_HEADER).not.toBe('undefined')
669+
})
670+
})
429671
})
430672
})

src/constants.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,90 @@ export const AttributeKeys = {
6464
VTEX_IO_APP_AUTHOR_TYPE: ATTR_VTEX_IO_APP_AUTHOR_TYPE,
6565
}
6666

67+
/** @deprecated Use HeaderKeys.CACHE_CONTROL instead */
68+
export const CACHE_CONTROL_HEADER = HeaderKeys.CACHE_CONTROL
69+
70+
/** @deprecated Use HeaderKeys.SEGMENT instead */
71+
export const SEGMENT_HEADER = HeaderKeys.SEGMENT
72+
73+
/** @deprecated Use HeaderKeys.SESSION instead */
74+
export const SESSION_HEADER = HeaderKeys.SESSION
75+
76+
/** @deprecated Use HeaderKeys.PRODUCT instead */
77+
export const PRODUCT_HEADER = HeaderKeys.PRODUCT
78+
79+
/** @deprecated Use HeaderKeys.LOCALE instead */
80+
export const LOCALE_HEADER = HeaderKeys.LOCALE
81+
82+
/** @deprecated Use HeaderKeys.FORWARDED_HOST instead */
83+
export const FORWARDED_HOST_HEADER = HeaderKeys.FORWARDED_HOST
84+
85+
/** @deprecated Use HeaderKeys.TENANT instead */
86+
export const TENANT_HEADER = HeaderKeys.TENANT
87+
88+
/** @deprecated Use HeaderKeys.BINDING instead */
89+
export const BINDING_HEADER = HeaderKeys.BINDING
90+
91+
/** @deprecated Use HeaderKeys.META instead */
92+
export const META_HEADER = HeaderKeys.META
93+
94+
/** @deprecated Use HeaderKeys.META_BUCKET instead */
95+
export const META_HEADER_BUCKET = HeaderKeys.META_BUCKET
96+
97+
/** @deprecated Use HeaderKeys.ETAG instead */
98+
export const ETAG_HEADER = HeaderKeys.ETAG
99+
100+
/** @deprecated Use HeaderKeys.ACCOUNT instead */
101+
export const ACCOUNT_HEADER = HeaderKeys.ACCOUNT
102+
103+
/** @deprecated Use HeaderKeys.CREDENTIAL instead */
104+
export const CREDENTIAL_HEADER = HeaderKeys.CREDENTIAL
105+
106+
/** @deprecated Use HeaderKeys.REQUEST_ID instead */
107+
export const REQUEST_ID_HEADER = HeaderKeys.REQUEST_ID
108+
109+
/** @deprecated Use HeaderKeys.ROUTER_CACHE instead */
110+
export const ROUTER_CACHE_HEADER = HeaderKeys.ROUTER_CACHE
111+
112+
/** @deprecated Use HeaderKeys.OPERATION_ID instead */
113+
export const OPERATION_ID_HEADER = HeaderKeys.OPERATION_ID
114+
115+
/** @deprecated Use HeaderKeys.PLATFORM instead */
116+
export const PLATFORM_HEADER = HeaderKeys.PLATFORM
117+
118+
/** @deprecated Use HeaderKeys.WORKSPACE_IS_PRODUCTION instead */
119+
export const WORKSPACE_IS_PRODUCTION_HEADER = HeaderKeys.WORKSPACE_IS_PRODUCTION
120+
121+
/** @deprecated Use HeaderKeys.WORKSPACE instead */
122+
export const WORKSPACE_HEADER = HeaderKeys.WORKSPACE
123+
124+
/** @deprecated Use HeaderKeys.EVENT_KEY instead */
125+
export const EVENT_KEY_HEADER = HeaderKeys.EVENT_KEY
126+
127+
/** @deprecated Use HeaderKeys.EVENT_SENDER instead */
128+
export const EVENT_SENDER_HEADER = HeaderKeys.EVENT_SENDER
129+
130+
/** @deprecated Use HeaderKeys.EVENT_SUBJECT instead */
131+
export const EVENT_SUBJECT_HEADER = HeaderKeys.EVENT_SUBJECT
132+
133+
/** @deprecated Use HeaderKeys.EVENT_HANDLER_ID instead */
134+
export const EVENT_HANDLER_ID_HEADER = HeaderKeys.EVENT_HANDLER_ID
135+
136+
/** @deprecated Use HeaderKeys.COLOSSUS_ROUTE_DECLARER instead */
137+
export const COLOSSUS_ROUTE_DECLARER_HEADER = HeaderKeys.COLOSSUS_ROUTE_DECLARER
138+
139+
/** @deprecated Use HeaderKeys.COLOSSUS_ROUTE_ID instead */
140+
export const COLOSSUS_ROUTE_ID_HEADER = HeaderKeys.COLOSSUS_ROUTE_ID
141+
142+
/** @deprecated Use HeaderKeys.COLOSSUS_PARAMS instead */
143+
export const COLOSSUS_PARAMS_HEADER = HeaderKeys.COLOSSUS_PARAMS
144+
145+
/** @deprecated Use HeaderKeys.TRACE_ID instead */
146+
export const TRACE_ID_HEADER = HeaderKeys.TRACE_ID
147+
148+
/** @deprecated Use HeaderKeys.PROVIDER instead */
149+
export const PROVIDER_HEADER = HeaderKeys.PROVIDER
150+
67151
export type VaryHeaders = typeof HeaderKeys.SEGMENT | typeof HeaderKeys.SESSION | typeof HeaderKeys.PRODUCT | typeof HeaderKeys.LOCALE
68152

69153
export const BODY_HASH = '__graphqlBodyHash'

0 commit comments

Comments
 (0)