@bleedingdev/modern-js-bff-effect 3.9.0-ultramodern.4 → 3.9.0-ultramodern.6

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 (115) hide show
  1. package/README.md +20 -4
  2. package/dist/cjs/{effect-client/guards.js → assembly.js} +15 -9
  3. package/dist/cjs/data-platform/trace.js +2 -2
  4. package/dist/cjs/effect/edge-dispatcher.js +7 -7
  5. package/dist/cjs/effect/edge.js +15 -2
  6. package/dist/cjs/effect/endpoint-contracts.js +3 -4
  7. package/dist/cjs/effect/entry-shape.js +2 -2
  8. package/dist/cjs/effect/generated-entry.js +57 -0
  9. package/dist/cjs/effect/handler/batch-handler.js +5 -4
  10. package/dist/cjs/effect/handler/batch.js +8 -1
  11. package/dist/cjs/effect/handler/definition.js +1 -21
  12. package/dist/cjs/effect/module.js +4 -4
  13. package/dist/cjs/effect/operation-context.js +7 -7
  14. package/dist/cjs/effect-client/cross-project.js +92 -0
  15. package/dist/cjs/effect-client/index.js +5 -3
  16. package/dist/cjs/microvertical-api.js +120 -0
  17. package/dist/esm/assembly.mjs +12 -0
  18. package/dist/esm/data-platform/trace.mjs +1 -1
  19. package/dist/esm/effect/edge-dispatcher.mjs +5 -5
  20. package/dist/esm/effect/edge.mjs +1 -0
  21. package/dist/esm/effect/endpoint-contracts.mjs +2 -3
  22. package/dist/esm/effect/entry-shape.mjs +2 -2
  23. package/dist/esm/effect/generated-entry.mjs +19 -0
  24. package/dist/esm/effect/handler/batch-handler.mjs +5 -4
  25. package/dist/esm/effect/handler/batch.mjs +8 -1
  26. package/dist/esm/effect/handler/definition.mjs +1 -21
  27. package/dist/esm/effect/module.mjs +4 -4
  28. package/dist/esm/effect/operation-context.mjs +1 -1
  29. package/dist/esm/effect-client/cross-project.mjs +54 -0
  30. package/dist/esm/effect-client/index.mjs +4 -2
  31. package/dist/esm/microvertical-api.mjs +67 -0
  32. package/dist/esm-node/assembly.mjs +13 -0
  33. package/dist/esm-node/data-platform/trace.mjs +1 -1
  34. package/dist/esm-node/effect/edge-dispatcher.mjs +5 -5
  35. package/dist/esm-node/effect/edge.mjs +1 -0
  36. package/dist/esm-node/effect/endpoint-contracts.mjs +2 -3
  37. package/dist/esm-node/effect/entry-shape.mjs +2 -2
  38. package/dist/esm-node/effect/generated-entry.mjs +20 -0
  39. package/dist/esm-node/effect/handler/batch-handler.mjs +5 -4
  40. package/dist/esm-node/effect/handler/batch.mjs +8 -1
  41. package/dist/esm-node/effect/handler/definition.mjs +1 -21
  42. package/dist/esm-node/effect/module.mjs +4 -4
  43. package/dist/esm-node/effect/operation-context.mjs +1 -1
  44. package/dist/esm-node/effect-client/cross-project.mjs +55 -0
  45. package/dist/esm-node/effect-client/index.mjs +4 -2
  46. package/dist/esm-node/microvertical-api.mjs +68 -0
  47. package/dist/types/assembly.d.ts +10 -0
  48. package/dist/types/effect/context.d.ts +1 -1
  49. package/dist/types/effect/edge-context.d.ts +1 -1
  50. package/dist/types/effect/edge-dispatcher.d.ts +2 -2
  51. package/dist/types/effect/edge.d.ts +1 -0
  52. package/dist/types/effect/endpoint-contracts.d.ts +2 -20
  53. package/dist/types/effect/entry-shape.d.ts +2 -2
  54. package/dist/types/effect/generated-entry.d.ts +7 -0
  55. package/dist/types/effect/handler/batch.d.ts +7 -1
  56. package/dist/types/effect/handler/types.d.ts +0 -15
  57. package/dist/types/effect/handler.d.ts +1 -1
  58. package/dist/types/effect/operation-context.d.ts +1 -1
  59. package/dist/types/effect-client/cross-project.d.ts +10 -0
  60. package/dist/types/effect-client/index.d.ts +4 -1
  61. package/dist/types/microvertical-api.d.ts +65 -0
  62. package/package.json +26 -13
  63. package/src/assembly.ts +43 -0
  64. package/src/data-platform/batch/queue.ts +2 -3
  65. package/src/data-platform/trace.ts +1 -1
  66. package/src/effect/edge-dispatcher.ts +7 -5
  67. package/src/effect/edge.ts +1 -0
  68. package/src/effect/endpoint-contracts.ts +3 -24
  69. package/src/effect/entry-shape.ts +10 -5
  70. package/src/effect/generated-entry.ts +36 -0
  71. package/src/effect/handler/batch-handler.ts +21 -16
  72. package/src/effect/handler/batch.ts +9 -1
  73. package/src/effect/handler/definition.ts +0 -44
  74. package/src/effect/handler/http.ts +2 -2
  75. package/src/effect/handler/types.ts +0 -27
  76. package/src/effect/handler.ts +0 -1
  77. package/src/effect/module.ts +4 -4
  78. package/src/effect/operation-context.ts +1 -1
  79. package/src/effect-client/cross-project.ts +109 -0
  80. package/src/effect-client/index.ts +13 -3
  81. package/src/microvertical-api.ts +102 -0
  82. package/dist/cjs/effect-client/batch.js +0 -73
  83. package/dist/cjs/effect-client/envelope.js +0 -145
  84. package/dist/cjs/effect-client/request-context.js +0 -64
  85. package/dist/cjs/effect-client/request.js +0 -66
  86. package/dist/cjs/effect-client/runtime.js +0 -100
  87. package/dist/cjs/effect-client/types.js +0 -18
  88. package/dist/esm/effect-client/batch.mjs +0 -35
  89. package/dist/esm/effect-client/envelope.mjs +0 -104
  90. package/dist/esm/effect-client/guards.mjs +0 -3
  91. package/dist/esm/effect-client/request-context.mjs +0 -23
  92. package/dist/esm/effect-client/request.mjs +0 -28
  93. package/dist/esm/effect-client/runtime.mjs +0 -62
  94. package/dist/esm/effect-client/types.mjs +0 -0
  95. package/dist/esm-node/effect-client/batch.mjs +0 -36
  96. package/dist/esm-node/effect-client/envelope.mjs +0 -105
  97. package/dist/esm-node/effect-client/guards.mjs +0 -4
  98. package/dist/esm-node/effect-client/request-context.mjs +0 -24
  99. package/dist/esm-node/effect-client/request.mjs +0 -29
  100. package/dist/esm-node/effect-client/runtime.mjs +0 -63
  101. package/dist/esm-node/effect-client/types.mjs +0 -1
  102. package/dist/types/effect-client/batch.d.ts +0 -2
  103. package/dist/types/effect-client/envelope.d.ts +0 -15
  104. package/dist/types/effect-client/guards.d.ts +0 -2
  105. package/dist/types/effect-client/request-context.d.ts +0 -3
  106. package/dist/types/effect-client/request.d.ts +0 -1
  107. package/dist/types/effect-client/runtime.d.ts +0 -5
  108. package/dist/types/effect-client/types.d.ts +0 -68
  109. package/src/effect-client/batch.ts +0 -50
  110. package/src/effect-client/envelope.ts +0 -238
  111. package/src/effect-client/guards.ts +0 -6
  112. package/src/effect-client/request-context.ts +0 -54
  113. package/src/effect-client/request.ts +0 -51
  114. package/src/effect-client/runtime.ts +0 -122
  115. package/src/effect-client/types.ts +0 -84
package/package.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "modern.js",
19
19
  "ultramodern.js"
20
20
  ],
21
- "version": "3.9.0-ultramodern.4",
21
+ "version": "3.9.0-ultramodern.6",
22
22
  "files": [
23
23
  "dist",
24
24
  "src"
@@ -67,14 +67,14 @@
67
67
  "import": "./dist/esm/effect-client/index.mjs",
68
68
  "default": "./dist/esm/effect-client/index.mjs"
69
69
  },
70
- "./effect-client-runtime": {
71
- "types": "./dist/types/effect-client/runtime.d.ts",
70
+ "./microvertical-api": {
71
+ "types": "./dist/types/microvertical-api.d.ts",
72
72
  "node": {
73
- "import": "./dist/esm-node/effect-client/runtime.mjs",
74
- "require": "./dist/cjs/effect-client/runtime.js"
73
+ "import": "./dist/esm-node/microvertical-api.mjs",
74
+ "require": "./dist/cjs/microvertical-api.js"
75
75
  },
76
- "import": "./dist/esm/effect-client/runtime.mjs",
77
- "default": "./dist/esm/effect-client/runtime.mjs"
76
+ "import": "./dist/esm/microvertical-api.mjs",
77
+ "default": "./dist/esm/microvertical-api.mjs"
78
78
  },
79
79
  "./data-platform": {
80
80
  "types": "./dist/types/data-platform/index.d.ts",
@@ -84,6 +84,14 @@
84
84
  },
85
85
  "import": "./dist/esm/data-platform/index.mjs",
86
86
  "default": "./dist/esm/data-platform/index.mjs"
87
+ },
88
+ "./assembly": {
89
+ "types": "./dist/types/assembly.d.ts",
90
+ "node": {
91
+ "import": "./dist/esm-node/assembly.mjs",
92
+ "require": "./dist/cjs/assembly.js"
93
+ },
94
+ "default": "./dist/esm/assembly.mjs"
87
95
  }
88
96
  },
89
97
  "typesVersions": {
@@ -104,13 +112,17 @@
104
112
  "./dist/types/effect-client/index.d.ts",
105
113
  "./src/effect-client/index.ts"
106
114
  ],
107
- "effect-client-runtime": [
108
- "./dist/types/effect-client/runtime.d.ts",
109
- "./src/effect-client/runtime.ts"
115
+ "microvertical-api": [
116
+ "./dist/types/microvertical-api.d.ts",
117
+ "./src/microvertical-api.ts"
110
118
  ],
111
119
  "data-platform": [
112
120
  "./dist/types/data-platform/index.d.ts",
113
121
  "./src/data-platform/index.ts"
122
+ ],
123
+ "assembly": [
124
+ "./dist/types/assembly.d.ts",
125
+ "./src/assembly.ts"
114
126
  ]
115
127
  }
116
128
  },
@@ -118,9 +130,10 @@
118
130
  "node": ">=26.7.0"
119
131
  },
120
132
  "dependencies": {
121
- "@modern-js/bff-core": "npm:@bleedingdev/modern-js-bff-core@3.9.0-ultramodern.4",
122
- "@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.9.0-ultramodern.4",
123
- "@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.0-ultramodern.4",
133
+ "@modern-js/bff-core": "npm:@bleedingdev/modern-js-bff-core@3.9.0-ultramodern.6",
134
+ "@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.9.0-ultramodern.6",
135
+ "@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.0-ultramodern.6",
136
+ "@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.0-ultramodern.6",
124
137
  "@opentelemetry/api": "1.9.1",
125
138
  "@opentelemetry/api-logs": "0.222.0",
126
139
  "@opentelemetry/resources": "2.11.0",
@@ -0,0 +1,43 @@
1
+ /** Server-only assembly for the invariant tail of a strict Effect BFF runtime factory. */
2
+ import * as Layer from 'effect/Layer';
3
+ import type { HttpApi, HttpApiGroup } from 'effect/unstable/httpapi';
4
+ import { HttpApiBuilder } from 'effect/unstable/httpapi';
5
+ import { defineEffectBff } from './effect/handler/definition';
6
+ import type { EffectRuntimeRequirements } from './effect/handler/types';
7
+
8
+ export interface EffectBffRuntimeAssembly<
9
+ ApiId extends string,
10
+ Groups extends HttpApiGroup.Constraint,
11
+ HandlerRequirements extends EffectRuntimeRequirements,
12
+ TransportRequirements extends EffectRuntimeRequirements = never,
13
+ > {
14
+ readonly api: HttpApi.HttpApi<ApiId, Groups>;
15
+ readonly handlers: Layer.Layer<
16
+ HttpApiGroup.ToService<ApiId, Groups>,
17
+ never,
18
+ HandlerRequirements
19
+ >;
20
+ readonly transport?: Layer.Layer<never, never, TransportRequirements>;
21
+ }
22
+
23
+ export const assembleEffectBffRuntime = <
24
+ ApiId extends string,
25
+ Groups extends HttpApiGroup.Constraint,
26
+ HandlerRequirements extends EffectRuntimeRequirements,
27
+ TransportRequirements extends EffectRuntimeRequirements = never,
28
+ >({
29
+ api,
30
+ handlers,
31
+ transport,
32
+ }: EffectBffRuntimeAssembly<
33
+ ApiId,
34
+ Groups,
35
+ HandlerRequirements,
36
+ TransportRequirements
37
+ >) => {
38
+ const apiLayer = HttpApiBuilder.layer(api).pipe(Layer.provide(handlers));
39
+ const layer =
40
+ transport === undefined ? apiLayer : apiLayer.pipe(Layer.merge(transport));
41
+
42
+ return defineEffectBff({ api, layer });
43
+ };
@@ -307,15 +307,14 @@ export function createBatchTransportQueue({
307
307
  abortBatchIfUnused?.();
308
308
  };
309
309
 
310
- const runSingle = (request: QueuedBatchRequest) => {
311
- return runWithSignal(request.lifetime.signal, async () => {
310
+ const runSingle = (request: QueuedBatchRequest) =>
311
+ runWithSignal(request.lifetime.signal, async () => {
312
312
  const response = await baseFetch(
313
313
  request.requestInput,
314
314
  request.requestInit,
315
315
  );
316
316
  return parseResponseLikeCreateRequest(response);
317
317
  });
318
- };
319
318
 
320
319
  const settleRequests = async (
321
320
  items: QueuedBatchRequest[],
@@ -1,5 +1,5 @@
1
1
  // @effect-diagnostics asyncFunction:off globalDate:off globalRandom:off globalTimers:off newPromise:off strictBooleanExpressions:off
2
- import { parseTraceparent } from '@modern-js/create-request';
2
+ import { parseTraceparent } from '@modern-js/runtime-extensions/request-context';
3
3
 
4
4
  import type { TraceContext } from './types';
5
5
 
@@ -3,7 +3,8 @@ import {
3
3
  evaluateCrossProjectPolicy,
4
4
  type NormalizedCrossProjectPolicy,
5
5
  resolveCrossProjectRequestObservation,
6
- } from '@modern-js/bff-core/security/cross-project-policy';
6
+ } from '@modern-js/server-runtime-extensions/bff-policy';
7
+ import * as Effect from 'effect/Effect';
7
8
  import { toHeaderRecord } from '../headers';
8
9
  import {
9
10
  type DispatchEffectBffRequestOptions,
@@ -91,7 +92,6 @@ export {
91
92
  } from './edge-context';
92
93
  export type {
93
94
  EffectApiClientFromApi,
94
- EffectApiPromiseClientFromApi,
95
95
  EffectBffDefinition,
96
96
  EffectBffHandlerFactory,
97
97
  EffectBffOpenApiConfig,
@@ -285,9 +285,11 @@ export async function createEffectBffEdgeDispatcher(
285
285
  disposePromise ??= (async () => {
286
286
  acceptingRequests = false;
287
287
  if (activeDispatches > 0) {
288
- await new Promise<void>(resolve => {
289
- resolveDrain = resolve;
290
- });
288
+ await Effect.runPromise(
289
+ Effect.callback<void>(resume => {
290
+ resolveDrain = () => resume(Effect.void);
291
+ }),
292
+ );
291
293
  }
292
294
  await loaded.dispose?.();
293
295
  })();
@@ -1 +1,2 @@
1
1
  export * from './edge-dispatcher';
2
+ export { createEffectBffEdgeDispatcherFactory } from './generated-entry';
@@ -1,21 +1,9 @@
1
1
  // @effect-diagnostics asyncFunction:off strictBooleanExpressions:off
2
- /**
3
- * Shared Effect HttpApi endpoint reflection used by BOTH sides of the
4
- * cross-project contract:
5
- *
6
- * - the `@modern-js/plugin-bff-extensions/client-generator` package stamps
7
- * each generated operation with a per-endpoint contract hash;
8
- * - the `@modern-js/plugin-bff-extensions/effect-adapter` package derives the
9
- * expected operation-contract map for the cross-project policy from the
10
- * same endpoints.
11
- *
12
- * Keeping route-path normalization and the endpoint -> contract mapping in
13
- * one module is what guarantees the two hashes agree.
14
- */
2
+ /** Shared endpoint normalization and identity hashing for server policy metadata. */
15
3
  import {
16
4
  createOperationContractHash,
17
5
  type OperationContractSource,
18
- } from '@modern-js/bff-core';
6
+ } from '@modern-js/server-runtime-extensions/bff-policy/node';
19
7
  import {
20
8
  classifyEffectBffEntryModule,
21
9
  isValidatorAwareHandlerFactory,
@@ -38,13 +26,7 @@ type HttpApiGroupLike = {
38
26
  };
39
27
 
40
28
  type HttpApiEndpointLike = {
41
- /**
42
- * effect 4.0.0-beta.98 renamed the endpoint `name` property to `identifier`,
43
- * matching `HttpApiGroup`. `name` is kept as a fallback so reflection over an
44
- * older Effect build does not silently degrade to the class name.
45
- */
46
29
  identifier?: unknown;
47
- name?: unknown;
48
30
  method?: unknown;
49
31
  path?: unknown;
50
32
  };
@@ -53,9 +35,6 @@ function resolveEffectEndpointName(endpoint: HttpApiEndpointLike): string {
53
35
  if (typeof endpoint.identifier === 'string' && endpoint.identifier) {
54
36
  return endpoint.identifier;
55
37
  }
56
- if (typeof endpoint.name === 'string' && endpoint.name) {
57
- return endpoint.name;
58
- }
59
38
  return '';
60
39
  }
61
40
 
@@ -168,7 +147,7 @@ export async function extractHttpApiFromModule(
168
147
  isValidatorAwareHandlerFactory,
169
148
  isHttpApi,
170
149
  });
171
- return facts?.legacyShape ||
150
+ return facts?.unsupportedShape ||
172
151
  facts?.api === undefined ||
173
152
  !facts.hasRuntimeLayer ||
174
153
  (facts.createHandler !== undefined && !facts.createHandlerValidatorAware)
@@ -42,8 +42,11 @@ function loadNodeValidatorAwareHandlerFactoryRegistry(): ValidatorAwareHandlerFa
42
42
  }
43
43
  }
44
44
 
45
+ declare const __MODERN_EFFECT_NODE_RUNTIME__: boolean;
46
+
45
47
  const validatorAwareHandlerFactoryRegistry =
46
- process.env.MODERN_EFFECT_NODE_RUNTIME === 'true'
48
+ typeof __MODERN_EFFECT_NODE_RUNTIME__ !== 'undefined' &&
49
+ __MODERN_EFFECT_NODE_RUNTIME__
47
50
  ? loadNodeValidatorAwareHandlerFactoryRegistry()
48
51
  : createLocalValidatorAwareHandlerFactoryRegistry();
49
52
 
@@ -55,11 +58,13 @@ type EffectBffEntryModule = {
55
58
  default?: unknown;
56
59
  };
57
60
 
58
- type LegacyEffectBffEntryShape = '`handler` export' | 'default request handler';
61
+ type UnsupportedEffectBffEntryShape =
62
+ | '`handler` export'
63
+ | 'default request handler';
59
64
 
60
65
  export type EffectBffEntryShapeFacts = {
61
66
  module: EffectBffEntryModule;
62
- legacyShape?: LegacyEffectBffEntryShape;
67
+ unsupportedShape?: UnsupportedEffectBffEntryShape;
63
68
  createHandler?: unknown;
64
69
  createHandlerValidatorAware: boolean;
65
70
  api?: unknown;
@@ -132,7 +137,7 @@ export function classifyEffectBffEntryModule(
132
137
  function createEntryShapeFacts(
133
138
  module: EffectBffEntryModule,
134
139
  predicates: EffectBffEntryShapePredicates,
135
- legacyShape?: LegacyEffectBffEntryShape,
140
+ unsupportedShape?: UnsupportedEffectBffEntryShape,
136
141
  ): EffectBffEntryShapeFacts {
137
142
  const createHandler =
138
143
  typeof module.createHandler === 'function'
@@ -143,7 +148,7 @@ function createEntryShapeFacts(
143
148
 
144
149
  return {
145
150
  module,
146
- legacyShape,
151
+ unsupportedShape,
147
152
  createHandler,
148
153
  createHandlerValidatorAware:
149
154
  createHandler !== undefined &&
@@ -0,0 +1,36 @@
1
+ import {
2
+ createEffectBffEdgeDispatcher,
3
+ type EffectBffEdgeHandlerOptions,
4
+ } from './edge-dispatcher';
5
+
6
+ type OperationContracts = NonNullable<
7
+ EffectBffEdgeHandlerOptions['crossProjectPolicy']
8
+ >['expectedOperationContracts'];
9
+
10
+ export function createEffectBffEdgeDispatcherFactory(
11
+ module: EffectBffEdgeHandlerOptions['module'],
12
+ generatedOperationContracts: OperationContracts,
13
+ ) {
14
+ return (options?: Omit<EffectBffEdgeHandlerOptions, 'module'>) => {
15
+ const policy = options?.crossProjectPolicy;
16
+ const crossProjectPolicy =
17
+ policy === undefined ||
18
+ policy === null ||
19
+ policy.expectedOperationContracts === null ||
20
+ typeof policy.expectedOperationContracts !== 'object' ||
21
+ Array.isArray(policy.expectedOperationContracts)
22
+ ? policy
23
+ : {
24
+ ...policy,
25
+ expectedOperationContracts: {
26
+ ...policy.expectedOperationContracts,
27
+ ...generatedOperationContracts,
28
+ },
29
+ };
30
+ return createEffectBffEdgeDispatcher({
31
+ ...options,
32
+ crossProjectPolicy,
33
+ module,
34
+ });
35
+ };
36
+ }
@@ -1,4 +1,5 @@
1
1
  // @effect-diagnostics anyUnknownInErrorContext:off asyncFunction:off globalDate:off globalTimers:off newPromise:off strictBooleanExpressions:off
2
+ import * as Effect from 'effect/Effect';
2
3
  import {
3
4
  type DataBatchResponsePayload,
4
5
  DEFAULT_DATA_BATCH_HEADER,
@@ -417,14 +418,16 @@ export function createDataPlatformBatchRequestHandler<TContext>(options: {
417
418
  );
418
419
  } catch (error) {
419
420
  if (error instanceof BatchItemTimeoutError) {
420
- console.error({
421
- event: 'bff.batch.item.timeout',
422
- batchId: payload.batchId,
423
- itemId,
424
- method: itemMethod,
425
- path: itemPathname,
426
- errorName: error.name,
427
- });
421
+ Effect.runSync(
422
+ Effect.logError({
423
+ event: 'bff.batch.item.timeout',
424
+ batchId: payload.batchId,
425
+ itemId,
426
+ method: itemMethod,
427
+ path: itemPathname,
428
+ errorName: error.name,
429
+ }),
430
+ );
428
431
  return toBatchItemError(
429
432
  itemId,
430
433
  504,
@@ -435,14 +438,16 @@ export function createDataPlatformBatchRequestHandler<TContext>(options: {
435
438
  return toBatchResponseItem(itemId, error, itemMethod === 'HEAD');
436
439
  }
437
440
 
438
- console.error({
439
- event: 'bff.batch.item.failure',
440
- batchId: payload.batchId,
441
- itemId,
442
- method: itemMethod,
443
- path: itemPathname,
444
- errorName: error instanceof Error ? error.name : 'UnknownError',
445
- });
441
+ Effect.runSync(
442
+ Effect.logError({
443
+ event: 'bff.batch.item.failure',
444
+ batchId: payload.batchId,
445
+ itemId,
446
+ method: itemMethod,
447
+ path: itemPathname,
448
+ errorName: error instanceof Error ? error.name : 'UnknownError',
449
+ }),
450
+ );
446
451
  return toBatchItemError(itemId, 500, 'Internal Server Error');
447
452
  } finally {
448
453
  request.signal.removeEventListener('abort', abortFromBatchRequest);
@@ -1,5 +1,6 @@
1
1
  // @effect-diagnostics anyUnknownInErrorContext:off asyncFunction:off globalDate:off globalTimers:off newPromise:off strictBooleanExpressions:off
2
2
 
3
+ import * as Data from 'effect/Data';
3
4
  import {
4
5
  type DataBatchRequestPayload,
5
6
  type DataBatchResponseItem,
@@ -117,7 +118,14 @@ export function toBatchItemError(
117
118
  };
118
119
  }
119
120
 
120
- export class BatchItemTimeoutError extends Error {}
121
+ export class BatchItemTimeoutError extends Data.TaggedError(
122
+ 'BatchItemTimeoutError',
123
+ )<{ readonly message: string }> {
124
+ constructor(message: string) {
125
+ super({ message });
126
+ this.name = 'Error';
127
+ }
128
+ }
121
129
 
122
130
  export function promiseWithTimeout<T>(
123
131
  effect: Promise<T>,
@@ -7,7 +7,6 @@ import { mergeDataPlatformOptions } from './envelope';
7
7
  import { createHttpApiHandler } from './http';
8
8
  import { createRpcApiHandler } from './rpc';
9
9
  import type {
10
- EffectApiPromiseClientFromApi,
11
10
  EffectBffDefinition,
12
11
  EffectBffHandlerFactory,
13
12
  EffectBffRuntime,
@@ -51,55 +50,12 @@ export function defineEffectBff<
51
50
  validateRequest: options?.validateRequest,
52
51
  });
53
52
  });
54
- const client = createLoaderMaterializedClientPlaceholder<TApi>();
55
53
  return {
56
54
  ...definition,
57
55
  createHandler,
58
- client,
59
56
  };
60
57
  }
61
58
 
62
- const LOADER_CLIENT_IGNORED_KEYS = new Set<PropertyKey>([
63
- 'then',
64
- 'catch',
65
- 'finally',
66
- 'toJSON',
67
- '$$typeof',
68
- ]);
69
-
70
- /**
71
- * `defineEffectBff(...).client` is only materialized when the entry module
72
- * is imported through the `@api/index` transform (the webpack/rspack
73
- * loader swaps the module for generated client code). Importing the entry
74
- * directly (server code, scripts, tests) used to return a `client` typed as
75
- * fully functional but `undefined` at runtime — a bare TypeError with zero
76
- * explanation. This placeholder keeps the type contract while failing with
77
- * an actionable error on first property access.
78
- */
79
- function createLoaderMaterializedClientPlaceholder<
80
- TApi extends HttpApi.Constraint,
81
- >(): EffectApiPromiseClientFromApi<TApi> {
82
- const explain = (property: PropertyKey): never => {
83
- throw new Error(
84
- `[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.`,
85
- );
86
- };
87
-
88
- return new Proxy(Object.create(null), {
89
- get(_target, property) {
90
- // Keep async/inspection protocols (await, console.log, React internals)
91
- // from throwing so the placeholder stays inert until really used.
92
- if (
93
- typeof property === 'symbol' ||
94
- LOADER_CLIENT_IGNORED_KEYS.has(property)
95
- ) {
96
- return undefined;
97
- }
98
- return explain(property);
99
- },
100
- }) as EffectApiPromiseClientFromApi<TApi>;
101
- }
102
-
103
59
  export function defineEffectRpcBff<
104
60
  TRpcs extends Rpc.Any = Rpc.Any,
105
61
  TLayer extends EffectRpcRuntimeLayer<TRpcs> = EffectRpcRuntimeLayer<TRpcs>,
@@ -66,8 +66,8 @@ export function createHttpApiHandler<
66
66
  ) => {
67
67
  // Policy seam first: every HttpApi request, direct or batched item,
68
68
  // passes through here, so batch fan-out cannot bypass validator. The
69
- // interceptor receives the original request and may own malformed or
70
- // legacy payloads; delegated requests still pass through body validation.
69
+ // interceptor receives the original request; delegated requests still
70
+ // pass through body validation.
71
71
  const policyDenial = options.validateRequest?.(request);
72
72
  if (policyDenial) {
73
73
  return policyDenial;
@@ -77,22 +77,6 @@ export type EffectRpcBffHandlerOptions = Pick<
77
77
  | 'disableFatalDefects'
78
78
  >;
79
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
80
  export type EffectApiClientFromApi<
97
81
  TApi extends HttpApi.Constraint = HttpApi.Top,
98
82
  > =
@@ -104,10 +88,6 @@ export type EffectApiClientFromApi<
104
88
  >
105
89
  : never;
106
90
 
107
- export type EffectApiPromiseClientFromApi<
108
- TApi extends HttpApi.Constraint = HttpApi.Top,
109
- > = EffectApiPromiseClient<EffectApiClientFromApi<TApi>>;
110
-
111
91
  export type EffectBffDefinition<
112
92
  TApi extends HttpApi.Constraint = HttpApi.Top,
113
93
  TLayer extends EffectRuntimeLayer = EffectRuntimeLayer,
@@ -147,12 +127,6 @@ export type EffectDataPlatformBatchOptions = {
147
127
  * Defaults to `65536` (64KiB).
148
128
  */
149
129
  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
130
  /**
157
131
  * Maximum per-batch internal request concurrency.
158
132
  * Defaults to `4`.
@@ -241,7 +215,6 @@ export type EffectBffRuntime<
241
215
  TLayer extends EffectRuntimeLayer = EffectRuntimeLayer,
242
216
  > = {
243
217
  createHandler: EffectBffHandlerFactory<TApi, TLayer>;
244
- client: EffectApiPromiseClientFromApi<TApi>;
245
218
  };
246
219
 
247
220
  export type EffectBffOpenApiConfig =
@@ -14,7 +14,6 @@ export { defineEffectBff, defineEffectRpcBff } from './handler/definition';
14
14
  export { createHttpApiHandler } from './handler/http';
15
15
  export type {
16
16
  EffectApiClientFromApi,
17
- EffectApiPromiseClientFromApi,
18
17
  EffectBffDefinition,
19
18
  EffectBffHandlerFactory,
20
19
  EffectBffOpenApiConfig,
@@ -67,7 +67,7 @@ function shareDispose(dispose: () => Promise<void>) {
67
67
  };
68
68
  }
69
69
 
70
- function rejectLegacyEffectModuleShape(
70
+ function rejectUnsupportedEffectModuleShape(
71
71
  options: ResolveEffectBffModuleHandlerOptions,
72
72
  shape: string,
73
73
  ) {
@@ -119,8 +119,8 @@ function resolveClassifiedEffectBffModuleHandler(
119
119
  return null;
120
120
  }
121
121
 
122
- if (facts.legacyShape) {
123
- if (rejectLegacyEffectModuleShape(options, facts.legacyShape)) {
122
+ if (facts.unsupportedShape) {
123
+ if (rejectUnsupportedEffectModuleShape(options, facts.unsupportedShape)) {
124
124
  return null;
125
125
  }
126
126
  }
@@ -137,7 +137,7 @@ function resolveClassifiedEffectBffModuleHandler(
137
137
  }
138
138
 
139
139
  if (facts.api === undefined || !facts.hasRuntimeLayer) {
140
- rejectLegacyEffectModuleShape(
140
+ rejectUnsupportedEffectModuleShape(
141
141
  options,
142
142
  'unbranded `createHandler` export',
143
143
  );
@@ -6,7 +6,7 @@ import {
6
6
  BFF_TRACEPARENT_HEADER,
7
7
  type OperationContext,
8
8
  parseTraceparent,
9
- } from '@modern-js/create-request';
9
+ } from '@modern-js/runtime-extensions/request-context';
10
10
 
11
11
  export type EffectContext = {
12
12
  request: Request;