@bleedingdev/modern-js-plugin-bff 3.2.0-ultramodern.98 → 3.4.0-ultramodern.0

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 (72) hide show
  1. package/dist/cjs/cli.js +9 -5
  2. package/dist/cjs/constants.js +13 -9
  3. package/dist/cjs/index.js +9 -5
  4. package/dist/cjs/loader.js +9 -5
  5. package/dist/cjs/runtime/create-request/index.js +9 -5
  6. package/dist/cjs/runtime/data-platform/index.js +11 -18
  7. package/dist/cjs/runtime/effect/adapter.js +87 -14
  8. package/dist/cjs/runtime/effect/context.js +9 -5
  9. package/dist/cjs/runtime/effect/edge.js +26 -26
  10. package/dist/cjs/runtime/effect/endpoint-contracts.js +130 -0
  11. package/dist/cjs/runtime/effect/handler.js +107 -63
  12. package/dist/cjs/runtime/effect/index.js +28 -16
  13. package/dist/cjs/runtime/effect/module.js +71 -35
  14. package/dist/cjs/runtime/effect/operation-context.js +10 -18
  15. package/dist/cjs/runtime/effect-client/index.js +10 -6
  16. package/dist/cjs/runtime/effect-client/runtime.js +266 -0
  17. package/dist/cjs/runtime/hono/adapter.js +30 -14
  18. package/dist/cjs/runtime/hono/index.js +9 -5
  19. package/dist/cjs/runtime/hono/operators.js +9 -5
  20. package/dist/cjs/runtime/safe-failure.js +83 -0
  21. package/dist/cjs/server.js +9 -5
  22. package/dist/cjs/utils/clientGenerator.js +13 -9
  23. package/dist/cjs/utils/createHonoRoutes.js +9 -5
  24. package/dist/cjs/utils/crossProjectApiPlugin.js +9 -5
  25. package/dist/cjs/utils/crossProjectServerPolicy.js +104 -0
  26. package/dist/cjs/utils/effectClientGenerator.js +99 -488
  27. package/dist/cjs/utils/pluginGenerator.js +9 -5
  28. package/dist/cjs/utils/runtimeGenerator.js +9 -5
  29. package/dist/esm/runtime/data-platform/index.mjs +2 -13
  30. package/dist/esm/runtime/effect/adapter.mjs +78 -9
  31. package/dist/esm/runtime/effect/edge.mjs +2 -9
  32. package/dist/esm/runtime/effect/endpoint-contracts.mjs +68 -0
  33. package/dist/esm/runtime/effect/handler.mjs +41 -8
  34. package/dist/esm/runtime/effect/index.mjs +2 -0
  35. package/dist/esm/runtime/effect/module.mjs +63 -31
  36. package/dist/esm/runtime/effect/operation-context.mjs +1 -13
  37. package/dist/esm/runtime/effect-client/index.mjs +1 -1
  38. package/dist/esm/runtime/effect-client/runtime.mjs +228 -0
  39. package/dist/esm/runtime/hono/adapter.mjs +21 -9
  40. package/dist/esm/runtime/safe-failure.mjs +45 -0
  41. package/dist/esm/utils/clientGenerator.mjs +5 -5
  42. package/dist/esm/utils/crossProjectServerPolicy.mjs +50 -0
  43. package/dist/esm/utils/effectClientGenerator.mjs +88 -484
  44. package/dist/esm-node/runtime/data-platform/index.mjs +2 -13
  45. package/dist/esm-node/runtime/effect/adapter.mjs +78 -9
  46. package/dist/esm-node/runtime/effect/edge.mjs +2 -9
  47. package/dist/esm-node/runtime/effect/endpoint-contracts.mjs +69 -0
  48. package/dist/esm-node/runtime/effect/handler.mjs +41 -8
  49. package/dist/esm-node/runtime/effect/index.mjs +2 -0
  50. package/dist/esm-node/runtime/effect/module.mjs +63 -31
  51. package/dist/esm-node/runtime/effect/operation-context.mjs +1 -13
  52. package/dist/esm-node/runtime/effect-client/index.mjs +1 -1
  53. package/dist/esm-node/runtime/effect-client/runtime.mjs +229 -0
  54. package/dist/esm-node/runtime/hono/adapter.mjs +21 -9
  55. package/dist/esm-node/runtime/safe-failure.mjs +46 -0
  56. package/dist/esm-node/utils/clientGenerator.mjs +5 -5
  57. package/dist/esm-node/utils/crossProjectServerPolicy.mjs +52 -0
  58. package/dist/esm-node/utils/effectClientGenerator.mjs +88 -484
  59. package/dist/types/runtime/effect/adapter.d.ts +25 -0
  60. package/dist/types/runtime/effect/context.d.ts +1 -1
  61. package/dist/types/runtime/effect/endpoint-contracts.d.ts +62 -0
  62. package/dist/types/runtime/effect/handler.d.ts +37 -4
  63. package/dist/types/runtime/effect/index.d.ts +1 -0
  64. package/dist/types/runtime/effect/module.d.ts +22 -2
  65. package/dist/types/runtime/effect-client/runtime.d.ts +71 -0
  66. package/dist/types/runtime/hono/adapter.d.ts +3 -0
  67. package/dist/types/runtime/safe-failure.d.ts +1 -0
  68. package/dist/types/server.d.ts +1 -1
  69. package/dist/types/utils/createHonoRoutes.d.ts +3 -3
  70. package/dist/types/utils/crossProjectServerPolicy.d.ts +35 -0
  71. package/dist/types/utils/effectClientGenerator.d.ts +16 -2
  72. package/package.json +40 -27
@@ -1,4 +1,5 @@
1
1
  import type { ServerMiddleware, ServerPluginAPI } from '@modern-js/server-core';
2
+ import { type ResolvedCrossProjectPolicy } from '../../utils/crossProjectServerPolicy';
2
3
  interface MiddlewareOptions {
3
4
  prefix: string;
4
5
  enableHandleWeb?: boolean;
@@ -7,12 +8,36 @@ export declare class EffectAdapter {
7
8
  api: ServerPluginAPI;
8
9
  isEffect: boolean;
9
10
  effectMiddleware: ServerMiddleware | null;
11
+ crossProjectPolicy: ResolvedCrossProjectPolicy | undefined;
10
12
  private handler;
11
13
  private dispose;
14
+ private prefix;
15
+ /**
16
+ * True when the loaded handler does NOT run the policy seam internally
17
+ * (plain `handler` exports); the adapter middleware enforces it instead.
18
+ */
19
+ private policyEnforcedInMiddleware;
12
20
  constructor(api: ServerPluginAPI);
13
21
  registerMiddleware: (options: MiddlewareOptions) => Promise<void>;
14
22
  onApiHandlersUpdated: () => Promise<void>;
15
23
  private resolveEntryFile;
24
+ private isApiRequestPath;
25
+ /**
26
+ * Contract sources for the lambda-lane handlers hosted alongside the
27
+ * effect entry. Cross-project SDKs generate per-operation contracts for
28
+ * `api/lambda` handlers with the exact same `ApiRouter` derivation used
29
+ * here, so the server-side expected-contract map must include them —
30
+ * otherwise every lambda-lane operation of a hosted producer SDK would be
31
+ * denied as `unknown_operation_contract`.
32
+ */
33
+ private collectLambdaContractSources;
34
+ /**
35
+ * Resolves the cross-project policy from the reflected HttpApi endpoints
36
+ * (plus any hosted lambda-lane handlers) so the expected operation
37
+ * contracts match the hashes the client generators stamp into generated
38
+ * SDKs.
39
+ */
40
+ private refreshCrossProjectPolicy;
16
41
  private loadEffectHandlerFromModule;
17
42
  private reloadHandler;
18
43
  private disposeCurrentHandler;
@@ -2,4 +2,4 @@ import type { EffectContext } from './operation-context';
2
2
  export { type CreateEffectOperationContextOptions, createEffectOperationContext, type EffectContext, } from './operation-context';
3
3
  export declare const runWithEffectContext: <T>(context: EffectContext, cb: () => T) => T;
4
4
  export declare const useEffectContext: () => EffectContext;
5
- export declare const useOperationContext: () => OperationContext;
5
+ export declare const useOperationContext: () => import("@modern-js/create-request").OperationContext;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Shared Effect HttpApi endpoint reflection used by BOTH sides of the
3
+ * cross-project contract:
4
+ *
5
+ * - the client generator (`utils/effectClientGenerator`) stamps each
6
+ * generated operation with a per-endpoint contract hash;
7
+ * - the effect server adapter derives the expected operation-contract map
8
+ * for the cross-project policy from the same endpoints.
9
+ *
10
+ * Keeping route-path normalization and the endpoint -> contract mapping in
11
+ * one module is what guarantees the two hashes agree.
12
+ */
13
+ import { type OperationContractSource } from '@modern-js/bff-core';
14
+ export type EffectEndpointMeta = {
15
+ apiId: string;
16
+ groupName: string;
17
+ endpointName: string;
18
+ method: string;
19
+ routePath: string;
20
+ };
21
+ export type HttpApiLike = {
22
+ identifier?: unknown;
23
+ };
24
+ export type HttpApiGroupLike = {
25
+ identifier?: unknown;
26
+ };
27
+ export type HttpApiEndpointLike = {
28
+ name?: unknown;
29
+ method?: unknown;
30
+ path?: unknown;
31
+ };
32
+ export type HttpApiReflect = (api: unknown, handlers: {
33
+ /** Group metadata is unused by contract collection; kept as a no-op. */
34
+ onGroup?: () => void;
35
+ onEndpoint: (input: {
36
+ group: HttpApiGroupLike;
37
+ endpoint: HttpApiEndpointLike;
38
+ }) => void;
39
+ }) => void;
40
+ export declare function ensureLeadingSlash(pathname: string): string;
41
+ export declare function normalizeEffectPrefix(prefix: string): string;
42
+ export declare function getEffectRoutePath(prefix: string, endpointPath: string): string;
43
+ export declare function resolveEffectApiId(api: HttpApiLike): string;
44
+ export declare function collectEffectEndpoints(reflect: HttpApiReflect, api: HttpApiLike, prefix: string): EffectEndpointMeta[];
45
+ /**
46
+ * Maps reflected endpoints onto operation-contract sources for
47
+ * `buildOperationContractMap`/`resolveCrossProjectPolicy`. Effect endpoints
48
+ * carry no zod metadata, so the contract hash covers the endpoint identity.
49
+ */
50
+ export declare function toOperationContractSources(endpoints: EffectEndpointMeta[]): OperationContractSource[];
51
+ export declare function createEffectOperationContractSource(endpoint: EffectEndpointMeta): OperationContractSource;
52
+ /**
53
+ * Extracts the HttpApi instance from a loaded effect entry module. Mirrors
54
+ * the module shapes accepted by `resolveEffectBffModuleHandler`:
55
+ * `{ api }`, `{ default: { api } }` and zero-arg default factories.
56
+ */
57
+ export declare function extractHttpApiFromModule(mod: unknown, isHttpApi: (value: unknown) => boolean): Promise<HttpApiLike | null>;
58
+ /**
59
+ * Per-endpoint contract hash; MUST stay in sync with the server-side
60
+ * contract map (it is the same bff-core hash over the same inputs).
61
+ */
62
+ export declare function createEffectEndpointContractHash(endpoint: EffectEndpointMeta, requestId: string): string;
@@ -1,8 +1,10 @@
1
+ import type { FileSystem, Path } from 'effect';
2
+ import * as Context from 'effect/Context';
1
3
  import type * as EffectType from 'effect/Effect';
2
4
  import * as Layer from 'effect/Layer';
5
+ import { Etag, HttpPlatform, HttpRouter } from 'effect/unstable/http';
3
6
  import { type HttpApi, type HttpApiClient, type HttpApiGroup } from 'effect/unstable/httpapi';
4
7
  import { type Rpc, type RpcGroup } from 'effect/unstable/rpc';
5
- export * as OpenTelemetry from '@effect/opentelemetry';
6
8
  export * as Config from 'effect/Config';
7
9
  export * as Effect from 'effect/Effect';
8
10
  export * as Layer from 'effect/Layer';
@@ -13,7 +15,8 @@ export { HttpTraceContext } from 'effect/unstable/http';
13
15
  export * from 'effect/unstable/httpapi';
14
16
  export { HttpApiBuilder } from 'effect/unstable/httpapi';
15
17
  export * from 'effect/unstable/rpc';
16
- export type EffectRuntimeLayer = Layer.Layer<never, unknown, unknown>;
18
+ export type EffectRuntimeRequirements = Etag.Generator | FileSystem.FileSystem | HttpPlatform.HttpPlatform | HttpRouter.HttpRouter | HttpRouter.Request<'Error', any> | HttpRouter.Request<'GlobalError', any> | HttpRouter.Request<'GlobalRequires', any> | HttpRouter.Request<'Requires', any> | Path.Path;
19
+ export type EffectRuntimeLayer = Layer.Layer<never, never, EffectRuntimeRequirements>;
17
20
  export type EffectRpcSerialization = 'json' | 'ndjson' | 'jsonRpc' | 'ndJsonRpc' | 'msgPack';
18
21
  export type EffectRpcRuntimeLayer<TRpcs extends Rpc.Any = Rpc.Any> = Layer.Layer<Rpc.ToHandler<TRpcs> | Rpc.Middleware<TRpcs> | Rpc.ServicesServer<TRpcs>, unknown, never>;
19
22
  export type EffectRpcBffDefinition<TRpcs extends Rpc.Any = Rpc.Any, TRpcLayer extends EffectRpcRuntimeLayer<TRpcs> = EffectRpcRuntimeLayer<TRpcs>> = {
@@ -127,10 +130,39 @@ export type EffectDataPlatformValidationOptions = {
127
130
  */
128
131
  batch?: EffectDataPlatformBatchOptions;
129
132
  };
133
+ /**
134
+ * Server-seam request validator applied to every HttpApi request — direct,
135
+ * batched (per item) and rpc. Returning a Response short-circuits the
136
+ * request; returning `null`/`undefined` lets it through. Used by the effect
137
+ * adapter to enforce the cross-project policy at one seam so batched items
138
+ * cannot bypass it.
139
+ */
140
+ export type EffectRequestValidator = (request: Request) => Response | null | undefined;
141
+ /**
142
+ * Brand stamped on `createHandler` factories that are guaranteed to forward
143
+ * `options.validateRequest` into `createHttpApiHandler` — i.e. the policy
144
+ * seam runs inside the produced handler, including per batched item.
145
+ *
146
+ * `defineEffectBff` brands its factory automatically. Hand-written factories
147
+ * are NOT assumed to honor `validateRequest` (the option is newer than the
148
+ * factory shape), so the effect adapter falls back to middleware enforcement
149
+ * for unbranded factories instead of silently skipping the policy. Custom
150
+ * factories that do forward `validateRequest` into `createHttpApiHandler`
151
+ * may opt in by setting this symbol property to `true`.
152
+ *
153
+ * Registered via `Symbol.for` so independently loaded runtime copies agree.
154
+ */
155
+ export declare const EFFECT_VALIDATOR_AWARE_FACTORY: symbol;
156
+ /**
157
+ * True when `factory` is branded as validator-aware (see
158
+ * {@link EFFECT_VALIDATOR_AWARE_FACTORY}).
159
+ */
160
+ export declare function isValidatorAwareHandlerFactory(factory: unknown): boolean;
130
161
  export type EffectBffHandlerFactory<TApi extends HttpApi.AnyWithProps = HttpApi.AnyWithProps, TLayer extends EffectRuntimeLayer = EffectRuntimeLayer> = (options?: {
131
162
  openapi?: EffectBffOpenApiConfig;
132
163
  rpc?: Partial<EffectRpcBffHandlerOptions>;
133
164
  dataPlatform?: Partial<EffectDataPlatformValidationOptions>;
165
+ validateRequest?: EffectRequestValidator;
134
166
  }) => ReturnType<typeof createHttpApiHandler>;
135
167
  export type EffectBffRuntime<TApi extends HttpApi.AnyWithProps = HttpApi.AnyWithProps, TLayer extends EffectRuntimeLayer = EffectRuntimeLayer> = {
136
168
  createHandler: EffectBffHandlerFactory<TApi, TLayer>;
@@ -141,7 +173,7 @@ export type EffectBffOpenApiConfig = boolean | {
141
173
  };
142
174
  export type EffectRpcBffHandlerFactory<TRpcs extends Rpc.Any = Rpc.Any> = (options?: Partial<EffectRpcBffHandlerOptions>) => ReturnType<typeof createRpcApiHandler<TRpcs>>;
143
175
  declare function createRpcApiHandler<TRpcs extends Rpc.Any = Rpc.Any>(options: EffectRpcBffDefinition<TRpcs>): {
144
- readonly handler: (request: globalThis.Request, context?: import("effect/Context").Context<never> | undefined) => Promise<Response>;
176
+ readonly handler: (request: globalThis.Request, context?: Context.Context<never> | undefined) => Promise<Response>;
145
177
  readonly dispose: () => Promise<void>;
146
178
  };
147
179
  export declare function defineEffectBff<TApi extends HttpApi.AnyWithProps, TLayer extends EffectRuntimeLayer, TRpcs extends Rpc.Any = Rpc.Any>(definition: {
@@ -164,7 +196,8 @@ export declare function createHttpApiHandler<TApi extends HttpApi.AnyWithProps =
164
196
  openapi?: EffectBffOpenApiConfig;
165
197
  rpc?: EffectRpcBffDefinition<TRpcs>;
166
198
  dataPlatform?: EffectDataPlatformValidationOptions;
199
+ validateRequest?: EffectRequestValidator;
167
200
  }): {
168
- handler: (request: Request, context?: Parameters<(request: globalThis.Request, context: import("effect/Context").Context<any>) => Promise<Response>>[1]) => Promise<Response>;
201
+ handler: (request: Request, context?: Parameters<(request: globalThis.Request, context: Context.Context<any>) => Promise<Response>>[1]) => Promise<Response>;
169
202
  dispose: () => Promise<void>;
170
203
  };
@@ -1,2 +1,3 @@
1
+ export * as OpenTelemetry from '@effect/opentelemetry';
1
2
  export { type CreateEffectOperationContextOptions, createEffectOperationContext, type EffectContext, useEffectContext, useOperationContext, } from './context';
2
3
  export * from './handler';
@@ -1,10 +1,11 @@
1
1
  import type * as EffectServiceContext from 'effect/Context';
2
- import { type EffectBffOpenApiConfig, type EffectDataPlatformValidationOptions } from './handler';
2
+ import { type EffectBffOpenApiConfig, type EffectDataPlatformValidationOptions, type EffectRequestValidator } from './handler';
3
3
  import type { EffectContext } from './operation-context';
4
- export type EffectBffRequestHandler = (request: Request, context?: EffectServiceContext.Context<never> | EffectContext) => Promise<Response> | Response;
4
+ export type EffectBffRequestHandler = (request: Request, context?: EffectServiceContext.Context<any> | EffectContext) => Promise<Response> | Response;
5
5
  export type EffectBffHandlerFactory = (options?: {
6
6
  openapi?: EffectBffOpenApiConfig;
7
7
  dataPlatform?: EffectDataPlatformValidationOptions;
8
+ validateRequest?: EffectRequestValidator;
8
9
  }) => {
9
10
  handler: EffectBffRequestHandler;
10
11
  dispose: () => Promise<void>;
@@ -19,10 +20,29 @@ export type EffectApiModule = {
19
20
  export type LoadedEffectBffHandler = {
20
21
  handler: EffectBffRequestHandler;
21
22
  dispose?: () => Promise<void>;
23
+ /**
24
+ * True when the handler is known to apply `validateRequest` internally
25
+ * (including per batched item): `{ api, layer }` modules resolved through
26
+ * `createHttpApiHandler`, and `createHandler` factories branded by
27
+ * `defineEffectBff` (see `EFFECT_VALIDATOR_AWARE_FACTORY`). Plain
28
+ * `handler` exports and unbranded custom factories leave this unset; the
29
+ * adapter middleware must enforce the policy itself for those.
30
+ */
31
+ appliesRequestValidator?: boolean;
22
32
  };
23
33
  export type ResolveEffectBffModuleHandlerOptions = {
24
34
  openapi?: EffectBffOpenApiConfig;
25
35
  dataPlatform?: EffectDataPlatformValidationOptions;
36
+ /**
37
+ * Server-seam request validator (cross-project policy). Applied by
38
+ * `createHttpApiHandler` to direct, batched and rpc requests.
39
+ *
40
+ * Note: plain `handler` exports and unbranded custom `createHandler`
41
+ * factories bypass this seam — the effect adapter enforces the policy in
42
+ * its middleware for those shapes instead (see
43
+ * `LoadedEffectBffHandler.appliesRequestValidator`).
44
+ */
45
+ validateRequest?: EffectRequestValidator;
26
46
  onWarning?: (message: string) => void;
27
47
  };
28
48
  export declare function resolveEffectBffModuleHandler(mod: EffectApiModule, options?: ResolveEffectBffModuleHandlerOptions): Promise<LoadedEffectBffHandler | null>;
@@ -0,0 +1,71 @@
1
+ export type GeneratedEffectEndpoint = {
2
+ apiId: string;
3
+ group: string;
4
+ endpoint: string;
5
+ method: string;
6
+ routePath: string;
7
+ /** Per-endpoint operation contract hash (bff-core hash). */
8
+ schemaHash: string;
9
+ operationVersion: number;
10
+ };
11
+ export type GeneratedEffectBatchConfig = {
12
+ enabled: boolean;
13
+ endpoint: string;
14
+ flushIntervalMs: number;
15
+ maxBatchSize: number;
16
+ maxBatchBytes: number;
17
+ requestTimeoutMs: number;
18
+ allowedMethods: string[];
19
+ };
20
+ export type GeneratedEffectClientConfig = {
21
+ appNamespace: string;
22
+ /** Cross-project producer id; absent for same-project clients. */
23
+ requestId?: string;
24
+ port: number;
25
+ /** Resolve the port from process.env.PORT first (server bundles). */
26
+ useEnvPort?: boolean;
27
+ defaultOrigin: string;
28
+ httpMethodDecider?: string;
29
+ batch: GeneratedEffectBatchConfig;
30
+ };
31
+ export type EffectOperationDescriptor = {
32
+ appNamespace: string;
33
+ apiId: string;
34
+ group: string;
35
+ endpoint: string;
36
+ operationId: string;
37
+ routePath: string;
38
+ method: string;
39
+ operationVersion: number;
40
+ schemaHash: string;
41
+ version: number;
42
+ };
43
+ export type EffectClientOperation = (request?: unknown) => Promise<unknown>;
44
+ export type EffectClientGroup = Record<string, EffectClientOperation>;
45
+ export type EffectClient = Record<string, EffectClientGroup>;
46
+ export type EffectOperationManifest = Record<string, Record<string, EffectOperationDescriptor>>;
47
+ export type EffectRequestContextInput = Record<string, unknown>;
48
+ export type EffectRequestContext = {
49
+ headers: Record<string, string>;
50
+ } & Record<string, unknown>;
51
+ /** Structural slice of `@modern-js/create-request` the runtime relies on. */
52
+ export type EffectRequestRuntime = {
53
+ createRequest: (options: {
54
+ path: string;
55
+ method: string;
56
+ port: number | string;
57
+ operationContext: Record<string, unknown>;
58
+ httpMethodDecider: string;
59
+ requestId?: string;
60
+ }) => (...args: unknown[]) => Promise<unknown>;
61
+ configure?: (options: Record<string, unknown>) => void;
62
+ createRequestContextHeaders?: (input: EffectRequestContextInput) => Record<string, string>;
63
+ };
64
+ export type GeneratedEffectClientModule = {
65
+ client: EffectClient;
66
+ operationManifest: EffectOperationManifest;
67
+ createEffectRequestContext: (requestContext: EffectRequestContextInput) => EffectRequestContext;
68
+ };
69
+ export declare const createGeneratedEffectClient: (manifest: {
70
+ endpoints: GeneratedEffectEndpoint[];
71
+ }, config: GeneratedEffectClientConfig, requestRuntime: EffectRequestRuntime) => GeneratedEffectClientModule;
@@ -1,5 +1,6 @@
1
1
  import type { MiddlewareHandler, ServerMiddleware, ServerPluginAPI } from '@modern-js/server-core';
2
2
  import { Hono } from '@modern-js/server-core';
3
+ import { type ResolvedCrossProjectPolicy } from '../../utils/crossProjectServerPolicy';
3
4
  interface MiddlewareOptions {
4
5
  prefix: string;
5
6
  enableHandleWeb?: boolean;
@@ -9,6 +10,8 @@ export declare class HonoAdapter {
9
10
  apiServer: Hono | null;
10
11
  api: ServerPluginAPI;
11
12
  isHono: boolean;
13
+ prefix: string;
14
+ crossProjectPolicy: ResolvedCrossProjectPolicy | undefined;
12
15
  constructor(api: ServerPluginAPI);
13
16
  setHandlers: () => Promise<void>;
14
17
  registerApiRoutes: () => Promise<void>;
@@ -0,0 +1 @@
1
+ export declare const createSafeFailureResponse: (error: unknown) => Response;
@@ -1,3 +1,3 @@
1
1
  import type { ServerPlugin } from '@modern-js/server-core';
2
- declare const _default: () => ServerPlugin;
3
2
  export default _default;
3
+ declare function _default(): ServerPlugin;
@@ -3,8 +3,8 @@ import type { Context } from '@modern-js/server-core';
3
3
  type Handler = APIHandlerInfo['handler'];
4
4
  declare const createHonoRoutes: (handlerInfos?: APIHandlerInfo[]) => {
5
5
  method: any;
6
- path: APIHandlerInfo;
7
- handler: any[] | ((c: Context) => Promise<any>);
6
+ path: string;
7
+ handler: any[] | ((c: Context) => Promise<void | Response>);
8
8
  }[];
9
- export declare const createHonoHandler: (handler: Handler) => (c: Context) => Promise<any>;
9
+ export declare const createHonoHandler: (handler: Handler) => (c: Context) => Promise<void | Response>;
10
10
  export default createHonoRoutes;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Server-side cross-project policy wiring shared by the hono and effect BFF
3
+ * runtime adapters.
4
+ *
5
+ * The generated cross-project SDK force-enables `bff.crossProjectPolicy`
6
+ * (see `crossProjectApiPlugin`), so producer servers MUST actually evaluate
7
+ * it: this module resolves the policy once per adapter (deriving the
8
+ * operation-contract map and the producer operation version from the app's
9
+ * package.json major) and exposes a request check both adapters call before
10
+ * dispatching API handlers.
11
+ *
12
+ * The envelope/operation-context headers are client-asserted; see the threat
13
+ * model in `@modern-js/bff-core` `security/crossProjectPolicy.ts`. Operators
14
+ * who need a real trust boundary must additionally configure
15
+ * `verifyProducerIdentity` to bind the namespace to a verified channel.
16
+ */
17
+ import { type OperationContractSource, type ResolvedCrossProjectPolicy } from '@modern-js/bff-core';
18
+ import type { ServerPluginAPI } from '@modern-js/server-core';
19
+ export type { ResolvedCrossProjectPolicy };
20
+ /**
21
+ * Resolves the effective cross-project policy for a BFF runtime adapter.
22
+ * Returns `undefined` when no policy is configured and the server is not a
23
+ * generated cross-project producer (no middleware should be installed).
24
+ */
25
+ export declare const resolveAdapterCrossProjectPolicy: (api: ServerPluginAPI, handlers: OperationContractSource[]) => ResolvedCrossProjectPolicy | undefined;
26
+ /**
27
+ * Evaluates the policy against a header record and returns the denial
28
+ * Response every adapter must send, or `null` when the request passes.
29
+ */
30
+ export declare const checkCrossProjectPolicyResponse: (headers: Record<string, unknown>, policy: ResolvedCrossProjectPolicy | undefined) => Response | null;
31
+ /**
32
+ * Adapts the policy check to a WHATWG `Request` (effect lane seam): returns
33
+ * the denial Response or `null`.
34
+ */
35
+ export declare const checkCrossProjectPolicyForRequest: (request: Request, policy: ResolvedCrossProjectPolicy | undefined) => Response | null;
@@ -1,4 +1,5 @@
1
1
  import type { HttpMethodDecider } from '@modern-js/types';
2
+ import { type EffectEndpointMeta } from '../runtime/effect/endpoint-contracts';
2
3
  export type EffectClientCodegenOptions = {
3
4
  appDir: string;
4
5
  apiDir: string;
@@ -18,10 +19,23 @@ export type EffectClientCodegenOptions = {
18
19
  allowedMethods?: string[];
19
20
  };
20
21
  };
21
- export declare function renderEffectClientDeclaration(): string;
22
+ export type GeneratedEffectClientArtifacts = {
23
+ code: string;
24
+ declaration: string;
25
+ endpoints: EffectEndpointMeta[];
26
+ };
27
+ export declare function renderEffectClientDeclaration(endpoints?: EffectEndpointMeta[]): string;
28
+ /**
29
+ * Generates the Effect client module plus its type declaration. The module
30
+ * body is a thin manifest + one call into
31
+ * `@modern-js/plugin-bff/effect-client-runtime`; the declaration preserves
32
+ * the group/endpoint structure of the HttpApi instead of erasing it to
33
+ * `Record<string, ...>`.
34
+ */
35
+ export declare function generateEffectClient(options: EffectClientCodegenOptions): Promise<GeneratedEffectClientArtifacts | null>;
22
36
  export declare function generateEffectClientCode(options: EffectClientCodegenOptions): Promise<string | null>;
23
37
  export declare function resolveEffectEntryFile(options: {
24
38
  appDir: string;
25
39
  apiDir: string;
26
40
  effectEntry?: string;
27
- }): any;
41
+ }): string | false | undefined;
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.2.0-ultramodern.98",
20
+ "version": "3.4.0-ultramodern.0",
21
21
  "types": "./dist/types/cli.d.ts",
22
22
  "main": "./dist/cjs/cli.js",
23
23
  "exports": {
@@ -82,6 +82,15 @@
82
82
  },
83
83
  "default": "./dist/cjs/runtime/effect-client/index.js"
84
84
  },
85
+ "./effect-client-runtime": {
86
+ "types": "./dist/types/runtime/effect-client/runtime.d.ts",
87
+ "modern:source": "./src/runtime/effect-client/runtime.ts",
88
+ "node": {
89
+ "import": "./dist/esm-node/runtime/effect-client/runtime.mjs",
90
+ "require": "./dist/cjs/runtime/effect-client/runtime.js"
91
+ },
92
+ "default": "./dist/esm/runtime/effect-client/runtime.mjs"
93
+ },
85
94
  "./data-platform": {
86
95
  "types": "./dist/types/runtime/data-platform/index.d.ts",
87
96
  "node": {
@@ -121,6 +130,9 @@
121
130
  "effect-client": [
122
131
  "./dist/types/runtime/effect-client/index.d.ts"
123
132
  ],
133
+ "effect-client-runtime": [
134
+ "./dist/types/runtime/effect-client/runtime.d.ts"
135
+ ],
124
136
  "data-platform": [
125
137
  "./dist/types/runtime/data-platform/index.d.ts"
126
138
  ],
@@ -130,43 +142,44 @@
130
142
  }
131
143
  },
132
144
  "dependencies": {
133
- "@effect/opentelemetry": "4.0.0-beta.66",
145
+ "@effect/opentelemetry": "4.0.0-beta.84",
134
146
  "@opentelemetry/api": "1.9.1",
135
- "@opentelemetry/api-logs": "0.218.0",
136
- "@opentelemetry/resources": "2.7.1",
137
- "@opentelemetry/sdk-logs": "0.218.0",
138
- "@opentelemetry/sdk-metrics": "2.7.1",
139
- "@opentelemetry/sdk-trace-base": "2.7.1",
140
- "@opentelemetry/sdk-trace-node": "2.7.1",
141
- "@opentelemetry/sdk-trace-web": "2.7.1",
147
+ "@opentelemetry/api-logs": "0.219.0",
148
+ "@opentelemetry/resources": "2.8.0",
149
+ "@opentelemetry/sdk-logs": "0.219.0",
150
+ "@opentelemetry/sdk-metrics": "2.8.0",
151
+ "@opentelemetry/sdk-trace-base": "2.8.0",
152
+ "@opentelemetry/sdk-trace-node": "2.8.0",
153
+ "@opentelemetry/sdk-trace-web": "2.8.0",
142
154
  "@opentelemetry/semantic-conventions": "1.41.1",
143
- "@swc/core": "1.15.40",
155
+ "@swc/core": "1.15.43",
144
156
  "@swc/helpers": "^0.5.23",
145
- "effect": "4.0.0-beta.66",
157
+ "effect": "4.0.0-beta.84",
146
158
  "qs": "^6.15.2",
147
159
  "type-is": "^2.1.0",
148
- "@modern-js/bff-core": "npm:@bleedingdev/modern-js-bff-core@3.2.0-ultramodern.98",
149
- "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.2.0-ultramodern.98",
150
- "@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.2.0-ultramodern.98",
151
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.2.0-ultramodern.98",
152
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.98",
153
- "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.2.0-ultramodern.98"
160
+ "@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.4.0-ultramodern.0",
161
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.4.0-ultramodern.0",
162
+ "@modern-js/bff-core": "npm:@bleedingdev/modern-js-bff-core@3.4.0-ultramodern.0",
163
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.4.0-ultramodern.0",
164
+ "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.4.0-ultramodern.0",
165
+ "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.4.0-ultramodern.0"
154
166
  },
155
167
  "devDependencies": {
156
- "@rsbuild/core": "2.0.7",
157
- "@rslib/core": "0.21.5",
158
- "@types/node": "^25.9.1",
168
+ "@rsbuild/core": "2.0.15",
169
+ "@rslib/core": "0.23.0",
170
+ "@types/node": "^26.0.0",
159
171
  "@types/qs": "^6.15.1",
160
172
  "@types/type-is": "^1.6.7",
161
- "@typescript/native-preview": "7.0.0-dev.20260527.2",
162
- "memfs": "^4.57.2",
173
+ "@typescript/native-preview": "7.0.0-dev.20260624.1",
174
+ "memfs": "^4.57.8",
175
+ "typescript": "^6.0.3",
163
176
  "zod": "^4.4.3",
164
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.98",
165
- "@modern-js/bff-runtime": "npm:@bleedingdev/modern-js-bff-runtime@3.2.0-ultramodern.98",
166
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.98",
167
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.98",
177
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.4.0-ultramodern.0",
178
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.4.0-ultramodern.0",
179
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.4.0-ultramodern.0",
168
180
  "@scripts/rstest-config": "2.66.0",
169
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.98"
181
+ "@modern-js/bff-runtime": "npm:@bleedingdev/modern-js-bff-runtime@3.4.0-ultramodern.0",
182
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.4.0-ultramodern.0"
170
183
  },
171
184
  "sideEffects": false,
172
185
  "publishConfig": {