@bleedingdev/modern-js-create-request 3.9.0-ultramodern.2 → 3.9.0-ultramodern.20

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,150 +1,53 @@
1
1
  import { compile } from "path-to-regexp";
2
2
  import { stringify } from "qs";
3
- import { IdentityBindingViolationError, ProducerDomainNotConfiguredError, TRACEPARENT_HEADER, attachOperationContextHeaders, buildEnvelopeHeaderValue, deleteHeader, extractPathParamNames, firstHeaderValue, isEmptyDomain, isSecuredRequestId, parseTraceparentValue, readHeader, resolveConfiguredRequest, toOrigin, writeHeader } from "./policyCore.mjs";
4
- import { executeWithResilience } from "./transport.mjs";
5
- import { BFF_DEFAULT_PROTECTED_IDENTITY_HEADERS, BFF_ENVELOPE_HEADER, BFF_OPERATION_CONTEXT_DETAIL_HEADER, BFF_OPERATION_CONTEXT_HEADER } from "./types.mjs";
3
+ import { readHeader, writeHeader } from "./headers.mjs";
6
4
  import { getUploadPayload } from "./utiles.mjs";
7
- const OPERATION_CONTEXT_DETAIL_HEADER = BFF_OPERATION_CONTEXT_DETAIL_HEADER;
5
+ const extractPathParamNames = (path)=>Array.from(path.matchAll(/:([A-Za-z0-9_]+)/g)).flatMap(([, key])=>key ? [
6
+ key
7
+ ] : []);
8
8
  const createRequestFactory = (environment)=>{
9
9
  const isServerTarget = 'server' === environment.target;
10
10
  const realRequest = new Map();
11
11
  const realAllowedHeaders = new Map();
12
- const realResolveHeaders = new Map();
13
- const realRequireEnvelope = new Map();
14
- const realAllowCrossOriginEnvelope = new Map();
15
- const realTransportResilience = new Map();
16
- const realIdentityBinding = new Map();
17
- const realOperationContract = new Map();
18
12
  const domainMap = new Map();
19
- const attachEnvelopeHeaderIfRequired = (headers, requestId, url, incomingHeaders)=>{
20
- const shouldRequireEnvelope = realRequireEnvelope.get(requestId) ?? isSecuredRequestId(requestId);
21
- if (!shouldRequireEnvelope) return;
22
- headers[BFF_ENVELOPE_HEADER] = buildEnvelopeHeaderValue({
13
+ const startRequest = (requestId, hooks)=>{
14
+ const context = {
23
15
  requestId,
24
16
  target: environment.target,
25
- sourceOrigin: environment.resolveSourceOrigin(incomingHeaders),
26
- targetOrigin: toOrigin(url),
27
- traceContext: parseTraceparentValue(readHeader(headers, TRACEPARENT_HEADER)),
28
- allowCrossOriginEnvelope: realAllowCrossOriginEnvelope.get(requestId)
29
- });
30
- };
31
- const attachSecuredOperationHeaders = (headers, requestId, method, path, operationContext)=>{
32
- if (!isSecuredRequestId(requestId)) return;
33
- attachOperationContextHeaders({
34
- headers,
35
- requestId,
36
- target: environment.target,
37
- method,
38
- path,
39
- operationContext,
40
- operationContract: realOperationContract.get(requestId),
41
- operationContextHeader: BFF_OPERATION_CONTEXT_HEADER,
42
- operationContextDetailHeader: OPERATION_CONTEXT_DETAIL_HEADER
43
- });
17
+ incomingHeaders: environment.readIncomingHeaders(),
18
+ configured: realRequest.has(requestId),
19
+ configuredDomain: domainMap.get(requestId)
20
+ };
21
+ hooks?.onStart?.(context);
22
+ return context;
44
23
  };
45
- const applyIdentityAndForwardedHeaders = (headers, requestId, incomingHeaders)=>{
46
- const identityBinding = realIdentityBinding.get(requestId);
47
- const identityBindingEnabled = identityBinding?.enabled ?? isSecuredRequestId(requestId);
48
- const identityBindingStrict = identityBinding?.strict ?? isSecuredRequestId(requestId);
49
- const protectedIdentityHeaders = (identityBinding?.protectedHeaders || BFF_DEFAULT_PROTECTED_IDENTITY_HEADERS).map((header)=>header.toLowerCase());
50
- const targetAllowedHeaders = realAllowedHeaders.get(requestId) || [];
24
+ const prepareHeaders = (headers, context, hooks)=>{
25
+ const allowedHeaders = realAllowedHeaders.get(context.requestId) || [];
51
26
  const forwardedHeaders = {};
52
- if (isServerTarget) for (const key of targetAllowedHeaders){
53
- const incomingValue = readHeader(incomingHeaders, key);
54
- if (void 0 !== incomingValue) writeHeader(forwardedHeaders, key, incomingValue);
55
- }
56
- if (identityBindingEnabled) {
57
- const derivedIdentityHeaders = {};
58
- if (isServerTarget) for (const header of protectedIdentityHeaders){
59
- const incomingHeaderValue = readHeader(incomingHeaders, header);
60
- if (void 0 !== incomingHeaderValue) writeHeader(derivedIdentityHeaders, header, incomingHeaderValue);
61
- }
62
- const customDerivedHeaders = identityBinding?.deriveHeaders?.({
63
- requestId,
64
- target: environment.target,
65
- incomingHeaders: isServerTarget ? {
66
- ...incomingHeaders
67
- } : {},
68
- protectedHeaders: [
69
- ...protectedIdentityHeaders
70
- ]
71
- });
72
- if (customDerivedHeaders && 'object' == typeof customDerivedHeaders) for (const header of protectedIdentityHeaders){
73
- const customValue = readHeader(customDerivedHeaders, header);
74
- if (void 0 !== customValue) writeHeader(derivedIdentityHeaders, header, customValue);
75
- }
76
- for (const header of protectedIdentityHeaders){
77
- const attemptedValue = readHeader(headers, header);
78
- if (void 0 === attemptedValue) continue;
79
- const violation = {
80
- requestId,
81
- target: environment.target,
82
- header,
83
- attemptedValue,
84
- derivedValue: readHeader(derivedIdentityHeaders, header),
85
- reason: 'client_override_blocked'
86
- };
87
- identityBinding?.onViolation?.(violation);
88
- if (identityBindingStrict) throw new IdentityBindingViolationError(violation);
89
- deleteHeader(headers, header);
90
- }
91
- Object.keys(derivedIdentityHeaders).forEach((header)=>{
92
- isServerTarget ? writeHeader(forwardedHeaders, header, derivedIdentityHeaders[header]) : writeHeader(headers, header, derivedIdentityHeaders[header]);
93
- });
94
- }
95
- if (isServerTarget) {
96
- const resolveHeaders = realResolveHeaders.get(requestId);
97
- if (resolveHeaders) {
98
- const resolvedHeaders = resolveHeaders({
99
- requestId,
100
- allowedHeaders: targetAllowedHeaders,
101
- incomingHeaders: {
102
- ...forwardedHeaders
103
- }
104
- });
105
- if (resolvedHeaders && 'object' == typeof resolvedHeaders) for (const key of targetAllowedHeaders){
106
- const resolvedValue = readHeader(resolvedHeaders, key);
107
- if (void 0 !== resolvedValue) {
108
- if (identityBindingEnabled && protectedIdentityHeaders.includes(key.toLowerCase())) {
109
- writeHeader(forwardedHeaders, key.toLowerCase(), resolvedValue);
110
- continue;
111
- }
112
- writeHeader(forwardedHeaders, key, resolvedValue);
113
- }
114
- }
115
- }
27
+ if (isServerTarget) for (const key of allowedHeaders){
28
+ const value = readHeader(context.incomingHeaders, key);
29
+ if (void 0 !== value) writeHeader(forwardedHeaders, key, value);
116
30
  }
31
+ hooks?.prepareHeaders?.({
32
+ ...context,
33
+ headers,
34
+ forwardedHeaders,
35
+ allowedHeaders
36
+ });
117
37
  if (isServerTarget) for (const [header, value] of Object.entries(forwardedHeaders))writeHeader(headers, header, value);
118
38
  return headers;
119
39
  };
40
+ const dispatch = (context, hooks)=>hooks?.dispatch ? hooks.dispatch(context) : context.fetcher(context.url, context.init);
120
41
  const configure = (options)=>{
121
- const { request, interceptor, allowedHeaders, resolveHeaders, transport, requireEnvelope, allowCrossOriginEnvelope, identityBinding, operationContract, setDomain, requestId = 'default' } = options;
122
- const hasExistingDomain = domainMap.has(requestId);
123
- if ('default' !== requestId && !setDomain && !hasExistingDomain) throw new ProducerDomainNotConfiguredError(requestId);
42
+ const { request, interceptor, allowedHeaders, setDomain, requestId = 'default' } = options;
124
43
  let configuredRequest = request || environment.originFetch;
125
44
  if (interceptor && !request) configuredRequest = interceptor(environment.getFetch());
126
- let resolvedDomain;
127
- if (setDomain) {
128
- resolvedDomain = setDomain({
129
- target: environment.target,
130
- requestId
131
- });
132
- if ('default' !== requestId && isEmptyDomain(resolvedDomain)) throw new ProducerDomainNotConfiguredError(requestId);
133
- }
45
+ const resolvedDomain = setDomain?.({
46
+ target: environment.target,
47
+ requestId
48
+ });
134
49
  realAllowedHeaders.delete(requestId);
135
- realResolveHeaders.delete(requestId);
136
- realTransportResilience.delete(requestId);
137
- realIdentityBinding.delete(requestId);
138
- realOperationContract.delete(requestId);
139
- realRequireEnvelope.delete(requestId);
140
- realAllowCrossOriginEnvelope.delete(requestId);
141
50
  if (Array.isArray(allowedHeaders)) realAllowedHeaders.set(requestId, allowedHeaders);
142
- if (isServerTarget && 'function' == typeof resolveHeaders) realResolveHeaders.set(requestId, resolveHeaders);
143
- if (transport && 'object' == typeof transport) realTransportResilience.set(requestId, transport);
144
- if (identityBinding && 'object' == typeof identityBinding) realIdentityBinding.set(requestId, identityBinding);
145
- if (operationContract && 'object' == typeof operationContract) realOperationContract.set(requestId, operationContract);
146
- if ('boolean' == typeof requireEnvelope) realRequireEnvelope.set(requestId, requireEnvelope);
147
- if ('boolean' == typeof allowCrossOriginEnvelope || 'function' == typeof allowCrossOriginEnvelope) realAllowCrossOriginEnvelope.set(requestId, allowCrossOriginEnvelope);
148
51
  if ('string' == typeof resolvedDomain) domainMap.set(requestId, resolvedDomain);
149
52
  realRequest.set(requestId, configuredRequest);
150
53
  };
@@ -156,22 +59,21 @@ const createRequestFactory = (environment)=>{
156
59
  httpMethodDecider: args[3],
157
60
  fetch: args[4],
158
61
  requestId: args[5],
159
- operationContext: args[6]
62
+ hooks: args[6]
160
63
  };
161
- const { path, method, port, httpMethodDecider = 'functionName', fetch = environment.originFetch, domain, requestId = 'default', operationContext } = options;
64
+ const { path, method, port, httpMethodDecider = 'functionName', fetch = environment.originFetch, domain, requestId = 'default', hooks } = options;
162
65
  const getFinalPath = compile(path, {
163
66
  encode: encodeURIComponent
164
67
  });
165
68
  const keyNames = extractPathParamNames(path);
166
69
  const send = (...senderArgs)=>{
167
- const fetcher = resolveConfiguredRequest(realRequest, requestId, fetch);
168
- const incomingHeaders = environment.readIncomingHeaders();
70
+ const context = startRequest(requestId, hooks);
71
+ const fetcher = realRequest.get(requestId) || fetch;
169
72
  let body;
170
73
  let headers;
171
74
  let url;
172
75
  if ('inputParams' === httpMethodDecider) {
173
76
  const configDomain = domainMap.get(requestId);
174
- if ('default' !== requestId && isEmptyDomain(configDomain)) throw new ProducerDomainNotConfiguredError(requestId);
175
77
  url = environment.resolveRequestUrl({
176
78
  configDomain,
177
79
  domain,
@@ -182,7 +84,7 @@ const createRequestFactory = (environment)=>{
182
84
  headers = {
183
85
  'Content-Type': 'application/json'
184
86
  };
185
- headers = applyIdentityAndForwardedHeaders(headers, requestId, incomingHeaders);
87
+ headers = prepareHeaders(headers, context, hooks);
186
88
  } else {
187
89
  const payload = 'object' == typeof senderArgs[senderArgs.length - 1] ? senderArgs[senderArgs.length - 1] : {};
188
90
  payload.params = payload.params || {};
@@ -200,7 +102,7 @@ const createRequestFactory = (environment)=>{
200
102
  headers = payload.headers ? {
201
103
  ...payload.headers
202
104
  } : {};
203
- headers = applyIdentityAndForwardedHeaders(headers, requestId, incomingHeaders);
105
+ headers = prepareHeaders(headers, context, hooks);
204
106
  if (payload.data) {
205
107
  headers['Content-Type'] = 'application/json';
206
108
  body = 'object' == typeof payload.data ? JSON.stringify(payload.data) : payload.body;
@@ -213,7 +115,6 @@ const createRequestFactory = (environment)=>{
213
115
  body = !isServerTarget && "u" > typeof URLSearchParams && payload.formUrlencoded instanceof URLSearchParams ? payload.formUrlencoded : 'object' == typeof payload.formUrlencoded ? stringify(payload.formUrlencoded) : payload.formUrlencoded;
214
116
  }
215
117
  const configDomain = domainMap.get(requestId);
216
- if ('default' !== requestId && isEmptyDomain(configDomain)) throw new ProducerDomainNotConfiguredError(requestId);
217
118
  url = environment.resolveRequestUrl({
218
119
  configDomain,
219
120
  domain,
@@ -222,20 +123,12 @@ const createRequestFactory = (environment)=>{
222
123
  });
223
124
  }
224
125
  if (!isServerTarget) writeHeader(headers, 'accept', "application/json,*/*;q=0.8");
225
- if (isServerTarget) {
226
- if (void 0 === readHeader(headers, TRACEPARENT_HEADER)) {
227
- const incomingTraceparent = firstHeaderValue(readHeader(incomingHeaders, TRACEPARENT_HEADER));
228
- if ('string' == typeof incomingTraceparent) writeHeader(headers, TRACEPARENT_HEADER, incomingTraceparent);
229
- }
230
- if (void 0 === readHeader(headers, TRACEPARENT_HEADER) && operationContext?.traceparent) writeHeader(headers, TRACEPARENT_HEADER, operationContext.traceparent);
231
- }
232
- attachEnvelopeHeaderIfRequired(headers, requestId, url, incomingHeaders);
233
- attachSecuredOperationHeaders(headers, requestId, method, path, operationContext);
234
126
  if ('get' === method.toLowerCase()) body = void 0;
235
127
  if (isServerTarget) writeHeader(headers, 'accept', "application/json,*/*;q=0.8");
236
- return executeWithResilience({
128
+ return dispatch({
129
+ ...context,
130
+ path,
237
131
  requestId,
238
- target: environment.target,
239
132
  method,
240
133
  url,
241
134
  init: {
@@ -243,19 +136,19 @@ const createRequestFactory = (environment)=>{
243
136
  body,
244
137
  headers
245
138
  },
246
- fetcher: fetcher,
247
- transport: realTransportResilience.get(requestId)
248
- });
139
+ fetcher: fetcher
140
+ }, hooks);
249
141
  };
250
142
  const sender = isServerTarget ? send : async (...senderArgs)=>send(...senderArgs);
251
143
  return sender;
252
144
  };
253
- const createUploader = ({ path, domain, requestId = 'default', operationContext })=>{
145
+ const createUploader = ({ path, domain, requestId = 'default', hooks })=>{
254
146
  const getUploadPath = isServerTarget ? void 0 : compile(path, {
255
147
  encode: encodeURIComponent
256
148
  });
257
149
  const sender = (...args)=>{
258
- const fetcher = resolveConfiguredRequest(realRequest, requestId, environment.originFetch);
150
+ const context = startRequest(requestId, hooks);
151
+ const fetcher = realRequest.get(requestId) || environment.originFetch;
259
152
  const { body, headers: uploadHeaders, params } = getUploadPayload(args);
260
153
  let headers = {
261
154
  ...uploadHeaders
@@ -267,13 +160,11 @@ const createRequestFactory = (environment)=>{
267
160
  domain,
268
161
  path: finalPath
269
162
  });
270
- const incomingHeaders = environment.readIncomingHeaders();
271
- headers = applyIdentityAndForwardedHeaders(headers, requestId, incomingHeaders);
272
- attachEnvelopeHeaderIfRequired(headers, requestId, finalURL, incomingHeaders);
273
- attachSecuredOperationHeaders(headers, requestId, 'POST', path, operationContext);
274
- return executeWithResilience({
163
+ headers = prepareHeaders(headers, context, hooks);
164
+ return dispatch({
165
+ ...context,
166
+ path,
275
167
  requestId,
276
- target: environment.target,
277
168
  method: 'POST',
278
169
  url: finalURL,
279
170
  init: {
@@ -281,9 +172,8 @@ const createRequestFactory = (environment)=>{
281
172
  body,
282
173
  headers
283
174
  },
284
- fetcher: fetcher,
285
- transport: realTransportResilience.get(requestId)
286
- });
175
+ fetcher: fetcher
176
+ }, hooks);
287
177
  };
288
178
  return sender;
289
179
  };
@@ -293,4 +183,4 @@ const createRequestFactory = (environment)=>{
293
183
  createUploader
294
184
  };
295
185
  };
296
- export { createRequestFactory };
186
+ export { createRequestFactory, extractPathParamNames };
@@ -1,10 +0,0 @@
1
- const BFF_ENVELOPE_HEADER = 'x-modernjs-bff-envelope';
2
- const BFF_OPERATION_CONTEXT_HEADER = 'x-operation-id';
3
- const BFF_OPERATION_CONTEXT_DETAIL_HEADER = 'x-modernjs-bff-operation-context';
4
- const BFF_DEFAULT_PROTECTED_IDENTITY_HEADERS = [
5
- 'x-tenant-id',
6
- 'x-subject-id',
7
- 'x-user-id',
8
- BFF_OPERATION_CONTEXT_HEADER
9
- ];
10
- export { BFF_DEFAULT_PROTECTED_IDENTITY_HEADERS, BFF_ENVELOPE_HEADER, BFF_OPERATION_CONTEXT_DETAIL_HEADER, BFF_OPERATION_CONTEXT_HEADER };
@@ -1,27 +1,22 @@
1
1
  import "node:module";
2
2
  import { handleRes } from "./handleRes.mjs";
3
3
  import { createRequestFactory } from "./requestFactory.mjs";
4
- export * from "./requestContext.mjs";
5
- export * from "./traceparent.mjs";
6
4
  export * from "./types.mjs";
7
- const resolveBrowserOrigin = ()=>"u" > typeof window ? window.location.origin : void 0;
8
5
  const originFetch = (...params)=>{
9
6
  const [url, init] = params;
10
7
  if (init?.method?.toLowerCase() === 'get') init.body = void 0;
11
8
  return fetch(url, init).then(handleRes);
12
9
  };
13
- const requestFactory = createRequestFactory({
14
- target: 'browser',
15
- getFetch: ()=>fetch,
16
- originFetch: originFetch,
17
- readIncomingHeaders: ()=>({}),
18
- resolveSourceOrigin: resolveBrowserOrigin,
19
- createInputParamsBody: (args)=>JSON.stringify({
20
- args
21
- }),
22
- resolveRequestUrl: ({ configDomain, domain, path })=>`${configDomain || domain || ''}${path}`,
23
- resolveUploadUrl: ({ configDomain, domain, path })=>`${configDomain || domain || ''}${path}`
24
- });
25
- const { configure: configure, createRequest: createRequest, createUploader: createUploader } = requestFactory;
26
- export { CrossOriginEnvelopePolicyError, IdentityBindingViolationError, OperationContractViolationError, ProducerClientNotInitializedError, ProducerDomainNotConfiguredError } from "./policyCore.mjs";
27
- export { configure, createRequest, createUploader };
10
+ const createClient = ()=>createRequestFactory({
11
+ target: 'browser',
12
+ getFetch: ()=>fetch,
13
+ originFetch: originFetch,
14
+ readIncomingHeaders: ()=>({}),
15
+ createInputParamsBody: (args)=>JSON.stringify({
16
+ args
17
+ }),
18
+ resolveRequestUrl: ({ configDomain, domain, path })=>`${configDomain || domain || ''}${path}`,
19
+ resolveUploadUrl: ({ configDomain, domain, path })=>`${configDomain || domain || ''}${path}`
20
+ });
21
+ const { configure: configure, createRequest: createRequest, createUploader: createUploader } = createClient();
22
+ export { configure, createClient, createRequest, createUploader };
@@ -0,0 +1,16 @@
1
+ import "node:module";
2
+ const findHeaderKey = (headers, header)=>{
3
+ const normalized = header.toLowerCase();
4
+ return Object.keys(headers).find((key)=>key.toLowerCase() === normalized);
5
+ };
6
+ const readHeader = (headers, header)=>{
7
+ const key = findHeaderKey(headers, header);
8
+ return 'string' == typeof key ? headers[key] : void 0;
9
+ };
10
+ const writeHeader = (headers, header, value)=>{
11
+ if (void 0 === value) return;
12
+ const key = findHeaderKey(headers, header);
13
+ if ('string' == typeof key && key !== header) delete headers[key];
14
+ headers[header] = value;
15
+ };
16
+ export { findHeaderKey, readHeader, writeHeader };
@@ -1,21 +1,8 @@
1
1
  import "node:module";
2
2
  import { storage } from "@modern-js/runtime-utils/node";
3
3
  import { handleRes } from "./handleRes.mjs";
4
- import { CrossOriginEnvelopePolicyError, IdentityBindingViolationError, OperationContractViolationError, ProducerClientNotInitializedError, ProducerDomainNotConfiguredError, firstHeaderValue, toOrigin } from "./policyCore.mjs";
5
4
  import { createRequestFactory } from "./requestFactory.mjs";
6
- export * from "./requestContext.mjs";
7
- export * from "./traceparent.mjs";
8
5
  export * from "./types.mjs";
9
- const resolveSourceOrigin = (headers)=>{
10
- const origin = toOrigin(firstHeaderValue(headers.origin));
11
- if (origin) return origin;
12
- const referer = toOrigin(firstHeaderValue(headers.referer));
13
- if (referer) return referer;
14
- const host = firstHeaderValue(headers.host);
15
- if (!host) return;
16
- const proto = firstHeaderValue(headers['x-forwarded-proto']) || 'http';
17
- return `${proto}://${host}`;
18
- };
19
6
  const readIncomingWebHeaders = ()=>{
20
7
  try {
21
8
  return storage.useContext().headers || {};
@@ -28,15 +15,14 @@ const originFetch = (...params)=>{
28
15
  if (init?.method?.toLowerCase() === 'get') init.body = void 0;
29
16
  return fetch(...params).then(handleRes);
30
17
  };
31
- const requestFactory = createRequestFactory({
32
- target: 'server',
33
- getFetch: ()=>fetch,
34
- originFetch: originFetch,
35
- readIncomingHeaders: readIncomingWebHeaders,
36
- resolveSourceOrigin: resolveSourceOrigin,
37
- createInputParamsBody: (args)=>args,
38
- resolveRequestUrl: ({ configDomain, path, port })=>`${configDomain || `http://127.0.0.1:${port}`}${path}`,
39
- resolveUploadUrl: ({ configDomain, path })=>`${configDomain || ''}${path}`
40
- });
41
- const { configure: configure, createRequest: createRequest, createUploader: createUploader } = requestFactory;
42
- export { CrossOriginEnvelopePolicyError, IdentityBindingViolationError, OperationContractViolationError, ProducerClientNotInitializedError, ProducerDomainNotConfiguredError, configure, createRequest, createUploader };
18
+ const createClient = ()=>createRequestFactory({
19
+ target: 'server',
20
+ getFetch: ()=>fetch,
21
+ originFetch: originFetch,
22
+ readIncomingHeaders: readIncomingWebHeaders,
23
+ createInputParamsBody: (args)=>args,
24
+ resolveRequestUrl: ({ configDomain, path, port })=>`${configDomain || `http://127.0.0.1:${port}`}${path}`,
25
+ resolveUploadUrl: ({ configDomain, path })=>`${configDomain || ''}${path}`
26
+ });
27
+ const { configure: configure, createRequest: createRequest, createUploader: createUploader } = createClient();
28
+ export { configure, createClient, createRequest, createUploader };