@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,255 @@
1
+ // @effect-diagnostics anyUnknownInErrorContext:off asyncFunction:off globalDate:off globalTimers:off newPromise:off strictBooleanExpressions:off
2
+ import type { FileSystem, Path } from 'effect';
3
+ import * as Context from 'effect/Context';
4
+ import type * as EffectType from 'effect/Effect';
5
+ import type * as Layer from 'effect/Layer';
6
+ import type { Etag, HttpPlatform, HttpRouter } from 'effect/unstable/http';
7
+ import type {
8
+ HttpApi,
9
+ HttpApiClient,
10
+ HttpApiGroup,
11
+ } from 'effect/unstable/httpapi';
12
+ import type { Rpc, RpcGroup } from 'effect/unstable/rpc';
13
+
14
+ import type { createHttpApiHandler } from './http';
15
+ import type { createRpcApiHandler } from './rpc';
16
+
17
+ export type EffectRuntimeRequirements =
18
+ | Etag.Generator
19
+ | FileSystem.FileSystem
20
+ | HttpPlatform.HttpPlatform
21
+ | HttpRouter.HttpRouter
22
+ | HttpRouter.Request<'Error', unknown>
23
+ | HttpRouter.Request<'GlobalError', unknown>
24
+ | HttpRouter.Request<'GlobalRequires', unknown>
25
+ | HttpRouter.Request<'Requires', unknown>
26
+ | Path.Path;
27
+ export type EffectRuntimeLayer = Layer.Layer<
28
+ never,
29
+ never,
30
+ EffectRuntimeRequirements
31
+ >;
32
+ const emptyEffectServiceContext = Context.empty() as Context.Context<any>;
33
+
34
+ const isEffectServiceContext = (
35
+ context: unknown,
36
+ ): context is Context.Context<any> =>
37
+ typeof context === 'object' && context !== null && 'mapUnsafe' in context;
38
+
39
+ export const toEffectServiceContext = (context: unknown) =>
40
+ isEffectServiceContext(context) ? context : emptyEffectServiceContext;
41
+
42
+ export type EffectRpcSerialization =
43
+ | 'json'
44
+ | 'ndjson'
45
+ | 'jsonRpc'
46
+ | 'ndJsonRpc'
47
+ | 'msgPack';
48
+
49
+ export type EffectRpcRuntimeLayer<TRpcs extends Rpc.Any = Rpc.Any> =
50
+ Layer.Layer<
51
+ Rpc.ToHandler<TRpcs> | Rpc.Middleware<TRpcs> | Rpc.ServicesServer<TRpcs>,
52
+ unknown,
53
+ never
54
+ >;
55
+
56
+ export type EffectRpcBffDefinition<
57
+ TRpcs extends Rpc.Any = Rpc.Any,
58
+ TRpcLayer extends EffectRpcRuntimeLayer<TRpcs> = EffectRpcRuntimeLayer<TRpcs>,
59
+ > = {
60
+ group: RpcGroup.RpcGroup<TRpcs>;
61
+ layer: TRpcLayer;
62
+ path?: `/${string}`;
63
+ serialization?: EffectRpcSerialization;
64
+ disableTracing?: boolean;
65
+ spanPrefix?: string;
66
+ spanAttributes?: Record<string, unknown>;
67
+ disableFatalDefects?: boolean;
68
+ };
69
+
70
+ export type EffectRpcBffHandlerOptions = Pick<
71
+ EffectRpcBffDefinition,
72
+ | 'path'
73
+ | 'serialization'
74
+ | 'disableTracing'
75
+ | 'spanPrefix'
76
+ | 'spanAttributes'
77
+ | 'disableFatalDefects'
78
+ >;
79
+
80
+ type EffectApiPromiseClient<TClient> = {
81
+ [GroupName in keyof TClient]: {
82
+ [EndpointName in keyof TClient[GroupName]]: TClient[GroupName][EndpointName] extends (
83
+ ...args: infer TArgs
84
+ ) => infer TResult
85
+ ? TResult extends EffectType.Effect<unknown, unknown, unknown>
86
+ ? (
87
+ ...args: TArgs
88
+ ) => Promise<
89
+ Exclude<EffectType.Success<TResult>, readonly [unknown, unknown]>
90
+ >
91
+ : never
92
+ : never;
93
+ };
94
+ };
95
+
96
+ export type EffectApiClientFromApi<
97
+ TApi extends HttpApi.Constraint = HttpApi.Top,
98
+ > =
99
+ TApi extends HttpApi.HttpApi<infer _ApiId, infer Groups>
100
+ ? HttpApiClient.Client<
101
+ Extract<Groups, HttpApiGroup.Constraint>,
102
+ never,
103
+ never
104
+ >
105
+ : never;
106
+
107
+ export type EffectApiPromiseClientFromApi<
108
+ TApi extends HttpApi.Constraint = HttpApi.Top,
109
+ > = EffectApiPromiseClient<EffectApiClientFromApi<TApi>>;
110
+
111
+ export type EffectBffDefinition<
112
+ TApi extends HttpApi.Constraint = HttpApi.Top,
113
+ TLayer extends EffectRuntimeLayer = EffectRuntimeLayer,
114
+ TRpcs extends Rpc.Any = Rpc.Any,
115
+ > = {
116
+ api: TApi;
117
+ layer: TLayer;
118
+ rpc?: EffectRpcBffDefinition<TRpcs>;
119
+ dataPlatform?: EffectDataPlatformValidationOptions;
120
+ interceptRequest?: EffectRequestInterceptor;
121
+ };
122
+
123
+ export type EffectDataPlatformSelectionValidationOptions = {
124
+ maxDepth?: number;
125
+ maxFields?: number;
126
+ allowedLeafPaths?: string[];
127
+ };
128
+
129
+ export type EffectDataPlatformBatchOptions = {
130
+ /**
131
+ * Enable network batching endpoint for Effect HttpApi requests.
132
+ * Defaults to `true`.
133
+ */
134
+ enabled?: boolean;
135
+ /**
136
+ * Batch endpoint path mounted under BFF prefix.
137
+ * Defaults to `/_data/batch`.
138
+ */
139
+ endpoint?: `/${string}`;
140
+ /**
141
+ * Maximum request items accepted per batch call.
142
+ * Defaults to `16`.
143
+ */
144
+ maxBatchSize?: number;
145
+ /**
146
+ * Maximum serialized request payload size in bytes.
147
+ * Defaults to `65536` (64KiB).
148
+ */
149
+ maxBatchBytes?: number;
150
+ /**
151
+ * Client-side micro-batch flush window in milliseconds.
152
+ * Server runtime ignores this value and passes it through for codegen.
153
+ * Defaults to `8`.
154
+ */
155
+ flushIntervalMs?: number;
156
+ /**
157
+ * Maximum per-batch internal request concurrency.
158
+ * Defaults to `4`.
159
+ */
160
+ maxConcurrency?: number;
161
+ /**
162
+ * Optional timeout per internal request in milliseconds.
163
+ * Defaults to `10000`.
164
+ */
165
+ requestTimeoutMs?: number;
166
+ /**
167
+ * Allowed HTTP methods for internal batched dispatch.
168
+ * Defaults to `['GET']`.
169
+ */
170
+ allowedMethods?: string[];
171
+ };
172
+
173
+ export type EffectDataPlatformValidationOptions = {
174
+ /**
175
+ * Enable envelope validation for HttpApi requests.
176
+ * Defaults to `true`.
177
+ */
178
+ enabled?: boolean;
179
+ /**
180
+ * Require every HttpApi request to include an envelope header.
181
+ * Defaults to `false`.
182
+ */
183
+ requireEnvelope?: boolean;
184
+ /**
185
+ * Header name used by client/server for the serialized request envelope.
186
+ * Defaults to `x-modernjs-data-envelope`.
187
+ */
188
+ envelopeHeader?: string;
189
+ /**
190
+ * Optional namespace assertion for envelope validation.
191
+ */
192
+ expectedNamespace?: string;
193
+ /**
194
+ * Validate envelope origin against incoming request origin.
195
+ * Defaults to `true`.
196
+ */
197
+ validateOrigin?: boolean;
198
+ /**
199
+ * Require trace context inside request envelope.
200
+ * Defaults to `false`.
201
+ */
202
+ requireTraceContext?: boolean;
203
+ /**
204
+ * Selection plan guardrails for server-side validation.
205
+ */
206
+ selection?: EffectDataPlatformSelectionValidationOptions;
207
+ /**
208
+ * Network batching gateway configuration.
209
+ */
210
+ batch?: EffectDataPlatformBatchOptions;
211
+ };
212
+
213
+ /**
214
+ * Server-seam request validator applied to every HttpApi request — direct,
215
+ * batched (per item) and rpc. Returning a Response short-circuits the
216
+ * request; returning `null`/`undefined` lets it through. Used by the effect
217
+ * adapter to enforce the cross-project policy at one seam so batched items
218
+ * cannot bypass it.
219
+ */
220
+ export type EffectRequestValidator = (
221
+ request: Request,
222
+ ) => Response | null | undefined;
223
+
224
+ export type EffectRequestInterceptor = (options: {
225
+ request: Request;
226
+ next: () => Promise<Response>;
227
+ }) => Response | Promise<Response>;
228
+
229
+ export type EffectBffHandlerFactory<
230
+ TApi extends HttpApi.Constraint = HttpApi.Top,
231
+ TLayer extends EffectRuntimeLayer = EffectRuntimeLayer,
232
+ > = (options?: {
233
+ openapi?: EffectBffOpenApiConfig;
234
+ rpc?: Partial<EffectRpcBffHandlerOptions>;
235
+ dataPlatform?: Partial<EffectDataPlatformValidationOptions>;
236
+ validateRequest?: EffectRequestValidator;
237
+ }) => ReturnType<typeof createHttpApiHandler>;
238
+
239
+ export type EffectBffRuntime<
240
+ TApi extends HttpApi.Constraint = HttpApi.Top,
241
+ TLayer extends EffectRuntimeLayer = EffectRuntimeLayer,
242
+ > = {
243
+ createHandler: EffectBffHandlerFactory<TApi, TLayer>;
244
+ client: EffectApiPromiseClientFromApi<TApi>;
245
+ };
246
+
247
+ export type EffectBffOpenApiConfig =
248
+ | boolean
249
+ | {
250
+ path?: string;
251
+ };
252
+
253
+ export type EffectRpcBffHandlerFactory<TRpcs extends Rpc.Any = Rpc.Any> = (
254
+ options?: Partial<EffectRpcBffHandlerOptions>,
255
+ ) => ReturnType<typeof createRpcApiHandler<TRpcs>>;
@@ -0,0 +1,33 @@
1
+ // @effect-diagnostics anyUnknownInErrorContext:off asyncFunction:off globalDate:off globalTimers:off newPromise:off strictBooleanExpressions:off
2
+ export * as Config from 'effect/Config';
3
+ export * as Effect from 'effect/Effect';
4
+ export * as Layer from 'effect/Layer';
5
+ export * as Option from 'effect/Option';
6
+ export * as Schema from 'effect/Schema';
7
+ export * from 'effect/unstable/http';
8
+ export { HttpTraceContext } from 'effect/unstable/http';
9
+ export * from 'effect/unstable/httpapi';
10
+ export { HttpApiBuilder } from 'effect/unstable/httpapi';
11
+ export * from 'effect/unstable/rpc';
12
+
13
+ export { defineEffectBff, defineEffectRpcBff } from './handler/definition';
14
+ export { createHttpApiHandler } from './handler/http';
15
+ export type {
16
+ EffectApiClientFromApi,
17
+ EffectApiPromiseClientFromApi,
18
+ EffectBffDefinition,
19
+ EffectBffHandlerFactory,
20
+ EffectBffOpenApiConfig,
21
+ EffectBffRuntime,
22
+ EffectDataPlatformBatchOptions,
23
+ EffectDataPlatformSelectionValidationOptions,
24
+ EffectDataPlatformValidationOptions,
25
+ EffectRequestValidator,
26
+ EffectRpcBffDefinition,
27
+ EffectRpcBffHandlerFactory,
28
+ EffectRpcBffHandlerOptions,
29
+ EffectRpcRuntimeLayer,
30
+ EffectRpcSerialization,
31
+ EffectRuntimeLayer,
32
+ EffectRuntimeRequirements,
33
+ } from './handler/types';
@@ -0,0 +1,22 @@
1
+ export * as OpenTelemetry from '@effect/opentelemetry';
2
+ export {
3
+ type CreateEffectOperationContextOptions,
4
+ createEffectOperationContext,
5
+ type EffectContext,
6
+ runWithEffectContext,
7
+ useEffectContext,
8
+ useOperationContext,
9
+ } from './context';
10
+ export {
11
+ createEffectBffEdgeHandler,
12
+ createEffectBffTestHandler,
13
+ dispatchEffectBffRequest,
14
+ } from './edge';
15
+ export * from './endpoint-contracts';
16
+ export * from './handler';
17
+ export {
18
+ type EffectApiModule,
19
+ type EffectBffHandlerFactory,
20
+ type EffectBffRequestHandler,
21
+ resolveEffectBffModuleHandler,
22
+ } from './module';
@@ -0,0 +1,186 @@
1
+ // @effect-diagnostics strictBooleanExpressions:off
2
+ import type * as EffectServiceContext from 'effect/Context';
3
+ import { HttpApi } from 'effect/unstable/httpapi';
4
+ import {
5
+ classifyEffectBffEntryModule,
6
+ type EffectBffEntryShapeFacts,
7
+ isValidatorAwareHandlerFactory,
8
+ strictEffectApproachMessage,
9
+ } from './entry-shape';
10
+ import {
11
+ createHttpApiHandler,
12
+ type EffectBffOpenApiConfig,
13
+ type EffectDataPlatformValidationOptions,
14
+ type EffectRequestValidator,
15
+ type EffectRuntimeLayer,
16
+ } from './handler';
17
+ import type { EffectContext } from './operation-context';
18
+
19
+ export type EffectBffRequestHandler = (
20
+ request: Request,
21
+ context?: EffectServiceContext.Context<any> | EffectContext,
22
+ ) => Promise<Response> | Response;
23
+
24
+ export type EffectBffHandlerFactory = (options?: {
25
+ openapi?: EffectBffOpenApiConfig;
26
+ dataPlatform?: EffectDataPlatformValidationOptions;
27
+ validateRequest?: EffectRequestValidator;
28
+ }) => {
29
+ handler: EffectBffRequestHandler;
30
+ dispose: () => Promise<void>;
31
+ };
32
+
33
+ export type EffectApiModule = {
34
+ api?: unknown;
35
+ layer?: unknown;
36
+ handler?: EffectBffRequestHandler;
37
+ createHandler?: EffectBffHandlerFactory;
38
+ default?: unknown;
39
+ };
40
+
41
+ type LoadedEffectBffHandler = {
42
+ handler: EffectBffRequestHandler;
43
+ dispose?: () => Promise<void>;
44
+ };
45
+
46
+ type ResolveEffectBffModuleHandlerOptions = {
47
+ openapi?: EffectBffOpenApiConfig;
48
+ dataPlatform?: EffectDataPlatformValidationOptions;
49
+ /**
50
+ * Server-seam request validator (cross-project policy). Strict Effect
51
+ * entries forward it into `createHttpApiHandler`, where it applies to
52
+ * direct, batched and rpc requests.
53
+ */
54
+ validateRequest?: EffectRequestValidator;
55
+ onWarning?: (message: string) => void;
56
+ };
57
+
58
+ function isRequestHandler(value: unknown): value is EffectBffRequestHandler {
59
+ return typeof value === 'function';
60
+ }
61
+
62
+ function shareDispose(dispose: () => Promise<void>) {
63
+ let disposePromise: Promise<void> | undefined;
64
+ return () => {
65
+ disposePromise ??= Promise.resolve().then(dispose);
66
+ return disposePromise;
67
+ };
68
+ }
69
+
70
+ function rejectLegacyEffectModuleShape(
71
+ options: ResolveEffectBffModuleHandlerOptions,
72
+ shape: string,
73
+ ) {
74
+ options.onWarning?.(`${strictEffectApproachMessage} Rejected ${shape}.`);
75
+ return true;
76
+ }
77
+
78
+ function callEffectBffRequestHandler(
79
+ handler: EffectBffRequestHandler,
80
+ request: Request,
81
+ context: Parameters<EffectBffRequestHandler>[1],
82
+ ) {
83
+ return context === undefined ? handler(request) : handler(request, context);
84
+ }
85
+
86
+ function createLoadedHandler(webHandler: {
87
+ handler: EffectBffRequestHandler;
88
+ dispose: () => Promise<void>;
89
+ }): LoadedEffectBffHandler {
90
+ return {
91
+ handler: (request, context) =>
92
+ callEffectBffRequestHandler(webHandler.handler, request, context),
93
+ dispose: shareDispose(webHandler.dispose),
94
+ };
95
+ }
96
+
97
+ function createLoadedHttpApiHandler(
98
+ webHandler: ReturnType<typeof createHttpApiHandler>,
99
+ ): LoadedEffectBffHandler {
100
+ return createLoadedHandler({
101
+ handler: webHandler.handler as EffectBffRequestHandler,
102
+ dispose: webHandler.dispose,
103
+ });
104
+ }
105
+
106
+ function classifyEffectBffRuntimeEntryShape(module: EffectApiModule) {
107
+ return classifyEffectBffEntryModule(module, {
108
+ isRequestHandler,
109
+ isValidatorAwareHandlerFactory,
110
+ isHttpApi: HttpApi.isHttpApi,
111
+ });
112
+ }
113
+
114
+ function resolveClassifiedEffectBffModuleHandler(
115
+ facts: EffectBffEntryShapeFacts | null,
116
+ options: ResolveEffectBffModuleHandlerOptions = {},
117
+ ): LoadedEffectBffHandler | null {
118
+ if (!facts) {
119
+ return null;
120
+ }
121
+
122
+ if (facts.legacyShape) {
123
+ if (rejectLegacyEffectModuleShape(options, facts.legacyShape)) {
124
+ return null;
125
+ }
126
+ }
127
+
128
+ if (typeof facts.createHandler === 'function') {
129
+ if (facts.createHandlerValidatorAware) {
130
+ const factory = facts.createHandler as EffectBffHandlerFactory;
131
+ const webHandler = factory({
132
+ openapi: options.openapi,
133
+ dataPlatform: options.dataPlatform,
134
+ validateRequest: options.validateRequest,
135
+ });
136
+ return createLoadedHandler(webHandler);
137
+ }
138
+
139
+ if (facts.api === undefined || !facts.hasRuntimeLayer) {
140
+ rejectLegacyEffectModuleShape(
141
+ options,
142
+ 'unbranded `createHandler` export',
143
+ );
144
+ return null;
145
+ }
146
+
147
+ options.onWarning?.(
148
+ `${strictEffectApproachMessage} Ignored unbranded \`createHandler\` export and rebuilt the handler from its { api, layer } exports.`,
149
+ );
150
+ }
151
+
152
+ if (facts.api !== undefined && facts.hasRuntimeLayer) {
153
+ if (facts.createHandler === undefined) {
154
+ options.onWarning?.(
155
+ '[BFF][Effect] Detected { api, layer } export without createHandler. Prefer `defineEffectBff(...)` from @modern-js/bff-effect/effect to avoid module instance mismatch.',
156
+ );
157
+ }
158
+ const webHandler = createHttpApiHandler({
159
+ api: facts.api as HttpApi.Top,
160
+ layer: facts.layer as EffectRuntimeLayer,
161
+ openapi: options.openapi,
162
+ dataPlatform: options.dataPlatform,
163
+ validateRequest: options.validateRequest,
164
+ });
165
+ return createLoadedHttpApiHandler(webHandler);
166
+ }
167
+
168
+ return null;
169
+ }
170
+
171
+ function resolveNormalizedEffectBffModuleHandler(
172
+ normalizedModule: EffectApiModule,
173
+ options: ResolveEffectBffModuleHandlerOptions = {},
174
+ ): LoadedEffectBffHandler | null {
175
+ return resolveClassifiedEffectBffModuleHandler(
176
+ classifyEffectBffRuntimeEntryShape(normalizedModule),
177
+ options,
178
+ );
179
+ }
180
+
181
+ export function resolveEffectBffModuleHandler(
182
+ mod: EffectApiModule,
183
+ options: ResolveEffectBffModuleHandlerOptions = {},
184
+ ): Promise<LoadedEffectBffHandler | null> {
185
+ return Promise.resolve(resolveNormalizedEffectBffModuleHandler(mod, options));
186
+ }
@@ -0,0 +1,144 @@
1
+ // @effect-diagnostics strictBooleanExpressions:off
2
+ import {
3
+ BFF_LOCALE_HEADER,
4
+ BFF_OPERATION_CONTEXT_DETAIL_HEADER,
5
+ BFF_OPERATION_CONTEXT_HEADER,
6
+ BFF_TRACEPARENT_HEADER,
7
+ type OperationContext,
8
+ parseTraceparent,
9
+ } from '@modern-js/create-request';
10
+
11
+ export type EffectContext = {
12
+ request: Request;
13
+ env: Record<string, unknown>;
14
+ path: string;
15
+ method: string;
16
+ operationContext: OperationContext;
17
+ };
18
+
19
+ export type EffectContextStorage = {
20
+ getStore: () => EffectContext | undefined;
21
+ run: <TResult>(value: EffectContext, cb: () => TResult) => TResult;
22
+ };
23
+
24
+ export const kEffectContextStorage = Symbol.for(
25
+ 'modernjs.plugin-bff.effectContextStorage',
26
+ );
27
+
28
+ export type CreateEffectOperationContextOptions = Omit<
29
+ EffectContext,
30
+ 'operationContext'
31
+ >;
32
+
33
+ type OperationContextStringField =
34
+ | 'requestId'
35
+ | 'operationId'
36
+ | 'schemaHash'
37
+ | 'traceparent'
38
+ | 'traceId'
39
+ | 'spanId';
40
+
41
+ const readHeader = (headers: Headers, header: string) => {
42
+ const value = headers.get(header);
43
+ return value && value.length > 0 ? value : undefined;
44
+ };
45
+
46
+ const copyStringField = (
47
+ target: Partial<OperationContext>,
48
+ details: Record<string, unknown>,
49
+ key: OperationContextStringField,
50
+ ) => {
51
+ const value = details[key];
52
+ if (typeof value === 'string' && value.length > 0) {
53
+ target[key] = value;
54
+ }
55
+ };
56
+
57
+ const readOperationContextDetails = (
58
+ request: Request,
59
+ ): Partial<OperationContext> => {
60
+ const rawDetails = readHeader(
61
+ request.headers,
62
+ BFF_OPERATION_CONTEXT_DETAIL_HEADER,
63
+ );
64
+ if (!rawDetails) {
65
+ return {};
66
+ }
67
+
68
+ try {
69
+ const parsed = JSON.parse(rawDetails);
70
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
71
+ return {};
72
+ }
73
+ const details = parsed as Record<string, unknown>;
74
+ const safeDetails: Partial<OperationContext> = {};
75
+ copyStringField(safeDetails, details, 'requestId');
76
+ copyStringField(safeDetails, details, 'operationId');
77
+ copyStringField(safeDetails, details, 'schemaHash');
78
+ copyStringField(safeDetails, details, 'traceparent');
79
+ copyStringField(safeDetails, details, 'traceId');
80
+ copyStringField(safeDetails, details, 'spanId');
81
+ if (typeof details.operationVersion === 'number') {
82
+ safeDetails.operationVersion = details.operationVersion;
83
+ }
84
+ return safeDetails;
85
+ } catch {
86
+ return {};
87
+ }
88
+ };
89
+
90
+ export const createEffectOperationContext = ({
91
+ request,
92
+ path,
93
+ method,
94
+ }: CreateEffectOperationContextOptions): OperationContext => {
95
+ const details = readOperationContextDetails(request);
96
+ const operationDetails = { ...details };
97
+ delete operationDetails.traceparent;
98
+ delete operationDetails.traceId;
99
+ delete operationDetails.spanId;
100
+ const servicePath = new URL(request.url).pathname;
101
+ const headerTraceparent = readHeader(request.headers, BFF_TRACEPARENT_HEADER);
102
+ const headerTraceContext = parseTraceparent(headerTraceparent);
103
+ const detailTraceContext = parseTraceparent(details.traceparent);
104
+ const traceparent = headerTraceContext
105
+ ? headerTraceparent
106
+ : detailTraceContext
107
+ ? details.traceparent
108
+ : undefined;
109
+ const parsedTraceparent =
110
+ headerTraceContext ||
111
+ detailTraceContext ||
112
+ (details.traceId && details.spanId
113
+ ? {
114
+ traceId: details.traceId,
115
+ spanId: details.spanId,
116
+ }
117
+ : undefined);
118
+ const locale = readHeader(request.headers, BFF_LOCALE_HEADER);
119
+ const headerOperationId = readHeader(
120
+ request.headers,
121
+ BFF_OPERATION_CONTEXT_HEADER,
122
+ );
123
+
124
+ return {
125
+ ...operationDetails,
126
+ ...(headerOperationId || details.operationId
127
+ ? { operationId: headerOperationId || details.operationId }
128
+ : {}),
129
+ routePath: servicePath,
130
+ method: (method || request.method || 'GET').toUpperCase(),
131
+ source: 'effect-adapter',
132
+ ...(path && path !== servicePath
133
+ ? { attributes: { mountedPath: path } }
134
+ : {}),
135
+ ...(locale ? { locale } : {}),
136
+ ...(traceparent ? { traceparent } : {}),
137
+ ...(parsedTraceparent
138
+ ? {
139
+ traceId: parsedTraceparent.traceId,
140
+ spanId: parsedTraceparent.spanId,
141
+ }
142
+ : {}),
143
+ };
144
+ };
@@ -0,0 +1,20 @@
1
+ import { RpcSerialization } from 'effect/unstable/rpc';
2
+
3
+ import type { EffectRpcSerialization } from './handler/types';
4
+
5
+ export function getRpcSerializationLayer(
6
+ serialization: EffectRpcSerialization | undefined,
7
+ ) {
8
+ switch (serialization) {
9
+ case 'ndjson':
10
+ return RpcSerialization.layerNdjson;
11
+ case 'jsonRpc':
12
+ return RpcSerialization.layerJsonRpc();
13
+ case 'ndJsonRpc':
14
+ return RpcSerialization.layerNdJsonRpc();
15
+ case 'msgPack':
16
+ return RpcSerialization.layerMsgPack;
17
+ default:
18
+ return RpcSerialization.layerJsonRpc();
19
+ }
20
+ }