@bleedingdev/modern-js-bff-effect 3.9.0-ultramodern.2 → 3.9.0-ultramodern.21

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 (142) hide show
  1. package/README.md +20 -4
  2. package/dist/cjs/assembly.js +50 -0
  3. package/dist/cjs/{effect-client/guards.js → data-platform/batch/options.js} +13 -9
  4. package/dist/cjs/data-platform/batch/queue.js +48 -83
  5. package/dist/cjs/data-platform/trace.js +2 -2
  6. package/dist/cjs/effect/context.js +28 -4
  7. package/dist/cjs/effect/edge-dispatcher.js +12 -12
  8. package/dist/cjs/effect/edge.js +36 -3
  9. package/dist/cjs/effect/endpoint-contracts.js +3 -4
  10. package/dist/cjs/effect/entry-shape.js +40 -12
  11. package/dist/cjs/effect/generated-entry.js +57 -0
  12. package/dist/cjs/effect/handler/batch-handler.js +7 -7
  13. package/dist/cjs/effect/handler/batch.js +8 -10
  14. package/dist/cjs/effect/handler/definition.js +1 -21
  15. package/dist/cjs/effect/index.js +8 -8
  16. package/dist/cjs/effect/module.js +8 -4
  17. package/dist/cjs/effect/operation-context.js +10 -14
  18. package/dist/cjs/effect/rpcSerialization.js +0 -2
  19. package/dist/cjs/effect-client/cross-project.js +89 -0
  20. package/dist/cjs/effect-client/index.js +5 -3
  21. package/dist/cjs/microvertical-api.js +120 -0
  22. package/dist/esm/assembly.mjs +12 -0
  23. package/dist/esm/data-platform/batch/options.mjs +10 -0
  24. package/dist/esm/data-platform/batch/queue.mjs +48 -83
  25. package/dist/esm/data-platform/trace.mjs +1 -1
  26. package/dist/esm/effect/context.mjs +21 -6
  27. package/dist/esm/effect/edge-dispatcher.mjs +6 -6
  28. package/dist/esm/effect/edge.mjs +3 -0
  29. package/dist/esm/effect/endpoint-contracts.mjs +2 -3
  30. package/dist/esm/effect/entry-shape-registry.mjs +2 -2
  31. package/dist/esm/effect/entry-shape.mjs +29 -6
  32. package/dist/esm/effect/generated-entry.mjs +19 -0
  33. package/dist/esm/effect/handler/batch-handler.mjs +8 -8
  34. package/dist/esm/effect/handler/batch.mjs +9 -8
  35. package/dist/esm/effect/handler/definition.mjs +1 -21
  36. package/dist/esm/effect/index.mjs +1 -1
  37. package/dist/esm/effect/module.mjs +8 -4
  38. package/dist/esm/effect/operation-context.mjs +3 -4
  39. package/dist/esm/effect/rpcSerialization.mjs +0 -2
  40. package/dist/esm/effect-client/cross-project.mjs +51 -0
  41. package/dist/esm/effect-client/index.mjs +4 -2
  42. package/dist/esm/microvertical-api.mjs +67 -0
  43. package/dist/esm-node/assembly.mjs +13 -0
  44. package/dist/esm-node/data-platform/batch/options.mjs +11 -0
  45. package/dist/esm-node/data-platform/batch/queue.mjs +48 -83
  46. package/dist/esm-node/data-platform/trace.mjs +1 -1
  47. package/dist/esm-node/effect/context.mjs +21 -6
  48. package/dist/esm-node/effect/edge-dispatcher.mjs +6 -6
  49. package/dist/esm-node/effect/edge.mjs +3 -0
  50. package/dist/esm-node/effect/endpoint-contracts.mjs +2 -3
  51. package/dist/esm-node/effect/entry-shape.mjs +29 -6
  52. package/dist/esm-node/effect/generated-entry.mjs +20 -0
  53. package/dist/esm-node/effect/handler/batch-handler.mjs +8 -8
  54. package/dist/esm-node/effect/handler/batch.mjs +9 -8
  55. package/dist/esm-node/effect/handler/definition.mjs +1 -21
  56. package/dist/esm-node/effect/index.mjs +1 -1
  57. package/dist/esm-node/effect/module.mjs +8 -4
  58. package/dist/esm-node/effect/operation-context.mjs +3 -4
  59. package/dist/esm-node/effect/rpcSerialization.mjs +0 -2
  60. package/dist/esm-node/effect-client/cross-project.mjs +52 -0
  61. package/dist/esm-node/effect-client/index.mjs +4 -2
  62. package/dist/esm-node/microvertical-api.mjs +68 -0
  63. package/dist/types/assembly.d.ts +10 -0
  64. package/dist/types/data-platform/batch/options.d.ts +7 -0
  65. package/dist/types/data-platform/batch/queue.d.ts +0 -1
  66. package/dist/types/effect/context.d.ts +15 -2
  67. package/dist/types/effect/edge-dispatcher.d.ts +3 -3
  68. package/dist/types/effect/edge.d.ts +3 -0
  69. package/dist/types/effect/endpoint-contracts.d.ts +2 -20
  70. package/dist/types/effect/entry-shape.d.ts +16 -2
  71. package/dist/types/effect/generated-entry.d.ts +7 -0
  72. package/dist/types/effect/handler/batch.d.ts +7 -2
  73. package/dist/types/effect/handler/types.d.ts +2 -17
  74. package/dist/types/effect/handler.d.ts +1 -1
  75. package/dist/types/effect/module.d.ts +2 -1
  76. package/dist/types/effect/operation-context.d.ts +1 -2
  77. package/dist/types/effect-client/cross-project.d.ts +10 -0
  78. package/dist/types/effect-client/index.d.ts +7 -4
  79. package/dist/types/microvertical-api.d.ts +65 -0
  80. package/package.json +47 -42
  81. package/src/assembly.ts +43 -0
  82. package/src/data-platform/batch/options.ts +20 -0
  83. package/src/data-platform/batch/queue.ts +63 -92
  84. package/src/data-platform/trace.ts +1 -1
  85. package/src/effect/context.ts +64 -12
  86. package/src/effect/edge-dispatcher.ts +14 -11
  87. package/src/effect/edge.ts +6 -0
  88. package/src/effect/endpoint-contracts.ts +3 -24
  89. package/src/effect/entry-shape.ts +105 -10
  90. package/src/effect/generated-entry.ts +36 -0
  91. package/src/effect/handler/batch-handler.ts +27 -28
  92. package/src/effect/handler/batch.ts +9 -13
  93. package/src/effect/handler/definition.ts +0 -45
  94. package/src/effect/handler/http.ts +2 -2
  95. package/src/effect/handler/types.ts +3 -32
  96. package/src/effect/handler.ts +0 -1
  97. package/src/effect/index.ts +1 -1
  98. package/src/effect/module.ts +11 -4
  99. package/src/effect/operation-context.ts +2 -6
  100. package/src/effect/rpcSerialization.ts +0 -2
  101. package/src/effect-client/cross-project.ts +104 -0
  102. package/src/effect-client/index.ts +17 -7
  103. package/src/microvertical-api.ts +102 -0
  104. package/dist/cjs/effect/edge-context.js +0 -61
  105. package/dist/cjs/effect-client/batch.js +0 -73
  106. package/dist/cjs/effect-client/envelope.js +0 -145
  107. package/dist/cjs/effect-client/request-context.js +0 -64
  108. package/dist/cjs/effect-client/request.js +0 -66
  109. package/dist/cjs/effect-client/runtime.js +0 -100
  110. package/dist/cjs/effect-client/types.js +0 -18
  111. package/dist/esm/effect/edge-context.mjs +0 -14
  112. package/dist/esm/effect-client/batch.mjs +0 -35
  113. package/dist/esm/effect-client/envelope.mjs +0 -104
  114. package/dist/esm/effect-client/guards.mjs +0 -3
  115. package/dist/esm/effect-client/request-context.mjs +0 -23
  116. package/dist/esm/effect-client/request.mjs +0 -28
  117. package/dist/esm/effect-client/runtime.mjs +0 -62
  118. package/dist/esm/effect-client/types.mjs +0 -0
  119. package/dist/esm-node/effect/edge-context.mjs +0 -15
  120. package/dist/esm-node/effect-client/batch.mjs +0 -36
  121. package/dist/esm-node/effect-client/envelope.mjs +0 -105
  122. package/dist/esm-node/effect-client/guards.mjs +0 -4
  123. package/dist/esm-node/effect-client/request-context.mjs +0 -24
  124. package/dist/esm-node/effect-client/request.mjs +0 -29
  125. package/dist/esm-node/effect-client/runtime.mjs +0 -63
  126. package/dist/esm-node/effect-client/types.mjs +0 -1
  127. package/dist/types/effect/edge-context.d.ts +0 -5
  128. package/dist/types/effect-client/batch.d.ts +0 -2
  129. package/dist/types/effect-client/envelope.d.ts +0 -15
  130. package/dist/types/effect-client/guards.d.ts +0 -2
  131. package/dist/types/effect-client/request-context.d.ts +0 -3
  132. package/dist/types/effect-client/request.d.ts +0 -1
  133. package/dist/types/effect-client/runtime.d.ts +0 -5
  134. package/dist/types/effect-client/types.d.ts +0 -68
  135. package/src/effect/edge-context.ts +0 -51
  136. package/src/effect-client/batch.ts +0 -50
  137. package/src/effect-client/envelope.ts +0 -238
  138. package/src/effect-client/guards.ts +0 -6
  139. package/src/effect-client/request-context.ts +0 -54
  140. package/src/effect-client/request.ts +0 -51
  141. package/src/effect-client/runtime.ts +0 -122
  142. package/src/effect-client/types.ts +0 -84
@@ -1,16 +1,11 @@
1
1
  import { DEFAULT_DATA_BATCH_ENDPOINT, isPlainObject, normalizeMethod } from "../../data-platform/index.mjs";
2
2
  import { encodeBatchText } from "../../data-platform/batch/protocol.mjs";
3
+ import * as __rspack_external_effect_Data_2b3f4efb from "effect/Data";
3
4
  function normalizeBatchPath(pathname) {
4
5
  if (!pathname || '/' === pathname) return DEFAULT_DATA_BATCH_ENDPOINT;
5
6
  if (!pathname.startsWith('/')) return `/${pathname}`;
6
7
  return pathname;
7
8
  }
8
- function normalizeBatchAllowedMethods(allowedMethods) {
9
- const source = Array.isArray(allowedMethods) && allowedMethods.length > 0 ? allowedMethods : [
10
- 'GET'
11
- ];
12
- return new Set(source.map((method)=>method.toUpperCase()));
13
- }
14
9
  function isBatchRequestPayload(value) {
15
10
  return isPlainObject(value) && 2 === value.protocolVersion && 'string' == typeof value.batchId && 'number' == typeof value.sentAt && Array.isArray(value.items);
16
11
  }
@@ -64,7 +59,13 @@ function toBatchItemError(id, status, message) {
64
59
  }))
65
60
  };
66
61
  }
67
- class BatchItemTimeoutError extends Error {
62
+ class BatchItemTimeoutError extends __rspack_external_effect_Data_2b3f4efb.TaggedError('BatchItemTimeoutError') {
63
+ constructor(message){
64
+ super({
65
+ message
66
+ });
67
+ this.name = 'Error';
68
+ }
68
69
  }
69
70
  function promiseWithTimeout(effect, timeoutMs, onTimeout) {
70
71
  if (timeoutMs <= 0) return effect;
@@ -102,4 +103,4 @@ async function mapWithConcurrency(items, concurrency, mapper) {
102
103
  return output;
103
104
  }
104
105
  export { toHeaderRecord } from "../../headers.mjs";
105
- export { BatchItemTimeoutError, createBatchValidationResponse, isBatchRequestPayload, mapWithConcurrency, normalizeBatchAllowedMethods, normalizeBatchPath, prepareJsonRequestBody, promiseWithTimeout, toBatchItemError };
106
+ export { BatchItemTimeoutError, createBatchValidationResponse, isBatchRequestPayload, mapWithConcurrency, normalizeBatchPath, prepareJsonRequestBody, promiseWithTimeout, toBatchItemError };
@@ -20,30 +20,10 @@ function defineEffectBff(definition) {
20
20
  validateRequest: options?.validateRequest
21
21
  });
22
22
  });
23
- const client = createLoaderMaterializedClientPlaceholder();
24
23
  return {
25
24
  ...definition,
26
- createHandler,
27
- client
28
- };
29
- }
30
- const LOADER_CLIENT_IGNORED_KEYS = new Set([
31
- 'then',
32
- 'catch',
33
- 'finally',
34
- 'toJSON',
35
- '$$typeof'
36
- ]);
37
- function createLoaderMaterializedClientPlaceholder() {
38
- const explain = (property)=>{
39
- 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.`);
25
+ createHandler
40
26
  };
41
- return new Proxy(Object.create(null), {
42
- get (_target, property) {
43
- if ('symbol' == typeof property || LOADER_CLIENT_IGNORED_KEYS.has(property)) return;
44
- return explain(property);
45
- }
46
- });
47
27
  }
48
28
  function defineEffectRpcBff(definition) {
49
29
  const createHandler = (options)=>createRpcApiHandler({
@@ -1,7 +1,7 @@
1
1
  import * as __rspack_external__effect_opentelemetry_66324c75 from "@effect/opentelemetry";
2
2
  export * from "./endpoint-contracts.mjs";
3
3
  export * from "./handler.mjs";
4
- export { createEffectOperationContext, runWithEffectContext, useEffectContext, useOperationContext } from "./context.mjs";
4
+ export { createEffectOperationContext, runWithEffectContext, useEffectContext, useOperationContext } from "@modern-js/bff-effect/context";
5
5
  export { createEffectBffEdgeHandler, createEffectBffTestHandler, dispatchEffectBffRequest } from "./edge.mjs";
6
6
  export { resolveEffectBffModuleHandler } from "./module.mjs";
7
7
  export { __rspack_external__effect_opentelemetry_66324c75 as OpenTelemetry };
@@ -11,7 +11,7 @@ function shareDispose(dispose) {
11
11
  return disposePromise;
12
12
  };
13
13
  }
14
- function rejectLegacyEffectModuleShape(options, shape) {
14
+ function rejectUnsupportedEffectModuleShape(options, shape) {
15
15
  options.onWarning?.(`${strictEffectApproachMessage} Rejected ${shape}.`);
16
16
  return true;
17
17
  }
@@ -39,8 +39,12 @@ function classifyEffectBffRuntimeEntryShape(module) {
39
39
  }
40
40
  function resolveClassifiedEffectBffModuleHandler(facts, options = {}) {
41
41
  if (!facts) return null;
42
- if (facts.legacyShape) {
43
- if (rejectLegacyEffectModuleShape(options, facts.legacyShape)) return null;
42
+ if (facts.unsupportedShape) {
43
+ if (rejectUnsupportedEffectModuleShape(options, facts.unsupportedShape)) return null;
44
+ }
45
+ if (void 0 !== facts.module.rpc && !facts.createHandlerValidatorAware) {
46
+ rejectUnsupportedEffectModuleShape(options, 'unbranded RPC runtime');
47
+ return null;
44
48
  }
45
49
  if ('function' == typeof facts.createHandler) {
46
50
  if (facts.createHandlerValidatorAware) {
@@ -53,7 +57,7 @@ function resolveClassifiedEffectBffModuleHandler(facts, options = {}) {
53
57
  return createLoadedHandler(webHandler);
54
58
  }
55
59
  if (void 0 === facts.api || !facts.hasRuntimeLayer) {
56
- rejectLegacyEffectModuleShape(options, 'unbranded `createHandler` export');
60
+ rejectUnsupportedEffectModuleShape(options, 'unbranded `createHandler` export');
57
61
  return null;
58
62
  }
59
63
  options.onWarning?.(`${strictEffectApproachMessage} Ignored unbranded \`createHandler\` export and rebuilt the handler from its { api, layer } exports.`);
@@ -1,5 +1,4 @@
1
- import { BFF_LOCALE_HEADER, BFF_OPERATION_CONTEXT_DETAIL_HEADER, BFF_OPERATION_CONTEXT_HEADER, BFF_TRACEPARENT_HEADER, parseTraceparent } from "@modern-js/create-request";
2
- const kEffectContextStorage = Symbol.for('modernjs.plugin-bff.effectContextStorage');
1
+ import { BFF_LOCALE_HEADER, BFF_OPERATION_CONTEXT_DETAIL_HEADER, BFF_OPERATION_CONTEXT_HEADER, BFF_TRACEPARENT_HEADER, parseTraceparent } from "@modern-js/runtime-extensions/request-context";
3
2
  const readHeader = (headers, header)=>{
4
3
  const value = headers.get(header);
5
4
  return value && value.length > 0 ? value : void 0;
@@ -54,7 +53,7 @@ const createEffectOperationContext = ({ request, path, method })=>{
54
53
  } : {},
55
54
  routePath: servicePath,
56
55
  method: (method || request.method || 'GET').toUpperCase(),
57
- source: 'effect-adapter',
56
+ source: 'effect-server',
58
57
  ...path && path !== servicePath ? {
59
58
  attributes: {
60
59
  mountedPath: path
@@ -72,4 +71,4 @@ const createEffectOperationContext = ({ request, path, method })=>{
72
71
  } : {}
73
72
  };
74
73
  };
75
- export { createEffectOperationContext, kEffectContextStorage };
74
+ export { createEffectOperationContext };
@@ -7,8 +7,6 @@ function getRpcSerializationLayer(serialization) {
7
7
  return RpcSerialization.layerJsonRpc();
8
8
  case 'ndJsonRpc':
9
9
  return RpcSerialization.layerNdJsonRpc();
10
- case 'msgPack':
11
- return RpcSerialization.layerMsgPack;
12
10
  default:
13
11
  return RpcSerialization.layerJsonRpc();
14
12
  }
@@ -0,0 +1,51 @@
1
+ import { BFF_ENVELOPE_HEADER, BFF_OPERATION_CONTEXT_DETAIL_HEADER, BFF_OPERATION_CONTEXT_HEADER, digestOperationContract, resolveCrossProjectRequestObservation } from "@modern-js/server-runtime-extensions/bff-policy";
2
+ import { HttpClient, HttpClientRequest } from "effect/unstable/http";
3
+ import { HttpApi } from "effect/unstable/httpapi";
4
+ import * as __rspack_external_effect_Effect_41060786 from "effect/Effect";
5
+ import * as __rspack_external_effect_Schema_6065f72d from "effect/Schema";
6
+ const encodeJson = __rspack_external_effect_Schema_6065f72d.encodeSync(__rspack_external_effect_Schema_6065f72d.fromJsonString(__rspack_external_effect_Schema_6065f72d.Unknown));
7
+ function withCrossProjectPolicy(api, options, client) {
8
+ const contracts = {};
9
+ const prefix = options.prefix.replace(/\/$/, '');
10
+ HttpApi.reflect(api, {
11
+ onGroup: ()=>{},
12
+ onEndpoint: ({ endpoint })=>{
13
+ const routePath = `${prefix}${'/' === endpoint.path ? '' : endpoint.path}` || '/';
14
+ const name = endpoint.identifier;
15
+ contracts[`${endpoint.method}:${routePath}`] = {
16
+ operationId: `${options.requestId}:${name}`,
17
+ name,
18
+ method: endpoint.method,
19
+ routePath
20
+ };
21
+ }
22
+ });
23
+ return client.pipe(HttpClient.mapRequestEffect((request)=>{
24
+ const observed = resolveCrossProjectRequestObservation({
25
+ method: request.method,
26
+ pathname: new URL(request.url, 'http://effect.invalid').pathname
27
+ }, {
28
+ expectedOperationContracts: contracts
29
+ });
30
+ const contract = observed && contracts[`${observed.method}:${observed.routePath}`];
31
+ if (void 0 === contract) return __rspack_external_effect_Effect_41060786.die(new Error('[BFF][Effect] Request has no matching shared operation contract.'));
32
+ return __rspack_external_effect_Effect_41060786.gen(function*() {
33
+ const schemaHash = yield* __rspack_external_effect_Effect_41060786.promise(()=>digestOperationContract({
34
+ httpMethod: contract.method,
35
+ name: contract.name,
36
+ routePath: contract.routePath
37
+ }, options.requestId));
38
+ return request.pipe(HttpClientRequest.setHeader(BFF_ENVELOPE_HEADER, encodeJson({
39
+ requestId: options.requestId
40
+ })), HttpClientRequest.setHeader(BFF_OPERATION_CONTEXT_HEADER, contract.operationId), HttpClientRequest.setHeader(BFF_OPERATION_CONTEXT_DETAIL_HEADER, encodeJson({
41
+ requestId: options.requestId,
42
+ operationId: contract.operationId,
43
+ method: contract.method,
44
+ routePath: contract.routePath,
45
+ schemaHash,
46
+ operationVersion: options.operationVersion
47
+ })));
48
+ });
49
+ }));
50
+ }
51
+ export { withCrossProjectPolicy };
@@ -1,10 +1,11 @@
1
- import { createRequestContextHeaders } from "@modern-js/create-request";
1
+ import { createRequestContextHeaders } from "@modern-js/runtime-extensions/request-context";
2
2
  import * as __rspack_external_effect_Effect_41060786 from "effect/Effect";
3
3
  import * as __rspack_external_effect_Layer_69c46453 from "effect/Layer";
4
4
  import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http";
5
5
  import { HttpApi, HttpApiClient, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/unstable/httpapi";
6
6
  import { Rpc, RpcClient, RpcGroup, RpcSchema, RpcSerialization } from "effect/unstable/rpc";
7
7
  import { getRpcSerializationLayer } from "../effect/rpcSerialization.mjs";
8
+ import { withCrossProjectPolicy } from "./cross-project.mjs";
8
9
  import * as __rspack_external_effect_Schema_6065f72d from "effect/Schema";
9
10
  import * as __rspack_external_effect_unstable_http_HttpClientError_4b9eaee6 from "effect/unstable/http/HttpClientError";
10
11
  import * as __rspack_external_effect_Data_2b3f4efb from "effect/Data";
@@ -46,7 +47,8 @@ function makeEffectHttpApiClient(api, options) {
46
47
  for (const [header, value] of Object.entries(requestContextHeaders))if (void 0 === nextRequest.headers[header.toLowerCase()]) nextRequest = HttpClientRequest.setHeader(nextRequest, header, value);
47
48
  return nextRequest;
48
49
  }));
49
- return 'function' == typeof options?.transformClient ? options.transformClient(contextClient) : contextClient;
50
+ const policyClient = options?.crossProject !== void 0 ? withCrossProjectPolicy(api, options.crossProject, contextClient) : contextClient;
51
+ return 'function' == typeof options?.transformClient ? options.transformClient(policyClient) : policyClient;
50
52
  };
51
53
  return HttpApiClient.make(api, {
52
54
  baseUrl: options?.baseUrl,
@@ -0,0 +1,67 @@
1
+ import { Schema } from "effect";
2
+ const MicroVerticalAppIdSchema = Schema.String.pipe(Schema.brand('MicroVerticalAppId'), Schema.decodeTo(Schema.String));
3
+ const MicroVerticalUnitIdSchema = Schema.String.pipe(Schema.brand('MicroVerticalUnitId'), Schema.decodeTo(Schema.String));
4
+ const MicroVerticalOperationIdSchema = Schema.String.pipe(Schema.brand('MicroVerticalOperationId'), Schema.decodeTo(Schema.String));
5
+ const MicroVerticalTraceIdSchema = Schema.String.pipe(Schema.brand('MicroVerticalTraceId'), Schema.decodeTo(Schema.String));
6
+ const MicroVerticalBuildMarkerSchema = Schema.Struct({
7
+ appId: MicroVerticalAppIdSchema,
8
+ build: Schema.String,
9
+ buildMarker: Schema.String,
10
+ deployProfile: Schema.String,
11
+ packageName: Schema.String,
12
+ sourceRevision: Schema.String,
13
+ surface: Schema.String,
14
+ unitId: MicroVerticalUnitIdSchema,
15
+ version: Schema.String
16
+ });
17
+ const MicroVerticalReadinessSchema = Schema.Struct({
18
+ checks: Schema.Struct({
19
+ api: Schema.Literal('ready'),
20
+ moduleFederation: Schema.Literal('ready'),
21
+ ssr: Schema.Literal('ready'),
22
+ translations: Schema.Literal('ready')
23
+ }),
24
+ marker: MicroVerticalBuildMarkerSchema,
25
+ status: Schema.Literal('ready'),
26
+ versionSkew: Schema.Literal('none')
27
+ });
28
+ const MicroVerticalOperationSourceSchema = Schema.Literals([
29
+ 'client',
30
+ 'server',
31
+ 'generated-client',
32
+ 'effect-server',
33
+ 'data-platform',
34
+ 'unknown'
35
+ ]);
36
+ const MicroVerticalOperationContextSchema = Schema.Struct({
37
+ method: Schema.String,
38
+ operationId: MicroVerticalOperationIdSchema,
39
+ routePath: Schema.String,
40
+ source: MicroVerticalOperationSourceSchema,
41
+ traceId: Schema.optionalKey(MicroVerticalTraceIdSchema)
42
+ });
43
+ const createMicroVerticalOperationContext = (input)=>{
44
+ const context = {
45
+ method: input.method,
46
+ operationId: input.operationId,
47
+ routePath: input.routePath,
48
+ source: 'generated-client'
49
+ };
50
+ return void 0 === input.traceId ? context : {
51
+ ...context,
52
+ traceId: input.traceId
53
+ };
54
+ };
55
+ const microVerticalOperationAttributes = (operationContext)=>{
56
+ const attributes = {
57
+ 'modernjs.operation.id': operationContext.operationId,
58
+ 'modernjs.operation.method': operationContext.method,
59
+ 'modernjs.operation.route': operationContext.routePath,
60
+ 'modernjs.operation.source': operationContext.source
61
+ };
62
+ return void 0 === operationContext.traceId ? attributes : {
63
+ ...attributes,
64
+ 'modernjs.trace.id': operationContext.traceId
65
+ };
66
+ };
67
+ export { MicroVerticalBuildMarkerSchema, MicroVerticalOperationContextSchema, MicroVerticalOperationSourceSchema, MicroVerticalReadinessSchema, createMicroVerticalOperationContext, microVerticalOperationAttributes };
@@ -0,0 +1,13 @@
1
+ import "node:module";
2
+ import { HttpApiBuilder } from "effect/unstable/httpapi";
3
+ import { defineEffectBff } from "./effect/handler/definition.mjs";
4
+ import * as __rspack_external_effect_Layer_69c46453 from "effect/Layer";
5
+ const assembleEffectBffRuntime = ({ api, handlers, transport })=>{
6
+ const apiLayer = HttpApiBuilder.layer(api).pipe(__rspack_external_effect_Layer_69c46453.provide(handlers));
7
+ const layer = void 0 === transport ? apiLayer : apiLayer.pipe(__rspack_external_effect_Layer_69c46453.merge(transport));
8
+ return defineEffectBff({
9
+ api,
10
+ layer
11
+ });
12
+ };
13
+ export { assembleEffectBffRuntime };
@@ -0,0 +1,11 @@
1
+ import "node:module";
2
+ function normalizeBatchLimits(options = {}) {
3
+ return {
4
+ maxBatchSize: Math.max(1, options.maxBatchSize ?? 16),
5
+ maxBatchBytes: Math.max(1024, options.maxBatchBytes ?? 65536),
6
+ allowedMethods: new Set((void 0 !== options.allowedMethods && options.allowedMethods.length > 0 ? options.allowedMethods : [
7
+ 'GET'
8
+ ]).map((method)=>method.toUpperCase()))
9
+ };
10
+ }
11
+ export { normalizeBatchLimits };
@@ -1,6 +1,7 @@
1
1
  import "node:module";
2
2
  import { stableStringify } from "../codec.mjs";
3
3
  import { DEFAULT_DATA_BATCH_HEADER } from "../types.mjs";
4
+ import { normalizeBatchLimits } from "./options.mjs";
4
5
  import { decodeBatchBody, isNullBodyStatus } from "./protocol.mjs";
5
6
  import { createBatchId, measureTextBytes, normalizeMethod, shouldBatchRequest, toHeaderRecord, toRequestBody } from "./request.mjs";
6
7
  import { isBatchResponsePayload, parseResponseLikeCreateRequest } from "./response.mjs";
@@ -79,7 +80,6 @@ class BatchBucketRegistry {
79
80
  if (existing) return existing;
80
81
  const next = {
81
82
  items: [],
82
- bytes: 0,
83
83
  batchId: createBatchId(),
84
84
  sentAt: Date.now(),
85
85
  timer: null,
@@ -98,12 +98,8 @@ class BatchBucketRegistry {
98
98
  }
99
99
  function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
100
100
  const flushIntervalMs = Math.max(0, options.flushIntervalMs ?? 8);
101
- const maxBatchSize = Math.max(1, options.maxBatchSize ?? 16);
102
- const maxBatchBytes = Math.max(1024, options.maxBatchBytes ?? 65536);
101
+ const { maxBatchSize, maxBatchBytes, allowedMethods } = normalizeBatchLimits(options);
103
102
  const requestTimeoutMs = options.requestTimeoutMs;
104
- const allowedMethods = new Set((options.allowedMethods && options.allowedMethods.length > 0 ? options.allowedMethods : [
105
- 'GET'
106
- ]).map((method)=>method.toUpperCase()));
107
103
  const onEvent = options.onEvent;
108
104
  const buckets = bucketRegistry ?? new BatchBucketRegistry();
109
105
  const pendingByKey = new Map();
@@ -136,7 +132,6 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
136
132
  const index = bucket.items.indexOf(request);
137
133
  if (-1 === index) return;
138
134
  bucket.items.splice(index, 1);
139
- bucket.bytes = measurePayload(bucket, bucket.items);
140
135
  if (0 === bucket.items.length && bucket.timer) {
141
136
  clearTimeout(bucket.timer);
142
137
  bucket.timer = null;
@@ -178,7 +173,11 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
178
173
  if (!SAFE_REPLAY_METHODS.has(request.item.method)) throw new Error(`Batch result is unknown for ${request.item.method}; automatic replay is disabled (${reason}).`);
179
174
  return runSingle(request);
180
175
  };
181
- const settleAmbiguousBatchRequests = (items, reason)=>settleRequests(items, (request)=>runAfterAmbiguousBatch(request, reason));
176
+ const finishFlush = (bucketKey, bucket)=>{
177
+ bucket.flushing = false;
178
+ if (bucket.items.length > 0 && !bucket.timer) flushBucket(bucketKey);
179
+ buckets.releaseIfIdle(bucketKey, bucket);
180
+ };
182
181
  const flushBucket = async (bucketKey)=>{
183
182
  const bucket = buckets.get(bucketKey);
184
183
  if (!bucket || bucket.flushing) return;
@@ -195,7 +194,6 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
195
194
  sentAt: bucket.sentAt
196
195
  };
197
196
  bucket.items = [];
198
- bucket.bytes = 0;
199
197
  rotatePayloadIdentity(bucket);
200
198
  const firstItem = items[0];
201
199
  if (!firstItem) {
@@ -211,14 +209,25 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
211
209
  size: items.length,
212
210
  reason: disabledEndpoints.has(endpoint) ? 'batch-disabled' : void 0
213
211
  });
214
- await settleRequests(items, runSingle);
215
- bucket.flushing = false;
216
- if (bucket.items.length > 0 && !bucket.timer) flushBucket(bucketKey);
217
- buckets.releaseIfIdle(bucketKey, bucket);
212
+ try {
213
+ await settleRequests(items, runSingle);
214
+ } finally{
215
+ finishFlush(bucketKey, bucket);
216
+ }
218
217
  return;
219
218
  }
220
219
  const payload = createPayload(payloadIdentity, items);
221
220
  const batchId = payload.batchId;
221
+ const settleAmbiguousBatchRequests = (reason)=>{
222
+ emitDataBatchTransportEvent(onEvent, {
223
+ type: 'fallback',
224
+ endpoint,
225
+ batchId,
226
+ size: items.length,
227
+ reason
228
+ });
229
+ return settleRequests(items, (request)=>runAfterAmbiguousBatch(request, reason));
230
+ };
222
231
  emitDataBatchTransportEvent(onEvent, {
223
232
  type: 'flush',
224
233
  endpoint,
@@ -269,42 +278,13 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
269
278
  batchId,
270
279
  reason: `batch-endpoint-unavailable-${String(response.status)}`
271
280
  });
272
- } else emitDataBatchTransportEvent(onEvent, {
273
- type: 'fallback',
274
- endpoint,
275
- batchId,
276
- size: items.length,
277
- reason: `batch-response-${String(response.status)}`
278
- });
279
- await settleAmbiguousBatchRequests(items, `batch-response-${String(response.status)}`);
280
- bucket.flushing = false;
281
+ await settleRequests(items, (request)=>runAfterAmbiguousBatch(request, `batch-response-${String(response.status)}`));
282
+ } else await settleAmbiguousBatchRequests(`batch-response-${String(response.status)}`);
281
283
  return;
282
284
  }
283
285
  const result = await runWithSignal(controller.signal, async ()=>response.json());
284
- if (!isBatchResponsePayload(result)) {
285
- emitDataBatchTransportEvent(onEvent, {
286
- type: 'fallback',
287
- endpoint,
288
- batchId,
289
- size: items.length,
290
- reason: 'invalid-batch-response'
291
- });
292
- await settleAmbiguousBatchRequests(items, 'invalid-batch-response');
293
- bucket.flushing = false;
294
- return;
295
- }
296
- if (result.batchId !== batchId) {
297
- emitDataBatchTransportEvent(onEvent, {
298
- type: 'fallback',
299
- endpoint,
300
- batchId,
301
- size: items.length,
302
- reason: 'mismatched-batch-id'
303
- });
304
- await settleAmbiguousBatchRequests(items, 'mismatched-batch-id');
305
- bucket.flushing = false;
306
- return;
307
- }
286
+ if (!isBatchResponsePayload(result)) return void await settleAmbiguousBatchRequests('invalid-batch-response');
287
+ if (result.batchId !== batchId) return void await settleAmbiguousBatchRequests('mismatched-batch-id');
308
288
  const itemMap = new Map();
309
289
  for (const item of result.items)itemMap.set(item.id, item);
310
290
  await settleRequests(items, async (request)=>{
@@ -328,19 +308,10 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
328
308
  });
329
309
  return;
330
310
  }
331
- emitDataBatchTransportEvent(onEvent, {
332
- type: 'fallback',
333
- endpoint,
334
- batchId,
335
- size: items.length,
336
- reason: 'batch-transport-error'
337
- });
338
- await settleAmbiguousBatchRequests(items, 'batch-transport-error');
311
+ await settleAmbiguousBatchRequests('batch-transport-error');
339
312
  } finally{
340
313
  for (const item of items)item.abortBatchIfUnused = void 0;
341
- bucket.flushing = false;
342
- if (bucket.items.length > 0 && !bucket.timer) flushBucket(bucketKey);
343
- buckets.releaseIfIdle(bucketKey, bucket);
314
+ finishFlush(bucketKey, bucket);
344
315
  }
345
316
  };
346
317
  const pendingReadPromises = new Map();
@@ -364,7 +335,20 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
364
335
  const sourceSignal = initDefinesSignal ? initSignal ?? void 0 : sourceRequest?.signal;
365
336
  const requestUrl = toAbsoluteUrl(input);
366
337
  const endpoint = normalizeBatchEndpoint(requestUrl, options.endpoint).toString();
338
+ const itemHeaders = {
339
+ ...headers
340
+ };
341
+ delete itemHeaders.authorization;
342
+ delete itemHeaders.cookie;
367
343
  return {
344
+ itemHeaders,
345
+ itemPath: `${requestUrl.pathname}${requestUrl.search}`,
346
+ bucketKey: stableStringify({
347
+ endpoint,
348
+ authorization: headers.authorization ?? null,
349
+ cookie: headers.cookie ?? null,
350
+ credentials
351
+ }),
368
352
  requestUrl,
369
353
  endpoint,
370
354
  method,
@@ -397,22 +381,11 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
397
381
  batchEndpoint: request.endpoint,
398
382
  requestUrl: request.requestUrl
399
383
  })) return;
400
- const itemHeaders = {
401
- ...request.headers
402
- };
403
- delete itemHeaders.authorization;
404
- delete itemHeaders.cookie;
405
- const bucketKey = stableStringify({
406
- endpoint: request.endpoint,
407
- authorization: request.headers.authorization ?? null,
408
- cookie: request.headers.cookie ?? null,
409
- credentials: request.credentials
410
- });
411
384
  return stableStringify({
412
- bucketKey,
413
- path: `${request.requestUrl.pathname}${request.requestUrl.search}`,
385
+ bucketKey: request.bucketKey,
386
+ path: request.itemPath,
414
387
  method: request.method,
415
- headers: itemHeaders,
388
+ headers: request.itemHeaders,
416
389
  body: null
417
390
  });
418
391
  };
@@ -430,26 +403,19 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
430
403
  const itemId = `${Date.now().toString(36)}_${nextItemId.toString(36)}_${Math.random().toString(16).slice(2, 8)}`;
431
404
  nextItemId += 1;
432
405
  const itemHeaders = {
433
- ...request.headers
406
+ ...request.itemHeaders
434
407
  };
435
- delete itemHeaders.authorization;
436
- delete itemHeaders.cookie;
437
408
  if (preparedBody.body && preparedBody.inferredContentType && !itemHeaders['content-type']) itemHeaders['content-type'] = preparedBody.inferredContentType;
438
409
  const item = {
439
410
  id: itemId,
440
- path: `${request.requestUrl.pathname}${request.requestUrl.search}`,
411
+ path: request.itemPath,
441
412
  method: request.method,
442
413
  headers: itemHeaders,
443
414
  ...preparedBody.body ? {
444
415
  body: preparedBody.body
445
416
  } : {}
446
417
  };
447
- const bucketKey = stableStringify({
448
- endpoint: request.endpoint,
449
- authorization: request.headers.authorization ?? null,
450
- cookie: request.headers.cookie ?? null,
451
- credentials: request.credentials
452
- });
418
+ const { bucketKey } = request;
453
419
  const key = stableStringify({
454
420
  bucketKey,
455
421
  path: item.path,
@@ -543,13 +509,12 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
543
509
  }
544
510
  }
545
511
  bucket.items.push(queued);
546
- bucket.bytes = measurePayload(bucket, bucket.items);
547
512
  emitDataBatchTransportEvent(onEvent, {
548
513
  type: 'enqueue',
549
514
  endpoint: request.endpoint,
550
515
  size: bucket.items.length
551
516
  });
552
- if (bucket.items.length >= maxBatchSize || bucket.bytes >= maxBatchBytes) {
517
+ if (bucket.items.length >= maxBatchSize || measurePayload(bucket, bucket.items) >= maxBatchBytes) {
553
518
  flushBucket(bucketKey);
554
519
  return queued.promise;
555
520
  }
@@ -1,5 +1,5 @@
1
1
  import "node:module";
2
- import { parseTraceparent } from "@modern-js/create-request";
2
+ import { parseTraceparent } from "@modern-js/runtime-extensions/request-context";
3
3
  function isAllZeroHex(value) {
4
4
  return /^0+$/.test(value);
5
5
  }
@@ -1,13 +1,28 @@
1
1
  import "node:module";
2
- import { AsyncLocalStorage } from "node:async_hooks";
3
- import { createEffectOperationContext, kEffectContextStorage } from "./operation-context.mjs";
4
2
  const globalStore = globalThis;
5
- const effectContextStorage = globalStore[kEffectContextStorage] ?? (globalStore[kEffectContextStorage] = new AsyncLocalStorage());
6
- const runWithEffectContext = (context, cb)=>effectContextStorage.run(context, cb);
3
+ const asyncHooks = globalStore.process?.getBuiltinModule?.('node:async_hooks');
4
+ const AsyncLocalStorage = asyncHooks?.AsyncLocalStorage;
5
+ if ('function' != typeof AsyncLocalStorage) throw new Error('[BFF][Effect] The edge runtime must provide AsyncLocalStorage. Enable Node.js compatibility or the nodejs_als compatibility flag.');
6
+ const EFFECT_BFF_CONTEXT_STORAGE_SHARE = '@modern-js/bff-effect/context-storage';
7
+ let effectContextStorage = new AsyncLocalStorage();
8
+ let effectContextStorageUsed = false;
9
+ const usedEffectContextStorage = ()=>{
10
+ effectContextStorageUsed = true;
11
+ return effectContextStorage;
12
+ };
13
+ const runWithEffectContext = (context, cb)=>usedEffectContextStorage().run(context, cb);
7
14
  const useEffectContext = ()=>{
8
- const context = effectContextStorage.getStore();
15
+ const context = usedEffectContextStorage().getStore();
9
16
  if (!context) throw new Error("Can't call useEffectContext out of Effect runtime scope");
10
17
  return context;
11
18
  };
19
+ const getEffectContextStorage = ()=>effectContextStorage;
20
+ function adoptEffectContextStorage(storage) {
21
+ const candidate = storage;
22
+ if ('function' != typeof candidate?.run || 'function' != typeof candidate.getStore) throw new Error(`[BFF][Effect] Share ${EFFECT_BFF_CONTEXT_STORAGE_SHARE} must provide an AsyncLocalStorage.`);
23
+ if (effectContextStorageUsed && candidate !== effectContextStorage) throw new Error('[BFF][Effect] A backend federation container received the host request storage after an Effect API already used its own. Evaluate exposes only after container init().');
24
+ effectContextStorage = candidate;
25
+ }
12
26
  const useOperationContext = ()=>useEffectContext().operationContext;
13
- export { createEffectOperationContext, runWithEffectContext, useEffectContext, useOperationContext };
27
+ export { createEffectOperationContext } from "./operation-context.mjs";
28
+ export { EFFECT_BFF_CONTEXT_STORAGE_SHARE, adoptEffectContextStorage, getEffectContextStorage, runWithEffectContext, useEffectContext, useOperationContext };
@@ -1,11 +1,11 @@
1
1
  import "node:module";
2
- import { evaluateCrossProjectPolicy, resolveCrossProjectRequestObservation } from "@modern-js/bff-core/security/cross-project-policy";
2
+ import { runWithEffectContext, useEffectContext, useOperationContext } from "@modern-js/bff-effect/context";
3
+ import { evaluateCrossProjectPolicy, resolveCrossProjectRequestObservation } from "@modern-js/server-runtime-extensions/bff-policy";
4
+ import * as __rspack_external_effect_Effect_41060786 from "effect/Effect";
3
5
  import { toHeaderRecord } from "../headers.mjs";
4
6
  import { dispatchEffectBffRequestWithContext } from "./dispatch.mjs";
5
- import { runWithEffectContext, useEffectContext, useOperationContext } from "./edge-context.mjs";
6
7
  import { resolveEffectBffModuleHandler } from "./module.mjs";
7
8
  import * as __rspack_external_effect_Config_76cfa59d from "effect/Config";
8
- import * as __rspack_external_effect_Effect_41060786 from "effect/Effect";
9
9
  import * as __rspack_external_effect_Layer_69c46453 from "effect/Layer";
10
10
  import * as __rspack_external_effect_Option_0498e63a from "effect/Option";
11
11
  import * as __rspack_external_effect_Schema_6065f72d from "effect/Schema";
@@ -91,9 +91,9 @@ async function createEffectBffEdgeDispatcher(options) {
91
91
  dispose: ()=>{
92
92
  disposePromise ??= (async ()=>{
93
93
  acceptingRequests = false;
94
- if (activeDispatches > 0) await new Promise((resolve)=>{
95
- resolveDrain = resolve;
96
- });
94
+ if (activeDispatches > 0) await __rspack_external_effect_Effect_41060786.runPromise(__rspack_external_effect_Effect_41060786.callback((resume)=>{
95
+ resolveDrain = ()=>resume(__rspack_external_effect_Effect_41060786["void"]);
96
+ }));
97
97
  await loaded.dispose?.();
98
98
  })();
99
99
  return disposePromise;
@@ -1,2 +1,5 @@
1
1
  import "node:module";
2
2
  export * from "./edge-dispatcher.mjs";
3
+ export { EFFECT_BFF_CONTEXT_STORAGE_SHARE } from "@modern-js/bff-effect/context";
4
+ export { EFFECT_BFF_HANDLER_FACTORY_REGISTRY_SHARE, adoptEffectBffShareScope } from "./entry-shape.mjs";
5
+ export { createEffectBffEdgeDispatcherFactory } from "./generated-entry.mjs";