@bleedingdev/modern-js-runtime-extensions 3.9.0-ultramodern.4 → 3.9.0-ultramodern.6

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 (155) hide show
  1. package/dist/cjs/contextExtensions.js +74 -0
  2. package/dist/cjs/headRuntime.js +91 -0
  3. package/dist/cjs/helmetContext.js +54 -0
  4. package/dist/cjs/index.js +18 -4
  5. package/dist/cjs/prefetchPolicy.js +52 -0
  6. package/dist/cjs/request-policy/browser.js +139 -0
  7. package/dist/cjs/request-policy/client.js +234 -0
  8. package/dist/cjs/request-policy/node.js +149 -0
  9. package/dist/cjs/request-policy/policyCore.js +266 -0
  10. package/dist/cjs/request-policy/requestContext.js +137 -0
  11. package/dist/cjs/request-policy/traceparent.js +56 -0
  12. package/dist/cjs/request-policy/transport.js +234 -0
  13. package/dist/cjs/request-policy/types.js +57 -0
  14. package/dist/cjs/routeHydration.js +127 -0
  15. package/dist/cjs/routerPrefetchPolicy.js +153 -0
  16. package/dist/cjs/routerProvider.js +105 -0
  17. package/dist/cjs/routerState.js +167 -0
  18. package/dist/cjs/routerStatePlugin.js +86 -0
  19. package/dist/cjs/routerStateTypes.js +18 -0
  20. package/dist/cjs/runtimeContextProjection.js +112 -0
  21. package/dist/cjs/server-config/bffRuntime.js +18 -0
  22. package/dist/cjs/server-config/index.js +18 -0
  23. package/dist/cjs/server-config/serverTelemetry.js +18 -0
  24. package/dist/cjs/shellStream.js +34 -2
  25. package/dist/cjs/workspaceEvents.js +159 -0
  26. package/dist/esm/contextExtensions.mjs +33 -0
  27. package/dist/esm/headRuntime.mjs +43 -0
  28. package/dist/esm/helmetContext.mjs +13 -0
  29. package/dist/esm/index.mjs +2 -0
  30. package/dist/esm/prefetchPolicy.mjs +11 -0
  31. package/dist/esm/request-policy/browser.mjs +11 -0
  32. package/dist/esm/request-policy/client.mjs +195 -0
  33. package/dist/esm/request-policy/policyCore.mjs +171 -0
  34. package/dist/esm/request-policy/requestContext.mjs +75 -0
  35. package/dist/esm/request-policy/traceparent.mjs +18 -0
  36. package/dist/esm/request-policy/transport.mjs +196 -0
  37. package/dist/esm/request-policy/types.mjs +10 -0
  38. package/dist/esm/routeHydration.mjs +77 -0
  39. package/dist/esm/routerPrefetchPolicy.mjs +115 -0
  40. package/dist/esm/routerProvider.mjs +58 -0
  41. package/dist/esm/routerState.mjs +105 -0
  42. package/dist/esm/routerStatePlugin.mjs +48 -0
  43. package/dist/esm/routerStateTypes.mjs +0 -0
  44. package/dist/esm/runtimeContextProjection.mjs +74 -0
  45. package/dist/esm/server-config/bffRuntime.mjs +0 -0
  46. package/dist/esm/server-config/index.mjs +0 -0
  47. package/dist/esm/server-config/serverTelemetry.mjs +0 -0
  48. package/dist/esm/shellStream.mjs +34 -2
  49. package/dist/esm/workspaceEvents.mjs +66 -0
  50. package/dist/esm-node/contextExtensions.mjs +34 -0
  51. package/dist/esm-node/headRuntime.mjs +44 -0
  52. package/dist/esm-node/helmetContext.mjs +14 -0
  53. package/dist/esm-node/index.mjs +2 -0
  54. package/dist/esm-node/prefetchPolicy.mjs +12 -0
  55. package/dist/esm-node/request-policy/browser.mjs +12 -0
  56. package/dist/esm-node/request-policy/client.mjs +196 -0
  57. package/dist/esm-node/request-policy/node.mjs +22 -0
  58. package/dist/esm-node/request-policy/policyCore.mjs +172 -0
  59. package/dist/esm-node/request-policy/requestContext.mjs +76 -0
  60. package/dist/esm-node/request-policy/traceparent.mjs +19 -0
  61. package/dist/esm-node/request-policy/transport.mjs +197 -0
  62. package/dist/esm-node/request-policy/types.mjs +11 -0
  63. package/dist/esm-node/routeHydration.mjs +78 -0
  64. package/dist/esm-node/routerPrefetchPolicy.mjs +116 -0
  65. package/dist/esm-node/routerProvider.mjs +59 -0
  66. package/dist/esm-node/routerState.mjs +106 -0
  67. package/dist/esm-node/routerStatePlugin.mjs +49 -0
  68. package/dist/esm-node/routerStateTypes.mjs +1 -0
  69. package/dist/esm-node/runtimeContextProjection.mjs +75 -0
  70. package/dist/esm-node/server-config/bffRuntime.mjs +1 -0
  71. package/dist/esm-node/server-config/index.mjs +1 -0
  72. package/dist/esm-node/server-config/serverTelemetry.mjs +1 -0
  73. package/dist/esm-node/shellStream.mjs +34 -2
  74. package/dist/esm-node/workspaceEvents.mjs +67 -0
  75. package/dist/types/contextExtensions.d.ts +34 -0
  76. package/dist/types/headRuntime.d.ts +12 -0
  77. package/dist/types/helmetContext.d.ts +10 -0
  78. package/dist/types/index.d.ts +2 -0
  79. package/dist/types/prefetchPolicy.d.ts +4 -0
  80. package/dist/types/request-policy/browser.d.ts +10 -0
  81. package/dist/types/request-policy/client.d.ts +14 -0
  82. package/dist/types/request-policy/node.d.ts +10 -0
  83. package/dist/types/request-policy/policyCore.d.ts +107 -0
  84. package/dist/types/request-policy/requestContext.d.ts +22 -0
  85. package/dist/types/request-policy/traceparent.d.ts +10 -0
  86. package/dist/types/request-policy/transport.d.ts +12 -0
  87. package/dist/types/request-policy/types.d.ts +144 -0
  88. package/dist/types/routeHydration.d.ts +24 -0
  89. package/dist/types/routerPrefetchPolicy.d.ts +28 -0
  90. package/dist/types/routerProvider.d.ts +55 -0
  91. package/dist/types/routerState.d.ts +12 -0
  92. package/dist/types/routerStatePlugin.d.ts +19 -0
  93. package/dist/types/routerStateTypes.d.ts +40 -0
  94. package/dist/types/runtimeContextProjection.d.ts +30 -0
  95. package/dist/types/server-config/bffRuntime.d.ts +122 -0
  96. package/dist/types/server-config/index.d.ts +2 -0
  97. package/dist/types/server-config/serverTelemetry.d.ts +322 -0
  98. package/dist/types/workspaceEvents.d.ts +62 -0
  99. package/package.json +198 -13
  100. package/rslib.config.mts +17 -2
  101. package/rstest.config.mts +33 -2
  102. package/src/contextExtensions.ts +81 -0
  103. package/src/headRuntime.tsx +77 -0
  104. package/src/helmetContext.ts +31 -0
  105. package/src/index.ts +2 -0
  106. package/src/prefetchPolicy.ts +33 -0
  107. package/src/request-policy/browser.ts +22 -0
  108. package/src/request-policy/client.ts +404 -0
  109. package/src/request-policy/node.ts +41 -0
  110. package/src/request-policy/policyCore.ts +424 -0
  111. package/src/request-policy/requestContext.ts +149 -0
  112. package/src/request-policy/traceparent.ts +43 -0
  113. package/src/request-policy/transport.ts +349 -0
  114. package/src/request-policy/types.ts +229 -0
  115. package/src/routeHydration.ts +208 -0
  116. package/src/routerPrefetchPolicy.ts +195 -0
  117. package/src/routerProvider.ts +160 -0
  118. package/src/routerState.ts +208 -0
  119. package/src/routerStatePlugin.ts +86 -0
  120. package/src/routerStateTypes.ts +46 -0
  121. package/src/runtimeContextProjection.ts +105 -0
  122. package/src/server-config/bffRuntime.ts +136 -0
  123. package/src/server-config/index.ts +2 -0
  124. package/src/server-config/serverTelemetry.ts +337 -0
  125. package/src/shellStream.ts +55 -25
  126. package/src/workspaceEvents.ts +324 -0
  127. package/tests/buildIdentity.test.ts +2 -4
  128. package/tests/contextExtensions.test.ts +71 -0
  129. package/tests/headRuntime.test.tsx +369 -0
  130. package/tests/nativeSafeFailureCompatibility.test.ts +117 -0
  131. package/tests/prefetchPolicy.test.ts +51 -0
  132. package/tests/rendererHead.test.ts +1 -72
  133. package/tests/request-policy/browser-policy.test.ts +608 -0
  134. package/tests/request-policy/client-isolation.test.ts +149 -0
  135. package/tests/request-policy/identityBinding.test.ts +75 -0
  136. package/tests/request-policy/node-policy.test.ts +741 -0
  137. package/tests/request-policy/policyCore.test.ts +250 -0
  138. package/tests/request-policy/requestContext.test.ts +80 -0
  139. package/tests/request-policy/requestFactory.outbound-contract.test.ts +691 -0
  140. package/tests/request-policy/traceparent.test.ts +55 -0
  141. package/tests/request-policy/transport-retry.test.ts +370 -0
  142. package/tests/routeHydration.test.ts +180 -0
  143. package/tests/routerPrefetchPolicy.test.ts +102 -0
  144. package/tests/routerProvider.test.ts +29 -0
  145. package/tests/routerState.test.ts +157 -0
  146. package/tests/routerStatePlugin.test.ts +108 -0
  147. package/tests/runtimeContextProjection.test.ts +135 -0
  148. package/tests/shellStream.test.ts +37 -0
  149. package/tests/workspaceEvents.test.ts +391 -0
  150. package/dist/cjs/rscHtmlStream.js +0 -128
  151. package/dist/esm/rscHtmlStream.mjs +0 -86
  152. package/dist/esm-node/rscHtmlStream.mjs +0 -87
  153. package/dist/types/rscHtmlStream.d.ts +0 -4
  154. package/src/rscHtmlStream.ts +0 -142
  155. package/tests/rscHtmlStream.test.ts +0 -153
@@ -0,0 +1,349 @@
1
+ import type {
2
+ DegradedModeEvent,
3
+ RetryBackoffOptions,
4
+ RetryDecisionContext,
5
+ TransportResilienceOptions,
6
+ TransportTarget,
7
+ } from './types';
8
+
9
+ const DEFAULT_RETRYABLE_STATUS_CODES = [408, 425, 429, 500, 502, 503, 504];
10
+ const DEFAULT_BASE_DELAY_MS = 100;
11
+ const DEFAULT_MAX_DELAY_MS = 1000;
12
+ const DEFAULT_JITTER_RATIO = 0.1;
13
+ const DEFAULT_RETRYABLE_METHODS = new Set([
14
+ 'DELETE',
15
+ 'GET',
16
+ 'HEAD',
17
+ 'OPTIONS',
18
+ 'PUT',
19
+ 'TRACE',
20
+ ]);
21
+
22
+ type ErrorLike = {
23
+ name?: unknown;
24
+ code?: unknown;
25
+ status?: unknown;
26
+ response?: { status?: unknown };
27
+ };
28
+
29
+ const createTimeoutError = (timeoutMs: number) => {
30
+ const error = new Error(`Request timed out after ${timeoutMs}ms`);
31
+ error.name = 'TimeoutError';
32
+ return error;
33
+ };
34
+
35
+ const unrefTimer = (timer: unknown) => {
36
+ if (
37
+ typeof timer === 'object' &&
38
+ timer !== null &&
39
+ 'unref' in timer &&
40
+ typeof timer.unref === 'function'
41
+ ) {
42
+ timer.unref();
43
+ }
44
+ };
45
+
46
+ const wait = (ms: number) =>
47
+ new Promise<void>(resolve => {
48
+ const timer = setTimeout(() => resolve(), ms);
49
+ unrefTimer(timer);
50
+ });
51
+
52
+ const toStatusCode = (error: unknown): number | undefined => {
53
+ if (!error || typeof error !== 'object') {
54
+ return undefined;
55
+ }
56
+
57
+ const status = (error as ErrorLike).status;
58
+ if (typeof status === 'number') {
59
+ return status;
60
+ }
61
+
62
+ const responseStatus = (error as ErrorLike).response?.status;
63
+ if (typeof responseStatus === 'number') {
64
+ return responseStatus;
65
+ }
66
+
67
+ return undefined;
68
+ };
69
+
70
+ const isRetryableNetworkError = (error: unknown) => {
71
+ if (!error || typeof error !== 'object') {
72
+ return false;
73
+ }
74
+
75
+ const name = (error as ErrorLike).name;
76
+ if (
77
+ name === 'FetchError' ||
78
+ name === 'TimeoutError' ||
79
+ name === 'TypeError'
80
+ ) {
81
+ return true;
82
+ }
83
+
84
+ const code = (error as ErrorLike).code;
85
+ return (
86
+ code === 'ECONNRESET' ||
87
+ code === 'ETIMEDOUT' ||
88
+ code === 'ECONNREFUSED' ||
89
+ code === 'EPIPE'
90
+ );
91
+ };
92
+
93
+ const normalizePositiveNumber = (value: unknown, fallback: number) =>
94
+ typeof value === 'number' && Number.isFinite(value) && value > 0
95
+ ? value
96
+ : fallback;
97
+
98
+ const normalizeNonNegativeInt = (value: unknown, fallback: number) =>
99
+ typeof value === 'number' && Number.isFinite(value) && value >= 0
100
+ ? Math.floor(value)
101
+ : fallback;
102
+
103
+ const normalizeJitterRatio = (value: unknown) => {
104
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
105
+ return DEFAULT_JITTER_RATIO;
106
+ }
107
+ return Math.max(0, Math.min(1, value));
108
+ };
109
+
110
+ const shouldRetryWithDefaults = (
111
+ method: string,
112
+ statusCode: number | undefined,
113
+ error: unknown,
114
+ retryableStatusCodes: number[],
115
+ ) => {
116
+ if (!DEFAULT_RETRYABLE_METHODS.has(method.toUpperCase())) {
117
+ return false;
118
+ }
119
+
120
+ if (typeof statusCode === 'number') {
121
+ return retryableStatusCodes.includes(statusCode);
122
+ }
123
+ return isRetryableNetworkError(error);
124
+ };
125
+
126
+ const shouldRetry = (
127
+ retryOptions: RetryBackoffOptions | undefined,
128
+ context: RetryDecisionContext,
129
+ retryableStatusCodes: number[],
130
+ ) => {
131
+ if (typeof retryOptions?.shouldRetry === 'function') {
132
+ try {
133
+ return retryOptions.shouldRetry(context);
134
+ } catch (error) {
135
+ return false;
136
+ }
137
+ }
138
+
139
+ return shouldRetryWithDefaults(
140
+ context.method,
141
+ context.statusCode,
142
+ context.error,
143
+ retryableStatusCodes,
144
+ );
145
+ };
146
+
147
+ const getBackoffMs = (
148
+ retryOptions: RetryBackoffOptions | undefined,
149
+ attempt: number,
150
+ ) => {
151
+ const baseDelayMs = normalizePositiveNumber(
152
+ retryOptions?.baseDelayMs,
153
+ DEFAULT_BASE_DELAY_MS,
154
+ );
155
+ const maxDelayMs = normalizePositiveNumber(
156
+ retryOptions?.maxDelayMs,
157
+ DEFAULT_MAX_DELAY_MS,
158
+ );
159
+ const jitterRatio = normalizeJitterRatio(retryOptions?.jitterRatio);
160
+ const exponentialDelay = Math.min(
161
+ maxDelayMs,
162
+ baseDelayMs * 2 ** Math.max(0, attempt - 1),
163
+ );
164
+ const jitterFactor =
165
+ jitterRatio === 0 ? 1 : 1 + (Math.random() * 2 - 1) * jitterRatio;
166
+ return Math.max(0, Math.floor(exponentialDelay * jitterFactor));
167
+ };
168
+
169
+ const emitDegradedEvent = (
170
+ transport: TransportResilienceOptions | undefined,
171
+ event: DegradedModeEvent,
172
+ ) => {
173
+ if (typeof transport?.onDegraded !== 'function') {
174
+ return;
175
+ }
176
+
177
+ try {
178
+ transport.onDegraded(event);
179
+ } catch (error) {
180
+ // best-effort telemetry only
181
+ }
182
+ };
183
+
184
+ const withTimeout = async <T>(
185
+ promise: Promise<T>,
186
+ timeoutMs: number | undefined,
187
+ signalBuilder?: () => AbortController | undefined,
188
+ ) => {
189
+ if (!timeoutMs || timeoutMs <= 0) {
190
+ const result = await promise;
191
+ return { result };
192
+ }
193
+
194
+ const controller = signalBuilder?.();
195
+
196
+ let timeoutTriggered = false;
197
+ let rejectTimeout: ((error: Error) => void) | undefined;
198
+ const timeoutPromise = new Promise<never>((_, reject) => {
199
+ rejectTimeout = reject;
200
+ });
201
+
202
+ const timer = setTimeout(() => {
203
+ timeoutTriggered = true;
204
+ if (controller) {
205
+ controller.abort();
206
+ }
207
+ rejectTimeout?.(createTimeoutError(timeoutMs));
208
+ }, timeoutMs);
209
+ unrefTimer(timer);
210
+
211
+ try {
212
+ const result = await Promise.race([promise, timeoutPromise]);
213
+ return { result, timeoutTriggered };
214
+ } catch (error) {
215
+ if (timeoutTriggered && (error as ErrorLike)?.name === 'AbortError') {
216
+ throw createTimeoutError(timeoutMs);
217
+ }
218
+ throw error;
219
+ } finally {
220
+ clearTimeout(timer);
221
+ }
222
+ };
223
+
224
+ type ExecuteWithResilienceOptions = {
225
+ requestId: string;
226
+ target: TransportTarget;
227
+ method: string;
228
+ url: string;
229
+ init: Record<string, any>;
230
+ fetcher: (...args: any[]) => Promise<any>;
231
+ transport?: TransportResilienceOptions;
232
+ };
233
+
234
+ export const executeWithResilience = async ({
235
+ requestId,
236
+ target,
237
+ method,
238
+ url,
239
+ init,
240
+ fetcher,
241
+ transport,
242
+ }: ExecuteWithResilienceOptions) => {
243
+ const retries = normalizeNonNegativeInt(transport?.retry?.retries, 0);
244
+ const timeoutMs =
245
+ typeof transport?.timeoutMs === 'number' && transport.timeoutMs > 0
246
+ ? transport.timeoutMs
247
+ : undefined;
248
+ const maxAttempts = retries + 1;
249
+ const retryableStatusCodes =
250
+ transport?.retry?.retryableStatusCodes &&
251
+ transport.retry.retryableStatusCodes.length > 0
252
+ ? transport.retry.retryableStatusCodes
253
+ : DEFAULT_RETRYABLE_STATUS_CODES;
254
+
255
+ let attempt = 0;
256
+
257
+ while (attempt < maxAttempts) {
258
+ attempt += 1;
259
+ const canUseAbortController = typeof AbortController !== 'undefined';
260
+ let controller: AbortController | undefined;
261
+ if (timeoutMs && canUseAbortController) {
262
+ controller = new AbortController();
263
+ }
264
+
265
+ const callerSignal = init.signal as AbortSignal | undefined;
266
+ const requestSignal = controller
267
+ ? callerSignal && !callerSignal.aborted
268
+ ? AbortSignal.any([callerSignal, controller.signal])
269
+ : callerSignal || controller.signal
270
+ : callerSignal;
271
+ const nextInit = requestSignal ? { ...init, signal: requestSignal } : init;
272
+
273
+ try {
274
+ const { result } = await withTimeout(
275
+ fetcher(url, nextInit),
276
+ timeoutMs,
277
+ () => controller,
278
+ );
279
+ return result;
280
+ } catch (error) {
281
+ const statusCode = toStatusCode(error);
282
+ const decisionContext: RetryDecisionContext = {
283
+ requestId,
284
+ target,
285
+ method,
286
+ url,
287
+ attempt,
288
+ maxAttempts,
289
+ error,
290
+ statusCode,
291
+ };
292
+
293
+ if ((error as ErrorLike)?.name === 'TimeoutError') {
294
+ emitDegradedEvent(transport, {
295
+ requestId,
296
+ target,
297
+ method,
298
+ url,
299
+ reason: 'timeout',
300
+ attempt,
301
+ maxAttempts,
302
+ timeoutMs,
303
+ statusCode,
304
+ error,
305
+ });
306
+ }
307
+
308
+ const canRetry =
309
+ attempt < maxAttempts &&
310
+ shouldRetry(transport?.retry, decisionContext, retryableStatusCodes);
311
+ if (!canRetry) {
312
+ if (retries > 0 || (error as ErrorLike)?.name === 'TimeoutError') {
313
+ emitDegradedEvent(transport, {
314
+ requestId,
315
+ target,
316
+ method,
317
+ url,
318
+ reason: 'retry_exhausted',
319
+ attempt,
320
+ maxAttempts,
321
+ timeoutMs,
322
+ statusCode,
323
+ error,
324
+ });
325
+ }
326
+ throw error;
327
+ }
328
+
329
+ const backoffMs = getBackoffMs(transport?.retry, attempt);
330
+ emitDegradedEvent(transport, {
331
+ requestId,
332
+ target,
333
+ method,
334
+ url,
335
+ reason: 'retry',
336
+ attempt,
337
+ maxAttempts,
338
+ timeoutMs,
339
+ backoffMs,
340
+ statusCode,
341
+ error,
342
+ });
343
+
344
+ if (backoffMs > 0) {
345
+ await wait(backoffMs);
346
+ }
347
+ }
348
+ }
349
+ };
@@ -0,0 +1,229 @@
1
+ import type {
2
+ HttpMethodDecider,
3
+ IOptions as NativeOptions,
4
+ RequestCreatorOptions as NativeRequestCreatorOptions,
5
+ UploadCreatorOptions as NativeUploadCreatorOptions,
6
+ Sender,
7
+ } from '@modern-js/create-request/client';
8
+
9
+ export type {
10
+ BFFRequestPayload,
11
+ HttpMethodDecider,
12
+ Sender,
13
+ } from '@modern-js/create-request/client';
14
+
15
+ export const BFF_ENVELOPE_HEADER = 'x-modernjs-bff-envelope';
16
+ export const BFF_OPERATION_CONTEXT_HEADER = 'x-operation-id';
17
+ export const BFF_OPERATION_CONTEXT_DETAIL_HEADER =
18
+ 'x-modernjs-bff-operation-context';
19
+ export const BFF_DEFAULT_PROTECTED_IDENTITY_HEADERS = [
20
+ 'x-tenant-id',
21
+ 'x-subject-id',
22
+ 'x-user-id',
23
+ BFF_OPERATION_CONTEXT_HEADER,
24
+ ] as const;
25
+
26
+ export type ResolveHeadersOptions = {
27
+ requestId: string;
28
+ allowedHeaders: string[];
29
+ incomingHeaders: Record<string, any>;
30
+ };
31
+
32
+ export type ResolveHeaders = (
33
+ options: ResolveHeadersOptions,
34
+ ) => Record<string, any> | void;
35
+
36
+ export type AllowCrossOriginEnvelopeOptions = {
37
+ requestId: string;
38
+ sourceOrigin?: string;
39
+ targetOrigin?: string;
40
+ target: 'server' | 'browser';
41
+ };
42
+
43
+ export type AllowCrossOriginEnvelope =
44
+ | boolean
45
+ | ((options: AllowCrossOriginEnvelopeOptions) => boolean);
46
+
47
+ export type TransportTarget = 'server' | 'browser';
48
+
49
+ export type RetryDecisionContext = {
50
+ requestId: string;
51
+ target: TransportTarget;
52
+ method: string;
53
+ url: string;
54
+ attempt: number;
55
+ maxAttempts: number;
56
+ error: unknown;
57
+ statusCode?: number;
58
+ };
59
+
60
+ export type RetryBackoffOptions = {
61
+ retries?: number;
62
+ baseDelayMs?: number;
63
+ maxDelayMs?: number;
64
+ jitterRatio?: number;
65
+ retryableStatusCodes?: number[];
66
+ shouldRetry?: (context: RetryDecisionContext) => boolean;
67
+ };
68
+
69
+ export type DegradedModeReason = 'timeout' | 'retry' | 'retry_exhausted';
70
+
71
+ export type DegradedModeEvent = {
72
+ requestId: string;
73
+ target: TransportTarget;
74
+ method: string;
75
+ url: string;
76
+ reason: DegradedModeReason;
77
+ attempt: number;
78
+ maxAttempts: number;
79
+ timeoutMs?: number;
80
+ backoffMs?: number;
81
+ statusCode?: number;
82
+ error?: unknown;
83
+ };
84
+
85
+ export type TransportResilienceOptions = {
86
+ timeoutMs?: number;
87
+ retry?: RetryBackoffOptions;
88
+ onDegraded?: (event: DegradedModeEvent) => void;
89
+ };
90
+
91
+ export type IdentityBindingViolationReason =
92
+ | 'client_override_blocked'
93
+ | 'client_override_rejected';
94
+
95
+ export type IdentityBindingViolation = {
96
+ requestId: string;
97
+ target: TransportTarget;
98
+ header: string;
99
+ reason: IdentityBindingViolationReason;
100
+ attemptedValue?: unknown;
101
+ derivedValue?: unknown;
102
+ };
103
+
104
+ export type DeriveIdentityHeadersOptions = {
105
+ requestId: string;
106
+ target: TransportTarget;
107
+ incomingHeaders: Record<string, any>;
108
+ protectedHeaders: string[];
109
+ };
110
+
111
+ export type IdentityBindingOptions = {
112
+ enabled?: boolean;
113
+ strict?: boolean;
114
+ protectedHeaders?: string[];
115
+ deriveHeaders?: (
116
+ options: DeriveIdentityHeadersOptions,
117
+ ) => Record<string, any> | void;
118
+ onViolation?: (violation: IdentityBindingViolation) => void;
119
+ };
120
+
121
+ export type OperationContractViolationReason =
122
+ | 'missing_schema_hash'
123
+ | 'missing_operation_version';
124
+
125
+ export type OperationContractViolation = {
126
+ requestId: string;
127
+ target: TransportTarget;
128
+ operationId: string;
129
+ reason: OperationContractViolationReason;
130
+ routePath?: string;
131
+ method?: string;
132
+ schemaHash?: string;
133
+ operationVersion?: number;
134
+ };
135
+
136
+ export type CrossProjectOperationContract = {
137
+ schemaHash?: string;
138
+ operationVersion?: number;
139
+ };
140
+
141
+ export type CrossProjectPolicyViolationReason =
142
+ | 'missing_envelope'
143
+ | 'invalid_envelope'
144
+ | 'missing_request_id'
145
+ | 'namespace_not_allowed'
146
+ | 'missing_operation_context'
147
+ | 'operation_context_mismatch'
148
+ | 'missing_operation_context_details'
149
+ | 'invalid_operation_context_details'
150
+ | 'operation_context_details_request_id_mismatch'
151
+ | 'missing_operation_schema_hash'
152
+ | 'missing_operation_version'
153
+ | 'unknown_operation_contract'
154
+ | 'operation_schema_hash_mismatch'
155
+ | 'operation_version_mismatch'
156
+ | 'producer_identity_mismatch';
157
+
158
+ export type CrossProjectPolicyViolation = {
159
+ code: 'BFF_CROSS_PROJECT_POLICY_DENIED';
160
+ reason: CrossProjectPolicyViolationReason;
161
+ message: string;
162
+ status: number;
163
+ };
164
+
165
+ export type OperationContractOptions = {
166
+ enabled?: boolean;
167
+ strict?: boolean;
168
+ requireSchemaHash?: boolean;
169
+ requireOperationVersion?: boolean;
170
+ onViolation?: (violation: OperationContractViolation) => void;
171
+ };
172
+
173
+ export type OperationContextSource =
174
+ | 'client'
175
+ | 'server'
176
+ | 'generated-client'
177
+ | 'effect-adapter'
178
+ | 'data-platform'
179
+ | 'unknown';
180
+
181
+ export type OperationContext = {
182
+ requestId?: string;
183
+ operationId?: string;
184
+ routePath?: string;
185
+ method?: string;
186
+ schemaHash?: string;
187
+ operationVersion?: number;
188
+ locale?: string;
189
+ traceparent?: string;
190
+ traceId?: string;
191
+ spanId?: string;
192
+ source?: OperationContextSource;
193
+ scope?: Record<string, unknown>;
194
+ sessionClaims?: Record<string, unknown>;
195
+ attributes?: Record<string, unknown>;
196
+ };
197
+
198
+ export type RequestCreatorOptions<F = typeof fetch> = Omit<
199
+ NativeRequestCreatorOptions<F>,
200
+ 'hooks'
201
+ > & { operationContext?: OperationContext };
202
+
203
+ export type RequestCreator<F = typeof fetch> = {
204
+ (options: RequestCreatorOptions<F>): Sender;
205
+ (
206
+ path: string,
207
+ method: string,
208
+ port: number,
209
+ httpMethodDecider?: HttpMethodDecider,
210
+ fetch?: F,
211
+ requestId?: string,
212
+ operationContext?: OperationContext,
213
+ ): Sender;
214
+ };
215
+
216
+ export type UploadCreatorOptions = Omit<NativeUploadCreatorOptions, 'hooks'> & {
217
+ operationContext?: OperationContext;
218
+ };
219
+
220
+ export type UploadCreator = (options: UploadCreatorOptions) => Sender;
221
+
222
+ export type IOptions<F = typeof fetch> = NativeOptions<F> & {
223
+ resolveHeaders?: ResolveHeaders;
224
+ transport?: TransportResilienceOptions;
225
+ requireEnvelope?: boolean;
226
+ allowCrossOriginEnvelope?: AllowCrossOriginEnvelope;
227
+ identityBinding?: IdentityBindingOptions;
228
+ operationContract?: OperationContractOptions;
229
+ };