@bleedingdev/modern-js-create-request 3.8.3-ultramodern.6 → 3.9.0-ultramodern.10

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,75 +0,0 @@
1
- import "node:module";
2
- import { parseTraceparent } from "./traceparent.mjs";
3
- const BFF_LOCALE_HEADER = 'accept-language';
4
- const BFF_TRACEPARENT_HEADER = 'traceparent';
5
- const readHeader = (headers, header)=>{
6
- if (!headers) return;
7
- const normalized = header.toLowerCase();
8
- const key = Object.keys(headers).find((current)=>current.toLowerCase() === normalized);
9
- if (!key) return;
10
- const value = headers[key];
11
- return Array.isArray(value) ? value[0] : value;
12
- };
13
- const readString = (value)=>'string' == typeof value && value.length > 0 ? value : void 0;
14
- function createOperationContextSnapshot(operationContext, safeContext) {
15
- if (!operationContext) return;
16
- const snapshot = {
17
- ...operationContext,
18
- ...safeContext.locale || operationContext?.locale ? {
19
- locale: safeContext.locale || operationContext?.locale
20
- } : {},
21
- ...safeContext.traceparent || operationContext?.traceparent ? {
22
- traceparent: safeContext.traceparent || operationContext?.traceparent
23
- } : {},
24
- ...safeContext.traceId || operationContext?.traceId ? {
25
- traceId: safeContext.traceId || operationContext?.traceId
26
- } : {},
27
- ...safeContext.spanId || operationContext?.spanId ? {
28
- spanId: safeContext.spanId || operationContext?.spanId
29
- } : {}
30
- };
31
- return Object.keys(snapshot).some((key)=>void 0 !== snapshot[key]) ? snapshot : void 0;
32
- }
33
- function createRequestContextSnapshot(input = {}) {
34
- const locale = readString(input.locale) || readString(readHeader(input.headers, BFF_LOCALE_HEADER));
35
- const traceparent = readString(input.traceparent) || readString(input.operationContext?.traceparent) || readString(readHeader(input.headers, BFF_TRACEPARENT_HEADER));
36
- const parsedTraceparent = input.operationContext?.traceId && input.operationContext?.spanId ? {
37
- traceId: input.operationContext.traceId,
38
- spanId: input.operationContext.spanId
39
- } : parseTraceparent(traceparent);
40
- const headers = {};
41
- if (locale) headers[BFF_LOCALE_HEADER] = locale;
42
- if (traceparent) headers[BFF_TRACEPARENT_HEADER] = traceparent;
43
- const operationContext = createOperationContextSnapshot(input.operationContext, {
44
- ...locale ? {
45
- locale
46
- } : {},
47
- ...traceparent ? {
48
- traceparent
49
- } : {},
50
- ...parsedTraceparent ? {
51
- traceId: parsedTraceparent.traceId,
52
- spanId: parsedTraceparent.spanId
53
- } : {}
54
- });
55
- return {
56
- headers,
57
- ...locale ? {
58
- locale
59
- } : {},
60
- ...traceparent ? {
61
- traceparent
62
- } : {},
63
- ...parsedTraceparent ? {
64
- traceId: parsedTraceparent.traceId,
65
- spanId: parsedTraceparent.spanId
66
- } : {},
67
- ...operationContext ? {
68
- operationContext
69
- } : {}
70
- };
71
- }
72
- function createRequestContextHeaders(input = {}) {
73
- return createRequestContextSnapshot(input).headers;
74
- }
75
- export { BFF_LOCALE_HEADER, BFF_TRACEPARENT_HEADER, createRequestContextHeaders, createRequestContextSnapshot };
@@ -1,19 +0,0 @@
1
- import "node:module";
2
- const TRACEPARENT_REGEX = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/i;
3
- const isAllZeroHex = (value)=>/^0+$/.test(value);
4
- function parseTraceparent(traceparent) {
5
- if (!traceparent) return;
6
- const match = traceparent.trim().match(TRACEPARENT_REGEX);
7
- if (!match) return;
8
- const [, rawTraceId, rawSpanId, rawFlags] = match;
9
- if (!rawTraceId || !rawSpanId || !rawFlags) return;
10
- const traceId = rawTraceId.toLowerCase();
11
- const spanId = rawSpanId.toLowerCase();
12
- if (isAllZeroHex(traceId) || isAllZeroHex(spanId)) return;
13
- return {
14
- traceId,
15
- spanId,
16
- sampled: (0x1 & Number.parseInt(rawFlags, 16)) === 1
17
- };
18
- }
19
- export { parseTraceparent };
@@ -1,197 +0,0 @@
1
- import "node:module";
2
- const DEFAULT_RETRYABLE_STATUS_CODES = [
3
- 408,
4
- 425,
5
- 429,
6
- 500,
7
- 502,
8
- 503,
9
- 504
10
- ];
11
- const DEFAULT_BASE_DELAY_MS = 100;
12
- const DEFAULT_MAX_DELAY_MS = 1000;
13
- const DEFAULT_JITTER_RATIO = 0.1;
14
- const DEFAULT_RETRYABLE_METHODS = new Set([
15
- 'DELETE',
16
- 'GET',
17
- 'HEAD',
18
- 'OPTIONS',
19
- 'PUT',
20
- 'TRACE'
21
- ]);
22
- const createTimeoutError = (timeoutMs)=>{
23
- const error = new Error(`Request timed out after ${timeoutMs}ms`);
24
- error.name = 'TimeoutError';
25
- return error;
26
- };
27
- const unrefTimer = (timer)=>{
28
- if ('object' == typeof timer && null !== timer && 'unref' in timer && 'function' == typeof timer.unref) timer.unref();
29
- };
30
- const wait = (ms)=>new Promise((resolve)=>{
31
- const timer = setTimeout(()=>resolve(), ms);
32
- unrefTimer(timer);
33
- });
34
- const toStatusCode = (error)=>{
35
- if (!error || 'object' != typeof error) return;
36
- const status = error.status;
37
- if ('number' == typeof status) return status;
38
- const responseStatus = error.response?.status;
39
- if ('number' == typeof responseStatus) return responseStatus;
40
- };
41
- const isRetryableNetworkError = (error)=>{
42
- if (!error || 'object' != typeof error) return false;
43
- const name = error.name;
44
- if ('FetchError' === name || 'TimeoutError' === name || 'TypeError' === name) return true;
45
- const code = error.code;
46
- return 'ECONNRESET' === code || 'ETIMEDOUT' === code || 'ECONNREFUSED' === code || 'EPIPE' === code;
47
- };
48
- const normalizePositiveNumber = (value, fallback)=>'number' == typeof value && Number.isFinite(value) && value > 0 ? value : fallback;
49
- const normalizeNonNegativeInt = (value, fallback)=>'number' == typeof value && Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;
50
- const normalizeJitterRatio = (value)=>{
51
- if ('number' != typeof value || !Number.isFinite(value)) return DEFAULT_JITTER_RATIO;
52
- return Math.max(0, Math.min(1, value));
53
- };
54
- const shouldRetryWithDefaults = (method, statusCode, error, retryableStatusCodes)=>{
55
- if (!DEFAULT_RETRYABLE_METHODS.has(method.toUpperCase())) return false;
56
- if ('number' == typeof statusCode) return retryableStatusCodes.includes(statusCode);
57
- return isRetryableNetworkError(error);
58
- };
59
- const shouldRetry = (retryOptions, context, retryableStatusCodes)=>{
60
- if ('function' == typeof retryOptions?.shouldRetry) try {
61
- return retryOptions.shouldRetry(context);
62
- } catch (error) {
63
- return false;
64
- }
65
- return shouldRetryWithDefaults(context.method, context.statusCode, context.error, retryableStatusCodes);
66
- };
67
- const getBackoffMs = (retryOptions, attempt)=>{
68
- const baseDelayMs = normalizePositiveNumber(retryOptions?.baseDelayMs, DEFAULT_BASE_DELAY_MS);
69
- const maxDelayMs = normalizePositiveNumber(retryOptions?.maxDelayMs, DEFAULT_MAX_DELAY_MS);
70
- const jitterRatio = normalizeJitterRatio(retryOptions?.jitterRatio);
71
- const exponentialDelay = Math.min(maxDelayMs, baseDelayMs * 2 ** Math.max(0, attempt - 1));
72
- const jitterFactor = 0 === jitterRatio ? 1 : 1 + (2 * Math.random() - 1) * jitterRatio;
73
- return Math.max(0, Math.floor(exponentialDelay * jitterFactor));
74
- };
75
- const emitDegradedEvent = (transport, event)=>{
76
- if ('function' != typeof transport?.onDegraded) return;
77
- try {
78
- transport.onDegraded(event);
79
- } catch (error) {}
80
- };
81
- const withTimeout = async (promise, timeoutMs, signalBuilder)=>{
82
- if (!timeoutMs || timeoutMs <= 0) {
83
- const result = await promise;
84
- return {
85
- result
86
- };
87
- }
88
- const controller = signalBuilder?.();
89
- let timeoutTriggered = false;
90
- let rejectTimeout;
91
- const timeoutPromise = new Promise((_, reject)=>{
92
- rejectTimeout = reject;
93
- });
94
- const timer = setTimeout(()=>{
95
- timeoutTriggered = true;
96
- if (controller) controller.abort();
97
- rejectTimeout?.(createTimeoutError(timeoutMs));
98
- }, timeoutMs);
99
- unrefTimer(timer);
100
- try {
101
- const result = await Promise.race([
102
- promise,
103
- timeoutPromise
104
- ]);
105
- return {
106
- result,
107
- timeoutTriggered
108
- };
109
- } catch (error) {
110
- if (timeoutTriggered && error?.name === 'AbortError') throw createTimeoutError(timeoutMs);
111
- throw error;
112
- } finally{
113
- clearTimeout(timer);
114
- }
115
- };
116
- const executeWithResilience = async ({ requestId, target, method, url, init, fetcher, transport })=>{
117
- const retries = normalizeNonNegativeInt(transport?.retry?.retries, 0);
118
- const timeoutMs = 'number' == typeof transport?.timeoutMs && transport.timeoutMs > 0 ? transport.timeoutMs : void 0;
119
- const maxAttempts = retries + 1;
120
- const retryableStatusCodes = transport?.retry?.retryableStatusCodes && transport.retry.retryableStatusCodes.length > 0 ? transport.retry.retryableStatusCodes : DEFAULT_RETRYABLE_STATUS_CODES;
121
- let attempt = 0;
122
- while(attempt < maxAttempts){
123
- attempt += 1;
124
- const canUseAbortController = "u" > typeof AbortController;
125
- let controller;
126
- if (timeoutMs && canUseAbortController) controller = new AbortController();
127
- const callerSignal = init.signal;
128
- const requestSignal = controller ? callerSignal && !callerSignal.aborted ? AbortSignal.any([
129
- callerSignal,
130
- controller.signal
131
- ]) : callerSignal || controller.signal : callerSignal;
132
- const nextInit = requestSignal ? {
133
- ...init,
134
- signal: requestSignal
135
- } : init;
136
- try {
137
- const { result } = await withTimeout(fetcher(url, nextInit), timeoutMs, ()=>controller);
138
- return result;
139
- } catch (error) {
140
- const statusCode = toStatusCode(error);
141
- const decisionContext = {
142
- requestId,
143
- target,
144
- method,
145
- url,
146
- attempt,
147
- maxAttempts,
148
- error,
149
- statusCode
150
- };
151
- if (error?.name === 'TimeoutError') emitDegradedEvent(transport, {
152
- requestId,
153
- target,
154
- method,
155
- url,
156
- reason: 'timeout',
157
- attempt,
158
- maxAttempts,
159
- timeoutMs,
160
- statusCode,
161
- error
162
- });
163
- const canRetry = attempt < maxAttempts && shouldRetry(transport?.retry, decisionContext, retryableStatusCodes);
164
- if (!canRetry) {
165
- if (retries > 0 || error?.name === 'TimeoutError') emitDegradedEvent(transport, {
166
- requestId,
167
- target,
168
- method,
169
- url,
170
- reason: 'retry_exhausted',
171
- attempt,
172
- maxAttempts,
173
- timeoutMs,
174
- statusCode,
175
- error
176
- });
177
- throw error;
178
- }
179
- const backoffMs = getBackoffMs(transport?.retry, attempt);
180
- emitDegradedEvent(transport, {
181
- requestId,
182
- target,
183
- method,
184
- url,
185
- reason: 'retry',
186
- attempt,
187
- maxAttempts,
188
- timeoutMs,
189
- backoffMs,
190
- statusCode,
191
- error
192
- });
193
- if (backoffMs > 0) await wait(backoffMs);
194
- }
195
- }
196
- };
197
- export { executeWithResilience };
@@ -1,108 +0,0 @@
1
- import type { IdentityBindingViolation, OperationContext, OperationContractOptions, OperationContractViolation, TransportTarget } from './types';
2
- export declare const TRACEPARENT_HEADER = "traceparent";
3
- export declare const isSecuredRequestId: (requestId: string) => boolean;
4
- export declare const isEmptyDomain: (domain?: string) => boolean;
5
- export declare const firstHeaderValue: (value: unknown) => any;
6
- export declare const findHeaderKey: (headers: Record<string, any>, header: string) => string | undefined;
7
- export declare const readHeader: (headers: Record<string, any>, header: string) => any;
8
- export declare const writeHeader: (headers: Record<string, any>, header: string, value: unknown) => void;
9
- export declare const deleteHeader: (headers: Record<string, any>, header: string) => void;
10
- export declare const toOrigin: (value?: string) => string | undefined;
11
- export declare const parseTraceparentValue: (value: unknown) => import("./traceparent").TraceparentContext | undefined;
12
- export declare const extractPathParamNames: (path: string) => string[];
13
- export declare const buildOperationContext: ({ requestId, method, path, operationContext, traceparent, }: {
14
- requestId: string;
15
- method: string;
16
- path: string;
17
- operationContext?: OperationContext | undefined;
18
- traceparent?: unknown;
19
- }) => {
20
- requestId: string;
21
- operationId: string;
22
- routePath: string;
23
- method: string;
24
- schemaHash?: string | undefined;
25
- operationVersion?: number | undefined;
26
- traceparent?: string | undefined;
27
- traceId?: string | undefined;
28
- spanId?: string | undefined;
29
- };
30
- type OperationContextPayload = ReturnType<typeof buildOperationContext>;
31
- export declare class ProducerClientNotInitializedError extends Error {
32
- readonly code = "BFF_PRODUCER_CLIENT_NOT_INITIALIZED";
33
- constructor(requestId: string);
34
- }
35
- export declare class ProducerDomainNotConfiguredError extends Error {
36
- readonly code = "BFF_PRODUCER_DOMAIN_NOT_CONFIGURED";
37
- constructor(requestId: string);
38
- }
39
- export declare class CrossOriginEnvelopePolicyError extends Error {
40
- readonly code = "BFF_CROSS_ORIGIN_ENVELOPE_NOT_ALLOWED";
41
- constructor(requestId: string, sourceOrigin?: string, targetOrigin?: string);
42
- }
43
- export declare class IdentityBindingViolationError extends Error {
44
- readonly code = "BFF_IDENTITY_BINDING_VIOLATION";
45
- readonly violation: IdentityBindingViolation;
46
- constructor(violation: IdentityBindingViolation);
47
- }
48
- export declare class OperationContractViolationError extends Error {
49
- readonly code = "BFF_OPERATION_CONTRACT_VIOLATION";
50
- readonly violation: OperationContractViolation;
51
- constructor(violation: OperationContractViolation);
52
- }
53
- export declare const validateOperationContract: ({ requestId, target, contextPayload, operationContract, }: {
54
- requestId: string;
55
- target: TransportTarget;
56
- contextPayload: OperationContextPayload;
57
- operationContract: OperationContractOptions | undefined;
58
- }) => void;
59
- export declare const resolveConfiguredRequest: <F>(configuredRequests: Map<string, F>, requestId: string, fallback: F) => F;
60
- /**
61
- * Builds the JSON value for the `x-modernjs-bff-envelope` header and throws
62
- * when the cross-origin envelope policy denies the flow. Returns the
63
- * serialized envelope.
64
- */
65
- export declare const buildEnvelopeHeaderValue: ({ requestId, target, sourceOrigin, targetOrigin, traceContext, allowCrossOriginEnvelope, }: {
66
- requestId: string;
67
- target: TransportTarget;
68
- sourceOrigin: string | undefined;
69
- targetOrigin: string | undefined;
70
- traceContext: {
71
- traceId: string;
72
- spanId: string;
73
- } | null | undefined;
74
- allowCrossOriginEnvelope: boolean | ((options: {
75
- requestId: string;
76
- sourceOrigin?: string;
77
- targetOrigin?: string;
78
- target: TransportTarget;
79
- }) => boolean) | undefined;
80
- }) => string;
81
- /**
82
- * Derives the operation context for a secured requestId, validates it against
83
- * the configured operation contract and writes the `x-operation-id` and
84
- * `x-modernjs-bff-operation-context` headers (without clobbering caller
85
- * provided values for the id header).
86
- */
87
- export declare const attachOperationContextHeaders: ({ headers, requestId, target, method, path, operationContext, operationContract, operationContextHeader, operationContextDetailHeader, }: {
88
- headers: Record<string, any>;
89
- requestId: string;
90
- target: TransportTarget;
91
- method: string;
92
- path: string;
93
- operationContext: OperationContext | undefined;
94
- operationContract: OperationContractOptions | undefined;
95
- operationContextHeader: string;
96
- operationContextDetailHeader: string;
97
- }) => {
98
- requestId: string;
99
- operationId: string;
100
- routePath: string;
101
- method: string;
102
- schemaHash?: string | undefined;
103
- operationVersion?: number | undefined;
104
- traceparent?: string | undefined;
105
- traceId?: string | undefined;
106
- spanId?: string | undefined;
107
- };
108
- export {};
@@ -1,19 +0,0 @@
1
- import type { OperationContext } from './types';
2
- export declare const BFF_LOCALE_HEADER = "accept-language";
3
- export declare const BFF_TRACEPARENT_HEADER = "traceparent";
4
- export type RequestContextInput = {
5
- headers?: Record<string, unknown>;
6
- locale?: string;
7
- traceparent?: string;
8
- operationContext?: OperationContext;
9
- };
10
- export type RequestContextSnapshot = {
11
- headers: Record<string, string>;
12
- locale?: string;
13
- traceparent?: string;
14
- traceId?: string;
15
- spanId?: string;
16
- operationContext?: OperationContext;
17
- };
18
- export declare function createRequestContextSnapshot(input?: RequestContextInput): RequestContextSnapshot;
19
- export declare function createRequestContextHeaders(input?: RequestContextInput): Record<string, string>;
@@ -1,10 +0,0 @@
1
- export type TraceparentContext = {
2
- traceId: string;
3
- spanId: string;
4
- sampled: boolean;
5
- };
6
- /**
7
- * Parse a W3C trace context `traceparent` header (version 00).
8
- * All-zero trace ids and span ids are rejected per the W3C spec.
9
- */
10
- export declare function parseTraceparent(traceparent: string | undefined | null): TraceparentContext | undefined;
@@ -1,12 +0,0 @@
1
- import type { TransportResilienceOptions, TransportTarget } from './types';
2
- type ExecuteWithResilienceOptions = {
3
- requestId: string;
4
- target: TransportTarget;
5
- method: string;
6
- url: string;
7
- init: Record<string, any>;
8
- fetcher: (...args: any[]) => Promise<any>;
9
- transport?: TransportResilienceOptions;
10
- };
11
- export declare const executeWithResilience: ({ requestId, target, method, url, init, fetcher, transport, }: ExecuteWithResilienceOptions) => Promise<any>;
12
- export {};