@bleedingdev/modern-js-app-tools-extensions 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.
- package/dist/cjs/backend-federation-build/codegen.js +267 -0
- package/dist/cjs/backend-federation-build/config.js +239 -0
- package/dist/cjs/backend-federation-build/emit.js +154 -0
- package/dist/cjs/backend-federation-build/index.js +53 -0
- package/dist/cjs/build-config/build-environment.js +129 -0
- package/dist/cjs/build-config/precompress/plugin.js +124 -0
- package/dist/cjs/build-config/precompress/types.js +18 -0
- package/dist/cjs/build-config/public.js +43 -0
- package/dist/cjs/build-config/remote-address.js +58 -0
- package/dist/cjs/cloudflare/artifacts.js +8 -19
- package/dist/cjs/cloudflare/constants.js +1 -1
- package/dist/cjs/cloudflare/delivery-unit.js +23 -42
- package/dist/cjs/cloudflare/index.js +10 -2
- package/dist/cjs/cloudflare/worker-manifest.js +13 -4
- package/dist/cjs/cloudflare/wrangler-config.js +35 -7
- package/dist/cjs/cloudflare-builder.js +252 -75
- package/dist/cjs/cloudflare-output-contract.js +69 -3
- package/dist/cjs/cloudflare-output-verifier/identity.js +29 -27
- package/dist/cjs/cloudflare-output-verifier/index.js +10 -0
- package/dist/cjs/cloudflare-output-verifier/manifest.js +46 -0
- package/dist/cjs/cloudflare-output-verifier/mutation-policy.js +7 -1
- package/dist/cjs/cloudflare-output-verifier/worker-bundles.js +14 -6
- package/dist/cjs/config.js +34 -1
- package/dist/cjs/deploy-output/npmAliases.js +251 -0
- package/dist/cjs/deploy-output/plugin.js +97 -0
- package/dist/cjs/deploy-output/public-assets.js +146 -0
- package/dist/cjs/deploy-output/target.js +104 -0
- package/dist/cjs/deploy-output/trace-files.js +90 -0
- package/dist/cjs/native-type-checker.js +193 -0
- package/dist/cjs/policy-defaults.js +101 -0
- package/dist/cjs/release-envelope/framework-output.js +153 -79
- package/dist/cjs/release-envelope/index.js +10 -4
- package/dist/cjs/release-envelope/plugin.js +18 -13
- package/dist/cjs/release-identity.js +5 -6
- package/dist/cjs/runtime-package-resolution.js +118 -0
- package/dist/cjs/templates/cloudflare-entry.001-bootstrap-security.mjs +0 -3
- package/dist/cjs/templates/cloudflare-entry.002-assets-routes.mjs +1 -1
- package/dist/cjs/templates/cloudflare-entry.004-rendering-css.mjs +5 -1
- package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +98 -10
- package/dist/cjs/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
- package/dist/cjs/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/esm/backend-federation-build/codegen.mjs +216 -0
- package/dist/esm/backend-federation-build/config.mjs +163 -0
- package/dist/esm/backend-federation-build/emit.mjs +103 -0
- package/dist/esm/backend-federation-build/index.mjs +1 -0
- package/dist/esm/build-config/build-environment.mjs +88 -0
- package/dist/esm/build-config/precompress/plugin.mjs +76 -0
- package/dist/esm/build-config/precompress/types.mjs +0 -0
- package/dist/esm/build-config/public.mjs +1 -0
- package/dist/esm/build-config/remote-address.mjs +20 -0
- package/dist/esm/cloudflare/artifacts.mjs +8 -19
- package/dist/esm/cloudflare/constants.mjs +1 -1
- package/dist/esm/cloudflare/delivery-unit.mjs +17 -36
- package/dist/esm/cloudflare/index.mjs +10 -2
- package/dist/esm/cloudflare/worker-manifest.mjs +13 -4
- package/dist/esm/cloudflare/wrangler-config.mjs +35 -7
- package/dist/esm/cloudflare-builder.mjs +246 -75
- package/dist/esm/cloudflare-output-contract.mjs +59 -2
- package/dist/esm/cloudflare-output-verifier/identity.mjs +27 -25
- package/dist/esm/cloudflare-output-verifier/index.mjs +10 -0
- package/dist/esm/cloudflare-output-verifier/manifest.mjs +46 -0
- package/dist/esm/cloudflare-output-verifier/mutation-policy.mjs +7 -1
- package/dist/esm/cloudflare-output-verifier/worker-bundles.mjs +15 -7
- package/dist/esm/config.mjs +3 -0
- package/dist/esm/deploy-output/npmAliases.mjs +199 -0
- package/dist/esm/deploy-output/plugin.mjs +46 -0
- package/dist/esm/deploy-output/public-assets.mjs +82 -0
- package/dist/esm/deploy-output/target.mjs +57 -0
- package/dist/esm/deploy-output/trace-files.mjs +38 -0
- package/dist/esm/native-type-checker.mjs +142 -0
- package/dist/esm/policy-defaults.mjs +45 -0
- package/dist/esm/release-envelope/framework-output.mjs +130 -56
- package/dist/esm/release-envelope/index.mjs +10 -4
- package/dist/esm/release-envelope/plugin.mjs +18 -13
- package/dist/esm/release-identity.mjs +5 -6
- package/dist/esm/runtime-package-resolution.mjs +60 -0
- package/dist/esm/templates/cloudflare-entry.001-bootstrap-security.mjs +0 -3
- package/dist/esm/templates/cloudflare-entry.002-assets-routes.mjs +1 -1
- package/dist/esm/templates/cloudflare-entry.004-rendering-css.mjs +5 -1
- package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +98 -10
- package/dist/esm/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
- package/dist/esm/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/esm-node/backend-federation-build/codegen.mjs +217 -0
- package/dist/esm-node/backend-federation-build/config.mjs +164 -0
- package/dist/esm-node/backend-federation-build/emit.mjs +104 -0
- package/dist/esm-node/backend-federation-build/index.mjs +2 -0
- package/dist/esm-node/build-config/build-environment.mjs +89 -0
- package/dist/esm-node/build-config/precompress/plugin.mjs +77 -0
- package/dist/esm-node/build-config/precompress/types.mjs +1 -0
- package/dist/esm-node/build-config/public.mjs +2 -0
- package/dist/esm-node/build-config/remote-address.mjs +21 -0
- package/dist/esm-node/cloudflare/artifacts.mjs +8 -19
- package/dist/esm-node/cloudflare/constants.mjs +1 -1
- package/dist/esm-node/cloudflare/delivery-unit.mjs +17 -36
- package/dist/esm-node/cloudflare/index.mjs +10 -2
- package/dist/esm-node/cloudflare/worker-manifest.mjs +13 -4
- package/dist/esm-node/cloudflare/wrangler-config.mjs +35 -7
- package/dist/esm-node/cloudflare-builder.mjs +246 -75
- package/dist/esm-node/cloudflare-output-contract.mjs +59 -2
- package/dist/esm-node/cloudflare-output-verifier/identity.mjs +27 -25
- package/dist/esm-node/cloudflare-output-verifier/index.mjs +10 -0
- package/dist/esm-node/cloudflare-output-verifier/manifest.mjs +46 -0
- package/dist/esm-node/cloudflare-output-verifier/mutation-policy.mjs +7 -1
- package/dist/esm-node/cloudflare-output-verifier/worker-bundles.mjs +15 -7
- package/dist/esm-node/config.mjs +3 -0
- package/dist/esm-node/deploy-output/npmAliases.mjs +200 -0
- package/dist/esm-node/deploy-output/plugin.mjs +49 -0
- package/dist/esm-node/deploy-output/public-assets.mjs +83 -0
- package/dist/esm-node/deploy-output/target.mjs +58 -0
- package/dist/esm-node/deploy-output/trace-files.mjs +39 -0
- package/dist/esm-node/native-type-checker.mjs +143 -0
- package/dist/esm-node/policy-defaults.mjs +46 -0
- package/dist/esm-node/release-envelope/framework-output.mjs +130 -56
- package/dist/esm-node/release-envelope/index.mjs +10 -4
- package/dist/esm-node/release-envelope/plugin.mjs +18 -13
- package/dist/esm-node/release-identity.mjs +5 -6
- package/dist/esm-node/runtime-package-resolution.mjs +61 -0
- package/dist/esm-node/templates/cloudflare-entry.001-bootstrap-security.mjs +0 -3
- package/dist/esm-node/templates/cloudflare-entry.002-assets-routes.mjs +1 -1
- package/dist/esm-node/templates/cloudflare-entry.004-rendering-css.mjs +5 -1
- package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +98 -10
- package/dist/esm-node/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
- package/dist/esm-node/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/types/backend-federation-build/codegen.d.ts +116 -0
- package/dist/types/backend-federation-build/config.d.ts +121 -0
- package/dist/types/backend-federation-build/emit.d.ts +21 -0
- package/dist/types/backend-federation-build/index.d.ts +2 -0
- package/dist/types/build-config/build-environment.d.ts +14 -0
- package/dist/types/build-config/precompress/plugin.d.ts +4 -0
- package/dist/types/build-config/precompress/types.d.ts +19 -0
- package/dist/types/build-config/public.d.ts +2 -0
- package/dist/types/build-config/remote-address.d.ts +3 -0
- package/dist/types/cloudflare/artifacts.d.ts +7 -6
- package/dist/types/cloudflare/constants.d.ts +1 -1
- package/dist/types/cloudflare/delivery-unit.d.ts +3 -3
- package/dist/types/cloudflare/worker-manifest.d.ts +2 -2
- package/dist/types/cloudflare/wrangler-config.d.ts +6 -0
- package/dist/types/cloudflare-builder.d.ts +49 -9
- package/dist/types/cloudflare-output-contract.d.ts +4 -1
- package/dist/types/cloudflare-output-verifier/identity.d.ts +1 -1
- package/dist/types/cloudflare-output-verifier/worker-bundles.d.ts +2 -2
- package/dist/types/config.d.ts +29 -1
- package/dist/types/deploy-output/npmAliases.d.ts +14 -0
- package/dist/types/deploy-output/plugin.d.ts +35 -0
- package/dist/types/deploy-output/public-assets.d.ts +51 -0
- package/dist/types/deploy-output/target.d.ts +29 -0
- package/dist/types/deploy-output/trace-files.d.ts +17 -0
- package/dist/types/native-type-checker.d.ts +15 -0
- package/dist/types/policy-defaults.d.ts +46 -0
- package/dist/types/precompress/LICENSE +20 -0
- package/dist/types/precompress/index.d.ts +76 -0
- package/dist/types/release-envelope/framework-output.d.ts +8 -3
- package/dist/types/release-envelope/plugin.d.ts +7 -7
- package/dist/types/runtime-package-resolution.d.ts +28 -0
- package/dist/types/templates/cloudflare-worker-route-data.d.mts +14 -0
- package/package.json +97 -15
- package/src/backend-federation-build/codegen.ts +254 -0
- package/src/backend-federation-build/config.ts +492 -0
- package/src/backend-federation-build/emit.ts +213 -0
- package/src/backend-federation-build/index.ts +2 -0
- package/src/build-config/build-environment.ts +160 -0
- package/src/build-config/precompress/plugin.ts +140 -0
- package/src/build-config/precompress/types.ts +26 -0
- package/src/build-config/public.ts +5 -0
- package/src/build-config/remote-address.ts +33 -0
- package/src/cloudflare/artifacts.ts +22 -43
- package/src/cloudflare/constants.ts +1 -1
- package/src/cloudflare/delivery-unit.ts +42 -53
- package/src/cloudflare/index.ts +24 -11
- package/src/cloudflare/worker-manifest.ts +20 -3
- package/src/cloudflare/wrangler-config.ts +86 -10
- package/src/cloudflare-builder.ts +475 -151
- package/src/cloudflare-output-contract.ts +70 -0
- package/src/cloudflare-output-verifier/identity.ts +5 -1
- package/src/cloudflare-output-verifier/index.ts +20 -0
- package/src/cloudflare-output-verifier/manifest.ts +77 -0
- package/src/cloudflare-output-verifier/mutation-policy.ts +11 -1
- package/src/cloudflare-output-verifier/worker-bundles.ts +32 -14
- package/src/config.ts +34 -6
- package/src/deploy-output/npmAliases.ts +382 -0
- package/src/deploy-output/plugin.ts +86 -0
- package/src/deploy-output/public-assets.ts +224 -0
- package/src/deploy-output/target.ts +106 -0
- package/src/deploy-output/trace-files.ts +67 -0
- package/src/native-type-checker.ts +183 -0
- package/src/policy-defaults.ts +127 -0
- package/src/release-envelope/framework-output.ts +346 -94
- package/src/release-envelope/index.ts +26 -5
- package/src/release-envelope/plugin.ts +43 -40
- package/src/release-identity.ts +13 -2
- package/src/runtime-package-resolution.ts +115 -0
- package/src/templates/cloudflare-entry.001-bootstrap-security.mjs +0 -3
- package/src/templates/cloudflare-entry.002-assets-routes.mjs +1 -1
- package/src/templates/cloudflare-entry.004-rendering-css.mjs +5 -1
- package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +98 -10
- package/src/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
- package/src/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/cjs/templates/cloudflare-worker-fs-promises.mjs +0 -11
- package/dist/cjs/templates/cloudflare-worker-loadable-server.mjs +0 -281
- package/dist/cjs/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
- package/dist/cjs/templates/cloudflare-worker-path.mjs +0 -98
- package/dist/esm/templates/cloudflare-worker-fs-promises.mjs +0 -11
- package/dist/esm/templates/cloudflare-worker-loadable-server.mjs +0 -281
- package/dist/esm/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
- package/dist/esm/templates/cloudflare-worker-path.mjs +0 -98
- package/dist/esm-node/templates/cloudflare-worker-fs-promises.mjs +0 -11
- package/dist/esm-node/templates/cloudflare-worker-loadable-server.mjs +0 -281
- package/dist/esm-node/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
- package/dist/esm-node/templates/cloudflare-worker-path.mjs +0 -98
- package/dist/types/templates/cloudflare-worker-fs-promises.d.mts +0 -5
- package/dist/types/templates/cloudflare-worker-loadable-server.d.mts +0 -48
- package/dist/types/templates/cloudflare-worker-mf-ssr-runtime-plugin.d.mts +0 -13
- package/dist/types/templates/cloudflare-worker-path.d.mts +0 -21
- package/src/templates/cloudflare-worker-fs-promises.mjs +0 -11
- package/src/templates/cloudflare-worker-loadable-server.mjs +0 -281
- package/src/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
- package/src/templates/cloudflare-worker-path.mjs +0 -98
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { resolveUltramodernSourceRevision } from '@modern-js/app-tools-extensions/release-identity';
|
|
5
|
+
import {
|
|
6
|
+
BACKEND_FEDERATION_MANIFEST_FILE as BACKEND_MANIFEST_FILE,
|
|
7
|
+
BACKEND_FEDERATION_REMOTE_ENTRY_FILE as BACKEND_REMOTE_ENTRY_FILE,
|
|
8
|
+
DELIVERY_UNIT_DEPLOY_PROFILE,
|
|
9
|
+
DELIVERY_UNIT_KIND,
|
|
10
|
+
DELIVERY_UNIT_SCHEMA_VERSION,
|
|
11
|
+
type DeliveryUnitContractBlock,
|
|
12
|
+
type DeliveryUnitRecord,
|
|
13
|
+
deliveryUnitContractBlock,
|
|
14
|
+
isUltramodernBuildArtifact,
|
|
15
|
+
ULTRAMODERN_BUILD_ARTIFACT_PATH,
|
|
16
|
+
type UltramodernBuildArtifact,
|
|
17
|
+
validateDeliveryUnitRecord,
|
|
18
|
+
validateUltramodernBuildArtifact,
|
|
19
|
+
} from '@modern-js/backend-federation-contracts';
|
|
20
|
+
|
|
21
|
+
export const REFERENCE_TOPOLOGY_PATH = 'topology/reference-topology.json';
|
|
22
|
+
export const DEVELOPMENT_OVERLAY_PATH =
|
|
23
|
+
'topology/local-overlays/development.json';
|
|
24
|
+
|
|
25
|
+
export type TopologyApp = {
|
|
26
|
+
id?: unknown;
|
|
27
|
+
domain?: unknown;
|
|
28
|
+
kind?: unknown;
|
|
29
|
+
path?: unknown;
|
|
30
|
+
package?: unknown;
|
|
31
|
+
portEnv?: unknown;
|
|
32
|
+
cloudflare?: { publicUrlEnv?: unknown };
|
|
33
|
+
api?: {
|
|
34
|
+
bff?: { prefix?: unknown };
|
|
35
|
+
protocol?: unknown;
|
|
36
|
+
rpcPath?: unknown;
|
|
37
|
+
rpcSerialization?: unknown;
|
|
38
|
+
stem?: unknown;
|
|
39
|
+
};
|
|
40
|
+
moduleFederation?: {
|
|
41
|
+
name?: unknown;
|
|
42
|
+
manifestUrl?: unknown;
|
|
43
|
+
};
|
|
44
|
+
backendFederation?: {
|
|
45
|
+
name?: unknown;
|
|
46
|
+
versionBoundary?: {
|
|
47
|
+
ui?: {
|
|
48
|
+
manifestUrl?: unknown;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
executionSurfaces?: {
|
|
52
|
+
node?: {
|
|
53
|
+
remoteName?: unknown;
|
|
54
|
+
manifestUrl?: unknown;
|
|
55
|
+
containerEntry?: unknown;
|
|
56
|
+
remoteType?: unknown;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
deliveryUnit?: {
|
|
61
|
+
unitId?: unknown;
|
|
62
|
+
buildMarker?: unknown;
|
|
63
|
+
sourceRevision?: unknown;
|
|
64
|
+
packageName?: unknown;
|
|
65
|
+
version?: unknown;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export type DevelopmentOverlay = {
|
|
70
|
+
ports?: Record<string, unknown>;
|
|
71
|
+
manifests?: Record<string, unknown>;
|
|
72
|
+
serverExecution?: Record<
|
|
73
|
+
string,
|
|
74
|
+
{
|
|
75
|
+
node?: {
|
|
76
|
+
remoteName?: unknown;
|
|
77
|
+
manifestUrl?: unknown;
|
|
78
|
+
containerEntry?: unknown;
|
|
79
|
+
remoteType?: unknown;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
>;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export type ReferenceTopology = {
|
|
86
|
+
shell?: TopologyApp;
|
|
87
|
+
shells?: TopologyApp[];
|
|
88
|
+
verticals?: TopologyApp[];
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export type BackendFederationApp = {
|
|
92
|
+
id: string;
|
|
93
|
+
directory: string;
|
|
94
|
+
packageName?: string;
|
|
95
|
+
version?: string;
|
|
96
|
+
buildVersion?: string;
|
|
97
|
+
unitId?: string;
|
|
98
|
+
sourceRevision?: string;
|
|
99
|
+
deliveryUnit?: DeliveryUnitContractBlock;
|
|
100
|
+
port: number;
|
|
101
|
+
apiPrefix: string;
|
|
102
|
+
apiProtocol: 'rest' | 'rpc';
|
|
103
|
+
rpcPath?: string;
|
|
104
|
+
apiStem: string;
|
|
105
|
+
backendName: string;
|
|
106
|
+
manifestUrl: string;
|
|
107
|
+
containerEntry: string;
|
|
108
|
+
remoteType: string;
|
|
109
|
+
uiManifestUrl?: string;
|
|
110
|
+
topologyDeliveryUnit?: {
|
|
111
|
+
unitId?: string;
|
|
112
|
+
buildMarker?: string;
|
|
113
|
+
sourceRevision?: string;
|
|
114
|
+
packageName?: string;
|
|
115
|
+
version?: string;
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export type BackendFederationBuildIdentity = {
|
|
120
|
+
appId?: string;
|
|
121
|
+
packageName?: string;
|
|
122
|
+
version?: string;
|
|
123
|
+
buildVersion?: string;
|
|
124
|
+
unitId?: string;
|
|
125
|
+
sourceRevision?: string;
|
|
126
|
+
artifact?: UltramodernBuildArtifact;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export const normalizeRelativePath = (value: string) =>
|
|
130
|
+
value.replace(/\\/gu, '/').replace(/^\.\/+/u, '');
|
|
131
|
+
|
|
132
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
133
|
+
typeof value === 'object' && value !== null;
|
|
134
|
+
|
|
135
|
+
const readJsonFile = async <T>(filePath: string): Promise<T> =>
|
|
136
|
+
JSON.parse(await fs.readFile(filePath, 'utf8')) as T;
|
|
137
|
+
|
|
138
|
+
export const buildArtifactPathFor = (appDirectory: string) =>
|
|
139
|
+
path.join(appDirectory, ULTRAMODERN_BUILD_ARTIFACT_PATH);
|
|
140
|
+
|
|
141
|
+
export const readBuildIdentity = async (
|
|
142
|
+
appDirectory: string,
|
|
143
|
+
): Promise<BackendFederationBuildIdentity> => {
|
|
144
|
+
const buildArtifactPath = buildArtifactPathFor(appDirectory);
|
|
145
|
+
if (existsSync(buildArtifactPath)) {
|
|
146
|
+
const artifact = await readJsonFile<unknown>(buildArtifactPath);
|
|
147
|
+
const artifactValidation = validateUltramodernBuildArtifact(
|
|
148
|
+
artifact,
|
|
149
|
+
buildArtifactPath,
|
|
150
|
+
);
|
|
151
|
+
if (!artifactValidation.ok) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`[backend-federation-build] Invalid delivery-unit build artifact at ${buildArtifactPath}.`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
if (!isUltramodernBuildArtifact(artifact)) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
`[backend-federation-build] Invalid delivery-unit build artifact at ${buildArtifactPath}.`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const deliveryUnit = artifact.deliveryUnit;
|
|
163
|
+
return {
|
|
164
|
+
artifact,
|
|
165
|
+
appId: deliveryUnit.appId,
|
|
166
|
+
buildVersion: deliveryUnit.buildMarker,
|
|
167
|
+
packageName: deliveryUnit.packageName,
|
|
168
|
+
version: deliveryUnit.version,
|
|
169
|
+
unitId: deliveryUnit.unitId,
|
|
170
|
+
sourceRevision: deliveryUnit.sourceRevision,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
throw new Error(
|
|
175
|
+
`[backend-federation-build] Missing delivery-unit build artifact at ${buildArtifactPath}.`,
|
|
176
|
+
);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const toPascalCase = (value: string) =>
|
|
180
|
+
value
|
|
181
|
+
.split(/[^a-zA-Z0-9]+/u)
|
|
182
|
+
.filter(Boolean)
|
|
183
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
|
184
|
+
.join('');
|
|
185
|
+
|
|
186
|
+
const stringValue = (value: unknown) =>
|
|
187
|
+
typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
188
|
+
|
|
189
|
+
const createBackendName = (app: TopologyApp, id: string) => {
|
|
190
|
+
const configuredName =
|
|
191
|
+
stringValue(app.backendFederation?.name) ??
|
|
192
|
+
stringValue(app.backendFederation?.executionSurfaces?.node?.remoteName);
|
|
193
|
+
|
|
194
|
+
if (configuredName) {
|
|
195
|
+
return configuredName;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const mfName = stringValue(app.moduleFederation?.name);
|
|
199
|
+
return mfName ? `${mfName}Backend` : `vertical${toPascalCase(id)}Backend`;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const rebaseDefaultLocalUrl = (
|
|
203
|
+
value: string,
|
|
204
|
+
defaultPort: number,
|
|
205
|
+
publicOrigin: string,
|
|
206
|
+
) => {
|
|
207
|
+
let url: URL;
|
|
208
|
+
try {
|
|
209
|
+
url = new URL(value);
|
|
210
|
+
} catch {
|
|
211
|
+
return value;
|
|
212
|
+
}
|
|
213
|
+
if (
|
|
214
|
+
url.protocol !== 'http:' ||
|
|
215
|
+
url.hostname !== 'localhost' ||
|
|
216
|
+
Number(url.port) !== defaultPort ||
|
|
217
|
+
url.username ||
|
|
218
|
+
url.password
|
|
219
|
+
) {
|
|
220
|
+
return value;
|
|
221
|
+
}
|
|
222
|
+
return `${publicOrigin}${url.pathname}${url.search}${url.hash}`;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export const findWorkspaceRoot = (appDirectory: string) => {
|
|
226
|
+
let current = appDirectory;
|
|
227
|
+
|
|
228
|
+
while (true) {
|
|
229
|
+
if (existsSync(path.join(current, REFERENCE_TOPOLOGY_PATH))) {
|
|
230
|
+
return current;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const parent = path.dirname(current);
|
|
234
|
+
if (parent === current) {
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
current = parent;
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export const resolveWorkspaceSourceRevision = async (workspaceRoot: string) => {
|
|
242
|
+
return resolveUltramodernSourceRevision(workspaceRoot);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
export const createStampedDeliveryUnit = (input: {
|
|
246
|
+
appId: string;
|
|
247
|
+
unitId?: string;
|
|
248
|
+
buildMarker?: string;
|
|
249
|
+
packageName?: string;
|
|
250
|
+
version?: string;
|
|
251
|
+
sourceRevision: string;
|
|
252
|
+
}): DeliveryUnitContractBlock | undefined => {
|
|
253
|
+
if (
|
|
254
|
+
!input.unitId ||
|
|
255
|
+
!input.buildMarker ||
|
|
256
|
+
!input.packageName ||
|
|
257
|
+
!input.version
|
|
258
|
+
) {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const record = {
|
|
263
|
+
schemaVersion: DELIVERY_UNIT_SCHEMA_VERSION,
|
|
264
|
+
kind: DELIVERY_UNIT_KIND,
|
|
265
|
+
appId: input.appId,
|
|
266
|
+
unitId: input.unitId,
|
|
267
|
+
packageName: input.packageName,
|
|
268
|
+
version: input.version,
|
|
269
|
+
buildMarker: input.buildMarker,
|
|
270
|
+
sourceRevision: input.sourceRevision,
|
|
271
|
+
deployProfile: DELIVERY_UNIT_DEPLOY_PROFILE,
|
|
272
|
+
};
|
|
273
|
+
const validation = validateDeliveryUnitRecord(record);
|
|
274
|
+
|
|
275
|
+
if (!validation.ok) {
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return deliveryUnitContractBlock(record as DeliveryUnitRecord);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
export const createAppFromTopology = (
|
|
283
|
+
workspaceRoot: string,
|
|
284
|
+
appDirectory: string,
|
|
285
|
+
topologyApp: TopologyApp,
|
|
286
|
+
overlay: DevelopmentOverlay,
|
|
287
|
+
packageManifest: { name?: unknown; version?: unknown },
|
|
288
|
+
): BackendFederationApp | undefined => {
|
|
289
|
+
const id = stringValue(topologyApp.id);
|
|
290
|
+
const appPath = stringValue(topologyApp.path);
|
|
291
|
+
|
|
292
|
+
if (
|
|
293
|
+
!id ||
|
|
294
|
+
!appPath ||
|
|
295
|
+
topologyApp.kind !== 'vertical' ||
|
|
296
|
+
!isRecord(topologyApp.api) ||
|
|
297
|
+
!isRecord(topologyApp.backendFederation)
|
|
298
|
+
) {
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const directory = normalizeRelativePath(appPath);
|
|
303
|
+
if (path.resolve(workspaceRoot, directory) !== path.resolve(appDirectory)) {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const defaultPort = overlay.ports?.[id];
|
|
308
|
+
if (
|
|
309
|
+
typeof defaultPort !== 'number' ||
|
|
310
|
+
!Number.isInteger(defaultPort) ||
|
|
311
|
+
defaultPort < 1 ||
|
|
312
|
+
defaultPort > 65535
|
|
313
|
+
) {
|
|
314
|
+
throw new Error(
|
|
315
|
+
`[backend-federation-build] Invalid development port for ${id}.`,
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
const portEnv =
|
|
319
|
+
stringValue(topologyApp.portEnv) ??
|
|
320
|
+
`VERTICAL_${(stringValue(topologyApp.domain) ?? id)
|
|
321
|
+
.replace(/[^a-zA-Z0-9]/gu, '_')
|
|
322
|
+
.toUpperCase()}_PORT`;
|
|
323
|
+
const port = Number(process.env[portEnv] ?? defaultPort);
|
|
324
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
325
|
+
throw new Error(
|
|
326
|
+
`[backend-federation-build] Invalid ${portEnv} development port for ${id}.`,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
const publicUrlEnv = stringValue(topologyApp.cloudflare?.publicUrlEnv);
|
|
330
|
+
const configuredPublicUrl = publicUrlEnv
|
|
331
|
+
? process.env[publicUrlEnv]?.trim()
|
|
332
|
+
: undefined;
|
|
333
|
+
let publicOrigin = `http://localhost:${port}`;
|
|
334
|
+
if (configuredPublicUrl) {
|
|
335
|
+
let url: URL;
|
|
336
|
+
try {
|
|
337
|
+
url = new URL(configuredPublicUrl);
|
|
338
|
+
} catch {
|
|
339
|
+
throw new Error(
|
|
340
|
+
`[backend-federation-build] Invalid ${publicUrlEnv} public URL for ${id}.`,
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
if (
|
|
344
|
+
!['http:', 'https:'].includes(url.protocol) ||
|
|
345
|
+
url.username ||
|
|
346
|
+
url.password
|
|
347
|
+
) {
|
|
348
|
+
throw new Error(
|
|
349
|
+
`[backend-federation-build] Invalid ${publicUrlEnv} public URL for ${id}.`,
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
publicOrigin = url.origin;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const apiPrefix = stringValue(topologyApp.api.bff?.prefix) ?? `/${id}-api`;
|
|
356
|
+
const apiProtocol = topologyApp.api.protocol ?? 'rest';
|
|
357
|
+
if (apiProtocol !== 'rest' && apiProtocol !== 'rpc') {
|
|
358
|
+
throw new Error(
|
|
359
|
+
`[backend-federation-build] Unsupported API protocol for ${id}.`,
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
const rpcPath =
|
|
363
|
+
apiProtocol === 'rpc' ? stringValue(topologyApp.api.rpcPath) : undefined;
|
|
364
|
+
if (
|
|
365
|
+
apiProtocol === 'rpc' &&
|
|
366
|
+
(!rpcPath || topologyApp.api.rpcSerialization !== 'json')
|
|
367
|
+
) {
|
|
368
|
+
throw new Error(
|
|
369
|
+
`[backend-federation-build] Missing JSON RPC route metadata for ${id}.`,
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
const apiStem = stringValue(topologyApp.api.stem) ?? id;
|
|
373
|
+
const backendName = createBackendName(topologyApp, id);
|
|
374
|
+
const nodeOverlay = overlay.serverExecution?.[id]?.node;
|
|
375
|
+
if (!isRecord(nodeOverlay)) {
|
|
376
|
+
throw new Error(
|
|
377
|
+
`[backend-federation-build] Missing declared Node server execution for ${id}.`,
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
const manifestUrl = rebaseDefaultLocalUrl(
|
|
381
|
+
stringValue(nodeOverlay?.manifestUrl) ??
|
|
382
|
+
`http://localhost:${defaultPort}/${BACKEND_MANIFEST_FILE}`,
|
|
383
|
+
defaultPort,
|
|
384
|
+
publicOrigin,
|
|
385
|
+
);
|
|
386
|
+
const containerEntry = rebaseDefaultLocalUrl(
|
|
387
|
+
stringValue(nodeOverlay?.containerEntry) ??
|
|
388
|
+
`http://localhost:${defaultPort}/${BACKEND_REMOTE_ENTRY_FILE}`,
|
|
389
|
+
defaultPort,
|
|
390
|
+
publicOrigin,
|
|
391
|
+
);
|
|
392
|
+
const configuredRemoteType =
|
|
393
|
+
stringValue(nodeOverlay?.remoteType) ??
|
|
394
|
+
stringValue(
|
|
395
|
+
topologyApp.backendFederation?.executionSurfaces?.node?.remoteType,
|
|
396
|
+
);
|
|
397
|
+
if (
|
|
398
|
+
configuredRemoteType !== undefined &&
|
|
399
|
+
configuredRemoteType !== 'commonjs-module'
|
|
400
|
+
) {
|
|
401
|
+
throw new Error(
|
|
402
|
+
`[backend-federation-build] Node backend federation remoteType must be "commonjs-module"; received "${configuredRemoteType}" for ${id}.`,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
if (
|
|
406
|
+
!new URL(containerEntry).pathname.endsWith(`/${BACKEND_REMOTE_ENTRY_FILE}`)
|
|
407
|
+
) {
|
|
408
|
+
throw new Error(
|
|
409
|
+
`[backend-federation-build] Node backend federation containerEntry must end with "/${BACKEND_REMOTE_ENTRY_FILE}"; received "${containerEntry}" for ${id}.`,
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
const remoteType = 'commonjs-module';
|
|
413
|
+
const packageName = stringValue(packageManifest.name);
|
|
414
|
+
const version = stringValue(packageManifest.version);
|
|
415
|
+
if (stringValue(topologyApp.package) !== packageName || !version) {
|
|
416
|
+
throw new Error(
|
|
417
|
+
`[backend-federation-build] Topology package identity must match package.json for ${id}.`,
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
const declaredUiManifestUrl = stringValue(overlay.manifests?.[id]);
|
|
421
|
+
const uiManifestUrl = declaredUiManifestUrl
|
|
422
|
+
? rebaseDefaultLocalUrl(declaredUiManifestUrl, defaultPort, publicOrigin)
|
|
423
|
+
: undefined;
|
|
424
|
+
const topologyDeliveryUnit = isRecord(topologyApp.deliveryUnit)
|
|
425
|
+
? {
|
|
426
|
+
unitId: stringValue(topologyApp.deliveryUnit.unitId),
|
|
427
|
+
buildMarker: stringValue(topologyApp.deliveryUnit.buildMarker),
|
|
428
|
+
sourceRevision: stringValue(topologyApp.deliveryUnit.sourceRevision),
|
|
429
|
+
packageName: stringValue(topologyApp.deliveryUnit.packageName),
|
|
430
|
+
version: stringValue(topologyApp.deliveryUnit.version),
|
|
431
|
+
}
|
|
432
|
+
: undefined;
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
id,
|
|
436
|
+
directory,
|
|
437
|
+
packageName,
|
|
438
|
+
version,
|
|
439
|
+
port,
|
|
440
|
+
apiPrefix,
|
|
441
|
+
apiProtocol,
|
|
442
|
+
rpcPath,
|
|
443
|
+
apiStem,
|
|
444
|
+
backendName,
|
|
445
|
+
manifestUrl,
|
|
446
|
+
containerEntry,
|
|
447
|
+
remoteType,
|
|
448
|
+
uiManifestUrl,
|
|
449
|
+
topologyDeliveryUnit,
|
|
450
|
+
};
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
export const findBackendFederationApp = async (
|
|
454
|
+
workspaceRoot: string,
|
|
455
|
+
appDirectory: string,
|
|
456
|
+
) => {
|
|
457
|
+
const topologyPath = path.join(workspaceRoot, REFERENCE_TOPOLOGY_PATH);
|
|
458
|
+
const topology = await readJsonFile<ReferenceTopology>(topologyPath);
|
|
459
|
+
if (!isRecord(topology) || !Array.isArray(topology.verticals)) {
|
|
460
|
+
throw new Error(
|
|
461
|
+
`[backend-federation-build] Invalid declared topology at ${topologyPath}.`,
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
const overlayPath = path.join(workspaceRoot, DEVELOPMENT_OVERLAY_PATH);
|
|
465
|
+
const overlay = await readJsonFile<DevelopmentOverlay>(overlayPath);
|
|
466
|
+
if (!isRecord(overlay) || !isRecord(overlay.ports)) {
|
|
467
|
+
throw new Error(
|
|
468
|
+
`[backend-federation-build] Invalid development overlay at ${overlayPath}.`,
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
const packageManifest = await readJsonFile<{
|
|
472
|
+
name?: unknown;
|
|
473
|
+
version?: unknown;
|
|
474
|
+
}>(path.join(appDirectory, 'package.json'));
|
|
475
|
+
|
|
476
|
+
for (const app of topology.verticals) {
|
|
477
|
+
const resolved = createAppFromTopology(
|
|
478
|
+
workspaceRoot,
|
|
479
|
+
appDirectory,
|
|
480
|
+
app,
|
|
481
|
+
overlay,
|
|
482
|
+
packageManifest,
|
|
483
|
+
);
|
|
484
|
+
if (resolved) {
|
|
485
|
+
return resolved;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
throw new Error(
|
|
490
|
+
`[backend-federation-build] ${appDirectory} is missing from the declared reference topology.`,
|
|
491
|
+
);
|
|
492
|
+
};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { resolveUltramodernReleaseIdentity } from '@modern-js/app-tools-extensions/release-identity';
|
|
5
|
+
import {
|
|
6
|
+
BACKEND_FEDERATION_MANIFEST_FILE as BACKEND_MANIFEST_FILE,
|
|
7
|
+
BACKEND_FEDERATION_REMOTE_ENTRY_FILE as BACKEND_REMOTE_ENTRY_FILE,
|
|
8
|
+
stampUltramodernBuildArtifactIdentity,
|
|
9
|
+
ULTRAMODERN_BUILD_ARTIFACT_FILE,
|
|
10
|
+
} from '@modern-js/backend-federation-contracts';
|
|
11
|
+
import { createBackendFederationEntryIntegrity } from '@modern-js/server-runtime-extensions/backend-federation-security/node';
|
|
12
|
+
import {
|
|
13
|
+
createBackendManifest,
|
|
14
|
+
createBackendRemoteEntrySource,
|
|
15
|
+
} from './codegen';
|
|
16
|
+
import {
|
|
17
|
+
type BackendFederationApp,
|
|
18
|
+
buildArtifactPathFor,
|
|
19
|
+
createStampedDeliveryUnit,
|
|
20
|
+
findBackendFederationApp,
|
|
21
|
+
findWorkspaceRoot,
|
|
22
|
+
REFERENCE_TOPOLOGY_PATH,
|
|
23
|
+
readBuildIdentity,
|
|
24
|
+
resolveWorkspaceSourceRevision,
|
|
25
|
+
} from './config';
|
|
26
|
+
|
|
27
|
+
export type { BackendFederationBuildIdentity } from './config';
|
|
28
|
+
|
|
29
|
+
export type BackendFederationArtifactResult = {
|
|
30
|
+
appId: string;
|
|
31
|
+
manifestPath: string;
|
|
32
|
+
containerPath: string;
|
|
33
|
+
deliveryUnitArtifactPath?: string;
|
|
34
|
+
remoteName: string;
|
|
35
|
+
remoteType: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const emitBackendFederationArtifacts = async (
|
|
39
|
+
appDirectory: string,
|
|
40
|
+
distDirectory: string,
|
|
41
|
+
): Promise<BackendFederationArtifactResult | undefined> => {
|
|
42
|
+
const workspaceRoot = findWorkspaceRoot(appDirectory);
|
|
43
|
+
if (!workspaceRoot) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const effectApiPath = path.join(appDirectory, 'api/effect-api.ts');
|
|
48
|
+
const backendFederationConfigPath = path.join(
|
|
49
|
+
appDirectory,
|
|
50
|
+
'backend-federation.config.ts',
|
|
51
|
+
);
|
|
52
|
+
if (!existsSync(effectApiPath) || !existsSync(backendFederationConfigPath)) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const app = await findBackendFederationApp(workspaceRoot, appDirectory);
|
|
57
|
+
if (!app) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const buildIdentity = await readBuildIdentity(appDirectory);
|
|
61
|
+
const topologyDeliveryUnit = app.topologyDeliveryUnit;
|
|
62
|
+
const hasTopologyDeliveryUnit =
|
|
63
|
+
topologyDeliveryUnit !== undefined &&
|
|
64
|
+
(topologyDeliveryUnit.unitId !== undefined ||
|
|
65
|
+
topologyDeliveryUnit.buildMarker !== undefined ||
|
|
66
|
+
topologyDeliveryUnit.packageName !== undefined ||
|
|
67
|
+
topologyDeliveryUnit.version !== undefined);
|
|
68
|
+
const hasBuildIdentity =
|
|
69
|
+
buildIdentity.unitId !== undefined ||
|
|
70
|
+
buildIdentity.buildVersion !== undefined ||
|
|
71
|
+
buildIdentity.packageName !== undefined ||
|
|
72
|
+
buildIdentity.version !== undefined;
|
|
73
|
+
|
|
74
|
+
if (hasBuildIdentity) {
|
|
75
|
+
const topologyPath = path.join(workspaceRoot, REFERENCE_TOPOLOGY_PATH);
|
|
76
|
+
const buildIdentityPath = buildArtifactPathFor(appDirectory);
|
|
77
|
+
const mismatches: string[] = [];
|
|
78
|
+
const compare = (
|
|
79
|
+
label: string,
|
|
80
|
+
a?: string,
|
|
81
|
+
b?: string,
|
|
82
|
+
leftLabel = 'deliveryUnit',
|
|
83
|
+
) => {
|
|
84
|
+
if (a !== undefined && b !== undefined && a !== b) {
|
|
85
|
+
mismatches.push(
|
|
86
|
+
`${label}: ${leftLabel}=${a} vs ultramodern-build=${b}`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
compare('appId', app.id, buildIdentity.appId, 'topology');
|
|
91
|
+
|
|
92
|
+
if (hasTopologyDeliveryUnit) {
|
|
93
|
+
compare('unitId', topologyDeliveryUnit?.unitId, buildIdentity.unitId);
|
|
94
|
+
compare(
|
|
95
|
+
'buildMarker/build',
|
|
96
|
+
topologyDeliveryUnit?.buildMarker,
|
|
97
|
+
buildIdentity.buildVersion,
|
|
98
|
+
);
|
|
99
|
+
compare(
|
|
100
|
+
'packageName',
|
|
101
|
+
topologyDeliveryUnit?.packageName,
|
|
102
|
+
buildIdentity.packageName,
|
|
103
|
+
);
|
|
104
|
+
compare('version', topologyDeliveryUnit?.version, buildIdentity.version);
|
|
105
|
+
}
|
|
106
|
+
compare(
|
|
107
|
+
'packageName',
|
|
108
|
+
app.packageName,
|
|
109
|
+
buildIdentity.packageName,
|
|
110
|
+
'package.json',
|
|
111
|
+
);
|
|
112
|
+
compare(
|
|
113
|
+
'packageName',
|
|
114
|
+
topologyDeliveryUnit?.packageName,
|
|
115
|
+
app.packageName,
|
|
116
|
+
'topology',
|
|
117
|
+
);
|
|
118
|
+
compare('version', app.version, buildIdentity.version, 'package.json');
|
|
119
|
+
compare('version', topologyDeliveryUnit?.version, app.version, 'topology');
|
|
120
|
+
|
|
121
|
+
if (mismatches.length > 0) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`[backend-federation-build] Delivery-unit identity drift between ${topologyPath}, package.json and ${buildIdentityPath}: ${mismatches.join('; ')}`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const unitId = topologyDeliveryUnit?.unitId ?? buildIdentity.unitId;
|
|
129
|
+
const generationBuildMarker =
|
|
130
|
+
topologyDeliveryUnit?.buildMarker ?? buildIdentity.buildVersion;
|
|
131
|
+
const sourceRevision = await resolveWorkspaceSourceRevision(workspaceRoot);
|
|
132
|
+
const buildVersion =
|
|
133
|
+
generationBuildMarker && unitId
|
|
134
|
+
? resolveUltramodernReleaseIdentity({
|
|
135
|
+
generationBuildMarker,
|
|
136
|
+
unitId,
|
|
137
|
+
workspaceRoot,
|
|
138
|
+
}).buildMarker
|
|
139
|
+
: undefined;
|
|
140
|
+
const packageName = app.packageName;
|
|
141
|
+
const version = app.version;
|
|
142
|
+
const deliveryUnit = createStampedDeliveryUnit({
|
|
143
|
+
appId: app.id,
|
|
144
|
+
unitId,
|
|
145
|
+
buildMarker: buildVersion,
|
|
146
|
+
packageName,
|
|
147
|
+
version,
|
|
148
|
+
sourceRevision,
|
|
149
|
+
});
|
|
150
|
+
const stampedBuildArtifact = buildIdentity.artifact
|
|
151
|
+
? stampUltramodernBuildArtifactIdentity(buildIdentity.artifact, {
|
|
152
|
+
buildMarker:
|
|
153
|
+
buildVersion ?? buildIdentity.artifact.deliveryUnit.buildMarker,
|
|
154
|
+
sourceRevision,
|
|
155
|
+
})
|
|
156
|
+
: undefined;
|
|
157
|
+
|
|
158
|
+
const resolvedApp: BackendFederationApp = {
|
|
159
|
+
...app,
|
|
160
|
+
packageName,
|
|
161
|
+
version,
|
|
162
|
+
buildVersion,
|
|
163
|
+
unitId,
|
|
164
|
+
sourceRevision,
|
|
165
|
+
deliveryUnit,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const manifestPath = path.join(distDirectory, BACKEND_MANIFEST_FILE);
|
|
169
|
+
const entryPath = path.join(distDirectory, BACKEND_REMOTE_ENTRY_FILE);
|
|
170
|
+
const deliveryUnitArtifactPath = stampedBuildArtifact
|
|
171
|
+
? path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE)
|
|
172
|
+
: undefined;
|
|
173
|
+
const entrySource = await createBackendRemoteEntrySource(
|
|
174
|
+
workspaceRoot,
|
|
175
|
+
resolvedApp,
|
|
176
|
+
effectApiPath,
|
|
177
|
+
entryPath,
|
|
178
|
+
);
|
|
179
|
+
await fs.mkdir(distDirectory, { recursive: true });
|
|
180
|
+
await fs.writeFile(entryPath, entrySource);
|
|
181
|
+
await fs.writeFile(
|
|
182
|
+
manifestPath,
|
|
183
|
+
`${JSON.stringify(createBackendManifest(workspaceRoot, distDirectory, resolvedApp, createBackendFederationEntryIntegrity(entrySource)), null, 2)}\n`,
|
|
184
|
+
);
|
|
185
|
+
if (stampedBuildArtifact && deliveryUnitArtifactPath) {
|
|
186
|
+
await fs.writeFile(
|
|
187
|
+
deliveryUnitArtifactPath,
|
|
188
|
+
`${JSON.stringify(stampedBuildArtifact, null, 2)}\n`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
appId: resolvedApp.id,
|
|
194
|
+
manifestPath,
|
|
195
|
+
containerPath: entryPath,
|
|
196
|
+
...(deliveryUnitArtifactPath ? { deliveryUnitArtifactPath } : {}),
|
|
197
|
+
remoteName: resolvedApp.backendName,
|
|
198
|
+
remoteType: resolvedApp.remoteType,
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export default () => ({
|
|
203
|
+
name: '@modern-js/backend-federation-build',
|
|
204
|
+
setup(api: {
|
|
205
|
+
getAppContext(): { appDirectory: string; distDirectory: string };
|
|
206
|
+
onAfterBuild(handler: () => Promise<void>): void;
|
|
207
|
+
}) {
|
|
208
|
+
api.onAfterBuild(async () => {
|
|
209
|
+
const { appDirectory, distDirectory } = api.getAppContext();
|
|
210
|
+
await emitBackendFederationArtifacts(appDirectory, distDirectory);
|
|
211
|
+
});
|
|
212
|
+
},
|
|
213
|
+
});
|