@bleedingdev/modern-js-runtime-extensions 3.9.0-ultramodern.3 → 3.9.0-ultramodern.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/dist/cjs/buildIdentity.js +73 -0
  2. package/dist/cjs/contextExtensions.js +74 -0
  3. package/dist/cjs/headRuntime.js +91 -0
  4. package/dist/cjs/helmetContext.js +54 -0
  5. package/dist/cjs/index.js +18 -4
  6. package/dist/cjs/localisedLoaderIdentity.js +51 -0
  7. package/dist/cjs/prefetchPolicy.js +52 -0
  8. package/dist/cjs/reactContext.js +45 -0
  9. package/dist/cjs/request-policy/browser.js +139 -0
  10. package/dist/cjs/request-policy/client.js +234 -0
  11. package/dist/cjs/request-policy/node.js +149 -0
  12. package/dist/cjs/request-policy/policyCore.js +266 -0
  13. package/dist/cjs/request-policy/requestContext.js +137 -0
  14. package/dist/cjs/request-policy/traceparent.js +56 -0
  15. package/dist/cjs/request-policy/transport.js +234 -0
  16. package/dist/cjs/request-policy/types.js +57 -0
  17. package/dist/cjs/routeHydration.js +127 -0
  18. package/dist/cjs/routerPrefetchPolicy.js +153 -0
  19. package/dist/cjs/routerProvider.js +170 -0
  20. package/dist/cjs/routerState.js +167 -0
  21. package/dist/cjs/routerStatePlugin.js +86 -0
  22. package/dist/cjs/routerStateTypes.js +18 -0
  23. package/dist/cjs/rsc-disabled/rscDisabledClientBrowserRuntime.js +69 -0
  24. package/dist/cjs/rsc-disabled/rscDisabledClientEdgeRuntime.js +61 -0
  25. package/dist/cjs/rsc-disabled/rscDisabledClientNodeRuntime.js +65 -0
  26. package/dist/cjs/rsc-disabled/rscDisabledRuntimeError.js +42 -0
  27. package/dist/cjs/rsc-disabled/rscDisabledServerEdgeRuntime.js +93 -0
  28. package/dist/cjs/rsc-disabled/rscDisabledServerNodeRuntime.js +101 -0
  29. package/dist/cjs/runtimeContextProjection.js +112 -0
  30. package/dist/cjs/server-config/bffRuntime.js +18 -0
  31. package/dist/cjs/server-config/index.js +18 -0
  32. package/dist/cjs/server-config/serverTelemetry.js +18 -0
  33. package/dist/cjs/shellStream.js +34 -2
  34. package/dist/cjs/workspaceEvents.js +159 -0
  35. package/dist/esm/buildIdentity.mjs +35 -0
  36. package/dist/esm/contextExtensions.mjs +33 -0
  37. package/dist/esm/headRuntime.mjs +43 -0
  38. package/dist/esm/helmetContext.mjs +13 -0
  39. package/dist/esm/index.mjs +2 -0
  40. package/dist/esm/localisedLoaderIdentity.mjs +13 -0
  41. package/dist/esm/prefetchPolicy.mjs +11 -0
  42. package/dist/esm/reactContext.mjs +7 -0
  43. package/dist/esm/request-policy/browser.mjs +11 -0
  44. package/dist/esm/request-policy/client.mjs +195 -0
  45. package/dist/esm/request-policy/policyCore.mjs +171 -0
  46. package/dist/esm/request-policy/requestContext.mjs +75 -0
  47. package/dist/esm/request-policy/traceparent.mjs +18 -0
  48. package/dist/esm/request-policy/transport.mjs +196 -0
  49. package/dist/esm/request-policy/types.mjs +10 -0
  50. package/dist/esm/routeHydration.mjs +77 -0
  51. package/dist/esm/routerPrefetchPolicy.mjs +115 -0
  52. package/dist/esm/routerProvider.mjs +117 -0
  53. package/dist/esm/routerState.mjs +105 -0
  54. package/dist/esm/routerStatePlugin.mjs +48 -0
  55. package/dist/esm/routerStateTypes.mjs +0 -0
  56. package/dist/esm/rsc-disabled/rscDisabledClientBrowserRuntime.mjs +10 -0
  57. package/dist/esm/rsc-disabled/rscDisabledClientEdgeRuntime.mjs +8 -0
  58. package/dist/esm/rsc-disabled/rscDisabledClientNodeRuntime.mjs +9 -0
  59. package/dist/esm/rsc-disabled/rscDisabledRuntimeError.mjs +4 -0
  60. package/dist/esm/rsc-disabled/rscDisabledServerEdgeRuntime.mjs +16 -0
  61. package/dist/esm/rsc-disabled/rscDisabledServerNodeRuntime.mjs +18 -0
  62. package/dist/esm/runtimeContextProjection.mjs +74 -0
  63. package/dist/esm/server-config/bffRuntime.mjs +0 -0
  64. package/dist/esm/server-config/index.mjs +0 -0
  65. package/dist/esm/server-config/serverTelemetry.mjs +0 -0
  66. package/dist/esm/shellStream.mjs +34 -2
  67. package/dist/esm/workspaceEvents.mjs +66 -0
  68. package/dist/esm-node/buildIdentity.mjs +36 -0
  69. package/dist/esm-node/contextExtensions.mjs +34 -0
  70. package/dist/esm-node/headRuntime.mjs +44 -0
  71. package/dist/esm-node/helmetContext.mjs +14 -0
  72. package/dist/esm-node/index.mjs +2 -0
  73. package/dist/esm-node/localisedLoaderIdentity.mjs +14 -0
  74. package/dist/esm-node/prefetchPolicy.mjs +12 -0
  75. package/dist/esm-node/reactContext.mjs +8 -0
  76. package/dist/esm-node/request-policy/browser.mjs +12 -0
  77. package/dist/esm-node/request-policy/client.mjs +196 -0
  78. package/dist/esm-node/request-policy/node.mjs +22 -0
  79. package/dist/esm-node/request-policy/policyCore.mjs +172 -0
  80. package/dist/esm-node/request-policy/requestContext.mjs +76 -0
  81. package/dist/esm-node/request-policy/traceparent.mjs +19 -0
  82. package/dist/esm-node/request-policy/transport.mjs +197 -0
  83. package/dist/esm-node/request-policy/types.mjs +11 -0
  84. package/dist/esm-node/routeHydration.mjs +78 -0
  85. package/dist/esm-node/routerPrefetchPolicy.mjs +116 -0
  86. package/dist/esm-node/routerProvider.mjs +118 -0
  87. package/dist/esm-node/routerState.mjs +106 -0
  88. package/dist/esm-node/routerStatePlugin.mjs +49 -0
  89. package/dist/esm-node/routerStateTypes.mjs +1 -0
  90. package/dist/esm-node/rsc-disabled/rscDisabledClientBrowserRuntime.mjs +11 -0
  91. package/dist/esm-node/rsc-disabled/rscDisabledClientEdgeRuntime.mjs +9 -0
  92. package/dist/esm-node/rsc-disabled/rscDisabledClientNodeRuntime.mjs +10 -0
  93. package/dist/esm-node/rsc-disabled/rscDisabledRuntimeError.mjs +5 -0
  94. package/dist/esm-node/rsc-disabled/rscDisabledServerEdgeRuntime.mjs +17 -0
  95. package/dist/esm-node/rsc-disabled/rscDisabledServerNodeRuntime.mjs +19 -0
  96. package/dist/esm-node/runtimeContextProjection.mjs +75 -0
  97. package/dist/esm-node/server-config/bffRuntime.mjs +1 -0
  98. package/dist/esm-node/server-config/index.mjs +1 -0
  99. package/dist/esm-node/server-config/serverTelemetry.mjs +1 -0
  100. package/dist/esm-node/shellStream.mjs +34 -2
  101. package/dist/esm-node/workspaceEvents.mjs +67 -0
  102. package/dist/types/buildIdentity.d.ts +37 -0
  103. package/dist/types/contextExtensions.d.ts +34 -0
  104. package/dist/types/headRuntime.d.ts +12 -0
  105. package/dist/types/helmetContext.d.ts +10 -0
  106. package/dist/types/index.d.ts +2 -0
  107. package/dist/types/localisedLoaderIdentity.d.ts +10 -0
  108. package/dist/types/prefetchPolicy.d.ts +4 -0
  109. package/dist/types/reactContext.d.ts +6 -0
  110. package/dist/types/request-policy/browser.d.ts +10 -0
  111. package/dist/types/request-policy/client.d.ts +14 -0
  112. package/dist/types/request-policy/node.d.ts +10 -0
  113. package/dist/types/request-policy/policyCore.d.ts +107 -0
  114. package/dist/types/request-policy/requestContext.d.ts +22 -0
  115. package/dist/types/request-policy/traceparent.d.ts +10 -0
  116. package/dist/types/request-policy/transport.d.ts +12 -0
  117. package/dist/types/request-policy/types.d.ts +144 -0
  118. package/dist/types/routeHydration.d.ts +24 -0
  119. package/dist/types/routerPrefetchPolicy.d.ts +28 -0
  120. package/dist/types/routerProvider.d.ts +77 -0
  121. package/dist/types/routerState.d.ts +12 -0
  122. package/dist/types/routerStatePlugin.d.ts +19 -0
  123. package/dist/types/routerStateTypes.d.ts +40 -0
  124. package/dist/types/rsc-disabled/rscDisabledClientBrowserRuntime.d.ts +9 -0
  125. package/dist/types/rsc-disabled/rscDisabledClientEdgeRuntime.d.ts +7 -0
  126. package/dist/types/rsc-disabled/rscDisabledClientNodeRuntime.d.ts +8 -0
  127. package/dist/types/rsc-disabled/rscDisabledRuntimeError.d.ts +1 -0
  128. package/dist/types/rsc-disabled/rscDisabledServerEdgeRuntime.d.ts +15 -0
  129. package/dist/types/rsc-disabled/rscDisabledServerNodeRuntime.d.ts +17 -0
  130. package/dist/types/runtimeContextProjection.d.ts +30 -0
  131. package/dist/types/server-config/bffRuntime.d.ts +122 -0
  132. package/dist/types/server-config/index.d.ts +2 -0
  133. package/dist/types/server-config/serverTelemetry.d.ts +322 -0
  134. package/dist/types/workspaceEvents.d.ts +62 -0
  135. package/package.json +238 -13
  136. package/rslib.config.mts +17 -2
  137. package/rstest.config.mts +33 -2
  138. package/src/buildIdentity.ts +61 -0
  139. package/src/contextExtensions.ts +81 -0
  140. package/src/headRuntime.tsx +77 -0
  141. package/src/helmetContext.ts +31 -0
  142. package/src/index.ts +2 -0
  143. package/src/localisedLoaderIdentity.ts +30 -0
  144. package/src/prefetchPolicy.ts +33 -0
  145. package/src/reactContext.ts +16 -0
  146. package/src/request-policy/browser.ts +22 -0
  147. package/src/request-policy/client.ts +404 -0
  148. package/src/request-policy/node.ts +41 -0
  149. package/src/request-policy/policyCore.ts +424 -0
  150. package/src/request-policy/requestContext.ts +149 -0
  151. package/src/request-policy/traceparent.ts +43 -0
  152. package/src/request-policy/transport.ts +349 -0
  153. package/src/request-policy/types.ts +229 -0
  154. package/src/routeHydration.ts +208 -0
  155. package/src/routerPrefetchPolicy.ts +195 -0
  156. package/src/routerProvider.ts +340 -0
  157. package/src/routerState.ts +208 -0
  158. package/src/routerStatePlugin.ts +86 -0
  159. package/src/routerStateTypes.ts +46 -0
  160. package/src/rsc-disabled/rscDisabledClientBrowserRuntime.ts +10 -0
  161. package/src/rsc-disabled/rscDisabledClientEdgeRuntime.ts +8 -0
  162. package/src/rsc-disabled/rscDisabledClientNodeRuntime.ts +9 -0
  163. package/src/rsc-disabled/rscDisabledRuntimeError.ts +3 -0
  164. package/src/rsc-disabled/rscDisabledServerEdgeRuntime.ts +16 -0
  165. package/src/rsc-disabled/rscDisabledServerNodeRuntime.ts +18 -0
  166. package/src/runtimeContextProjection.ts +105 -0
  167. package/src/server-config/bffRuntime.ts +136 -0
  168. package/src/server-config/index.ts +2 -0
  169. package/src/server-config/serverTelemetry.ts +337 -0
  170. package/src/shellStream.ts +55 -25
  171. package/src/workspaceEvents.ts +324 -0
  172. package/tests/buildIdentity.test.ts +70 -0
  173. package/tests/contextExtensions.test.ts +71 -0
  174. package/tests/headRuntime.test.tsx +369 -0
  175. package/tests/nativeSafeFailureCompatibility.test.ts +117 -0
  176. package/tests/prefetchPolicy.test.ts +51 -0
  177. package/tests/rendererHead.test.ts +1 -72
  178. package/tests/request-policy/browser-policy.test.ts +608 -0
  179. package/tests/request-policy/client-isolation.test.ts +149 -0
  180. package/tests/request-policy/identityBinding.test.ts +75 -0
  181. package/tests/request-policy/node-policy.test.ts +741 -0
  182. package/tests/request-policy/policyCore.test.ts +250 -0
  183. package/tests/request-policy/requestContext.test.ts +80 -0
  184. package/tests/request-policy/requestFactory.outbound-contract.test.ts +691 -0
  185. package/tests/request-policy/traceparent.test.ts +55 -0
  186. package/tests/request-policy/transport-retry.test.ts +370 -0
  187. package/tests/routeHydration.test.ts +180 -0
  188. package/tests/routerPrefetchPolicy.test.ts +102 -0
  189. package/tests/routerProvider.test.ts +38 -0
  190. package/tests/routerState.test.ts +157 -0
  191. package/tests/routerStatePlugin.test.ts +108 -0
  192. package/tests/runtimeContextProjection.test.ts +135 -0
  193. package/tests/shellStream.test.ts +37 -0
  194. package/tests/workspaceEvents.test.ts +391 -0
  195. package/dist/cjs/rscHtmlStream.js +0 -128
  196. package/dist/esm/rscHtmlStream.mjs +0 -86
  197. package/dist/esm-node/rscHtmlStream.mjs +0 -87
  198. package/dist/types/rscHtmlStream.d.ts +0 -4
  199. package/src/rscHtmlStream.ts +0 -142
  200. package/tests/rscHtmlStream.test.ts +0 -153
@@ -0,0 +1,250 @@
1
+ import {
2
+ attachOperationContextHeaders,
3
+ buildEnvelopeHeaderValue,
4
+ buildOperationContext,
5
+ CrossOriginEnvelopePolicyError,
6
+ deleteHeader,
7
+ findHeaderKey,
8
+ isSecuredRequestId,
9
+ OperationContractViolationError,
10
+ readHeader,
11
+ resolveConfiguredRequest,
12
+ toOrigin,
13
+ validateOperationContract,
14
+ writeHeader,
15
+ } from '../../src/request-policy/policyCore';
16
+
17
+ describe('policyCore (shared browser/node policy module)', () => {
18
+ test('header helpers are case-insensitive and normalize duplicate keys', () => {
19
+ const headers: Record<string, any> = { 'X-Foo': 'a' };
20
+
21
+ expect(findHeaderKey(headers, 'x-foo')).toBe('X-Foo');
22
+ expect(readHeader(headers, 'x-FOO')).toBe('a');
23
+
24
+ writeHeader(headers, 'x-foo', 'b');
25
+ expect(headers).toEqual({ 'x-foo': 'b' });
26
+
27
+ deleteHeader(headers, 'X-FOO');
28
+ expect(headers).toEqual({});
29
+ });
30
+
31
+ test('toOrigin behaves identically for both targets', () => {
32
+ expect(toOrigin('https://example.com/a/b?c=1')).toBe('https://example.com');
33
+ expect(toOrigin('not-a-url')).toBeUndefined();
34
+ });
35
+
36
+ test('isSecuredRequestId treats only "default" as unsecured by default', () => {
37
+ expect(isSecuredRequestId('default')).toBe(false);
38
+ expect(isSecuredRequestId('crm-producer')).toBe(true);
39
+ });
40
+
41
+ test('buildOperationContext prefixes operationId with the requestId', () => {
42
+ const context = buildOperationContext({
43
+ requestId: 'crm',
44
+ method: 'get',
45
+ path: '/hello',
46
+ operationContext: {
47
+ operationId: 'getHello',
48
+ schemaHash: 'abc',
49
+ operationVersion: 2,
50
+ },
51
+ });
52
+
53
+ expect(context.operationId).toBe('crm:getHello');
54
+ expect(context.method).toBe('GET');
55
+ expect(context.schemaHash).toBe('abc');
56
+ expect(context.operationVersion).toBe(2);
57
+ });
58
+
59
+ test('validateOperationContract throws on missing schema hash for secured ids', () => {
60
+ const contextPayload = buildOperationContext({
61
+ requestId: 'crm',
62
+ method: 'GET',
63
+ path: '/hello',
64
+ });
65
+
66
+ expect(() =>
67
+ validateOperationContract({
68
+ requestId: 'crm',
69
+ target: 'browser',
70
+ contextPayload,
71
+ operationContract: undefined,
72
+ }),
73
+ ).toThrow(OperationContractViolationError);
74
+ });
75
+
76
+ test('buildEnvelopeHeaderValue denies cross-origin flows unless allowed', () => {
77
+ expect(() =>
78
+ buildEnvelopeHeaderValue({
79
+ requestId: 'crm',
80
+ target: 'server',
81
+ sourceOrigin: 'https://a.example',
82
+ targetOrigin: 'https://b.example',
83
+ traceContext: undefined,
84
+ allowCrossOriginEnvelope: undefined,
85
+ }),
86
+ ).toThrow(CrossOriginEnvelopePolicyError);
87
+
88
+ const envelope = JSON.parse(
89
+ buildEnvelopeHeaderValue({
90
+ requestId: 'crm',
91
+ target: 'server',
92
+ sourceOrigin: 'https://a.example',
93
+ targetOrigin: 'https://b.example',
94
+ traceContext: { traceId: 't', spanId: 's' },
95
+ allowCrossOriginEnvelope: true,
96
+ }),
97
+ );
98
+ expect(envelope).toMatchObject({
99
+ requestId: 'crm',
100
+ target: 'server',
101
+ sourceOrigin: 'https://a.example',
102
+ targetOrigin: 'https://b.example',
103
+ traceId: 't',
104
+ spanId: 's',
105
+ });
106
+ });
107
+
108
+ test('buildEnvelopeHeaderValue requires cross-origin predicate to return true', () => {
109
+ const allowCrossOriginEnvelope = () => 'yes';
110
+
111
+ expect(() =>
112
+ buildEnvelopeHeaderValue({
113
+ requestId: 'crm',
114
+ target: 'server',
115
+ sourceOrigin: 'https://a.example',
116
+ targetOrigin: 'https://b.example',
117
+ traceContext: undefined,
118
+ allowCrossOriginEnvelope:
119
+ allowCrossOriginEnvelope as unknown as () => boolean,
120
+ }),
121
+ ).toThrow(CrossOriginEnvelopePolicyError);
122
+ });
123
+
124
+ test('attachOperationContextHeaders writes id and detail headers without clobbering caller id', () => {
125
+ const headers: Record<string, any> = {
126
+ 'x-operation-id': 'crm:custom-op',
127
+ };
128
+
129
+ attachOperationContextHeaders({
130
+ headers,
131
+ requestId: 'crm',
132
+ target: 'server',
133
+ method: 'GET',
134
+ path: '/hello',
135
+ operationContext: {
136
+ operationId: 'getHello',
137
+ schemaHash: 'abc',
138
+ operationVersion: 1,
139
+ },
140
+ operationContract: undefined,
141
+ operationContextHeader: 'x-operation-id',
142
+ operationContextDetailHeader: 'x-modernjs-bff-operation-context',
143
+ });
144
+
145
+ expect(headers['x-operation-id']).toBe('crm:custom-op');
146
+ const details = JSON.parse(headers['x-modernjs-bff-operation-context']);
147
+ expect(details).toMatchObject({
148
+ requestId: 'crm',
149
+ operationId: 'crm:getHello',
150
+ schemaHash: 'abc',
151
+ operationVersion: 1,
152
+ });
153
+ });
154
+
155
+ test('resolveConfiguredRequest throws for unconfigured non-default producers', () => {
156
+ const map = new Map<string, string>();
157
+ expect(() => resolveConfiguredRequest(map, 'crm', 'fallback')).toThrow(
158
+ 'Producer client "crm" is not initialized',
159
+ );
160
+ expect(resolveConfiguredRequest(map, 'default', 'fallback')).toBe(
161
+ 'fallback',
162
+ );
163
+ map.set('crm', 'configured');
164
+ expect(resolveConfiguredRequest(map, 'crm', 'fallback')).toBe('configured');
165
+ });
166
+ });
167
+
168
+ describe('createUploader policy headers (node entry)', () => {
169
+ test('attaches envelope and operation-context headers for secured producers', async () => {
170
+ const { configure, createUploader } = await import(
171
+ '@modern-js/runtime-extensions/request-policy/server'
172
+ );
173
+
174
+ const seenInit: Array<{ url: string; init: any }> = [];
175
+ const fakeFetch = (async (url: any, init: any) => {
176
+ seenInit.push({ url: String(url), init });
177
+ return new Response('{}', { status: 200 });
178
+ }) as typeof fetch;
179
+
180
+ configure({
181
+ requestId: 'upload-producer',
182
+ request: fakeFetch,
183
+ setDomain: () => 'http://producer.example',
184
+ });
185
+
186
+ const upload = createUploader({
187
+ path: '/api/upload',
188
+ requestId: 'upload-producer',
189
+ operationContext: {
190
+ operationId: 'upload',
191
+ routePath: '/api/upload',
192
+ method: 'POST',
193
+ schemaHash: 'hash-1',
194
+ operationVersion: 1,
195
+ },
196
+ });
197
+
198
+ const file = new File(['hello'], 'hello.txt', { type: 'text/plain' });
199
+ await upload({ files: { file } });
200
+
201
+ expect(seenInit).toHaveLength(1);
202
+ const { url, init } = seenInit[0]!;
203
+ expect(url).toBe('http://producer.example/api/upload');
204
+ expect(init.method).toBe('POST');
205
+
206
+ const envelope = JSON.parse(init.headers['x-modernjs-bff-envelope']);
207
+ expect(envelope.requestId).toBe('upload-producer');
208
+
209
+ expect(init.headers['x-operation-id']).toBe('upload-producer:upload');
210
+ const details = JSON.parse(
211
+ init.headers['x-modernjs-bff-operation-context'],
212
+ );
213
+ expect(details).toMatchObject({
214
+ requestId: 'upload-producer',
215
+ operationId: 'upload-producer:upload',
216
+ routePath: '/api/upload',
217
+ method: 'POST',
218
+ schemaHash: 'hash-1',
219
+ operationVersion: 1,
220
+ });
221
+ });
222
+
223
+ test('keeps the default uploader free of policy headers', async () => {
224
+ const { createUploader } = await import(
225
+ '@modern-js/runtime-extensions/request-policy/server'
226
+ );
227
+
228
+ const seenInit: Array<{ init: any }> = [];
229
+ const fakeFetch = (async (_url: any, init: any) => {
230
+ seenInit.push({ init });
231
+ return new Response('{}', { status: 200 });
232
+ }) as typeof fetch;
233
+
234
+ // default requestId falls back to the passed fetch only via configure-less
235
+ // path; install a custom request for determinism.
236
+ const { configure } = await import(
237
+ '@modern-js/runtime-extensions/request-policy/server'
238
+ );
239
+ configure({ request: fakeFetch });
240
+
241
+ const upload = createUploader({ path: '/api/upload' });
242
+ const file = new File(['hi'], 'hi.txt', { type: 'text/plain' });
243
+ await upload({ files: { file } });
244
+
245
+ expect(seenInit).toHaveLength(1);
246
+ const headers = seenInit[0]!.init.headers as Record<string, unknown>;
247
+ expect(headers['x-modernjs-bff-envelope']).toBeUndefined();
248
+ expect(headers['x-operation-id']).toBeUndefined();
249
+ });
250
+ });
@@ -0,0 +1,80 @@
1
+ import {
2
+ BFF_LOCALE_HEADER,
3
+ BFF_TRACEPARENT_HEADER,
4
+ createRequestContextHeaders,
5
+ createRequestContextSnapshot,
6
+ } from '@modern-js/runtime-extensions/request-policy/client';
7
+
8
+ describe('request context helpers', () => {
9
+ test('should create propagation headers from explicit locale and traceparent', () => {
10
+ expect(
11
+ createRequestContextHeaders({
12
+ locale: 'cs-CZ',
13
+ traceparent: '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01',
14
+ }),
15
+ ).toEqual({
16
+ [BFF_LOCALE_HEADER]: 'cs-CZ',
17
+ [BFF_TRACEPARENT_HEADER]:
18
+ '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01',
19
+ });
20
+ });
21
+
22
+ test('should derive locale and trace metadata from incoming headers', () => {
23
+ expect(
24
+ createRequestContextSnapshot({
25
+ headers: {
26
+ 'accept-language': 'en-US,en;q=0.8',
27
+ traceparent:
28
+ '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01',
29
+ },
30
+ }),
31
+ ).toEqual({
32
+ headers: {
33
+ 'accept-language': 'en-US,en;q=0.8',
34
+ traceparent: '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01',
35
+ },
36
+ locale: 'en-US,en;q=0.8',
37
+ traceparent: '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01',
38
+ traceId: '4bf92f3577b34da6a3ce929d0e0e4736',
39
+ spanId: '00f067aa0ba902b7',
40
+ });
41
+ });
42
+
43
+ test('should keep operation context in snapshots without widening propagation headers', () => {
44
+ const snapshot = createRequestContextSnapshot({
45
+ locale: 'cs-CZ',
46
+ operationContext: {
47
+ operationId: 'shell:list',
48
+ routePath: '/effect/recommendations',
49
+ method: 'GET',
50
+ source: 'generated-client',
51
+ scope: { workspace: 'demo' },
52
+ sessionClaims: { role: 'viewer' },
53
+ traceparent: '00-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-01',
54
+ },
55
+ });
56
+
57
+ expect(snapshot.headers).toEqual({
58
+ [BFF_LOCALE_HEADER]: 'cs-CZ',
59
+ [BFF_TRACEPARENT_HEADER]:
60
+ '00-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-01',
61
+ });
62
+ expect(snapshot).toMatchObject({
63
+ locale: 'cs-CZ',
64
+ traceparent: '00-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-01',
65
+ traceId: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
66
+ spanId: 'bbbbbbbbbbbbbbbb',
67
+ operationContext: {
68
+ operationId: 'shell:list',
69
+ routePath: '/effect/recommendations',
70
+ method: 'GET',
71
+ source: 'generated-client',
72
+ scope: { workspace: 'demo' },
73
+ sessionClaims: { role: 'viewer' },
74
+ traceparent: '00-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-01',
75
+ traceId: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
76
+ spanId: 'bbbbbbbbbbbbbbbb',
77
+ },
78
+ });
79
+ });
80
+ });