@bleedingdev/modern-js-app-tools 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.
- package/dist/cjs/builder/generator/index.js +0 -1
- package/dist/cjs/builder/shared/builderPlugins/index.js +4 -11
- package/dist/cjs/config/index.js +4 -11
- package/dist/cjs/index.js +80 -111
- package/dist/cjs/plugins/deploy/platforms/node.js +0 -13
- package/dist/cjs/plugins/serverBuild.js +2 -1
- package/dist/esm/builder/generator/index.mjs +1 -2
- package/dist/esm/builder/shared/builderPlugins/index.mjs +0 -1
- package/dist/esm/config/index.mjs +0 -1
- package/dist/esm/index.mjs +1 -6
- package/dist/esm/plugins/deploy/platforms/node.mjs +0 -13
- package/dist/esm/plugins/serverBuild.mjs +2 -1
- package/dist/esm-node/builder/generator/index.mjs +1 -2
- package/dist/esm-node/builder/shared/builderPlugins/index.mjs +0 -1
- package/dist/esm-node/config/index.mjs +0 -1
- package/dist/esm-node/index.mjs +1 -6
- package/dist/esm-node/plugins/deploy/platforms/node.mjs +0 -13
- package/dist/esm-node/plugins/serverBuild.mjs +2 -1
- package/dist/types/builder/shared/builderPlugins/index.d.ts +0 -1
- package/dist/types/config/index.d.ts +0 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/plugins/deploy/utils/generator.d.ts +1 -1
- package/dist/types/types/config/output.d.ts +0 -9
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/plugin.d.ts +56 -2
- package/dist/types/utils/initAppContext.d.ts +3 -2
- package/docs/configure/app/bff/effect.md +29 -24
- package/docs/guides/advanced-features/bff/data-platform.md +2 -2
- package/docs/guides/advanced-features/bff/frameworks.md +36 -24
- package/docs/guides/advanced-features/bff/function.md +2 -2
- package/docs/guides/advanced-features/bff/operators.md +17 -17
- package/docs/guides/get-started/quick-start.md +1 -5
- package/docs/guides/get-started/ultramodern.md +5 -86
- package/docs/llms.txt +2 -2
- package/package.json +14 -30
- package/rslib.config.mts +0 -5
- package/dist/cjs/builder/shared/builderPlugins/adapterPrecompress.js +0 -125
- package/dist/cjs/config/build-environment.js +0 -268
- package/dist/cjs/config/public.js +0 -46
- package/dist/cjs/plugins/backend-federation/codegen.js +0 -256
- package/dist/cjs/plugins/backend-federation/config.js +0 -204
- package/dist/cjs/plugins/backend-federation/emit.js +0 -150
- package/dist/cjs/plugins/backendFederationBuild.js +0 -53
- package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier/index.js +0 -62
- package/dist/cjs/plugins/deploy/utils/npmAliases.js +0 -226
- package/dist/cjs/plugins/ultramodernReleaseEnvelope.js +0 -44
- package/dist/cjs/presetUltramodern.js +0 -175
- package/dist/cjs/types/config/precompress.js +0 -18
- package/dist/esm/builder/shared/builderPlugins/adapterPrecompress.mjs +0 -77
- package/dist/esm/config/build-environment.mjs +0 -224
- package/dist/esm/config/public.mjs +0 -1
- package/dist/esm/plugins/backend-federation/codegen.mjs +0 -205
- package/dist/esm/plugins/backend-federation/config.mjs +0 -128
- package/dist/esm/plugins/backend-federation/emit.mjs +0 -99
- package/dist/esm/plugins/backendFederationBuild.mjs +0 -1
- package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier/index.mjs +0 -1
- package/dist/esm/plugins/deploy/utils/npmAliases.mjs +0 -174
- package/dist/esm/plugins/ultramodernReleaseEnvelope.mjs +0 -6
- package/dist/esm/presetUltramodern.mjs +0 -119
- package/dist/esm/types/config/precompress.mjs +0 -0
- package/dist/esm-node/builder/shared/builderPlugins/adapterPrecompress.mjs +0 -78
- package/dist/esm-node/config/build-environment.mjs +0 -225
- package/dist/esm-node/config/public.mjs +0 -2
- package/dist/esm-node/plugins/backend-federation/codegen.mjs +0 -206
- package/dist/esm-node/plugins/backend-federation/config.mjs +0 -129
- package/dist/esm-node/plugins/backend-federation/emit.mjs +0 -100
- package/dist/esm-node/plugins/backendFederationBuild.mjs +0 -2
- package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier/index.mjs +0 -2
- package/dist/esm-node/plugins/deploy/utils/npmAliases.mjs +0 -175
- package/dist/esm-node/plugins/ultramodernReleaseEnvelope.mjs +0 -7
- package/dist/esm-node/presetUltramodern.mjs +0 -120
- package/dist/esm-node/types/config/precompress.mjs +0 -1
- package/dist/types/builder/shared/builderPlugins/adapterPrecompress.d.ts +0 -3
- package/dist/types/config/build-environment.d.ts +0 -22
- package/dist/types/config/public.d.ts +0 -2
- package/dist/types/plugins/backend-federation/codegen.d.ts +0 -86
- package/dist/types/plugins/backend-federation/config.d.ts +0 -103
- package/dist/types/plugins/backend-federation/emit.d.ts +0 -13
- package/dist/types/plugins/backendFederationBuild.d.ts +0 -2
- package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier/index.d.ts +0 -1
- package/dist/types/plugins/deploy/utils/npmAliases.d.ts +0 -14
- package/dist/types/plugins/ultramodernReleaseEnvelope.d.ts +0 -3
- package/dist/types/precompress/LICENSE +0 -20
- package/dist/types/precompress/index.d.ts +0 -76
- package/dist/types/presetUltramodern.d.ts +0 -87
- package/dist/types/types/config/precompress.d.ts +0 -19
|
@@ -5,7 +5,6 @@ import { chalk, fs, removeModuleSyncFromExports } from "@modern-js/utils";
|
|
|
5
5
|
import { nodeDepEmit } from "ndepe";
|
|
6
6
|
import { readTemplate, resolveESMDependency } from "../utils/index.mjs";
|
|
7
7
|
import { generateHandler } from "../utils/generator.mjs";
|
|
8
|
-
import { preserveNpmAliases, readPackageIdentity } from "../utils/npmAliases.mjs";
|
|
9
8
|
const createNodePreset = ({ appContext, modernConfig })=>{
|
|
10
9
|
const { appDirectory, distDirectory, moduleType } = appContext;
|
|
11
10
|
const isEsmProject = 'module' === moduleType;
|
|
@@ -38,7 +37,6 @@ const createNodePreset = ({ appContext, modernConfig })=>{
|
|
|
38
37
|
const filter = (filePath)=>!filePath.startsWith(staticDirectory) && !filePath.endsWith('.map');
|
|
39
38
|
const entry = isEsmProject ? await resolveESMDependency('@modern-js/prod-server') : require.resolve('@modern-js/prod-server');
|
|
40
39
|
if (!entry) throw new Error('Cannot find @modern-js/prod-server');
|
|
41
|
-
const prodServerPackage = await readPackageIdentity(entry);
|
|
42
40
|
await nodeDepEmit({
|
|
43
41
|
appDir: appDirectory,
|
|
44
42
|
sourceDir: outputDirectory,
|
|
@@ -57,17 +55,6 @@ const createNodePreset = ({ appContext, modernConfig })=>{
|
|
|
57
55
|
};
|
|
58
56
|
}
|
|
59
57
|
});
|
|
60
|
-
await preserveNpmAliases({
|
|
61
|
-
appDirectory,
|
|
62
|
-
outputDirectory,
|
|
63
|
-
implicitAliases: [
|
|
64
|
-
{
|
|
65
|
-
aliasName: '@modern-js/prod-server',
|
|
66
|
-
targetName: prodServerPackage.name,
|
|
67
|
-
targetVersion: prodServerPackage.version
|
|
68
|
-
}
|
|
69
|
-
]
|
|
70
|
-
});
|
|
71
58
|
console.log('Static directory:', chalk.blue(node_path.relative(appDirectory, staticDirectory).replace(/\\/g, '/')));
|
|
72
59
|
console.log("You can preview this build by", chalk.blue('node .output/index'));
|
|
73
60
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { AppTools, CliPlugin } from './types/index.js';
|
|
2
2
|
import { initAppContext } from './utils/initAppContext.js';
|
|
3
3
|
export * from './defineConfig.js';
|
|
4
|
-
export * from './presetUltramodern.js';
|
|
5
4
|
export declare const appTools: () => CliPlugin<AppTools>;
|
|
6
5
|
export { build } from './commands/build.js';
|
|
7
6
|
export { deploy } from './commands/deploy.js';
|
|
@@ -6,7 +6,7 @@ export declare const serverAppContextTemplate: (appContext: AppToolsContext) =>
|
|
|
6
6
|
apiDirectory: string;
|
|
7
7
|
lambdaDirectory: string;
|
|
8
8
|
metaName: string;
|
|
9
|
-
bffRuntimeFramework: "
|
|
9
|
+
bffRuntimeFramework: "hono";
|
|
10
10
|
};
|
|
11
11
|
export type PluginItem = [string, Record<string, any> | undefined];
|
|
12
12
|
export declare const genPluginImportsCode: (plugins: PluginItem[], isESM?: boolean) => string;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { BuilderConfig } from '@modern-js/builder';
|
|
2
2
|
import type { SSGConfig, SSGMultiEntryOptions } from '@modern-js/types';
|
|
3
3
|
import type { UnwrapBuilderConfig } from '../utils.js';
|
|
4
|
-
import type { PrecompressConfig } from './precompress.js';
|
|
5
|
-
export type { PrecompressCodecOptions, PrecompressConfig } from './precompress.js';
|
|
6
4
|
export interface OutputUserConfig extends UnwrapBuilderConfig<BuilderConfig, 'output'> {
|
|
7
5
|
/**
|
|
8
6
|
* Enable SSG for self-controlled routing or conventional routing.
|
|
@@ -34,12 +32,5 @@ export interface OutputUserConfig extends UnwrapBuilderConfig<BuilderConfig, 'ou
|
|
|
34
32
|
* Specify the temporary directory for framework generated files.
|
|
35
33
|
*/
|
|
36
34
|
tempDir?: string;
|
|
37
|
-
/**
|
|
38
|
-
* Generate precompressed static assets by using compression-webpack-plugin.
|
|
39
|
-
* `true` means enabling both gzip and brotli with default options.
|
|
40
|
-
* Disabled unless explicitly configured; `presetUltramodern` enables it.
|
|
41
|
-
* @default false
|
|
42
|
-
*/
|
|
43
|
-
precompress?: boolean | PrecompressConfig;
|
|
44
35
|
}
|
|
45
36
|
export type { SSGConfig, SSGMultiEntryOptions, SSGRouteOptions, SSGSingleEntryOptions, } from '@modern-js/types';
|
|
@@ -7,4 +7,4 @@ export type { CLIPluginExtends, RuntimePluginConfig, ServerPluginConfig };
|
|
|
7
7
|
export type AppUserConfig = AppToolsUserConfig;
|
|
8
8
|
export type AppNormalizedConfig = AppToolsNormalizedConfig;
|
|
9
9
|
export type AppToolsAPI = CLIPluginAPI<AppTools>;
|
|
10
|
-
export type { AppToolsContext, AppToolsExtendAPI, AppToolsExtendContext, AppToolsExtendHooks, AppToolsHooks as AppToolsFeatureHooks, } from './plugin.js';
|
|
10
|
+
export type { AfterBffCompileFn, AppToolsContext, AppToolsExtendAPI, AppToolsExtendContext, AppToolsExtendHooks, AppToolsHooks as AppToolsFeatureHooks, BeforeBffCompileFn, BffClientArtifact, BffClientArtifacts, BffCompilation, BffGeneratedEntries, BffGeneratedModule, BffGeneration, ModifyBffClientArtifactsFn, ModifyBffGeneratedEntriesFn, } from './plugin.js';
|
|
@@ -1,11 +1,59 @@
|
|
|
1
1
|
import type { AppContext, AsyncHook, InternalContext, PluginHook, PluginHookTap, TransformFunction } from '@modern-js/plugin';
|
|
2
2
|
import type { Hooks } from '@modern-js/plugin/cli';
|
|
3
|
-
import type {
|
|
3
|
+
import type { BffRuntimeFramework } from '@modern-js/plugin/server';
|
|
4
|
+
import type { Entrypoint, HtmlPartials, HtmlTemplates, HttpMethodDecider, NestedRouteForCli, PageRoute, RouteLegacy, ServerPlugin, ServerRoute } from '@modern-js/types';
|
|
4
5
|
import type { EagerRouteComponentFilesByEntry } from '@modern-js/utils';
|
|
5
6
|
import type { EnvironmentConfig } from '@rsbuild/core';
|
|
6
7
|
import type { getHookRunners } from '../compat/hooks.js';
|
|
7
8
|
import type { AppTools } from './index.js';
|
|
8
9
|
import type { AppToolsNormalizedConfig, AppToolsUserConfig } from './config/index.js';
|
|
10
|
+
export interface BffCompilation {
|
|
11
|
+
readonly appDirectory: string;
|
|
12
|
+
readonly apiDirectory: string;
|
|
13
|
+
readonly sourceDirectories: readonly string[];
|
|
14
|
+
readonly outputDirectories: readonly string[];
|
|
15
|
+
readonly distDirectory: string;
|
|
16
|
+
readonly tsconfigPath?: string;
|
|
17
|
+
readonly moduleType: AppToolsContext['moduleType'];
|
|
18
|
+
}
|
|
19
|
+
export interface BffGeneration {
|
|
20
|
+
readonly appDirectory: string;
|
|
21
|
+
readonly apiDirectory: string;
|
|
22
|
+
readonly lambdaDirectory: string;
|
|
23
|
+
readonly existLambda: boolean;
|
|
24
|
+
readonly apiFiles: readonly string[];
|
|
25
|
+
readonly relativeDistPath: string;
|
|
26
|
+
readonly prefix: string;
|
|
27
|
+
readonly port?: number;
|
|
28
|
+
readonly requestId: string;
|
|
29
|
+
readonly requestCreator?: string;
|
|
30
|
+
readonly httpMethodDecider?: HttpMethodDecider;
|
|
31
|
+
}
|
|
32
|
+
export interface BffClientArtifact {
|
|
33
|
+
/** Canonical relative source path under generation.apiDirectory. */
|
|
34
|
+
readonly sourcePath: string;
|
|
35
|
+
readonly code: string;
|
|
36
|
+
readonly declaration: string;
|
|
37
|
+
}
|
|
38
|
+
export interface BffClientArtifacts {
|
|
39
|
+
readonly generation: BffGeneration;
|
|
40
|
+
additionalArtifacts: BffClientArtifact[];
|
|
41
|
+
}
|
|
42
|
+
export interface BffGeneratedModule {
|
|
43
|
+
readonly code: string;
|
|
44
|
+
readonly declaration: string;
|
|
45
|
+
}
|
|
46
|
+
export interface BffGeneratedEntries {
|
|
47
|
+
readonly generation: BffGeneration;
|
|
48
|
+
plugin: BffGeneratedModule;
|
|
49
|
+
runtime: BffGeneratedModule | null;
|
|
50
|
+
/** Direct dependencies required by the emitted SDK modules. */
|
|
51
|
+
packageDependencies: Record<string, string>;
|
|
52
|
+
}
|
|
53
|
+
export type BeforeBffCompileFn = (context: BffCompilation) => void | Promise<void>;
|
|
54
|
+
export type AfterBffCompileFn = (context: BffCompilation) => void | Promise<void>;
|
|
55
|
+
export type ModifyBffClientArtifactsFn = TransformFunction<BffClientArtifacts>;
|
|
56
|
+
export type ModifyBffGeneratedEntriesFn = TransformFunction<BffGeneratedEntries>;
|
|
9
57
|
export type AfterPrepareFn = () => Promise<void> | void;
|
|
10
58
|
export type CheckEntryPointFn = TransformFunction<{
|
|
11
59
|
path: string;
|
|
@@ -64,6 +112,10 @@ export interface AppToolsExtendAPI {
|
|
|
64
112
|
useHookRunners: () => ReturnType<typeof getHookRunners>;
|
|
65
113
|
}
|
|
66
114
|
export interface AppToolsExtendHooks extends Record<string, PluginHook<(...args: any[]) => any>> {
|
|
115
|
+
onBeforeBffCompile: AsyncHook<BeforeBffCompileFn>;
|
|
116
|
+
onAfterBffCompile: AsyncHook<AfterBffCompileFn>;
|
|
117
|
+
modifyBffClientArtifacts: AsyncHook<ModifyBffClientArtifactsFn>;
|
|
118
|
+
modifyBffGeneratedEntries: AsyncHook<ModifyBffGeneratedEntriesFn>;
|
|
67
119
|
onAfterPrepare: AsyncHook<AfterPrepareFn>;
|
|
68
120
|
deploy: AsyncHook<DeplpoyFn>;
|
|
69
121
|
checkEntryPoint: AsyncHook<CheckEntryPointFn>;
|
|
@@ -88,6 +140,8 @@ export interface AppToolsExtendContext {
|
|
|
88
140
|
runtimeConfigFile: string;
|
|
89
141
|
serverPlugins: ServerPlugin[];
|
|
90
142
|
moduleType: 'module' | 'commonjs';
|
|
143
|
+
/** Exact generated files excluded from server compilation. */
|
|
144
|
+
serverCompileExcludedFiles?: string[];
|
|
91
145
|
/** Information for entry points */
|
|
92
146
|
entrypoints: Entrypoint[];
|
|
93
147
|
/** Selected entry points */
|
|
@@ -115,7 +169,7 @@ export interface AppToolsExtendContext {
|
|
|
115
169
|
* Identification for bff runtime framework
|
|
116
170
|
* @private
|
|
117
171
|
*/
|
|
118
|
-
bffRuntimeFramework?:
|
|
172
|
+
bffRuntimeFramework?: BffRuntimeFramework;
|
|
119
173
|
/**
|
|
120
174
|
* Route component files collected from the FINAL file-system routes (after all
|
|
121
175
|
* `modifyFileSystemRoutes` consumers ran), keyed by entry name. Populated by
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BffRuntimeFramework } from '@modern-js/plugin/server';
|
|
1
2
|
export declare const initAppContext: ({ metaName, appDirectory, runtimeConfigFile, options, tempDir, }: {
|
|
2
3
|
metaName: string;
|
|
3
4
|
appDirectory: string;
|
|
@@ -7,7 +8,7 @@ export declare const initAppContext: ({ metaName, appDirectory, runtimeConfigFil
|
|
|
7
8
|
apiDir?: string;
|
|
8
9
|
distDir?: string;
|
|
9
10
|
sharedDir?: string;
|
|
10
|
-
bffRuntimeFramework?:
|
|
11
|
+
bffRuntimeFramework?: BffRuntimeFramework;
|
|
11
12
|
};
|
|
12
13
|
tempDir?: string;
|
|
13
14
|
}) => {
|
|
@@ -27,5 +28,5 @@ export declare const initAppContext: ({ metaName, appDirectory, runtimeConfigFil
|
|
|
27
28
|
apiOnly: boolean;
|
|
28
29
|
internalDirAlias: string;
|
|
29
30
|
internalSrcAlias: string;
|
|
30
|
-
bffRuntimeFramework: "
|
|
31
|
+
bffRuntimeFramework: "hono";
|
|
31
32
|
};
|
|
@@ -45,21 +45,33 @@ Please refer to the [Enable BFF](/guides/advanced-features/bff/function.md#enabl
|
|
|
45
45
|
|
|
46
46
|
:::caution Install the Effect peers yourself
|
|
47
47
|
`effect` and `@effect/opentelemetry` are **optional exact peer dependencies** of
|
|
48
|
-
`@modern-js/
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
`/effect-server`, `/effect-edge` or `/effect-client`, install the exact cohort:
|
|
48
|
+
`@modern-js/bff-effect`. Install them in the application so its API modules and
|
|
49
|
+
the framework use the same Effect instance. Before setting
|
|
50
|
+
`runtimeFramework: 'effect'` or importing `@modern-js/bff-effect/effect`,
|
|
51
|
+
`/effect-edge` or `/effect-client`, install the exact cohort:
|
|
53
52
|
|
|
54
53
|
```bash
|
|
55
54
|
pnpm add effect@4.0.0-rc.112 @effect/opentelemetry@4.0.0-rc.112
|
|
56
55
|
```
|
|
57
56
|
|
|
58
57
|
The pin is exact because UltraModern ships Effect as one lockstep cohort. Apps
|
|
59
|
-
using only `runtimeFramework: 'hono'` or
|
|
60
|
-
package.
|
|
58
|
+
using only native `runtimeFramework: 'hono'` or
|
|
59
|
+
`@modern-js/bff-effect/data-platform` need neither package.
|
|
61
60
|
:::
|
|
62
61
|
|
|
62
|
+
Use `bffPlugin` from `@modern-js/plugin-bff-build-extensions` to register the
|
|
63
|
+
Effect runtime. It composes the native BFF plugin. Install
|
|
64
|
+
`@modern-js/bff-effect` and `@modern-js/plugin-bff-extensions` from the same
|
|
65
|
+
framework cohort as application production dependencies, so the adapter remains
|
|
66
|
+
available after development dependencies are removed. The build plugin can be a
|
|
67
|
+
development dependency. See the [runtime configuration example](/guides/advanced-features/bff/frameworks.md).
|
|
68
|
+
|
|
69
|
+
Native `@modern-js/plugin-bff/server` exports Hono APIs. For Node Effect APIs,
|
|
70
|
+
import framework helpers such as `defineEffectBff` from
|
|
71
|
+
`@modern-js/bff-effect/effect` and namespaces from the corresponding `effect/*`
|
|
72
|
+
modules. Worker handlers and worker request context use
|
|
73
|
+
`@modern-js/bff-effect/effect-edge`.
|
|
74
|
+
|
|
63
75
|
Generated UltraModern workspaces use this runtime as the only generated HTTP API
|
|
64
76
|
path. The API contract lives at `shared/api.ts`, the server runtime lives at
|
|
65
77
|
`api/index.ts`, clients live under `src/api/*-client.ts`, and generated checks
|
|
@@ -171,7 +183,6 @@ export default defineConfig({
|
|
|
171
183
|
endpoint: '/_data/batch',
|
|
172
184
|
maxBatchSize: 16,
|
|
173
185
|
maxBatchBytes: 64 * 1024,
|
|
174
|
-
flushIntervalMs: 8,
|
|
175
186
|
maxConcurrency: 4,
|
|
176
187
|
requestTimeoutMs: 10000,
|
|
177
188
|
allowedMethods: ['GET'],
|
|
@@ -205,9 +216,9 @@ export default defineConfig({
|
|
|
205
216
|
});
|
|
206
217
|
```
|
|
207
218
|
|
|
208
|
-
`
|
|
219
|
+
`maxConcurrency` and `requestTimeoutMs` control the server batch gateway. Native `HttpApiClient` calls send individual requests; they do not automatically batch requests.
|
|
209
220
|
|
|
210
|
-
|
|
221
|
+
Import a shared `HttpApi` contract and pass it to `HttpApiClient.make` or `makeEffectHttpApiClient`. The client is fully type-inferred; `defineEffectBff` exposes server handlers and does not contain a client.
|
|
211
222
|
|
|
212
223
|
## Effect cohort
|
|
213
224
|
|
|
@@ -242,7 +253,7 @@ Strict Effect APIs should test the declared `HttpApi` contract, not a raw
|
|
|
242
253
|
request handler. Edge-compatible tests can use the framework helper:
|
|
243
254
|
|
|
244
255
|
```ts
|
|
245
|
-
import { createEffectBffTestHandler } from '@modern-js/
|
|
256
|
+
import { createEffectBffTestHandler } from '@modern-js/bff-effect/effect-edge';
|
|
246
257
|
import apiModule from '../api/index';
|
|
247
258
|
|
|
248
259
|
const testApi = await createEffectBffTestHandler({
|
|
@@ -257,12 +268,9 @@ If you manually compose an Effect web handler in a low-level proof, provide the
|
|
|
257
268
|
platform services explicitly:
|
|
258
269
|
|
|
259
270
|
```ts
|
|
260
|
-
import
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
HttpServer,
|
|
264
|
-
Layer,
|
|
265
|
-
} from '@modern-js/plugin-bff/effect-server';
|
|
271
|
+
import * as Layer from 'effect/Layer';
|
|
272
|
+
import { HttpRouter, HttpServer } from 'effect/unstable/http';
|
|
273
|
+
import { HttpApiBuilder } from 'effect/unstable/httpapi';
|
|
266
274
|
|
|
267
275
|
const handler = HttpRouter.toWebHandler(
|
|
268
276
|
HttpApiBuilder.layer(api).pipe(
|
|
@@ -282,13 +290,10 @@ current Effect v4 beta cohort, `HttpRouter.middleware(...)` returns a `Layer`
|
|
|
282
290
|
directly:
|
|
283
291
|
|
|
284
292
|
```ts
|
|
285
|
-
import
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
HttpRouter,
|
|
290
|
-
Layer,
|
|
291
|
-
} from '@modern-js/plugin-bff/effect-server';
|
|
293
|
+
import * as Effect from 'effect/Effect';
|
|
294
|
+
import * as Layer from 'effect/Layer';
|
|
295
|
+
import { HttpMiddleware, HttpRouter } from 'effect/unstable/http';
|
|
296
|
+
import { HttpApiBuilder } from 'effect/unstable/httpapi';
|
|
292
297
|
|
|
293
298
|
const corsLayer = HttpRouter.middleware(
|
|
294
299
|
Effect.succeed(
|
|
@@ -15,7 +15,7 @@ Modern.js Effect API runtime now supports a request-envelope based data platform
|
|
|
15
15
|
|
|
16
16
|
## Runtime contract helpers
|
|
17
17
|
|
|
18
|
-
Use `@modern-js/
|
|
18
|
+
Use `@modern-js/bff-effect/data-platform` helpers to build and validate contracts:
|
|
19
19
|
|
|
20
20
|
```ts
|
|
21
21
|
import {
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
validateHydrationEnvelope,
|
|
29
29
|
createInvalidationEvent,
|
|
30
30
|
shouldApplyInvalidation,
|
|
31
|
-
} from '@modern-js/
|
|
31
|
+
} from '@modern-js/bff-effect/data-platform';
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
## Effect runtime validation
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
# Runtime Framework
|
|
4
4
|
|
|
5
|
-
Modern.js
|
|
5
|
+
Modern.js and the UltraModern BFF extension provide two runtime frameworks:
|
|
6
6
|
|
|
7
|
-
- `
|
|
8
|
-
- `
|
|
7
|
+
- `hono` is the native plugin default and uses file-convention handlers from `api/lambda/**`.
|
|
8
|
+
- `effect` is the UltraModern extension default and uses [Effect HttpApi](https://effect.website/) from `api/index`.
|
|
9
9
|
|
|
10
10
|
`effect` and `hono` are strict runtime modes. There is no automatic fallback between them.
|
|
11
11
|
|
|
@@ -16,12 +16,18 @@ Generated UltraModern workspaces use strict Effect APIs: author HTTP APIs in
|
|
|
16
16
|
|
|
17
17
|
## Switch to Effect runtime
|
|
18
18
|
|
|
19
|
+
Use the fork build plugin below. It includes the native BFF plugin and registers
|
|
20
|
+
the Effect adapter. The application needs `@modern-js/bff-effect` and
|
|
21
|
+
`@modern-js/plugin-bff-extensions` as production dependencies from the same
|
|
22
|
+
framework cohort. Install the exact Effect peers described in
|
|
23
|
+
[`bff.effect`](/configure/app/bff/effect.md).
|
|
24
|
+
|
|
19
25
|
```ts title="modern.config.ts"
|
|
20
|
-
import { bffPlugin } from '@modern-js/plugin-bff';
|
|
21
|
-
import { defineConfig } from '@modern-js/app-tools';
|
|
26
|
+
import { bffPlugin } from '@modern-js/plugin-bff-build-extensions';
|
|
27
|
+
import { appTools, defineConfig } from '@modern-js/app-tools';
|
|
22
28
|
|
|
23
29
|
export default defineConfig({
|
|
24
|
-
plugins: [bffPlugin()],
|
|
30
|
+
plugins: [appTools(), bffPlugin()],
|
|
25
31
|
bff: {
|
|
26
32
|
runtimeFramework: 'effect',
|
|
27
33
|
effect: {
|
|
@@ -43,7 +49,7 @@ import {
|
|
|
43
49
|
HttpApiEndpoint,
|
|
44
50
|
HttpApiGroup,
|
|
45
51
|
Schema,
|
|
46
|
-
} from '@modern-js/
|
|
52
|
+
} from '@modern-js/bff-effect/effect-client';
|
|
47
53
|
|
|
48
54
|
export const bffApi = HttpApi.make('MyApi').add(
|
|
49
55
|
HttpApiGroup.make('hello').add(
|
|
@@ -57,14 +63,12 @@ export const bffApi = HttpApi.make('MyApi').add(
|
|
|
57
63
|
Implement your Effect API entry at `api/index.ts`:
|
|
58
64
|
|
|
59
65
|
```ts title="api/index.ts"
|
|
60
|
-
import {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
ServiceMap,
|
|
67
|
-
} from '@modern-js/plugin-bff/effect-server';
|
|
66
|
+
import { defineEffectBff } from '@modern-js/bff-effect/effect';
|
|
67
|
+
import * as Context from 'effect/Context';
|
|
68
|
+
import * as Effect from 'effect/Effect';
|
|
69
|
+
import * as Layer from 'effect/Layer';
|
|
70
|
+
import * as Schema from 'effect/Schema';
|
|
71
|
+
import { HttpApiBuilder } from 'effect/unstable/httpapi';
|
|
68
72
|
import { bffApi } from '../shared/api';
|
|
69
73
|
|
|
70
74
|
class GreetingUnavailableError extends Schema.TaggedError<GreetingUnavailableError>()(
|
|
@@ -74,7 +78,7 @@ class GreetingUnavailableError extends Schema.TaggedError<GreetingUnavailableErr
|
|
|
74
78
|
},
|
|
75
79
|
) {}
|
|
76
80
|
|
|
77
|
-
class GreetingService extends
|
|
81
|
+
class GreetingService extends Context.Service<GreetingService>()('GreetingService', {
|
|
78
82
|
make: Effect.succeed({
|
|
79
83
|
hello: Effect.fn('GreetingService.hello')(function* () {
|
|
80
84
|
if (Date.now() < 0) {
|
|
@@ -109,19 +113,27 @@ const layer = HttpApiBuilder.layer(bffApi).pipe(
|
|
|
109
113
|
export default defineEffectBff({ api: bffApi, layer });
|
|
110
114
|
```
|
|
111
115
|
|
|
112
|
-
|
|
116
|
+
Create a native, fully inferred client from the shared contract:
|
|
113
117
|
|
|
114
118
|
```ts title="src/routes/page.tsx"
|
|
115
|
-
import
|
|
119
|
+
import { Effect, makeEffectHttpApiClient } from '@modern-js/bff-effect/effect-client';
|
|
120
|
+
import { bffApi } from '../../shared/api';
|
|
116
121
|
|
|
117
|
-
const response = await
|
|
122
|
+
const response = await Effect.runPromise(
|
|
123
|
+
makeEffectHttpApiClient(bffApi, { baseUrl: '/api' }).pipe(
|
|
124
|
+
Effect.flatMap(client => client.hello.ping({})),
|
|
125
|
+
),
|
|
126
|
+
);
|
|
118
127
|
```
|
|
119
128
|
|
|
120
|
-
|
|
129
|
+
Requests, responses, and declared errors are inferred from `bffApi`. No client generation or server-entry import is needed.
|
|
121
130
|
|
|
122
131
|
For UltraModern, Effect `HttpApi` plus Effect BFF is the single blessed authored HTTP path. Use `HttpApi` endpoints with `query`, `params`, `payload`, `success`, and declared errors such as `HttpApiSchema.status(...)`; implement them with `HttpApiBuilder.group(...).handle(...)` and `HttpApiBuilder.layer(...).pipe(Layer.provide(...))`, then default-export the entry as `defineEffectBff({ api, layer })`. See `packages/server/bff-effect/tests/effect-edge-runtime.test.ts` for the live runtime shape.
|
|
123
132
|
|
|
124
|
-
Hono
|
|
133
|
+
Native Hono applications import operators from `@modern-js/plugin-bff/server`.
|
|
134
|
+
UltraModern-generated applications keep their strict Effect API model. Worker
|
|
135
|
+
handlers use `@modern-js/bff-effect/effect-edge`, including its worker request
|
|
136
|
+
context exports; Node handlers use the Effect entry shown above.
|
|
125
137
|
|
|
126
138
|
### Getting Request Context
|
|
127
139
|
|
|
@@ -146,7 +158,7 @@ For more details, refer to [useHonoContext](/apis/app/runtime/bff/use-backend-co
|
|
|
146
158
|
When getting cookies in BFF functions, you need to get the request context through `useHonoContext`, then use `c.req.header('cookie')` to get the Cookie string and parse it manually:
|
|
147
159
|
|
|
148
160
|
```ts title="api/lambda/cookies.ts"
|
|
149
|
-
import { Api, Get } from '@modern-js/plugin-bff/
|
|
161
|
+
import { Api, Get } from '@modern-js/plugin-bff/server';
|
|
150
162
|
import { useHonoContext } from '@modern-js/server-runtime';
|
|
151
163
|
|
|
152
164
|
// Helper function to parse Cookie string
|
|
@@ -189,7 +201,7 @@ The `c.req.cookie()` method does not exist in the current version. You need to u
|
|
|
189
201
|
When using Hono as the runtime framework, you can define interfaces through [Api functions](/guides/advanced-features/bff/operators.md):
|
|
190
202
|
|
|
191
203
|
```ts title="api/lambda/user.ts"
|
|
192
|
-
import { Api, Get, Query } from '@modern-js/plugin-bff/
|
|
204
|
+
import { Api, Get, Query } from '@modern-js/plugin-bff/server';
|
|
193
205
|
import { z } from 'zod';
|
|
194
206
|
|
|
195
207
|
const QuerySchema = z.object({
|
|
@@ -218,7 +230,7 @@ For more details about Api functions and operators, refer to [Creating Extensibl
|
|
|
218
230
|
Hono supports a rich middleware ecosystem, and you can use middleware in BFF functions:
|
|
219
231
|
|
|
220
232
|
```ts title="api/lambda/user.ts"
|
|
221
|
-
import { Api, Get, Middleware } from '@modern-js/plugin-bff/
|
|
233
|
+
import { Api, Get, Middleware } from '@modern-js/plugin-bff/server';
|
|
222
234
|
|
|
223
235
|
export const getUser = Api(
|
|
224
236
|
Get('/user'),
|
|
@@ -246,7 +246,7 @@ Parameters following the dynamic path are an object called `RequestOption`, whic
|
|
|
246
246
|
In a standard function without dynamic routes, `RequestOption` can be obtained from the first parameter, for example:
|
|
247
247
|
|
|
248
248
|
```ts title="api/lambda/hello.ts"
|
|
249
|
-
import type { RequestOption } from '@modern-js/plugin-bff/
|
|
249
|
+
import type { RequestOption } from '@modern-js/plugin-bff/server';
|
|
250
250
|
|
|
251
251
|
export async function post({
|
|
252
252
|
query,
|
|
@@ -259,7 +259,7 @@ export async function post({
|
|
|
259
259
|
Custom types can also be used here:
|
|
260
260
|
|
|
261
261
|
```ts title="api/lambda/hello.ts"
|
|
262
|
-
import type { RequestOption } from '@modern-js/plugin-bff/
|
|
262
|
+
import type { RequestOption } from '@modern-js/plugin-bff/server';
|
|
263
263
|
|
|
264
264
|
type IQuery = {
|
|
265
265
|
// some types
|
|
@@ -29,7 +29,7 @@ A BFF function created by the `Api` function consists of the following parts:
|
|
|
29
29
|
The server can define the input parameters and types of the interface. Based on the types, the server will automatically perform type validation at runtime:
|
|
30
30
|
|
|
31
31
|
```typescript title="api/lambda/user.ts"
|
|
32
|
-
import { Api, Post, Query, Data } from '@modern-js/plugin-bff/
|
|
32
|
+
import { Api, Post, Query, Data } from '@modern-js/plugin-bff/server';
|
|
33
33
|
import { z } from 'zod';
|
|
34
34
|
|
|
35
35
|
const UserSchema = z.object({
|
|
@@ -78,7 +78,7 @@ addUser({
|
|
|
78
78
|
As shown in the example below, you can specify the route and HTTP Method through the `Get` function:
|
|
79
79
|
|
|
80
80
|
```typescript title="api/lambda/user.ts"
|
|
81
|
-
import { Api, Get, Query, Data } from '@modern-js/plugin-bff/
|
|
81
|
+
import { Api, Get, Query, Data } from '@modern-js/plugin-bff/server';
|
|
82
82
|
|
|
83
83
|
// Specify the interface route, Modern.js sets `bff.prefix` to `/api` by default,
|
|
84
84
|
// so the interface route is `/api/user`, and the HTTP Method is GET.
|
|
@@ -92,7 +92,7 @@ export const getHello = Api(
|
|
|
92
92
|
When the route is not specified, the interface route is defined according to the file convention. As shown in the example below, with the function writing method, there is a code path `api/lambda/user.ts`, which will register the corresponding interface `/api/user`.
|
|
93
93
|
|
|
94
94
|
```typescript title="api/lambda/user.ts"
|
|
95
|
-
import { Api, Get, Query, Data } from '@modern-js/plugin-bff/
|
|
95
|
+
import { Api, Get, Query, Data } from '@modern-js/plugin-bff/server';
|
|
96
96
|
|
|
97
97
|
// No interface route specified, according to file convention and function name, the interface is api/user, HTTP Method is get.
|
|
98
98
|
export const get = Api(Query(UserSchema), async ({ query }) => query);
|
|
@@ -125,7 +125,7 @@ Using the `Query` function, you can define the type of query. After using the `Q
|
|
|
125
125
|
|
|
126
126
|
```typescript title="api/lambda/user.ts"
|
|
127
127
|
// Server-side code
|
|
128
|
-
import { Api, Query } from '@modern-js/plugin-bff/
|
|
128
|
+
import { Api, Query } from '@modern-js/plugin-bff/server';
|
|
129
129
|
import { z } from 'zod';
|
|
130
130
|
|
|
131
131
|
const UserSchema = z.object({
|
|
@@ -153,7 +153,7 @@ get({
|
|
|
153
153
|
URL query parameters are strings by default. If you need numeric types, you need to use `z.coerce.number()` for type conversion:
|
|
154
154
|
|
|
155
155
|
```typescript title="api/lambda/user.ts"
|
|
156
|
-
import { Api, Get, Query } from '@modern-js/plugin-bff/
|
|
156
|
+
import { Api, Get, Query } from '@modern-js/plugin-bff/server';
|
|
157
157
|
import { z } from 'zod';
|
|
158
158
|
|
|
159
159
|
const QuerySchema = z.object({
|
|
@@ -189,7 +189,7 @@ If you use the Data function, you must follow the HTTP protocol. When the HTTP M
|
|
|
189
189
|
:::
|
|
190
190
|
|
|
191
191
|
```typescript title="api/lambda/user.ts"
|
|
192
|
-
import { Api, Data } from '@modern-js/plugin-bff/
|
|
192
|
+
import { Api, Data } from '@modern-js/plugin-bff/server';
|
|
193
193
|
import { z } from 'zod';
|
|
194
194
|
|
|
195
195
|
const DataSchema = z.object({
|
|
@@ -218,7 +218,7 @@ post({
|
|
|
218
218
|
Route parameters can implement dynamic routes and get parameters from the path. You can specify path parameters through `Params<T>(schema: z.ZodType<T>)`
|
|
219
219
|
|
|
220
220
|
```typescript
|
|
221
|
-
import { Api, Get, Params } from '@modern-js/plugin-bff/
|
|
221
|
+
import { Api, Get, Params } from '@modern-js/plugin-bff/server';
|
|
222
222
|
import { z } from 'zod';
|
|
223
223
|
|
|
224
224
|
const UserSchema = z.object({
|
|
@@ -239,7 +239,7 @@ export const queryUser = Api(
|
|
|
239
239
|
You can define the request headers required by the interface through the `Headers<T>(schema: z.ZodType<T>)` function and pass the request headers through integrated calls:
|
|
240
240
|
|
|
241
241
|
```typescript
|
|
242
|
-
import { Api, Headers } from '@modern-js/plugin-bff/
|
|
242
|
+
import { Api, Headers } from '@modern-js/plugin-bff/server';
|
|
243
243
|
import { z } from 'zod';
|
|
244
244
|
|
|
245
245
|
const headerSchema = z.object({
|
|
@@ -297,7 +297,7 @@ The `Middleware` operator can be configured multiple times, and the execution or
|
|
|
297
297
|
:::
|
|
298
298
|
|
|
299
299
|
```typescript
|
|
300
|
-
import { Api, Query, Middleware } from '@modern-js/plugin-bff/
|
|
300
|
+
import { Api, Query, Middleware } from '@modern-js/plugin-bff/server';
|
|
301
301
|
import { z } from 'zod';
|
|
302
302
|
|
|
303
303
|
const UserSchema = z.object({
|
|
@@ -333,7 +333,7 @@ The `Pipe` operator can be configured multiple times. The execution order of fun
|
|
|
333
333
|
:::
|
|
334
334
|
|
|
335
335
|
```typescript
|
|
336
|
-
import { Api, Query, Pipe } from '@modern-js/plugin-bff/
|
|
336
|
+
import { Api, Query, Pipe } from '@modern-js/plugin-bff/server';
|
|
337
337
|
import { z } from 'zod';
|
|
338
338
|
|
|
339
339
|
const UserSchema = z.object({
|
|
@@ -361,7 +361,7 @@ export const get = Api(
|
|
|
361
361
|
Also,
|
|
362
362
|
|
|
363
363
|
```typescript
|
|
364
|
-
import { Api, Query, Pipe } from '@modern-js/plugin-bff/
|
|
364
|
+
import { Api, Query, Pipe } from '@modern-js/plugin-bff/server';
|
|
365
365
|
import { z } from 'zod';
|
|
366
366
|
|
|
367
367
|
const UserSchema = z.object({
|
|
@@ -392,7 +392,7 @@ export const get = Api(
|
|
|
392
392
|
If you need to do more custom operations on the response, you can pass a function to the `end` function. The input parameter of the function is Hono's Context (`c`), and you can operate on `c.req` and `c.res`:
|
|
393
393
|
|
|
394
394
|
```typescript
|
|
395
|
-
import { Api, Query, Pipe } from '@modern-js/plugin-bff/
|
|
395
|
+
import { Api, Query, Pipe } from '@modern-js/plugin-bff/server';
|
|
396
396
|
import { z } from 'zod';
|
|
397
397
|
|
|
398
398
|
const UserSchema = z.object({
|
|
@@ -432,7 +432,7 @@ The following are response-related operators. Through response operators, you ca
|
|
|
432
432
|
You can specify the status code returned by the interface through the `HttpCode(statusCode: number)` function
|
|
433
433
|
|
|
434
434
|
```typescript
|
|
435
|
-
import { Api, Query, Data, HttpCode } from '@modern-js/plugin-bff/
|
|
435
|
+
import { Api, Query, Data, HttpCode } from '@modern-js/plugin-bff/server';
|
|
436
436
|
import { z } from 'zod';
|
|
437
437
|
|
|
438
438
|
const UserSchema = z.object({
|
|
@@ -464,7 +464,7 @@ export const post = Api(
|
|
|
464
464
|
Supports setting response headers through the `SetHeaders(headers: Record<string, string>)` function
|
|
465
465
|
|
|
466
466
|
```typescript
|
|
467
|
-
import { Api, Get, SetHeaders } from '@modern-js/plugin-bff/
|
|
467
|
+
import { Api, Get, SetHeaders } from '@modern-js/plugin-bff/server';
|
|
468
468
|
|
|
469
469
|
export default Api(
|
|
470
470
|
Get('/hello'),
|
|
@@ -480,7 +480,7 @@ export default Api(
|
|
|
480
480
|
Supports redirecting the interface through `Redirect(url: string)`:
|
|
481
481
|
|
|
482
482
|
```typescript
|
|
483
|
-
import { Api, Get, Redirect } from '@modern-js/plugin-bff/
|
|
483
|
+
import { Api, Get, Redirect } from '@modern-js/plugin-bff/server';
|
|
484
484
|
|
|
485
485
|
export default Api(
|
|
486
486
|
Get('/hello'),
|
|
@@ -494,7 +494,7 @@ export default Api(
|
|
|
494
494
|
As mentioned above, through operators, you can get `query`, `data`, `params`, etc. in the input parameters of the interface processing function. But sometimes we need to get more request context information. At this time, we can get it through [`useHonoContext`](/apis/app/runtime/bff/use-backend-context.md):
|
|
495
495
|
|
|
496
496
|
```typescript title="api/lambda/user.ts"
|
|
497
|
-
import { Api, Get, Query } from '@modern-js/plugin-bff/
|
|
497
|
+
import { Api, Get, Query } from '@modern-js/plugin-bff/server';
|
|
498
498
|
import { useHonoContext } from '@modern-js/server-runtime';
|
|
499
499
|
import { z } from 'zod';
|
|
500
500
|
|
|
@@ -539,7 +539,7 @@ For specific comparisons of different solutions, you can refer to [Why Use Zod](
|
|
|
539
539
|
In frontend development, some server interfaces (such as some configuration interfaces) have long response times, but actually don't need to be updated for a long time. For such interfaces, we can set HTTP cache to improve page performance:
|
|
540
540
|
|
|
541
541
|
```typescript
|
|
542
|
-
import { Api, SetHeaders } from '@modern-js/plugin-bff/
|
|
542
|
+
import { Api, SetHeaders } from '@modern-js/plugin-bff/server';
|
|
543
543
|
|
|
544
544
|
export const get = Api(
|
|
545
545
|
// Cache will only take effect when using integrated calls or fetch for requests
|