@bleedingdev/modern-js-plugin-bff 3.2.0-ultramodern.12 → 3.2.0-ultramodern.121

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 (80) hide show
  1. package/dist/cjs/cli.js +9 -5
  2. package/dist/cjs/constants.js +13 -9
  3. package/dist/cjs/index.js +9 -5
  4. package/dist/cjs/loader.js +32 -5
  5. package/dist/cjs/runtime/create-request/index.js +9 -5
  6. package/dist/cjs/runtime/data-platform/index.js +50 -26
  7. package/dist/cjs/runtime/effect/adapter.js +99 -93
  8. package/dist/cjs/runtime/effect/context.js +19 -7
  9. package/dist/cjs/runtime/effect/edge.js +169 -0
  10. package/dist/cjs/runtime/effect/endpoint-contracts.js +130 -0
  11. package/dist/cjs/runtime/effect/handler.js +642 -0
  12. package/dist/cjs/runtime/effect/index.js +30 -547
  13. package/dist/cjs/runtime/effect/module.js +151 -0
  14. package/dist/cjs/runtime/effect/operation-context.js +103 -0
  15. package/dist/cjs/runtime/effect-client/index.js +22 -6
  16. package/dist/cjs/runtime/effect-client/runtime.js +266 -0
  17. package/dist/cjs/runtime/hono/adapter.js +30 -14
  18. package/dist/cjs/runtime/hono/index.js +9 -5
  19. package/dist/cjs/runtime/hono/operators.js +9 -5
  20. package/dist/cjs/runtime/safe-failure.js +83 -0
  21. package/dist/cjs/server.js +9 -5
  22. package/dist/cjs/utils/clientGenerator.js +13 -9
  23. package/dist/cjs/utils/createHonoRoutes.js +9 -5
  24. package/dist/cjs/utils/crossProjectApiPlugin.js +9 -5
  25. package/dist/cjs/utils/crossProjectServerPolicy.js +104 -0
  26. package/dist/cjs/utils/effectClientGenerator.js +116 -488
  27. package/dist/cjs/utils/pluginGenerator.js +9 -5
  28. package/dist/cjs/utils/runtimeGenerator.js +9 -5
  29. package/dist/esm/loader.mjs +23 -0
  30. package/dist/esm/runtime/data-platform/index.mjs +33 -22
  31. package/dist/esm/runtime/effect/adapter.mjs +91 -89
  32. package/dist/esm/runtime/effect/context.mjs +3 -1
  33. package/dist/esm/runtime/effect/edge.mjs +83 -0
  34. package/dist/esm/runtime/effect/endpoint-contracts.mjs +68 -0
  35. package/dist/esm/runtime/effect/handler.mjs +470 -0
  36. package/dist/esm/runtime/effect/index.mjs +3 -437
  37. package/dist/esm/runtime/effect/module.mjs +113 -0
  38. package/dist/esm/runtime/effect/operation-context.mjs +65 -0
  39. package/dist/esm/runtime/effect-client/index.mjs +14 -2
  40. package/dist/esm/runtime/effect-client/runtime.mjs +228 -0
  41. package/dist/esm/runtime/hono/adapter.mjs +21 -9
  42. package/dist/esm/runtime/safe-failure.mjs +45 -0
  43. package/dist/esm/utils/clientGenerator.mjs +5 -5
  44. package/dist/esm/utils/crossProjectServerPolicy.mjs +50 -0
  45. package/dist/esm/utils/effectClientGenerator.mjs +105 -484
  46. package/dist/esm-node/loader.mjs +23 -0
  47. package/dist/esm-node/runtime/data-platform/index.mjs +33 -22
  48. package/dist/esm-node/runtime/effect/adapter.mjs +91 -89
  49. package/dist/esm-node/runtime/effect/context.mjs +3 -1
  50. package/dist/esm-node/runtime/effect/edge.mjs +84 -0
  51. package/dist/esm-node/runtime/effect/endpoint-contracts.mjs +69 -0
  52. package/dist/esm-node/runtime/effect/handler.mjs +471 -0
  53. package/dist/esm-node/runtime/effect/index.mjs +3 -437
  54. package/dist/esm-node/runtime/effect/module.mjs +114 -0
  55. package/dist/esm-node/runtime/effect/operation-context.mjs +66 -0
  56. package/dist/esm-node/runtime/effect-client/index.mjs +14 -2
  57. package/dist/esm-node/runtime/effect-client/runtime.mjs +229 -0
  58. package/dist/esm-node/runtime/hono/adapter.mjs +21 -9
  59. package/dist/esm-node/runtime/safe-failure.mjs +46 -0
  60. package/dist/esm-node/utils/clientGenerator.mjs +5 -5
  61. package/dist/esm-node/utils/crossProjectServerPolicy.mjs +52 -0
  62. package/dist/esm-node/utils/effectClientGenerator.mjs +105 -484
  63. package/dist/types/runtime/create-request/index.d.ts +1 -0
  64. package/dist/types/runtime/data-platform/index.d.ts +4 -0
  65. package/dist/types/runtime/effect/adapter.d.ts +25 -0
  66. package/dist/types/runtime/effect/context.d.ts +3 -6
  67. package/dist/types/runtime/effect/edge.d.ts +25 -0
  68. package/dist/types/runtime/effect/endpoint-contracts.d.ts +62 -0
  69. package/dist/types/runtime/effect/handler.d.ts +203 -0
  70. package/dist/types/runtime/effect/index.d.ts +2 -170
  71. package/dist/types/runtime/effect/module.d.ts +48 -0
  72. package/dist/types/runtime/effect/operation-context.d.ts +10 -0
  73. package/dist/types/runtime/effect-client/index.d.ts +6 -1
  74. package/dist/types/runtime/effect-client/runtime.d.ts +71 -0
  75. package/dist/types/runtime/hono/adapter.d.ts +3 -0
  76. package/dist/types/runtime/safe-failure.d.ts +1 -0
  77. package/dist/types/utils/createHonoRoutes.d.ts +3 -3
  78. package/dist/types/utils/crossProjectServerPolicy.d.ts +35 -0
  79. package/dist/types/utils/effectClientGenerator.d.ts +16 -2
  80. package/package.json +41 -20
@@ -1,6 +1,7 @@
1
- import { DEFAULT_OPERATION_VERSION, createOperationEntries, createOperationSchemaHash } from "@modern-js/bff-core";
1
+ import { deriveOperationVersion } from "@modern-js/bff-core";
2
2
  import { compatibleRequire, findExists, fs, logger } from "@modern-js/utils";
3
3
  import path from "path";
4
+ import { collectEffectEndpoints, createEffectEndpointContractHash, ensureLeadingSlash, extractHttpApiFromModule, normalizeEffectPrefix } from "../runtime/effect/endpoint-contracts.mjs";
4
5
  const JS_OR_TS_EXTS = [
5
6
  '.js',
6
7
  '.jsx',
@@ -12,18 +13,11 @@ const JS_OR_TS_EXTS = [
12
13
  '.cts'
13
14
  ];
14
15
  const DEFAULT_REQUEST_CREATOR = '@modern-js/plugin-bff/client';
15
- const DEFAULT_DATA_PLATFORM_IMPORT = '@modern-js/plugin-bff/data-platform';
16
+ const EFFECT_CLIENT_RUNTIME_IMPORT = '@modern-js/plugin-bff/effect-client-runtime';
16
17
  let httpApiRuntimePromise;
17
18
  function isRecord(value) {
18
19
  return 'object' == typeof value && null !== value;
19
20
  }
20
- function ensureLeadingSlash(pathname) {
21
- return pathname.startsWith('/') ? pathname : `/${pathname}`;
22
- }
23
- function normalizePrefix(prefix) {
24
- if ('/' === prefix) return '';
25
- return ensureLeadingSlash(prefix || '/api');
26
- }
27
21
  function isAbsoluteUrl(value) {
28
22
  try {
29
23
  new URL(value);
@@ -35,32 +29,22 @@ function isAbsoluteUrl(value) {
35
29
  function resolveBatchEndpoint(prefix, endpoint) {
36
30
  const value = endpoint || '/_data/batch';
37
31
  if (isAbsoluteUrl(value)) return value;
38
- const normalizedPrefix = normalizePrefix(prefix);
32
+ const normalizedPrefix = normalizeEffectPrefix(prefix);
39
33
  const normalizedEndpoint = ensureLeadingSlash(value);
40
34
  if (!normalizedPrefix) return normalizedEndpoint;
41
35
  if (normalizedEndpoint === normalizedPrefix || normalizedEndpoint.startsWith(`${normalizedPrefix}/`)) return normalizedEndpoint;
42
36
  return `${normalizedPrefix}${'/' === normalizedEndpoint ? '' : normalizedEndpoint}`;
43
37
  }
44
- function getRoutePath(prefix, endpointPath) {
45
- const normalizedPrefix = normalizePrefix(prefix);
46
- const normalizedEndpointPath = ensureLeadingSlash(endpointPath);
47
- const finalEndpointPath = '/' === normalizedEndpointPath ? '' : endpointPath;
48
- if (!normalizedPrefix && !finalEndpointPath) return '/';
49
- return `${normalizedPrefix}${finalEndpointPath || ''}`;
50
- }
51
- function toSafeIdentifier(name) {
52
- const sanitized = name.replace(/[^a-zA-Z0-9_$]/g, '_');
53
- if (!sanitized) return '_';
54
- if (/^[0-9]/.test(sanitized)) return `_${sanitized}`;
55
- return sanitized;
56
- }
57
- function getPackageName(appDir) {
38
+ function getPackageInfo(appDir) {
58
39
  try {
59
40
  const packageJsonPath = path.resolve(appDir, './package.json');
60
41
  const packageJson = fs.readJSONSync(packageJsonPath);
61
- return packageJson.name;
42
+ return {
43
+ name: packageJson.name,
44
+ version: packageJson.version
45
+ };
62
46
  } catch {
63
- return;
47
+ return {};
64
48
  }
65
49
  }
66
50
  async function getHttpApiRuntime() {
@@ -83,472 +67,66 @@ async function getHttpApiRuntime() {
83
67
  })();
84
68
  return httpApiRuntimePromise;
85
69
  }
86
- function resolveApiId(api) {
87
- const fallback = 'EffectHttpApi';
88
- const maybeApi = api;
89
- if ('identifier' in maybeApi && 'string' == typeof maybeApi.identifier && maybeApi.identifier) return maybeApi.identifier;
90
- return fallback;
91
- }
92
- function collectEffectEndpoints(httpApiRuntime, api, prefix) {
93
- const endpoints = [];
94
- const apiId = resolveApiId(api);
95
- httpApiRuntime.reflect(api, {
96
- onGroup: ()=>{},
97
- onEndpoint: ({ group, endpoint })=>{
98
- endpoints.push({
99
- apiId,
100
- groupName: String(group.identifier),
101
- endpointName: String(endpoint.name),
102
- method: String(endpoint.method).toUpperCase(),
103
- routePath: getRoutePath(prefix, String(endpoint.path))
104
- });
105
- }
106
- });
107
- return endpoints.sort((a, b)=>{
108
- if (a.groupName === b.groupName) return a.endpointName.localeCompare(b.endpointName);
109
- return a.groupName.localeCompare(b.groupName);
110
- });
111
- }
112
70
  async function loadEffectApi(resourcePath) {
113
71
  const httpApiRuntime = await getHttpApiRuntime();
114
72
  const mod = await compatibleRequire(resourcePath, false);
115
- if (isRecord(mod) && httpApiRuntime.isHttpApi(mod.api)) return mod.api;
116
- if (isRecord(mod) && isRecord(mod.default) && httpApiRuntime.isHttpApi(mod.default.api)) return mod.default.api;
117
- if (isRecord(mod) && 'function' == typeof mod.default && 0 === mod.default.length) {
118
- const output = await mod.default();
119
- if (isRecord(output) && httpApiRuntime.isHttpApi(output.api)) return output.api;
120
- }
121
- return null;
73
+ return extractHttpApiFromModule(mod, httpApiRuntime.isHttpApi);
122
74
  }
123
75
  function renderEffectClientCode(endpoints, options) {
124
- const senderDeclarations = [];
125
- const operationDeclarations = [];
126
- const callerDeclarations = [];
127
- const groupedCallers = {};
128
- const groupedOperations = {};
129
76
  const requestCreator = options.requestCreator || DEFAULT_REQUEST_CREATOR;
130
- const dataPlatformImport = DEFAULT_DATA_PLATFORM_IMPORT;
131
77
  const httpMethodDecider = options.httpMethodDecider || 'functionName';
132
- const portCode = 'server' === options.target ? `process.env.PORT || ${String(options.port)}` : String(options.port);
133
- const packageName = getPackageName(options.appDir);
78
+ const packageInfo = getPackageInfo(options.appDir);
79
+ const packageName = packageInfo.name;
134
80
  const dataPlatformAppNamespace = packageName || 'unknown-app';
135
81
  const requestId = 'bundle' === options.target ? packageName || process.env.npm_package_name : void 0;
136
82
  const normalizedRequestId = requestId || 'default';
137
- const operationVersion = 'number' == typeof DEFAULT_OPERATION_VERSION ? DEFAULT_OPERATION_VERSION : 1;
138
- const schemaHash = createOperationSchemaHash(createOperationEntries(endpoints.map((endpoint)=>({
139
- name: endpoint.endpointName,
140
- httpMethod: endpoint.method,
141
- routePath: endpoint.routePath
142
- }))), normalizedRequestId);
83
+ const operationVersion = deriveOperationVersion(packageInfo.version);
143
84
  const batchConfig = options.dataPlatformBatch;
144
85
  const batchEndpoint = resolveBatchEndpoint(options.prefix, batchConfig?.endpoint);
145
- const batchConfigCode = JSON.stringify({
146
- enabled: batchConfig?.enabled ?? true,
147
- endpoint: batchEndpoint,
148
- flushIntervalMs: batchConfig?.flushIntervalMs ?? 8,
149
- maxBatchSize: batchConfig?.maxBatchSize ?? 16,
150
- maxBatchBytes: batchConfig?.maxBatchBytes ?? 65536,
151
- requestTimeoutMs: batchConfig?.requestTimeoutMs ?? 10000,
152
- allowedMethods: batchConfig?.allowedMethods && batchConfig.allowedMethods.length > 0 ? batchConfig.allowedMethods : [
153
- 'GET'
154
- ]
155
- });
156
- endpoints.forEach((endpoint, index)=>{
157
- const senderVar = `__sender_${toSafeIdentifier(endpoint.groupName)}_${toSafeIdentifier(endpoint.endpointName)}_${index}`;
158
- const callVar = `__call_${toSafeIdentifier(endpoint.groupName)}_${toSafeIdentifier(endpoint.endpointName)}_${index}`;
159
- const operationVar = `__operation_${toSafeIdentifier(endpoint.groupName)}_${toSafeIdentifier(endpoint.endpointName)}_${index}`;
160
- const operationId = `${endpoint.method}:${endpoint.routePath}`;
161
- const operationContextCode = JSON.stringify({
162
- operationId,
163
- routePath: endpoint.routePath,
164
- method: endpoint.method,
165
- schemaHash,
166
- operationVersion
167
- });
168
- const createRequestOptions = `{
169
- path: ${JSON.stringify(endpoint.routePath)},
170
- method: ${JSON.stringify(endpoint.method)},
171
- port: ${portCode},
172
- operationContext: ${operationContextCode},
173
- httpMethodDecider: ${JSON.stringify(httpMethodDecider)}${requestId ? `, requestId: ${JSON.stringify(requestId)}` : ''}
174
- }`.replace(/\n\s*/g, '');
175
- senderDeclarations.push(`const ${senderVar} = createRequest(${createRequestOptions});`);
176
- operationDeclarations.push(`const ${operationVar} = ${JSON.stringify({
177
- appNamespace: dataPlatformAppNamespace,
178
- apiId: endpoint.apiId,
179
- group: endpoint.groupName,
180
- endpoint: endpoint.endpointName,
181
- operationId,
182
- routePath: endpoint.routePath,
183
- method: endpoint.method,
184
- operationVersion,
185
- schemaHash,
186
- version: operationVersion
187
- })};`);
188
- callerDeclarations.push(`const ${callVar} = (request = {}) => ${senderVar}(__prepareEffectRequest(${JSON.stringify(endpoint.method)}, ${JSON.stringify(endpoint.routePath)}, ${operationVar}, request));`);
189
- groupedCallers[endpoint.groupName] ??= [];
190
- groupedCallers[endpoint.groupName].push({
191
- endpointName: endpoint.endpointName,
192
- callVar
193
- });
194
- groupedOperations[endpoint.groupName] ??= [];
195
- groupedOperations[endpoint.groupName].push({
196
- endpointName: endpoint.endpointName,
197
- operationVar
198
- });
199
- });
200
- const groupObjectEntries = Object.entries(groupedCallers).map(([groupName, groupCallers])=>{
201
- const endpointEntries = groupCallers.map((caller)=>`${JSON.stringify(caller.endpointName)}: ${caller.callVar}`).join(', ');
202
- return `${JSON.stringify(groupName)}: { ${endpointEntries} }`;
203
- });
204
- const clientObject = groupObjectEntries.length ? `{
205
- ${groupObjectEntries.join(',\n ')}
206
- }` : '{}';
207
- const operationManifestEntries = Object.entries(groupedOperations).map(([groupName, groupOperations])=>{
208
- const endpointEntries = groupOperations.map((operation)=>`${JSON.stringify(operation.endpointName)}: ${operation.operationVar}`).join(', ');
209
- return `${JSON.stringify(groupName)}: { ${endpointEntries} }`;
210
- });
211
- const operationManifestObject = operationManifestEntries.length ? `{
212
- ${operationManifestEntries.join(',\n ')}
213
- }` : '{}';
86
+ const manifest = {
87
+ endpoints: endpoints.map((endpoint)=>({
88
+ apiId: endpoint.apiId,
89
+ group: endpoint.groupName,
90
+ endpoint: endpoint.endpointName,
91
+ method: endpoint.method,
92
+ routePath: endpoint.routePath,
93
+ schemaHash: createEffectEndpointContractHash(endpoint, normalizedRequestId),
94
+ operationVersion
95
+ }))
96
+ };
97
+ const config = {
98
+ appNamespace: dataPlatformAppNamespace,
99
+ ...requestId ? {
100
+ requestId
101
+ } : {},
102
+ port: options.port,
103
+ useEnvPort: 'server' === options.target,
104
+ defaultOrigin: `http://localhost:${String(options.port)}`,
105
+ httpMethodDecider,
106
+ batch: {
107
+ enabled: batchConfig?.enabled ?? true,
108
+ endpoint: batchEndpoint,
109
+ flushIntervalMs: batchConfig?.flushIntervalMs ?? 8,
110
+ maxBatchSize: batchConfig?.maxBatchSize ?? 16,
111
+ maxBatchBytes: batchConfig?.maxBatchBytes ?? 65536,
112
+ requestTimeoutMs: batchConfig?.requestTimeoutMs ?? 10000,
113
+ allowedMethods: batchConfig?.allowedMethods && batchConfig.allowedMethods.length > 0 ? batchConfig.allowedMethods : [
114
+ 'GET'
115
+ ]
116
+ }
117
+ };
214
118
  return `import * as __requestRuntime from ${JSON.stringify(requestCreator)};
215
- import {
216
- createDataBatchTransport,
217
- DEFAULT_DATA_BATCH_HEADER,
218
- DEFAULT_DATA_ENVELOPE_HEADER,
219
- createRequestEnvelope,
220
- encodeRequestEnvelopeHeader,
221
- } from ${JSON.stringify(dataPlatformImport)};
222
-
223
- const createRequest = __requestRuntime.createRequest;
224
- const __configureRequest =
225
- typeof __requestRuntime.configure === 'function'
226
- ? __requestRuntime.configure
227
- : undefined;
228
- const __createRequestContextHeaders =
229
- typeof __requestRuntime.createRequestContextHeaders === 'function'
230
- ? __requestRuntime.createRequestContextHeaders
231
- : undefined;
232
-
233
- const __METHODS_WITHOUT_BODY = new Set(['GET', 'DELETE', 'HEAD', 'OPTIONS']);
234
- const __DATA_REQUEST_MODES = new Set(['cache-first', 'stale-while-revalidate', 'network-only']);
235
- const __DATA_MUTATION_MODES = new Set(['optimistic', 'pessimistic', 'fire-and-forget']);
236
- const __DEFAULT_APP_NAMESPACE = ${JSON.stringify(dataPlatformAppNamespace)};
237
- const __DEFAULT_ORIGIN = 'http://localhost:${String(options.port)}';
238
- const __DEFAULT_BATCH_CONFIG = ${batchConfigCode};
239
- const __REQUEST_ID = ${requestId ? JSON.stringify(requestId) : 'undefined'};
240
- const __RUNTIME_FETCH =
241
- typeof fetch === 'function' ? fetch.bind(globalThis) : undefined;
119
+ import { createGeneratedEffectClient } from ${JSON.stringify(EFFECT_CLIENT_RUNTIME_IMPORT)};
242
120
 
243
- if (__REQUEST_ID && __configureRequest) {
244
- const __configurePayload = {
245
- requestId: __REQUEST_ID,
246
- requireEnvelope: true,
247
- identityBinding: {
248
- enabled: true,
249
- strict: true,
250
- },
251
- operationContract: {
252
- enabled: true,
253
- strict: true,
254
- requireSchemaHash: true,
255
- requireOperationVersion: true,
256
- },
257
- setDomain: () => {
258
- if (
259
- typeof window !== 'undefined' &&
260
- window.location &&
261
- typeof window.location.origin === 'string' &&
262
- window.location.origin
263
- ) {
264
- return window.location.origin;
265
- }
121
+ const __manifest = ${JSON.stringify(manifest, null, 2)};
266
122
 
267
- if (
268
- typeof globalThis !== 'undefined' &&
269
- globalThis.location &&
270
- typeof globalThis.location.origin === 'string' &&
271
- globalThis.location.origin
272
- ) {
273
- return globalThis.location.origin;
274
- }
123
+ const __config = ${JSON.stringify(config, null, 2)};
275
124
 
276
- return __DEFAULT_ORIGIN;
277
- },
278
- };
279
-
280
- if (__DEFAULT_BATCH_CONFIG.enabled !== false && __RUNTIME_FETCH) {
281
- __configurePayload.request = createDataBatchTransport({
282
- fetch: __RUNTIME_FETCH,
283
- endpoint: __DEFAULT_BATCH_CONFIG.endpoint,
284
- flushIntervalMs: __DEFAULT_BATCH_CONFIG.flushIntervalMs,
285
- maxBatchSize: __DEFAULT_BATCH_CONFIG.maxBatchSize,
286
- maxBatchBytes: __DEFAULT_BATCH_CONFIG.maxBatchBytes,
287
- requestTimeoutMs: __DEFAULT_BATCH_CONFIG.requestTimeoutMs,
288
- allowedMethods: __DEFAULT_BATCH_CONFIG.allowedMethods,
289
- });
290
- }
125
+ const __generated = createGeneratedEffectClient(__manifest, __config, __requestRuntime);
291
126
 
292
- __configureRequest(__configurePayload);
293
- }
294
-
295
- const __isRecord = value => typeof value === 'object' && value !== null;
296
- const __stringOrUndefined = value =>
297
- typeof value === 'string' && value.length > 0 ? value : undefined;
298
- const __isDataRequestMode = value =>
299
- typeof value === 'string' && __DATA_REQUEST_MODES.has(value);
300
- const __isDataMutationMode = value =>
301
- typeof value === 'string' && __DATA_MUTATION_MODES.has(value);
302
- const __normalizeOrigin = value => {
303
- if (typeof value !== 'string' || value.length === 0) {
304
- return undefined;
305
- }
306
- try {
307
- return new URL(value).origin;
308
- } catch {
309
- return undefined;
310
- }
311
- };
312
-
313
- const __normalizeRequest = (method, request = {}) => {
314
- if (!__isRecord(request)) {
315
- return {};
316
- }
317
-
318
- const payload = { ...request };
319
-
320
- if (__isRecord(request.path) && !__isRecord(payload.params)) {
321
- payload.params = request.path;
322
- }
323
-
324
- if (__isRecord(request.urlParams) && !__isRecord(payload.query)) {
325
- payload.query = request.urlParams;
326
- }
327
-
328
- if (__isRecord(request.headers) && !__isRecord(payload.headers)) {
329
- payload.headers = request.headers;
330
- }
331
-
332
- if ('payload' in request && request.payload !== undefined) {
333
- if (request.payload instanceof FormData && !('formData' in payload)) {
334
- payload.formData = request.payload;
335
- } else if (__METHODS_WITHOUT_BODY.has(method)) {
336
- if (__isRecord(request.payload)) {
337
- payload.query = __isRecord(payload.query)
338
- ? { ...payload.query, ...request.payload }
339
- : request.payload;
340
- } else if (!('body' in payload)) {
341
- payload.body = request.payload;
342
- }
343
- } else if (__isRecord(request.payload) && !('data' in payload)) {
344
- payload.data = request.payload;
345
- } else if (!('body' in payload)) {
346
- payload.body = request.payload;
347
- }
348
- }
349
-
350
- return payload;
351
- };
352
-
353
- const __resolveOrigin = () => {
354
- if (
355
- typeof window !== 'undefined' &&
356
- window.location &&
357
- typeof window.location.origin === 'string' &&
358
- window.location.origin
359
- ) {
360
- return window.location.origin;
361
- }
362
-
363
- if (
364
- typeof globalThis !== 'undefined' &&
365
- globalThis.location &&
366
- typeof globalThis.location.origin === 'string' &&
367
- globalThis.location.origin
368
- ) {
369
- return globalThis.location.origin;
370
- }
371
-
372
- return __DEFAULT_ORIGIN;
373
- };
374
-
375
- const __resolveTargetOrigin = dataPlatform => {
376
- const explicitTargetOrigin =
377
- __stringOrUndefined(dataPlatform.targetOrigin) ||
378
- __stringOrUndefined(dataPlatform.endpointOrigin);
379
- if (explicitTargetOrigin) {
380
- return explicitTargetOrigin;
381
- }
382
- return __DEFAULT_ORIGIN;
383
- };
384
-
385
- const __shouldAttachEnvelopeHeader = dataPlatform => {
386
- if (dataPlatform.allowCrossOriginEnvelope === true) {
387
- return true;
388
- }
389
- const currentOrigin = __normalizeOrigin(__resolveOrigin());
390
- const targetOrigin = __normalizeOrigin(__resolveTargetOrigin(dataPlatform));
391
- if (!currentOrigin || !targetOrigin) {
392
- return true;
393
- }
394
- return currentOrigin === targetOrigin;
395
- };
396
-
397
- const __toEnvelopeInput = normalizedRequest => {
398
- if (!__isRecord(normalizedRequest)) {
399
- return {};
400
- }
401
-
402
- const payload = {};
403
- if (__isRecord(normalizedRequest.params)) {
404
- payload.path = normalizedRequest.params;
405
- }
406
- if (__isRecord(normalizedRequest.query)) {
407
- payload.query = normalizedRequest.query;
408
- }
409
- if ('data' in normalizedRequest && normalizedRequest.data !== undefined) {
410
- payload.data = normalizedRequest.data;
411
- }
412
- if ('body' in normalizedRequest && normalizedRequest.body !== undefined) {
413
- payload.body = normalizedRequest.body;
414
- }
415
- if (
416
- typeof FormData !== 'undefined' &&
417
- normalizedRequest.formData instanceof FormData
418
- ) {
419
- payload.formData = Array.from(normalizedRequest.formData.entries()).map(
420
- ([key, value]) => [key, String(value)],
421
- );
422
- }
423
- if (
424
- typeof URLSearchParams !== 'undefined' &&
425
- normalizedRequest.formUrlencoded instanceof URLSearchParams
426
- ) {
427
- payload.formUrlencoded = normalizedRequest.formUrlencoded.toString();
428
- }
429
- return payload;
430
- };
431
-
432
- const createEffectRequestContext = requestContext => {
433
- if (!__isRecord(requestContext)) {
434
- return {};
435
- }
436
-
437
- const headers = __createRequestContextHeaders
438
- ? __createRequestContextHeaders(requestContext)
439
- : {};
440
-
441
- return {
442
- ...requestContext,
443
- headers,
444
- };
445
- };
446
-
447
- const __applyRequestContext = (normalizedRequest, request = {}) => {
448
- if (!__isRecord(request) || !__isRecord(request.requestContext)) {
449
- return normalizedRequest;
450
- }
451
-
452
- const requestContext = createEffectRequestContext(request.requestContext);
453
- const requestHeaders = __isRecord(requestContext.headers)
454
- ? requestContext.headers
455
- : {};
456
-
457
- if (Object.keys(requestHeaders).length === 0) {
458
- return normalizedRequest;
459
- }
460
-
461
- return {
462
- ...normalizedRequest,
463
- headers: {
464
- ...requestHeaders,
465
- ...(__isRecord(normalizedRequest.headers) ? normalizedRequest.headers : {}),
466
- },
467
- };
468
- };
469
-
470
- const __prepareEffectRequest = (method, routePath, operation, request = {}) => {
471
- const normalizedRequest = __applyRequestContext(
472
- __normalizeRequest(method, request),
473
- request,
474
- );
475
- const dataPlatform = __isRecord(request) && __isRecord(request.dataPlatform)
476
- ? request.dataPlatform
477
- : {};
478
- const strictEnvelope =
479
- dataPlatform.requireEnvelope === true || dataPlatform.strict === true;
480
-
481
- if (!strictEnvelope && !__shouldAttachEnvelopeHeader(dataPlatform)) {
482
- return normalizedRequest;
483
- }
484
-
485
- try {
486
- const namespace =
487
- __stringOrUndefined(dataPlatform.appNamespace) || __DEFAULT_APP_NAMESPACE;
488
- const origin = __stringOrUndefined(dataPlatform.origin) || __resolveOrigin();
489
- const envelope = createRequestEnvelope({
490
- operation: {
491
- ...operation,
492
- appNamespace: namespace,
493
- },
494
- scope: {
495
- appNamespace: namespace,
496
- origin,
497
- tenantId: __stringOrUndefined(dataPlatform.tenantId),
498
- userId: __stringOrUndefined(dataPlatform.userId),
499
- sessionId: __stringOrUndefined(dataPlatform.sessionId),
500
- },
501
- requestInput: {
502
- method,
503
- routePath,
504
- payload: __toEnvelopeInput(normalizedRequest),
505
- },
506
- requestMode: __isDataRequestMode(dataPlatform.requestMode)
507
- ? dataPlatform.requestMode
508
- : undefined,
509
- mutationMode: __isDataMutationMode(dataPlatform.mutationMode)
510
- ? dataPlatform.mutationMode
511
- : undefined,
512
- selectionPlan: __isRecord(dataPlatform.selectionPlan)
513
- ? dataPlatform.selectionPlan
514
- : undefined,
515
- traceContext: __isRecord(dataPlatform.traceContext)
516
- ? dataPlatform.traceContext
517
- : undefined,
518
- requireTraceContext: dataPlatform.requireTraceContext === true,
519
- });
520
-
521
- const headerName =
522
- __stringOrUndefined(dataPlatform.envelopeHeader) ||
523
- DEFAULT_DATA_ENVELOPE_HEADER;
524
- const headers = __isRecord(normalizedRequest.headers)
525
- ? { ...normalizedRequest.headers }
526
- : {};
527
-
528
- if (dataPlatform.batch === false) {
529
- headers[DEFAULT_DATA_BATCH_HEADER] = 'off';
530
- }
531
-
532
- headers[headerName] = encodeRequestEnvelopeHeader(envelope);
533
-
534
- return {
535
- ...normalizedRequest,
536
- headers,
537
- };
538
- } catch (error) {
539
- if (strictEnvelope) {
540
- throw error;
541
- }
542
- return normalizedRequest;
543
- }
544
- };
545
-
546
- ${senderDeclarations.join('\n')}
547
- ${operationDeclarations.join('\n')}
548
- ${callerDeclarations.join('\n')}
549
-
550
- const client = ${clientObject};
551
- const operationManifest = ${operationManifestObject};
127
+ const client = __generated.client;
128
+ const operationManifest = __generated.operationManifest;
129
+ const createEffectRequestContext = __generated.createEffectRequestContext;
552
130
  const effectBffModule = {
553
131
  client,
554
132
  operationManifest,
@@ -559,7 +137,23 @@ export { client, createEffectRequestContext, operationManifest };
559
137
  export default effectBffModule;
560
138
  `;
561
139
  }
562
- function renderEffectClientDeclaration() {
140
+ function renderClientShape(endpoints, valueType) {
141
+ if (0 === endpoints.length) return `Record<string, Record<string, ${valueType}>>`;
142
+ const groups = new Map();
143
+ for (const endpoint of endpoints){
144
+ const group = groups.get(endpoint.groupName) || [];
145
+ group.push(endpoint.endpointName);
146
+ groups.set(endpoint.groupName, group);
147
+ }
148
+ const groupEntries = [
149
+ ...groups.entries()
150
+ ].map(([groupName, names])=>{
151
+ const endpointEntries = names.map((name)=>` ${JSON.stringify(name)}: ${valueType};`).join('\n');
152
+ return ` ${JSON.stringify(groupName)}: {\n${endpointEntries}\n };`;
153
+ });
154
+ return `{\n${groupEntries.join('\n')}\n}`;
155
+ }
156
+ function renderEffectClientDeclaration(endpoints = []) {
563
157
  return `export type EffectClientOperation = (
564
158
  request?: unknown,
565
159
  ) => Promise<unknown>;
@@ -581,37 +175,64 @@ export type EffectOperationManifest = Record<
581
175
  string,
582
176
  Record<string, EffectOperationDescriptor>
583
177
  >;
178
+ export type EffectOperationContext = {
179
+ requestId?: string;
180
+ operationId?: string;
181
+ routePath?: string;
182
+ method?: string;
183
+ schemaHash?: string;
184
+ operationVersion?: number;
185
+ locale?: string;
186
+ traceparent?: string;
187
+ traceId?: string;
188
+ spanId?: string;
189
+ source?: string;
190
+ scope?: Record<string, unknown>;
191
+ sessionClaims?: Record<string, unknown>;
192
+ attributes?: Record<string, unknown>;
193
+ };
584
194
  export type EffectRequestContext = {
585
195
  headers?: Record<string, string>;
586
196
  locale?: string;
197
+ operationContext?: EffectOperationContext;
587
198
  traceparent?: string;
588
199
  traceId?: string;
589
200
  spanId?: string;
590
201
  };
202
+ export type GeneratedEffectClient = ${renderClientShape(endpoints, 'EffectClientOperation')};
203
+ export type GeneratedEffectOperationManifest = ${renderClientShape(endpoints, "EffectOperationDescriptor")};
591
204
 
592
- export declare const client: EffectClient;
205
+ export declare const client: GeneratedEffectClient;
593
206
  export declare const createEffectRequestContext: (
594
207
  requestContext: Record<string, unknown>,
595
208
  ) => EffectRequestContext;
596
- export declare const operationManifest: EffectOperationManifest;
209
+ export declare const operationManifest: GeneratedEffectOperationManifest;
597
210
  declare const effectBffModule: {
598
- client: EffectClient;
211
+ client: GeneratedEffectClient;
599
212
  createEffectRequestContext: typeof createEffectRequestContext;
600
- operationManifest: EffectOperationManifest;
213
+ operationManifest: GeneratedEffectOperationManifest;
601
214
  };
602
215
 
603
216
  export default effectBffModule;
604
217
  `;
605
218
  }
606
- async function generateEffectClientCode(options) {
219
+ async function generateEffectClient(options) {
607
220
  const api = await loadEffectApi(options.resourcePath);
608
221
  if (!api) {
609
222
  logger.warn(`[BFF][Effect] Failed to generate client for ${options.resourcePath}: unable to resolve exported HttpApi.`);
610
223
  return null;
611
224
  }
612
225
  const httpApiRuntime = await getHttpApiRuntime();
613
- const endpoints = collectEffectEndpoints(httpApiRuntime, api, options.prefix);
614
- return renderEffectClientCode(endpoints, options);
226
+ const endpoints = collectEffectEndpoints(httpApiRuntime.reflect, api, options.prefix);
227
+ return {
228
+ code: renderEffectClientCode(endpoints, options),
229
+ declaration: renderEffectClientDeclaration(endpoints),
230
+ endpoints
231
+ };
232
+ }
233
+ async function generateEffectClientCode(options) {
234
+ const artifacts = await generateEffectClient(options);
235
+ return artifacts ? artifacts.code : null;
615
236
  }
616
237
  function resolveEffectEntryFile(options) {
617
238
  const { appDir, apiDir, effectEntry } = options;
@@ -620,4 +241,4 @@ function resolveEffectEntryFile(options) {
620
241
  if (path.extname(entryWithoutExt)) return fs.existsSync(entryWithoutExt) ? entryWithoutExt : void 0;
621
242
  return findExists(JS_OR_TS_EXTS.map((ext)=>`${entryWithoutExt}${ext}`));
622
243
  }
623
- export { generateEffectClientCode, renderEffectClientDeclaration, resolveEffectEntryFile };
244
+ export { generateEffectClient, generateEffectClientCode, renderEffectClientDeclaration, resolveEffectEntryFile };