@bleedingdev/modern-js-bff-effect 3.8.3-ultramodern.2

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 (230) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/dist/cjs/data-platform/batch/protocol.js +130 -0
  4. package/dist/cjs/data-platform/batch/queue.js +623 -0
  5. package/dist/cjs/data-platform/batch/request.js +121 -0
  6. package/dist/cjs/data-platform/batch/response.js +65 -0
  7. package/dist/cjs/data-platform/batch/telemetry.js +71 -0
  8. package/dist/cjs/data-platform/batch/url.js +69 -0
  9. package/dist/cjs/data-platform/batch-transport.js +59 -0
  10. package/dist/cjs/data-platform/codec.js +155 -0
  11. package/dist/cjs/data-platform/index.js +124 -0
  12. package/dist/cjs/data-platform/invalidation.js +61 -0
  13. package/dist/cjs/data-platform/trace.js +73 -0
  14. package/dist/cjs/data-platform/types.js +48 -0
  15. package/dist/cjs/data-platform/validation.js +178 -0
  16. package/dist/cjs/effect/context.js +59 -0
  17. package/dist/cjs/effect/dispatch.js +101 -0
  18. package/dist/cjs/effect/edge-context.js +61 -0
  19. package/dist/cjs/effect/edge-dispatcher.js +354 -0
  20. package/dist/cjs/effect/edge.js +62 -0
  21. package/dist/cjs/effect/endpoint-contracts.js +121 -0
  22. package/dist/cjs/effect/entry-shape-registry.js +31 -0
  23. package/dist/cjs/effect/entry-shape.js +113 -0
  24. package/dist/cjs/effect/handler/batch-handler.js +270 -0
  25. package/dist/cjs/effect/handler/batch.js +170 -0
  26. package/dist/cjs/effect/handler/definition.js +99 -0
  27. package/dist/cjs/effect/handler/envelope.js +104 -0
  28. package/dist/cjs/effect/handler/http.js +107 -0
  29. package/dist/cjs/effect/handler/openapi.js +58 -0
  30. package/dist/cjs/effect/handler/routing.js +100 -0
  31. package/dist/cjs/effect/handler/rpc.js +63 -0
  32. package/dist/cjs/effect/handler/types.js +43 -0
  33. package/dist/cjs/effect/handler.js +173 -0
  34. package/dist/cjs/effect/index.js +137 -0
  35. package/dist/cjs/effect/module.js +118 -0
  36. package/dist/cjs/effect/operation-context.js +116 -0
  37. package/dist/cjs/effect/rpcSerialization.js +54 -0
  38. package/dist/cjs/effect-client/batch.js +73 -0
  39. package/dist/cjs/effect-client/envelope.js +145 -0
  40. package/dist/cjs/effect-client/guards.js +44 -0
  41. package/dist/cjs/effect-client/index.js +208 -0
  42. package/dist/cjs/effect-client/request-context.js +64 -0
  43. package/dist/cjs/effect-client/request.js +66 -0
  44. package/dist/cjs/effect-client/runtime.js +100 -0
  45. package/dist/cjs/effect-client/types.js +18 -0
  46. package/dist/cjs/headers.js +46 -0
  47. package/dist/cjs/index.js +83 -0
  48. package/dist/esm/data-platform/batch/protocol.mjs +71 -0
  49. package/dist/esm/data-platform/batch/queue.mjs +582 -0
  50. package/dist/esm/data-platform/batch/request.mjs +68 -0
  51. package/dist/esm/data-platform/batch/response.mjs +24 -0
  52. package/dist/esm/data-platform/batch/telemetry.mjs +27 -0
  53. package/dist/esm/data-platform/batch/url.mjs +25 -0
  54. package/dist/esm/data-platform/batch-transport.mjs +12 -0
  55. package/dist/esm/data-platform/codec.mjs +90 -0
  56. package/dist/esm/data-platform/index.mjs +7 -0
  57. package/dist/esm/data-platform/invalidation.mjs +20 -0
  58. package/dist/esm/data-platform/trace.mjs +29 -0
  59. package/dist/esm/data-platform/types.mjs +4 -0
  60. package/dist/esm/data-platform/validation.mjs +128 -0
  61. package/dist/esm/effect/context.mjs +12 -0
  62. package/dist/esm/effect/dispatch.mjs +63 -0
  63. package/dist/esm/effect/edge-context.mjs +14 -0
  64. package/dist/esm/effect/edge-dispatcher.mjs +115 -0
  65. package/dist/esm/effect/edge.mjs +1 -0
  66. package/dist/esm/effect/endpoint-contracts.mjs +68 -0
  67. package/dist/esm/effect/entry-shape-registry.mjs +17 -0
  68. package/dist/esm/effect/entry-shape.mjs +51 -0
  69. package/dist/esm/effect/handler/batch-handler.mjs +232 -0
  70. package/dist/esm/effect/handler/batch.mjs +105 -0
  71. package/dist/esm/effect/handler/definition.mjs +58 -0
  72. package/dist/esm/effect/handler/envelope.mjs +63 -0
  73. package/dist/esm/effect/handler/http.mjs +69 -0
  74. package/dist/esm/effect/handler/openapi.mjs +20 -0
  75. package/dist/esm/effect/handler/routing.mjs +50 -0
  76. package/dist/esm/effect/handler/rpc.mjs +22 -0
  77. package/dist/esm/effect/handler/types.mjs +5 -0
  78. package/dist/esm/effect/handler.mjs +13 -0
  79. package/dist/esm/effect/index.mjs +7 -0
  80. package/dist/esm/effect/module.mjs +80 -0
  81. package/dist/esm/effect/operation-context.mjs +75 -0
  82. package/dist/esm/effect/rpcSerialization.mjs +16 -0
  83. package/dist/esm/effect-client/batch.mjs +35 -0
  84. package/dist/esm/effect-client/envelope.mjs +104 -0
  85. package/dist/esm/effect-client/guards.mjs +3 -0
  86. package/dist/esm/effect-client/index.mjs +110 -0
  87. package/dist/esm/effect-client/request-context.mjs +23 -0
  88. package/dist/esm/effect-client/request.mjs +28 -0
  89. package/dist/esm/effect-client/runtime.mjs +62 -0
  90. package/dist/esm/effect-client/types.mjs +0 -0
  91. package/dist/esm/headers.mjs +8 -0
  92. package/dist/esm/index.mjs +4 -0
  93. package/dist/esm/rslib-runtime.mjs +18 -0
  94. package/dist/esm-node/data-platform/batch/protocol.mjs +72 -0
  95. package/dist/esm-node/data-platform/batch/queue.mjs +583 -0
  96. package/dist/esm-node/data-platform/batch/request.mjs +69 -0
  97. package/dist/esm-node/data-platform/batch/response.mjs +25 -0
  98. package/dist/esm-node/data-platform/batch/telemetry.mjs +28 -0
  99. package/dist/esm-node/data-platform/batch/url.mjs +26 -0
  100. package/dist/esm-node/data-platform/batch-transport.mjs +13 -0
  101. package/dist/esm-node/data-platform/codec.mjs +91 -0
  102. package/dist/esm-node/data-platform/index.mjs +8 -0
  103. package/dist/esm-node/data-platform/invalidation.mjs +21 -0
  104. package/dist/esm-node/data-platform/trace.mjs +30 -0
  105. package/dist/esm-node/data-platform/types.mjs +5 -0
  106. package/dist/esm-node/data-platform/validation.mjs +129 -0
  107. package/dist/esm-node/effect/context.mjs +13 -0
  108. package/dist/esm-node/effect/dispatch.mjs +64 -0
  109. package/dist/esm-node/effect/edge-context.mjs +15 -0
  110. package/dist/esm-node/effect/edge-dispatcher.mjs +116 -0
  111. package/dist/esm-node/effect/edge.mjs +2 -0
  112. package/dist/esm-node/effect/endpoint-contracts.mjs +69 -0
  113. package/dist/esm-node/effect/entry-shape-registry.mjs +18 -0
  114. package/dist/esm-node/effect/entry-shape.mjs +63 -0
  115. package/dist/esm-node/effect/handler/batch-handler.mjs +233 -0
  116. package/dist/esm-node/effect/handler/batch.mjs +106 -0
  117. package/dist/esm-node/effect/handler/definition.mjs +59 -0
  118. package/dist/esm-node/effect/handler/envelope.mjs +64 -0
  119. package/dist/esm-node/effect/handler/http.mjs +70 -0
  120. package/dist/esm-node/effect/handler/openapi.mjs +21 -0
  121. package/dist/esm-node/effect/handler/routing.mjs +51 -0
  122. package/dist/esm-node/effect/handler/rpc.mjs +23 -0
  123. package/dist/esm-node/effect/handler/types.mjs +6 -0
  124. package/dist/esm-node/effect/handler.mjs +14 -0
  125. package/dist/esm-node/effect/index.mjs +8 -0
  126. package/dist/esm-node/effect/module.mjs +81 -0
  127. package/dist/esm-node/effect/operation-context.mjs +76 -0
  128. package/dist/esm-node/effect/rpcSerialization.mjs +17 -0
  129. package/dist/esm-node/effect-client/batch.mjs +36 -0
  130. package/dist/esm-node/effect-client/envelope.mjs +105 -0
  131. package/dist/esm-node/effect-client/guards.mjs +4 -0
  132. package/dist/esm-node/effect-client/index.mjs +111 -0
  133. package/dist/esm-node/effect-client/request-context.mjs +24 -0
  134. package/dist/esm-node/effect-client/request.mjs +29 -0
  135. package/dist/esm-node/effect-client/runtime.mjs +63 -0
  136. package/dist/esm-node/effect-client/types.mjs +1 -0
  137. package/dist/esm-node/headers.mjs +9 -0
  138. package/dist/esm-node/index.mjs +5 -0
  139. package/dist/esm-node/rslib-runtime.mjs +19 -0
  140. package/dist/types/data-platform/batch/protocol.d.ts +9 -0
  141. package/dist/types/data-platform/batch/queue.d.ts +47 -0
  142. package/dist/types/data-platform/batch/request.d.ts +19 -0
  143. package/dist/types/data-platform/batch/response.d.ts +3 -0
  144. package/dist/types/data-platform/batch/telemetry.d.ts +4 -0
  145. package/dist/types/data-platform/batch/url.d.ts +4 -0
  146. package/dist/types/data-platform/batch-transport.d.ts +3 -0
  147. package/dist/types/data-platform/codec.d.ts +17 -0
  148. package/dist/types/data-platform/index.d.ts +8 -0
  149. package/dist/types/data-platform/invalidation.d.ts +8 -0
  150. package/dist/types/data-platform/trace.d.ts +4 -0
  151. package/dist/types/data-platform/types.d.ts +151 -0
  152. package/dist/types/data-platform/validation.d.ts +29 -0
  153. package/dist/types/effect/context.d.ts +5 -0
  154. package/dist/types/effect/dispatch.d.ts +13 -0
  155. package/dist/types/effect/edge-context.d.ts +5 -0
  156. package/dist/types/effect/edge-dispatcher.d.ts +37 -0
  157. package/dist/types/effect/edge.d.ts +1 -0
  158. package/dist/types/effect/endpoint-contracts.d.ts +67 -0
  159. package/dist/types/effect/entry-shape.d.ts +32 -0
  160. package/dist/types/effect/handler/batch-handler.d.ts +11 -0
  161. package/dist/types/effect/handler/batch.d.ts +15 -0
  162. package/dist/types/effect/handler/definition.d.ts +7 -0
  163. package/dist/types/effect/handler/envelope.d.ts +13 -0
  164. package/dist/types/effect/handler/http.d.ts +15 -0
  165. package/dist/types/effect/handler/openapi.d.ts +4 -0
  166. package/dist/types/effect/handler/routing.d.ts +5 -0
  167. package/dist/types/effect/handler/rpc.d.ts +7 -0
  168. package/dist/types/effect/handler/types.d.ts +153 -0
  169. package/dist/types/effect/handler.d.ts +13 -0
  170. package/dist/types/effect/index.d.ts +6 -0
  171. package/dist/types/effect/module.d.ts +36 -0
  172. package/dist/types/effect/operation-context.d.ts +15 -0
  173. package/dist/types/effect/rpcSerialization.d.ts +3 -0
  174. package/dist/types/effect-client/batch.d.ts +2 -0
  175. package/dist/types/effect-client/envelope.d.ts +15 -0
  176. package/dist/types/effect-client/guards.d.ts +2 -0
  177. package/dist/types/effect-client/index.d.ts +58 -0
  178. package/dist/types/effect-client/request-context.d.ts +3 -0
  179. package/dist/types/effect-client/request.d.ts +1 -0
  180. package/dist/types/effect-client/runtime.d.ts +5 -0
  181. package/dist/types/effect-client/types.d.ts +68 -0
  182. package/dist/types/headers.d.ts +1 -0
  183. package/dist/types/index.d.ts +3 -0
  184. package/package.json +164 -0
  185. package/src/data-platform/batch/protocol.ts +112 -0
  186. package/src/data-platform/batch/queue.ts +876 -0
  187. package/src/data-platform/batch/request.ts +130 -0
  188. package/src/data-platform/batch/response.ts +74 -0
  189. package/src/data-platform/batch/telemetry.ts +47 -0
  190. package/src/data-platform/batch/url.ts +54 -0
  191. package/src/data-platform/batch-transport.ts +22 -0
  192. package/src/data-platform/codec.ts +145 -0
  193. package/src/data-platform/index.ts +63 -0
  194. package/src/data-platform/invalidation.ts +57 -0
  195. package/src/data-platform/trace.ts +49 -0
  196. package/src/data-platform/types.ts +186 -0
  197. package/src/data-platform/validation.ts +303 -0
  198. package/src/effect/context.ts +37 -0
  199. package/src/effect/dispatch.ts +132 -0
  200. package/src/effect/edge-context.ts +51 -0
  201. package/src/effect/edge-dispatcher.ts +310 -0
  202. package/src/effect/edge.ts +1 -0
  203. package/src/effect/endpoint-contracts.ts +191 -0
  204. package/src/effect/entry-shape-registry.cjs +16 -0
  205. package/src/effect/entry-shape-registry.d.cts +6 -0
  206. package/src/effect/entry-shape.ts +155 -0
  207. package/src/effect/handler/batch-handler.ts +475 -0
  208. package/src/effect/handler/batch.ts +182 -0
  209. package/src/effect/handler/definition.ts +121 -0
  210. package/src/effect/handler/envelope.ts +133 -0
  211. package/src/effect/handler/http.ts +133 -0
  212. package/src/effect/handler/openapi.ts +45 -0
  213. package/src/effect/handler/routing.ts +91 -0
  214. package/src/effect/handler/rpc.ts +45 -0
  215. package/src/effect/handler/types.ts +255 -0
  216. package/src/effect/handler.ts +33 -0
  217. package/src/effect/index.ts +22 -0
  218. package/src/effect/module.ts +186 -0
  219. package/src/effect/operation-context.ts +144 -0
  220. package/src/effect/rpcSerialization.ts +20 -0
  221. package/src/effect-client/batch.ts +50 -0
  222. package/src/effect-client/envelope.ts +238 -0
  223. package/src/effect-client/guards.ts +6 -0
  224. package/src/effect-client/index.ts +325 -0
  225. package/src/effect-client/request-context.ts +54 -0
  226. package/src/effect-client/request.ts +51 -0
  227. package/src/effect-client/runtime.ts +122 -0
  228. package/src/effect-client/types.ts +84 -0
  229. package/src/headers.ts +7 -0
  230. package/src/index.ts +3 -0
@@ -0,0 +1,116 @@
1
+ import "node:module";
2
+ import { evaluateCrossProjectPolicy, resolveCrossProjectRequestObservation } from "@modern-js/bff-core/security/cross-project-policy";
3
+ import { toHeaderRecord } from "../headers.mjs";
4
+ import { dispatchEffectBffRequestWithContext } from "./dispatch.mjs";
5
+ import { runWithEffectContext, useEffectContext, useOperationContext } from "./edge-context.mjs";
6
+ import { resolveEffectBffModuleHandler } from "./module.mjs";
7
+ import * as __rspack_external_effect_Config_76cfa59d from "effect/Config";
8
+ import * as __rspack_external_effect_Effect_41060786 from "effect/Effect";
9
+ import * as __rspack_external_effect_Layer_69c46453 from "effect/Layer";
10
+ import * as __rspack_external_effect_Option_0498e63a from "effect/Option";
11
+ import * as __rspack_external_effect_Schema_6065f72d from "effect/Schema";
12
+ const NORMALIZED_POLICY_BOOLEAN_FIELDS = [
13
+ 'enabled',
14
+ 'requireEnvelope',
15
+ 'requireOperationContext',
16
+ 'requireOperationContextDetails',
17
+ 'requireOperationSchemaHash',
18
+ 'requireOperationVersion',
19
+ 'allowUnknownOperations'
20
+ ];
21
+ function assertNormalizedCrossProjectPolicy(policy) {
22
+ if (void 0 === policy) return;
23
+ if (!policy || 'object' != typeof policy || Array.isArray(policy)) throw new Error('[BFF][Effect] Edge cross-project policy must be a normalized object.');
24
+ for (const field of NORMALIZED_POLICY_BOOLEAN_FIELDS)if ('boolean' != typeof policy[field]) throw new Error(`[BFF][Effect] Edge cross-project policy requires boolean ${field}.`);
25
+ if (!policy.expectedOperationContracts || 'object' != typeof policy.expectedOperationContracts || Array.isArray(policy.expectedOperationContracts)) throw new Error('[BFF][Effect] Edge cross-project policy requires expectedOperationContracts object.');
26
+ }
27
+ function restoreEffectBffMountPath(request, prefix) {
28
+ const pathname = new URL(request.url).pathname;
29
+ if (!prefix || '/' === prefix) return pathname;
30
+ const normalizedPrefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
31
+ return '/' === pathname ? normalizedPrefix : `${normalizedPrefix}${pathname}`;
32
+ }
33
+ async function dispatchEffectBffRequest(handler, request, options = {}) {
34
+ return dispatchEffectBffRequestWithContext(handler, request, {
35
+ ...options,
36
+ runWithEffectContext: runWithEffectContext
37
+ });
38
+ }
39
+ async function createEffectBffEdgeDispatcher(options) {
40
+ const crossProjectPolicy = options.crossProjectPolicy;
41
+ assertNormalizedCrossProjectPolicy(crossProjectPolicy);
42
+ const loaded = await resolveEffectBffModuleHandler(options.module, {
43
+ openapi: options.openapi,
44
+ dataPlatform: options.dataPlatform,
45
+ validateRequest: crossProjectPolicy?.enabled === true ? (request)=>{
46
+ const pathname = new URL(request.url).pathname;
47
+ const observedRequest = resolveCrossProjectRequestObservation({
48
+ method: request.method,
49
+ pathname
50
+ }, crossProjectPolicy) ?? {
51
+ method: request.method,
52
+ routePath: restoreEffectBffMountPath(request, options.prefix)
53
+ };
54
+ const violation = evaluateCrossProjectPolicy(toHeaderRecord(request.headers), crossProjectPolicy, observedRequest);
55
+ if (!violation) return null;
56
+ return new Response(JSON.stringify({
57
+ code: violation.code,
58
+ reason: violation.reason,
59
+ message: violation.message
60
+ }), {
61
+ status: violation.status,
62
+ headers: {
63
+ 'content-type': 'application/json; charset=utf-8'
64
+ }
65
+ });
66
+ } : void 0,
67
+ onWarning: options.onWarning
68
+ });
69
+ if (!loaded) throw new Error('[BFF][Effect] Invalid Effect edge module. Export defineEffectBff(...) or a { api, layer } HttpApi module.');
70
+ const retiredError = new Error('[BFF][Effect] Edge dispatcher is disposing or has been disposed.');
71
+ let acceptingRequests = true;
72
+ let activeDispatches = 0;
73
+ let resolveDrain;
74
+ let disposePromise;
75
+ return {
76
+ dispatch: (request, dispatchOptions = {})=>{
77
+ if (!acceptingRequests) return Promise.reject(retiredError);
78
+ activeDispatches += 1;
79
+ return dispatchEffectBffRequest(loaded.handler, request, {
80
+ ...dispatchOptions,
81
+ prefix: options.prefix,
82
+ onError: options.onError
83
+ }).finally(()=>{
84
+ activeDispatches -= 1;
85
+ if (0 === activeDispatches) {
86
+ resolveDrain?.();
87
+ resolveDrain = void 0;
88
+ }
89
+ });
90
+ },
91
+ dispose: ()=>{
92
+ disposePromise ??= (async ()=>{
93
+ acceptingRequests = false;
94
+ if (activeDispatches > 0) await new Promise((resolve)=>{
95
+ resolveDrain = resolve;
96
+ });
97
+ await loaded.dispose?.();
98
+ })();
99
+ return disposePromise;
100
+ }
101
+ };
102
+ }
103
+ const createEffectBffTestHandler = createEffectBffEdgeHandler;
104
+ async function createEffectBffEdgeHandler(options) {
105
+ const dispatcher = await createEffectBffEdgeDispatcher(options);
106
+ return {
107
+ handler: dispatcher.dispatch,
108
+ dispose: dispatcher.dispose
109
+ };
110
+ }
111
+ export { Cookies, Etag, FetchHttpClient, FindMyWay, Headers, HttpBody, HttpClient, HttpClientError, HttpClientRequest, HttpClientResponse, HttpEffect, HttpIncomingMessage, HttpMethod, HttpMiddleware, HttpPlatform, HttpRouter, HttpServer, HttpServerError, HttpServerRequest, HttpServerRespondable, HttpServerResponse, HttpStaticServer, HttpStatus, HttpTraceContext, Multipart, MultipartParser, Template, Url, UrlParams } from "effect/unstable/http";
112
+ export { HttpApi, HttpApiBuilder, HttpApiClient, HttpApiEndpoint, HttpApiError, HttpApiGroup, HttpApiMiddleware, HttpApiSchema, HttpApiSecurity, HttpApiTest, OpenApi } from "effect/unstable/httpapi";
113
+ export { Rpc, RpcClient, RpcClientError, RpcGroup, RpcMessage, RpcMiddleware, RpcSchema, RpcSerialization, RpcServer, RpcTest, RpcWorker, Utils } from "effect/unstable/rpc";
114
+ export { createHttpApiHandler, defineEffectBff, defineEffectRpcBff } from "./handler.mjs";
115
+ export { createEffectOperationContext } from "./operation-context.mjs";
116
+ export { __rspack_external_effect_Config_76cfa59d as Config, __rspack_external_effect_Effect_41060786 as Effect, __rspack_external_effect_Layer_69c46453 as Layer, __rspack_external_effect_Option_0498e63a as Option, __rspack_external_effect_Schema_6065f72d as Schema, createEffectBffEdgeDispatcher, createEffectBffEdgeHandler, createEffectBffTestHandler, dispatchEffectBffRequest, runWithEffectContext, useEffectContext, useOperationContext };
@@ -0,0 +1,2 @@
1
+ import "node:module";
2
+ export * from "./edge-dispatcher.mjs";
@@ -0,0 +1,69 @@
1
+ import "node:module";
2
+ import { createOperationContractHash } from "@modern-js/bff-core";
3
+ import { classifyEffectBffEntryModule, isValidatorAwareHandlerFactory } from "./entry-shape.mjs";
4
+ function resolveEffectEndpointName(endpoint) {
5
+ if ('string' == typeof endpoint.identifier && endpoint.identifier) return endpoint.identifier;
6
+ if ('string' == typeof endpoint.name && endpoint.name) return endpoint.name;
7
+ return '';
8
+ }
9
+ function ensureLeadingSlash(pathname) {
10
+ return pathname.startsWith('/') ? pathname : `/${pathname}`;
11
+ }
12
+ function normalizeEffectPrefix(prefix) {
13
+ if ('/' === prefix) return '';
14
+ return ensureLeadingSlash(prefix || '/api');
15
+ }
16
+ function getEffectRoutePath(prefix, endpointPath) {
17
+ const normalizedPrefix = normalizeEffectPrefix(prefix);
18
+ const normalizedEndpointPath = ensureLeadingSlash(endpointPath);
19
+ const finalEndpointPath = '/' === normalizedEndpointPath ? '' : endpointPath;
20
+ if (!normalizedPrefix && !finalEndpointPath) return '/';
21
+ return `${normalizedPrefix}${finalEndpointPath || ''}`;
22
+ }
23
+ function resolveEffectApiId(api) {
24
+ const fallback = 'EffectHttpApi';
25
+ if ('identifier' in api && 'string' == typeof api.identifier && api.identifier) return api.identifier;
26
+ return fallback;
27
+ }
28
+ function collectEffectEndpoints(reflect, api, prefix) {
29
+ const endpoints = [];
30
+ const apiId = resolveEffectApiId(api);
31
+ reflect(api, {
32
+ onGroup: ()=>{},
33
+ onEndpoint: ({ group, endpoint })=>{
34
+ endpoints.push({
35
+ apiId,
36
+ groupName: String(group.identifier),
37
+ endpointName: resolveEffectEndpointName(endpoint),
38
+ method: String(endpoint.method).toUpperCase(),
39
+ routePath: getEffectRoutePath(prefix, String(endpoint.path))
40
+ });
41
+ }
42
+ });
43
+ return endpoints.sort((a, b)=>{
44
+ if (a.groupName === b.groupName) return a.endpointName.localeCompare(b.endpointName);
45
+ return a.groupName.localeCompare(b.groupName);
46
+ });
47
+ }
48
+ function toOperationContractSources(endpoints) {
49
+ return endpoints.map(createEffectOperationContractSource);
50
+ }
51
+ function createEffectOperationContractSource(endpoint) {
52
+ return {
53
+ name: endpoint.endpointName,
54
+ httpMethod: endpoint.method,
55
+ routePath: endpoint.routePath
56
+ };
57
+ }
58
+ async function extractHttpApiFromModule(mod, isHttpApi) {
59
+ const facts = classifyEffectBffEntryModule(mod, {
60
+ isRequestHandler: (value)=>'function' == typeof value,
61
+ isValidatorAwareHandlerFactory: isValidatorAwareHandlerFactory,
62
+ isHttpApi
63
+ });
64
+ return !facts?.legacyShape && facts?.api !== void 0 && facts.hasRuntimeLayer && (void 0 === facts.createHandler || facts.createHandlerValidatorAware) ? facts.api : null;
65
+ }
66
+ function createEffectEndpointContractHash(endpoint, requestId) {
67
+ return createOperationContractHash(createEffectOperationContractSource(endpoint), requestId);
68
+ }
69
+ export { collectEffectEndpoints, createEffectEndpointContractHash, ensureLeadingSlash, extractHttpApiFromModule, normalizeEffectPrefix, toOperationContractSources };
@@ -0,0 +1,18 @@
1
+ import "node:module";
2
+ "use strict";
3
+ import { __webpack_require__ } from "../rslib-runtime.mjs";
4
+ __webpack_require__.add({
5
+ "./src/effect/entry-shape-registry.cjs" (module) {
6
+ const validatorAwareHandlerFactories = new WeakSet();
7
+ module.exports = {
8
+ register (factory) {
9
+ validatorAwareHandlerFactories.add(factory);
10
+ return factory;
11
+ },
12
+ is (factory) {
13
+ return 'function' == typeof factory && validatorAwareHandlerFactories.has(factory);
14
+ }
15
+ };
16
+ }
17
+ });
18
+ __webpack_require__("./src/effect/entry-shape-registry.cjs");
@@ -0,0 +1,63 @@
1
+ import "node:module";
2
+ function createLocalValidatorAwareHandlerFactoryRegistry() {
3
+ const factories = new WeakSet();
4
+ return {
5
+ register (factory) {
6
+ factories.add(factory);
7
+ return factory;
8
+ },
9
+ is (factory) {
10
+ return 'function' == typeof factory && factories.has(factory);
11
+ }
12
+ };
13
+ }
14
+ function loadNodeValidatorAwareHandlerFactoryRegistry() {
15
+ const moduleUrl = import.meta.url;
16
+ if ('string' != typeof moduleUrl || !moduleUrl.startsWith('file:')) return createLocalValidatorAwareHandlerFactoryRegistry();
17
+ const moduleBuiltin = process.getBuiltinModule('node:module');
18
+ try {
19
+ return moduleBuiltin.createRequire(moduleUrl)("#effect-entry-shape-registry");
20
+ } catch (error) {
21
+ if (null !== error && 'object' == typeof error && 'code' in error && 'MODULE_NOT_FOUND' === error.code) return createLocalValidatorAwareHandlerFactoryRegistry();
22
+ throw error;
23
+ }
24
+ }
25
+ const validatorAwareHandlerFactoryRegistry = loadNodeValidatorAwareHandlerFactoryRegistry();
26
+ function registerValidatorAwareHandlerFactory(factory) {
27
+ return validatorAwareHandlerFactoryRegistry.register(factory);
28
+ }
29
+ function isValidatorAwareHandlerFactory(factory) {
30
+ return validatorAwareHandlerFactoryRegistry.is(factory);
31
+ }
32
+ const strictEffectApproachMessage = '[BFF][Effect] strictEffectApproach is enforced: Effect API entries export defineEffectBff(...) or { api, layer } HttpApi module. Raw handler exports, default request handlers, unbranded custom createHandler factories not valid Effect API entries.';
33
+ function isEffectEntryRecord(value) {
34
+ return 'object' == typeof value && null !== value;
35
+ }
36
+ function classifyEffectBffEntryModule(value, predicates) {
37
+ if (!isEffectEntryRecord(value)) return null;
38
+ const rootModule = value;
39
+ if (predicates.isRequestHandler(rootModule.handler)) return createEntryShapeFacts(rootModule, predicates, '`handler` export');
40
+ const defaultEntry = rootModule.default;
41
+ if (predicates.isRequestHandler(defaultEntry)) return createEntryShapeFacts(rootModule, predicates, 'default request handler');
42
+ const module = isEffectEntryRecord(defaultEntry) ? {
43
+ ...rootModule,
44
+ ...defaultEntry
45
+ } : rootModule;
46
+ if (predicates.isRequestHandler(module.handler)) return createEntryShapeFacts(module, predicates, '`handler` export');
47
+ return createEntryShapeFacts(module, predicates);
48
+ }
49
+ function createEntryShapeFacts(module, predicates, legacyShape) {
50
+ const createHandler = 'function' == typeof module.createHandler ? module.createHandler : void 0;
51
+ const api = predicates.isHttpApi(module.api) ? module.api : void 0;
52
+ const hasRuntimeLayer = void 0 !== module.layer;
53
+ return {
54
+ module,
55
+ legacyShape,
56
+ createHandler,
57
+ createHandlerValidatorAware: void 0 !== createHandler && predicates.isValidatorAwareHandlerFactory(createHandler),
58
+ api,
59
+ layer: module.layer,
60
+ hasRuntimeLayer
61
+ };
62
+ }
63
+ export { classifyEffectBffEntryModule, isValidatorAwareHandlerFactory, registerValidatorAwareHandlerFactory, strictEffectApproachMessage };
@@ -0,0 +1,233 @@
1
+ import "node:module";
2
+ import { DEFAULT_DATA_BATCH_HEADER, DEFAULT_DATA_ENVELOPE_HEADER, decodeRequestEnvelopeHeader, isPlainObject, normalizeMethod } from "../../data-platform/index.mjs";
3
+ import { decodeBatchBody, isBatchBody, toBatchResponseItem } from "../../data-platform/batch/protocol.mjs";
4
+ import { BatchItemTimeoutError, createBatchValidationResponse, isBatchRequestPayload, mapWithConcurrency, normalizeBatchAllowedMethods, normalizeBatchPath, promiseWithTimeout, toBatchItemError } from "./batch.mjs";
5
+ import { getMountedPrefixFromContext, removeMountedPrefixFromBatchPath } from "./routing.mjs";
6
+ const HOP_BY_HOP_BATCH_ITEM_HEADERS = new Set([
7
+ 'connection',
8
+ 'keep-alive',
9
+ 'proxy-authenticate',
10
+ 'proxy-authorization',
11
+ 'proxy-connection',
12
+ 'te',
13
+ 'trailer',
14
+ 'transfer-encoding',
15
+ 'upgrade'
16
+ ]);
17
+ const OUTER_BOUND_BATCH_ITEM_HEADERS = new Set([
18
+ 'authorization',
19
+ 'cookie',
20
+ 'forwarded',
21
+ 'origin',
22
+ 'referer',
23
+ 'sec-fetch-dest',
24
+ 'sec-fetch-mode',
25
+ 'sec-fetch-site',
26
+ 'sec-fetch-user',
27
+ 'x-forwarded-client-cert',
28
+ 'x-forwarded-for',
29
+ 'x-forwarded-host',
30
+ 'x-forwarded-port',
31
+ 'x-forwarded-proto',
32
+ 'x-real-ip',
33
+ 'x-subject-id',
34
+ 'x-tenant-id',
35
+ 'x-user-id',
36
+ 'x-verified-producer'
37
+ ]);
38
+ const TRANSPORT_CONTROLLED_BATCH_ITEM_HEADERS = new Set([
39
+ 'content-length',
40
+ 'host',
41
+ ...HOP_BY_HOP_BATCH_ITEM_HEADERS
42
+ ]);
43
+ async function readRequestTextWithinLimit(request, maxBytes) {
44
+ const contentLength = request.headers.get('content-length');
45
+ if (null !== contentLength) {
46
+ if (!/^\d+$/.test(contentLength)) return {
47
+ status: 'invalid-content-length'
48
+ };
49
+ if (BigInt(contentLength) > BigInt(maxBytes)) return {
50
+ status: 'too-large'
51
+ };
52
+ }
53
+ if (null === request.body) return {
54
+ status: 'ok',
55
+ text: ''
56
+ };
57
+ const reader = request.body.getReader();
58
+ const decoder = new TextDecoder();
59
+ const text = [];
60
+ let bytesRead = 0;
61
+ try {
62
+ while(true){
63
+ const chunk = await reader.read();
64
+ if (chunk.done) {
65
+ text.push(decoder.decode());
66
+ return {
67
+ status: 'ok',
68
+ text: text.join('')
69
+ };
70
+ }
71
+ bytesRead += chunk.value.byteLength;
72
+ if (bytesRead > maxBytes) {
73
+ await reader.cancel('batch-payload-too-large');
74
+ return {
75
+ status: 'too-large'
76
+ };
77
+ }
78
+ text.push(decoder.decode(chunk.value, {
79
+ stream: true
80
+ }));
81
+ }
82
+ } catch {
83
+ return {
84
+ status: 'read-error'
85
+ };
86
+ } finally{
87
+ reader.releaseLock();
88
+ }
89
+ }
90
+ function createDataPlatformBatchRequestHandler(options) {
91
+ const dataPlatformBatchOptions = options.dataPlatform?.batch;
92
+ const batchEnabled = dataPlatformBatchOptions?.enabled !== false;
93
+ const batchPath = normalizeBatchPath(dataPlatformBatchOptions?.endpoint);
94
+ const batchMaxSize = Math.max(1, dataPlatformBatchOptions?.maxBatchSize ?? 16);
95
+ const batchMaxBytes = Math.max(1024, dataPlatformBatchOptions?.maxBatchBytes ?? 65536);
96
+ const batchConcurrency = Math.max(1, dataPlatformBatchOptions?.maxConcurrency ?? 4);
97
+ const batchItemTimeoutMs = Math.max(0, dataPlatformBatchOptions?.requestTimeoutMs ?? 10000);
98
+ const batchAllowedMethods = normalizeBatchAllowedMethods(dataPlatformBatchOptions?.allowedMethods);
99
+ const envelopeHeader = options.dataPlatform?.envelopeHeader || DEFAULT_DATA_ENVELOPE_HEADER;
100
+ const normalizedEnvelopeHeader = envelopeHeader.toLowerCase();
101
+ const handle = async (request, context)=>{
102
+ const mountedPrefix = getMountedPrefixFromContext(request, context);
103
+ const method = normalizeMethod(request.method);
104
+ if ('POST' !== method) return createBatchValidationResponse('Batch endpoint only supports POST requests', 405);
105
+ const contentType = request.headers.get('content-type') || '';
106
+ if (!contentType.includes('application/json')) return createBatchValidationResponse('Batch endpoint requires application/json content-type', 415);
107
+ const bodyResult = await readRequestTextWithinLimit(request, batchMaxBytes);
108
+ if ('too-large' === bodyResult.status) return createBatchValidationResponse(`Batch payload exceeds max size (${String(batchMaxBytes)} bytes)`, 413);
109
+ if ('invalid-content-length' === bodyResult.status) return createBatchValidationResponse('Batch payload has invalid content-length');
110
+ if ('read-error' === bodyResult.status) return createBatchValidationResponse('Unable to read batch payload');
111
+ const payloadText = bodyResult.text;
112
+ let payload;
113
+ try {
114
+ payload = JSON.parse(payloadText);
115
+ } catch {
116
+ return createBatchValidationResponse('Invalid batch payload JSON');
117
+ }
118
+ if (!isBatchRequestPayload(payload)) return createBatchValidationResponse('Invalid batch payload shape');
119
+ if (0 === payload.items.length) return createBatchValidationResponse('Batch payload items cannot be empty');
120
+ if (payload.items.length > batchMaxSize) return createBatchValidationResponse(`Batch item count exceeds max size (${String(batchMaxSize)})`, 413);
121
+ const batchRequestOrigin = new URL(request.url).origin;
122
+ const responseItems = await mapWithConcurrency(payload.items, batchConcurrency, async (rawItem, index)=>{
123
+ const fallbackId = `item_${String(index)}`;
124
+ const itemId = isPlainObject(rawItem) && 'string' == typeof rawItem.id ? rawItem.id : fallbackId;
125
+ if (!isPlainObject(rawItem)) return toBatchItemError(itemId, 400, 'Invalid batch item; expected object');
126
+ if ('string' != typeof rawItem.path || 0 === rawItem.path.length) return toBatchItemError(itemId, 400, 'Invalid batch item path');
127
+ if (!rawItem.path.startsWith('/')) return toBatchItemError(itemId, 400, 'Batch item path must start with "/"');
128
+ const normalizedItemPath = removeMountedPrefixFromBatchPath(rawItem.path, mountedPrefix);
129
+ const itemPathname = normalizedItemPath.split('?')[0] || normalizedItemPath;
130
+ if (itemPathname === batchPath || itemPathname.startsWith(`${batchPath}/`)) return toBatchItemError(itemId, 400, 'Batch item path cannot target batch endpoint');
131
+ const itemMethod = normalizeMethod('string' == typeof rawItem.method ? rawItem.method : void 0);
132
+ if (!batchAllowedMethods.has(itemMethod)) return toBatchItemError(itemId, 405, `Batch item method ${itemMethod} is not allowed`);
133
+ if (void 0 !== rawItem.body && !isBatchBody(rawItem.body)) return toBatchItemError(itemId, 400, 'Batch item body must use the base64 body protocol');
134
+ if (('GET' === itemMethod || 'HEAD' === itemMethod) && void 0 !== rawItem.body) return toBatchItemError(itemId, 400, `${itemMethod} batch item cannot include body`);
135
+ const normalizedHeaders = {};
136
+ if (void 0 !== rawItem.headers) {
137
+ if (!isPlainObject(rawItem.headers)) return toBatchItemError(itemId, 400, 'Batch item headers must be an object');
138
+ for (const [key, value] of Object.entries(rawItem.headers)){
139
+ if ('string' != typeof value) return toBatchItemError(itemId, 400, `Invalid header "${key}" for batch item`);
140
+ normalizedHeaders[key.toLowerCase()] = value;
141
+ }
142
+ }
143
+ const connectionScopedHeaders = new Set((normalizedHeaders.connection || '').split(',').map((header)=>header.trim().toLowerCase()).filter(Boolean));
144
+ for (const header of Object.keys(normalizedHeaders))if (TRANSPORT_CONTROLLED_BATCH_ITEM_HEADERS.has(header) || OUTER_BOUND_BATCH_ITEM_HEADERS.has(header) || connectionScopedHeaders.has(header)) delete normalizedHeaders[header];
145
+ if (!normalizedHeaders.traceparent) {
146
+ const encodedEnvelope = normalizedHeaders[normalizedEnvelopeHeader];
147
+ if ('string' == typeof encodedEnvelope) {
148
+ const envelope = decodeRequestEnvelopeHeader(encodedEnvelope);
149
+ if (envelope?.traceparent) normalizedHeaders.traceparent = envelope.traceparent;
150
+ }
151
+ }
152
+ for (const header of OUTER_BOUND_BATCH_ITEM_HEADERS){
153
+ const value = request.headers.get(header);
154
+ if (null !== value) normalizedHeaders[header] = value;
155
+ }
156
+ if (!normalizedHeaders.traceparent) {
157
+ const requestTraceparent = request.headers.get('traceparent');
158
+ if (requestTraceparent) normalizedHeaders.traceparent = requestTraceparent;
159
+ }
160
+ const targetUrl = new URL(normalizedItemPath, request.url);
161
+ if (targetUrl.origin !== batchRequestOrigin) return toBatchItemError(itemId, 400, 'Batch item path must stay on the same origin');
162
+ let requestHeaders;
163
+ try {
164
+ requestHeaders = new Headers(normalizedHeaders);
165
+ } catch {
166
+ return toBatchItemError(itemId, 400, 'Invalid batch item headers');
167
+ }
168
+ const body = 'GET' === itemMethod || 'HEAD' === itemMethod ? void 0 : void 0 === rawItem.body ? void 0 : decodeBatchBody(rawItem.body);
169
+ if (void 0 === body) requestHeaders.delete('content-type');
170
+ const itemAbortController = new AbortController();
171
+ const abortFromBatchRequest = ()=>itemAbortController.abort(request.signal.reason);
172
+ if (request.signal.aborted) abortFromBatchRequest();
173
+ else request.signal.addEventListener('abort', abortFromBatchRequest, {
174
+ once: true
175
+ });
176
+ const itemRequest = new Request(targetUrl.toString(), {
177
+ method: itemMethod,
178
+ headers: requestHeaders,
179
+ body,
180
+ signal: itemAbortController.signal
181
+ });
182
+ try {
183
+ const itemResponse = await promiseWithTimeout(options.handleItem(itemRequest, context), batchItemTimeoutMs, (error)=>itemAbortController.abort(error));
184
+ if (!(itemResponse instanceof Response)) return toBatchItemError(itemId, 500, 'Invalid response returned by batch item handler');
185
+ return toBatchResponseItem(itemId, itemResponse, 'HEAD' === itemMethod);
186
+ } catch (error) {
187
+ if (error instanceof BatchItemTimeoutError) {
188
+ console.error({
189
+ event: 'bff.batch.item.timeout',
190
+ batchId: payload.batchId,
191
+ itemId,
192
+ method: itemMethod,
193
+ path: itemPathname,
194
+ errorName: error.name
195
+ });
196
+ return toBatchItemError(itemId, 504, 'Batch item request timed out');
197
+ }
198
+ if (error instanceof Response) return toBatchResponseItem(itemId, error, 'HEAD' === itemMethod);
199
+ console.error({
200
+ event: 'bff.batch.item.failure',
201
+ batchId: payload.batchId,
202
+ itemId,
203
+ method: itemMethod,
204
+ path: itemPathname,
205
+ errorName: error instanceof Error ? error.name : 'UnknownError'
206
+ });
207
+ return toBatchItemError(itemId, 500, 'Internal Server Error');
208
+ } finally{
209
+ request.signal.removeEventListener('abort', abortFromBatchRequest);
210
+ }
211
+ });
212
+ const responsePayload = {
213
+ protocolVersion: 2,
214
+ batchId: payload.batchId,
215
+ receivedAt: Date.now(),
216
+ items: responseItems
217
+ };
218
+ return new Response(JSON.stringify(responsePayload), {
219
+ status: 200,
220
+ headers: {
221
+ 'content-type': 'application/json; charset=utf-8',
222
+ [DEFAULT_DATA_BATCH_HEADER]: '2',
223
+ 'x-modernjs-data-batch-id': payload.batchId
224
+ }
225
+ });
226
+ };
227
+ return {
228
+ enabled: batchEnabled,
229
+ path: batchPath,
230
+ handle
231
+ };
232
+ }
233
+ export { createDataPlatformBatchRequestHandler };
@@ -0,0 +1,106 @@
1
+ import "node:module";
2
+ import { DEFAULT_DATA_BATCH_ENDPOINT, isPlainObject, normalizeMethod } from "../../data-platform/index.mjs";
3
+ import { encodeBatchText } from "../../data-platform/batch/protocol.mjs";
4
+ function normalizeBatchPath(pathname) {
5
+ if (!pathname || '/' === pathname) return DEFAULT_DATA_BATCH_ENDPOINT;
6
+ if (!pathname.startsWith('/')) return `/${pathname}`;
7
+ return pathname;
8
+ }
9
+ function normalizeBatchAllowedMethods(allowedMethods) {
10
+ const source = Array.isArray(allowedMethods) && allowedMethods.length > 0 ? allowedMethods : [
11
+ 'GET'
12
+ ];
13
+ return new Set(source.map((method)=>method.toUpperCase()));
14
+ }
15
+ function isBatchRequestPayload(value) {
16
+ return isPlainObject(value) && 2 === value.protocolVersion && 'string' == typeof value.batchId && 'number' == typeof value.sentAt && Array.isArray(value.items);
17
+ }
18
+ function createBatchValidationResponse(message, status = 400) {
19
+ return new Response(JSON.stringify({
20
+ message
21
+ }), {
22
+ status,
23
+ headers: {
24
+ 'content-type': 'application/json; charset=utf-8'
25
+ }
26
+ });
27
+ }
28
+ function cloneWithoutJsonBodyHeaders(request) {
29
+ const headers = new Headers(request.headers);
30
+ headers.delete('content-type');
31
+ headers.delete('content-length');
32
+ return new Request(request.url, {
33
+ method: request.method,
34
+ headers,
35
+ signal: request.signal
36
+ });
37
+ }
38
+ async function prepareJsonRequestBody(request) {
39
+ const method = normalizeMethod(request.method);
40
+ if ('GET' === method || 'HEAD' === method) return request;
41
+ const contentType = (request.headers.get('content-type') || '').toLowerCase();
42
+ if (!contentType.includes('application/json')) return request;
43
+ if (null === request.body) return cloneWithoutJsonBodyHeaders(request);
44
+ try {
45
+ const bodyText = await request.clone().text();
46
+ if ('' === bodyText) return cloneWithoutJsonBodyHeaders(request);
47
+ JSON.parse(bodyText);
48
+ } catch {
49
+ return createBatchValidationResponse('Invalid JSON request body');
50
+ }
51
+ return request;
52
+ }
53
+ function toBatchItemError(id, status, message) {
54
+ return {
55
+ id,
56
+ status,
57
+ headers: [
58
+ [
59
+ 'content-type',
60
+ 'application/json; charset=utf-8'
61
+ ]
62
+ ],
63
+ body: encodeBatchText(JSON.stringify({
64
+ message
65
+ }))
66
+ };
67
+ }
68
+ class BatchItemTimeoutError extends Error {
69
+ }
70
+ function promiseWithTimeout(effect, timeoutMs, onTimeout) {
71
+ if (timeoutMs <= 0) return effect;
72
+ return new Promise((resolve, reject)=>{
73
+ const timer = setTimeout(()=>{
74
+ const error = new BatchItemTimeoutError(`Batch item timeout after ${String(timeoutMs)}ms`);
75
+ onTimeout?.(error);
76
+ reject(error);
77
+ }, timeoutMs);
78
+ effect.then((value)=>{
79
+ clearTimeout(timer);
80
+ resolve(value);
81
+ }, (error)=>{
82
+ clearTimeout(timer);
83
+ reject(error);
84
+ });
85
+ });
86
+ }
87
+ async function mapWithConcurrency(items, concurrency, mapper) {
88
+ if (0 === items.length) return [];
89
+ const normalizedConcurrency = Math.max(1, concurrency);
90
+ const output = new Array(items.length);
91
+ let index = 0;
92
+ const workers = Array.from({
93
+ length: Math.min(normalizedConcurrency, items.length)
94
+ }, async ()=>{
95
+ while(true){
96
+ const currentIndex = index;
97
+ index += 1;
98
+ if (currentIndex >= items.length) return;
99
+ output[currentIndex] = await mapper(items[currentIndex], currentIndex);
100
+ }
101
+ });
102
+ await Promise.all(workers);
103
+ return output;
104
+ }
105
+ export { toHeaderRecord } from "../../headers.mjs";
106
+ export { BatchItemTimeoutError, createBatchValidationResponse, isBatchRequestPayload, mapWithConcurrency, normalizeBatchAllowedMethods, normalizeBatchPath, prepareJsonRequestBody, promiseWithTimeout, toBatchItemError };
@@ -0,0 +1,59 @@
1
+ import "node:module";
2
+ import { registerValidatorAwareHandlerFactory } from "../entry-shape.mjs";
3
+ import { mergeDataPlatformOptions } from "./envelope.mjs";
4
+ import { createHttpApiHandler } from "./http.mjs";
5
+ import { createRpcApiHandler } from "./rpc.mjs";
6
+ function defineEffectBff(definition) {
7
+ const createHandler = registerValidatorAwareHandlerFactory((options)=>{
8
+ const rpcDefinition = definition.rpc;
9
+ let mergedRpcOptions = rpcDefinition;
10
+ if (rpcDefinition && options?.rpc) mergedRpcOptions = {
11
+ ...rpcDefinition,
12
+ ...options.rpc
13
+ };
14
+ return createHttpApiHandler({
15
+ api: definition.api,
16
+ layer: definition.layer,
17
+ openapi: options?.openapi,
18
+ rpc: mergedRpcOptions,
19
+ dataPlatform: mergeDataPlatformOptions(definition.dataPlatform, options?.dataPlatform),
20
+ interceptRequest: definition.interceptRequest,
21
+ validateRequest: options?.validateRequest
22
+ });
23
+ });
24
+ const client = createLoaderMaterializedClientPlaceholder();
25
+ return {
26
+ ...definition,
27
+ createHandler,
28
+ client
29
+ };
30
+ }
31
+ const LOADER_CLIENT_IGNORED_KEYS = new Set([
32
+ 'then',
33
+ 'catch',
34
+ 'finally',
35
+ 'toJSON',
36
+ '$$typeof'
37
+ ]);
38
+ function createLoaderMaterializedClientPlaceholder() {
39
+ const explain = (property)=>{
40
+ throw new Error(`[BFF][Effect] client.${String(property)} is not available here: the typed client only exists when the API entry is imported through the "@api/index" transformed path (the BFF loader replaces it with generated client code). On the server, use HttpApiClient or call the Effect layer directly.`);
41
+ };
42
+ return new Proxy(Object.create(null), {
43
+ get (_target, property) {
44
+ if ('symbol' == typeof property || LOADER_CLIENT_IGNORED_KEYS.has(property)) return;
45
+ return explain(property);
46
+ }
47
+ });
48
+ }
49
+ function defineEffectRpcBff(definition) {
50
+ const createHandler = (options)=>createRpcApiHandler({
51
+ ...definition,
52
+ ...options
53
+ });
54
+ return {
55
+ ...definition,
56
+ createHandler
57
+ };
58
+ }
59
+ export { defineEffectBff, defineEffectRpcBff };