@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.2 → 3.9.0-ultramodern.20
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 +251 -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.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 +245 -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.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 +245 -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.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 +472 -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.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
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
isUltramodernBuildArtifact,
|
|
8
8
|
ULTRAMODERN_BUILD_ARTIFACT_FILE,
|
|
9
9
|
type UltramodernBuildArtifact,
|
|
10
|
-
} from '@modern-js/
|
|
10
|
+
} from '@modern-js/backend-federation-contracts';
|
|
11
11
|
import {
|
|
12
12
|
canonicalSerializeMicroVerticalReleaseEnvelope,
|
|
13
13
|
createMicroVerticalReleaseEnvelope,
|
|
@@ -33,6 +33,38 @@ const MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_SCHEMA_VERSION = 1;
|
|
|
33
33
|
|
|
34
34
|
const COMPILED_MODULE_PATTERN = /\.(?:c|m)?js$/u;
|
|
35
35
|
const EFFECT_BFF_WORKER_PATTERN = /^worker\/__modern_bff_effect\.(?:c|m)?js$/u;
|
|
36
|
+
const EFFECT_BFF_WORKER_SUPPORT_PATTERN =
|
|
37
|
+
/^worker\/__modern_worker_(?:runtime|shared)_*\.(?:c|m)?js$/u;
|
|
38
|
+
const isEffectBffWorkerArtifact = (logicalPath: string) =>
|
|
39
|
+
EFFECT_BFF_WORKER_PATTERN.test(logicalPath) ||
|
|
40
|
+
EFFECT_BFF_WORKER_SUPPORT_PATTERN.test(logicalPath);
|
|
41
|
+
const CRAWLER_POLICY_PATH = 'public/robots.txt';
|
|
42
|
+
const isPublicMetadataPath = (logicalPath: string) =>
|
|
43
|
+
logicalPath === 'public/_headers' ||
|
|
44
|
+
/^public\/\.well-known\/[^/]+\.json$/u.test(logicalPath);
|
|
45
|
+
const PUBLIC_BUILD_ARTIFACT_PATH = `public/${ULTRAMODERN_BUILD_ARTIFACT_FILE}`;
|
|
46
|
+
// Files an app declared through `deploy.<target>.publicAssets`. The deploy
|
|
47
|
+
// output stages them from app-root sources; they are served data, not a
|
|
48
|
+
// UI/client surface, so API-only units may carry them.
|
|
49
|
+
const DECLARED_PUBLIC_ASSET_RUNTIME = 'public-asset';
|
|
50
|
+
|
|
51
|
+
const resolveDeclaredPublicAssets = (
|
|
52
|
+
files: readonly string[],
|
|
53
|
+
declaredPublicAssets: readonly string[],
|
|
54
|
+
target: 'Node' | 'Cloudflare',
|
|
55
|
+
) => {
|
|
56
|
+
const staged = new Set(files);
|
|
57
|
+
const declared = new Set<string>();
|
|
58
|
+
for (const logicalPath of declaredPublicAssets) {
|
|
59
|
+
if (!logicalPath.startsWith('public/') || !staged.has(logicalPath)) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`[ultramodern-release-envelope] final ${target} staging has no declared public asset file ${JSON.stringify(logicalPath)}.`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
declared.add(logicalPath);
|
|
65
|
+
}
|
|
66
|
+
return declared;
|
|
67
|
+
};
|
|
36
68
|
|
|
37
69
|
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
38
70
|
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -57,6 +89,111 @@ const collectNestedStrings = (value: unknown, key: string): string[] => {
|
|
|
57
89
|
]);
|
|
58
90
|
};
|
|
59
91
|
|
|
92
|
+
// Empty native MF producers emit no remote entry or exposed execution assets.
|
|
93
|
+
// Only their routeAssets declarations may supply the missing browser evidence.
|
|
94
|
+
const emptyProducerRouteModules = async (
|
|
95
|
+
distDirectory: string,
|
|
96
|
+
files: string[],
|
|
97
|
+
manifest: unknown,
|
|
98
|
+
manifestLogicalPath: string,
|
|
99
|
+
): Promise<string[]> => {
|
|
100
|
+
if (
|
|
101
|
+
!isRecord(manifest) ||
|
|
102
|
+
!Array.isArray(manifest.exposes) ||
|
|
103
|
+
manifest.exposes.length !== 0 ||
|
|
104
|
+
!Array.isArray(manifest.remotes) ||
|
|
105
|
+
manifest.remotes.length !== 0 ||
|
|
106
|
+
!isRecord(manifest.metaData) ||
|
|
107
|
+
!isRecord(manifest.metaData.remoteEntry) ||
|
|
108
|
+
manifest.metaData.remoteEntry.name !== '' ||
|
|
109
|
+
manifest.metaData.remoteEntry.path !== '' ||
|
|
110
|
+
manifest.metaData.remoteEntry.type !== 'global'
|
|
111
|
+
) {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
const publicPath = manifest.metaData.publicPath;
|
|
115
|
+
// Reject encoded/ambiguous paths before URL parsing can normalize traversal.
|
|
116
|
+
const safeReference = (value: unknown): value is string =>
|
|
117
|
+
typeof value === 'string' &&
|
|
118
|
+
value.length > 0 &&
|
|
119
|
+
!/[\\\\%?#\s]/u.test(value) &&
|
|
120
|
+
!value.split('/').some(segment => segment === '.' || segment === '..');
|
|
121
|
+
if (!safeReference(publicPath)) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
const absoluteBase = /^https?:\/\//u.test(publicPath);
|
|
125
|
+
if (
|
|
126
|
+
publicPath !== 'auto' &&
|
|
127
|
+
(!publicPath.endsWith('/') ||
|
|
128
|
+
(!absoluteBase &&
|
|
129
|
+
(!publicPath.startsWith('/') || publicPath.startsWith('//'))))
|
|
130
|
+
) {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
if (absoluteBase) {
|
|
134
|
+
const base = new URL(publicPath);
|
|
135
|
+
if (base.username || base.password || base.href !== publicPath) {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const directory = path.posix.dirname(manifestLogicalPath);
|
|
140
|
+
const routesManifest = await readJson(
|
|
141
|
+
path.join(distDirectory, directory, 'routes-manifest.json'),
|
|
142
|
+
);
|
|
143
|
+
if (!isRecord(routesManifest) || !isRecord(routesManifest.routeAssets)) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const modules = new Set<string>();
|
|
147
|
+
for (const route of Object.values(routesManifest.routeAssets)) {
|
|
148
|
+
if (!isRecord(route) || !Array.isArray(route.assets)) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
for (const reference of route.assets) {
|
|
152
|
+
if (!safeReference(reference) || reference.startsWith('//')) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
let relative = reference;
|
|
156
|
+
if (/^https?:\/\//u.test(reference)) {
|
|
157
|
+
if (!absoluteBase || !reference.startsWith(publicPath)) {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
relative = reference.slice(publicPath.length);
|
|
161
|
+
} else if (reference.startsWith('/')) {
|
|
162
|
+
const prefix =
|
|
163
|
+
publicPath === 'auto'
|
|
164
|
+
? '/'
|
|
165
|
+
: absoluteBase
|
|
166
|
+
? new URL(publicPath).pathname
|
|
167
|
+
: publicPath;
|
|
168
|
+
if (!reference.startsWith(prefix)) {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
relative = reference.slice(prefix.length);
|
|
172
|
+
}
|
|
173
|
+
// Never allow API, SSR, backend containers, or unrelated on-disk modules.
|
|
174
|
+
if (
|
|
175
|
+
!/^(?:static|html|public)\/[^:]+$/u.test(relative) ||
|
|
176
|
+
relative.includes('//')
|
|
177
|
+
) {
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
const logicalPath = path.posix.join(directory, relative);
|
|
181
|
+
if (!files.includes(logicalPath)) {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
// A browser-named symlink to server/private bytes is not browser evidence.
|
|
185
|
+
const assetStat = await fs.lstat(path.join(distDirectory, logicalPath));
|
|
186
|
+
if (!assetStat.isFile()) {
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
if (COMPILED_MODULE_PATTERN.test(logicalPath)) {
|
|
190
|
+
modules.add(logicalPath);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return [...modules];
|
|
195
|
+
};
|
|
196
|
+
|
|
60
197
|
const manifestReferencedClientModules = async (
|
|
61
198
|
distDirectory: string,
|
|
62
199
|
files: string[],
|
|
@@ -77,7 +214,7 @@ const manifestReferencedClientModules = async (
|
|
|
77
214
|
.map(value => value.replace(/^https?:\/\/[^/]+/u, ''))
|
|
78
215
|
.map(value => value.replace(/^\/+/u, '')),
|
|
79
216
|
);
|
|
80
|
-
|
|
217
|
+
const modules = files.filter(
|
|
81
218
|
logicalPath =>
|
|
82
219
|
COMPILED_MODULE_PATTERN.test(logicalPath) &&
|
|
83
220
|
[...normalizedReferences].some(
|
|
@@ -87,6 +224,14 @@ const manifestReferencedClientModules = async (
|
|
|
87
224
|
logicalPath.endsWith(`/${reference}`),
|
|
88
225
|
),
|
|
89
226
|
);
|
|
227
|
+
return modules.length > 0
|
|
228
|
+
? modules
|
|
229
|
+
: emptyProducerRouteModules(
|
|
230
|
+
distDirectory,
|
|
231
|
+
files,
|
|
232
|
+
manifest,
|
|
233
|
+
manifestLogicalPath,
|
|
234
|
+
);
|
|
90
235
|
};
|
|
91
236
|
|
|
92
237
|
const routeReferencedSsrModules = async (
|
|
@@ -209,7 +354,8 @@ const writeReleaseEnvelope = async (
|
|
|
209
354
|
|
|
210
355
|
const readBuildArtifact = async (
|
|
211
356
|
distDirectory: string,
|
|
212
|
-
|
|
357
|
+
requirePromotable = true,
|
|
358
|
+
): Promise<UltramodernBuildArtifact | undefined> => {
|
|
213
359
|
const artifactPath = path.join(
|
|
214
360
|
distDirectory,
|
|
215
361
|
ULTRAMODERN_BUILD_ARTIFACT_FILE,
|
|
@@ -221,8 +367,17 @@ const readBuildArtifact = async (
|
|
|
221
367
|
);
|
|
222
368
|
}
|
|
223
369
|
if (artifact.deliveryUnit.sourceRevision === 'workspace') {
|
|
370
|
+
// `resolveUltramodernSourceRevision` deliberately labels a dirty or
|
|
371
|
+
// non-Git checkout `workspace` so development stays usable; only a
|
|
372
|
+
// promotion may not carry that identity. A plain `modern build` is the
|
|
373
|
+
// development path, so it skips the envelope instead of failing the
|
|
374
|
+
// build. The deploy path still asks for a promotable artifact, which is
|
|
375
|
+
// where the release gate belongs.
|
|
376
|
+
if (!requirePromotable) {
|
|
377
|
+
return undefined;
|
|
378
|
+
}
|
|
224
379
|
throw new Error(
|
|
225
|
-
'[ultramodern-release-envelope] sourceRevision "workspace" cannot produce a promotable
|
|
380
|
+
'[ultramodern-release-envelope] sourceRevision "workspace" cannot produce a promotable envelope.',
|
|
226
381
|
);
|
|
227
382
|
}
|
|
228
383
|
return artifact;
|
|
@@ -393,7 +548,11 @@ const writeReleaseIdentityCarrierMetadata = async (
|
|
|
393
548
|
ReleaseIdentityCarrierSurface,
|
|
394
549
|
string[],
|
|
395
550
|
][]) {
|
|
396
|
-
if (
|
|
551
|
+
if (
|
|
552
|
+
logicalPaths.length === 0 &&
|
|
553
|
+
surface !== 'ssr' &&
|
|
554
|
+
surface !== 'uiClient'
|
|
555
|
+
) {
|
|
397
556
|
throw new Error(
|
|
398
557
|
`[ultramodern-release-envelope] ${surface} has no declared release-identity carrier artifact.`,
|
|
399
558
|
);
|
|
@@ -468,12 +627,10 @@ const createReleaseArtifactInputs = async (
|
|
|
468
627
|
distDirectory: string,
|
|
469
628
|
identity: MicroVerticalReleaseIdentity,
|
|
470
629
|
target: MicroVerticalReleaseTarget,
|
|
630
|
+
apiOnly: boolean,
|
|
471
631
|
) => {
|
|
472
632
|
const files = await collectFiles(distDirectory);
|
|
473
|
-
if (
|
|
474
|
-
target === 'node' &&
|
|
475
|
-
files.some(logicalPath => EFFECT_BFF_WORKER_PATTERN.test(logicalPath))
|
|
476
|
-
) {
|
|
633
|
+
if (target === 'node' && files.some(isEffectBffWorkerArtifact)) {
|
|
477
634
|
throw new Error(
|
|
478
635
|
'[ultramodern-release-envelope] Node target conflicts with a Cloudflare Effect API/BFF worker artifact; resolve the canonical deploy target instead of emitting a Node envelope for Cloudflare output.',
|
|
479
636
|
);
|
|
@@ -483,7 +640,9 @@ const createReleaseArtifactInputs = async (
|
|
|
483
640
|
return (
|
|
484
641
|
topLevel === 'static' ||
|
|
485
642
|
topLevel === 'html' ||
|
|
486
|
-
topLevel === 'public'
|
|
643
|
+
(topLevel === 'public' &&
|
|
644
|
+
logicalPath !== CRAWLER_POLICY_PATH &&
|
|
645
|
+
!isPublicMetadataPath(logicalPath)) ||
|
|
487
646
|
[
|
|
488
647
|
'index.html',
|
|
489
648
|
'mf-manifest.json',
|
|
@@ -505,7 +664,7 @@ const createReleaseArtifactInputs = async (
|
|
|
505
664
|
logicalPath =>
|
|
506
665
|
logicalPath.startsWith('worker/') &&
|
|
507
666
|
COMPILED_MODULE_PATTERN.test(logicalPath) &&
|
|
508
|
-
!
|
|
667
|
+
!isEffectBffWorkerArtifact(logicalPath),
|
|
509
668
|
);
|
|
510
669
|
const nodeApiEntryPaths = files.filter(
|
|
511
670
|
logicalPath =>
|
|
@@ -520,44 +679,48 @@ const createReleaseArtifactInputs = async (
|
|
|
520
679
|
logicalPath.startsWith('shared/')) &&
|
|
521
680
|
COMPILED_MODULE_PATTERN.test(logicalPath),
|
|
522
681
|
)
|
|
523
|
-
: files.filter(
|
|
524
|
-
EFFECT_BFF_WORKER_PATTERN.test(logicalPath),
|
|
525
|
-
);
|
|
682
|
+
: files.filter(isEffectBffWorkerArtifact);
|
|
526
683
|
|
|
527
|
-
if (uiClientPaths.length
|
|
684
|
+
if (apiOnly && (uiClientPaths.length > 0 || ssrPaths.length > 0)) {
|
|
685
|
+
throw new Error(
|
|
686
|
+
'[ultramodern-release-envelope] API-only MicroVertical emitted an undeclared UI/client or SSR surface.',
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
if (!apiOnly && uiClientPaths.length === 0) {
|
|
528
690
|
throw new Error(
|
|
529
691
|
`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no UI/client artifacts.`,
|
|
530
692
|
);
|
|
531
693
|
}
|
|
532
|
-
if (ssrPaths.length === 0) {
|
|
694
|
+
if (!apiOnly && ssrPaths.length === 0) {
|
|
533
695
|
throw new Error(
|
|
534
696
|
`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no ${target === 'node' ? 'Node' : 'workerd'} SSR artifacts.`,
|
|
535
697
|
);
|
|
536
698
|
}
|
|
537
699
|
if (
|
|
538
700
|
(target === 'node' && nodeApiEntryPaths.length === 0) ||
|
|
701
|
+
(target === 'cloudflare' &&
|
|
702
|
+
!files.some(logicalPath =>
|
|
703
|
+
EFFECT_BFF_WORKER_PATTERN.test(logicalPath),
|
|
704
|
+
)) ||
|
|
539
705
|
apiBackendPaths.length === 0
|
|
540
706
|
) {
|
|
541
707
|
throw new Error(
|
|
542
|
-
`[ultramodern-release-envelope] ${target}
|
|
708
|
+
`[ultramodern-release-envelope] ${target} MicroVertical has no actual ${target === 'node' ? 'compiled Node Effect API artifact' : 'Effect API/BFF worker artifact'}.`,
|
|
543
709
|
);
|
|
544
710
|
}
|
|
545
711
|
|
|
546
|
-
const clientExecutionPaths =
|
|
547
|
-
|
|
548
|
-
files
|
|
549
|
-
)
|
|
550
|
-
if (clientExecutionPaths.length === 0) {
|
|
712
|
+
const clientExecutionPaths = apiOnly
|
|
713
|
+
? []
|
|
714
|
+
: await manifestReferencedClientModules(distDirectory, files);
|
|
715
|
+
if (!apiOnly && clientExecutionPaths.length === 0) {
|
|
551
716
|
throw new Error(
|
|
552
717
|
'[ultramodern-release-envelope] UI/client manifest references no compiled execution module.',
|
|
553
718
|
);
|
|
554
719
|
}
|
|
555
|
-
const ssrExecutionPaths =
|
|
556
|
-
|
|
557
|
-
files,
|
|
558
|
-
|
|
559
|
-
);
|
|
560
|
-
if (ssrExecutionPaths.length === 0) {
|
|
720
|
+
const ssrExecutionPaths = apiOnly
|
|
721
|
+
? []
|
|
722
|
+
: await routeReferencedSsrModules(distDirectory, files, target);
|
|
723
|
+
if (!apiOnly && ssrExecutionPaths.length === 0) {
|
|
561
724
|
throw new Error(
|
|
562
725
|
`[ultramodern-release-envelope] route manifest references no emitted ${target === 'node' ? 'Node' : 'Cloudflare'} SSR execution module.`,
|
|
563
726
|
);
|
|
@@ -576,6 +739,11 @@ const createReleaseArtifactInputs = async (
|
|
|
576
739
|
}
|
|
577
740
|
};
|
|
578
741
|
add(uiClientPaths, 'browser');
|
|
742
|
+
add(
|
|
743
|
+
files.filter(logicalPath => logicalPath === CRAWLER_POLICY_PATH),
|
|
744
|
+
'crawler-policy',
|
|
745
|
+
);
|
|
746
|
+
add(files.filter(isPublicMetadataPath), 'public-metadata');
|
|
579
747
|
add(ssrPaths, target === 'node' ? 'nodejs' : 'workerd');
|
|
580
748
|
add(apiBackendPaths, target === 'node' ? 'nodejs' : 'workerd-effect');
|
|
581
749
|
add([BACKEND_FEDERATION_MANIFEST_FILE], 'module-federation-manifest');
|
|
@@ -635,15 +803,14 @@ const pathExists = async (filePath: string) => {
|
|
|
635
803
|
export const emitFrameworkMicroVerticalReleaseEnvelope = async ({
|
|
636
804
|
apiOnly,
|
|
637
805
|
distDirectory,
|
|
806
|
+
requirePromotable = true,
|
|
638
807
|
target,
|
|
639
808
|
}: {
|
|
640
809
|
apiOnly: boolean;
|
|
641
810
|
distDirectory: string;
|
|
811
|
+
requirePromotable?: boolean;
|
|
642
812
|
target: MicroVerticalReleaseTarget;
|
|
643
813
|
}): Promise<MicroVerticalReleaseEnvelope | undefined> => {
|
|
644
|
-
if (apiOnly) {
|
|
645
|
-
return undefined;
|
|
646
|
-
}
|
|
647
814
|
const backendManifestPath = path.join(
|
|
648
815
|
distDirectory,
|
|
649
816
|
BACKEND_FEDERATION_MANIFEST_FILE,
|
|
@@ -656,16 +823,22 @@ export const emitFrameworkMicroVerticalReleaseEnvelope = async ({
|
|
|
656
823
|
pathExists(backendManifestPath),
|
|
657
824
|
pathExists(backendContainerPath),
|
|
658
825
|
]);
|
|
659
|
-
if (!hasBackendManifest && !hasBackendContainer) {
|
|
826
|
+
if (!hasBackendManifest && !hasBackendContainer && !apiOnly) {
|
|
660
827
|
return undefined;
|
|
661
828
|
}
|
|
662
829
|
if (!hasBackendManifest || !hasBackendContainer) {
|
|
663
830
|
throw new Error(
|
|
664
|
-
'[ultramodern-release-envelope]
|
|
831
|
+
'[ultramodern-release-envelope] MicroVertical backend federation manifest and container must be emitted together.',
|
|
665
832
|
);
|
|
666
833
|
}
|
|
667
834
|
|
|
668
|
-
const buildArtifact = await readBuildArtifact(
|
|
835
|
+
const buildArtifact = await readBuildArtifact(
|
|
836
|
+
distDirectory,
|
|
837
|
+
requirePromotable,
|
|
838
|
+
);
|
|
839
|
+
if (!buildArtifact) {
|
|
840
|
+
return undefined;
|
|
841
|
+
}
|
|
669
842
|
const identity = {
|
|
670
843
|
unitId: buildArtifact.deliveryUnit.unitId,
|
|
671
844
|
buildMarker: buildArtifact.deliveryUnit.buildMarker,
|
|
@@ -677,6 +850,7 @@ export const emitFrameworkMicroVerticalReleaseEnvelope = async ({
|
|
|
677
850
|
distDirectory,
|
|
678
851
|
identity,
|
|
679
852
|
target,
|
|
853
|
+
apiOnly,
|
|
680
854
|
);
|
|
681
855
|
const envelope = await createMicroVerticalReleaseEnvelope({
|
|
682
856
|
artifactRoot: distDirectory,
|
|
@@ -723,21 +897,14 @@ export const verifyBuildOutputReleaseEnvelope = async (
|
|
|
723
897
|
distDirectory: string,
|
|
724
898
|
expectedTarget?: MicroVerticalReleaseTarget,
|
|
725
899
|
) => {
|
|
726
|
-
const [hasEnvelope, hasBuildArtifact
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
pathExists(path.join(distDirectory, BACKEND_FEDERATION_MANIFEST_FILE)),
|
|
731
|
-
pathExists(
|
|
732
|
-
path.join(distDirectory, BACKEND_FEDERATION_REMOTE_ENTRY_FILE),
|
|
733
|
-
),
|
|
734
|
-
]);
|
|
735
|
-
const isFullStackMicroVertical =
|
|
736
|
-
hasBuildArtifact && (hasBackendManifest || hasBackendEntry);
|
|
900
|
+
const [hasEnvelope, hasBuildArtifact] = await Promise.all([
|
|
901
|
+
pathExists(path.join(distDirectory, MICROVERTICAL_RELEASE_ENVELOPE_PATH)),
|
|
902
|
+
pathExists(path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE)),
|
|
903
|
+
]);
|
|
737
904
|
return readFrameworkMicroVerticalReleaseEnvelope({
|
|
738
905
|
artifactRoot: distDirectory,
|
|
739
906
|
...(expectedTarget ? { expectedTarget } : {}),
|
|
740
|
-
required: hasEnvelope ||
|
|
907
|
+
required: hasEnvelope || hasBuildArtifact,
|
|
741
908
|
});
|
|
742
909
|
};
|
|
743
910
|
|
|
@@ -756,8 +923,15 @@ export const verifyNodeReleaseEnvelopeStaging = async ({
|
|
|
756
923
|
const createNodeStagedReleaseArtifactInputs = async (
|
|
757
924
|
outputDirectory: string,
|
|
758
925
|
identity: MicroVerticalReleaseIdentity,
|
|
926
|
+
apiOnly: boolean,
|
|
927
|
+
declaredPublicAssets: readonly string[],
|
|
759
928
|
) => {
|
|
760
929
|
const files = await collectFiles(outputDirectory);
|
|
930
|
+
const declaredPublicAssetPaths = resolveDeclaredPublicAssets(
|
|
931
|
+
files,
|
|
932
|
+
declaredPublicAssets,
|
|
933
|
+
'Node',
|
|
934
|
+
);
|
|
761
935
|
for (const requiredPath of [
|
|
762
936
|
'index.js',
|
|
763
937
|
'package.json',
|
|
@@ -780,7 +954,10 @@ const createNodeStagedReleaseArtifactInputs = async (
|
|
|
780
954
|
return (
|
|
781
955
|
topLevel === 'static' ||
|
|
782
956
|
topLevel === 'html' ||
|
|
783
|
-
topLevel === 'public'
|
|
957
|
+
(topLevel === 'public' &&
|
|
958
|
+
logicalPath !== CRAWLER_POLICY_PATH &&
|
|
959
|
+
!isPublicMetadataPath(logicalPath) &&
|
|
960
|
+
!declaredPublicAssetPaths.has(logicalPath)) ||
|
|
784
961
|
[
|
|
785
962
|
'index.html',
|
|
786
963
|
'mf-manifest.json',
|
|
@@ -802,27 +979,35 @@ const createNodeStagedReleaseArtifactInputs = async (
|
|
|
802
979
|
(logicalPath.startsWith('api/') || logicalPath.startsWith('shared/')) &&
|
|
803
980
|
COMPILED_MODULE_PATTERN.test(logicalPath),
|
|
804
981
|
);
|
|
982
|
+
if (
|
|
983
|
+
apiOnly &&
|
|
984
|
+
(uiClientPaths.length > 0 || ssrPaths.some(path => path !== 'index.js'))
|
|
985
|
+
) {
|
|
986
|
+
throw new Error(
|
|
987
|
+
'[ultramodern-release-envelope] final API-only Node staging contains an undeclared UI/client or SSR surface.',
|
|
988
|
+
);
|
|
989
|
+
}
|
|
805
990
|
for (const [surface, paths] of [
|
|
806
991
|
['UI/client', uiClientPaths],
|
|
807
|
-
['Node SSR', ssrPaths],
|
|
992
|
+
['Node SSR', apiOnly ? [] : ssrPaths],
|
|
808
993
|
['Node API/backend', apiBackendPaths],
|
|
809
994
|
] as const) {
|
|
810
|
-
if (paths.length === 0) {
|
|
995
|
+
if (paths.length === 0 && (!apiOnly || surface === 'Node API/backend')) {
|
|
811
996
|
throw new Error(
|
|
812
997
|
`[ultramodern-release-envelope] final Node staging has no ${surface} artifacts.`,
|
|
813
998
|
);
|
|
814
999
|
}
|
|
815
1000
|
}
|
|
816
|
-
const clientExecutionPaths =
|
|
817
|
-
|
|
818
|
-
files
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
outputDirectory,
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
1001
|
+
const clientExecutionPaths = apiOnly
|
|
1002
|
+
? []
|
|
1003
|
+
: await manifestReferencedClientModules(outputDirectory, files);
|
|
1004
|
+
const ssrExecutionPaths = apiOnly
|
|
1005
|
+
? []
|
|
1006
|
+
: await routeReferencedSsrModules(outputDirectory, files, 'node');
|
|
1007
|
+
if (
|
|
1008
|
+
!apiOnly &&
|
|
1009
|
+
(clientExecutionPaths.length === 0 || ssrExecutionPaths.length === 0)
|
|
1010
|
+
) {
|
|
826
1011
|
throw new Error(
|
|
827
1012
|
'[ultramodern-release-envelope] final Node manifests must reference emitted UI/client and SSR execution modules.',
|
|
828
1013
|
);
|
|
@@ -836,7 +1021,16 @@ const createNodeStagedReleaseArtifactInputs = async (
|
|
|
836
1021
|
for (const logicalPath of uiClientPaths) {
|
|
837
1022
|
runtimeByPath.set(logicalPath, 'browser');
|
|
838
1023
|
}
|
|
839
|
-
for (const logicalPath of
|
|
1024
|
+
for (const logicalPath of declaredPublicAssetPaths) {
|
|
1025
|
+
runtimeByPath.set(logicalPath, DECLARED_PUBLIC_ASSET_RUNTIME);
|
|
1026
|
+
}
|
|
1027
|
+
if (files.includes(CRAWLER_POLICY_PATH)) {
|
|
1028
|
+
runtimeByPath.set(CRAWLER_POLICY_PATH, 'crawler-policy');
|
|
1029
|
+
}
|
|
1030
|
+
for (const logicalPath of files.filter(isPublicMetadataPath)) {
|
|
1031
|
+
runtimeByPath.set(logicalPath, 'public-metadata');
|
|
1032
|
+
}
|
|
1033
|
+
for (const logicalPath of apiOnly ? [] : ssrPaths) {
|
|
840
1034
|
runtimeByPath.set(logicalPath, 'nodejs');
|
|
841
1035
|
}
|
|
842
1036
|
for (const logicalPath of apiBackendPaths) {
|
|
@@ -857,12 +1051,14 @@ const createNodeStagedReleaseArtifactInputs = async (
|
|
|
857
1051
|
BACKEND_FEDERATION_MANIFEST_FILE,
|
|
858
1052
|
BACKEND_FEDERATION_REMOTE_ENTRY_FILE,
|
|
859
1053
|
],
|
|
860
|
-
ssr:
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
1054
|
+
ssr: apiOnly
|
|
1055
|
+
? []
|
|
1056
|
+
: [
|
|
1057
|
+
...new Set([
|
|
1058
|
+
...ssrExecutionPaths,
|
|
1059
|
+
...ssrPaths.filter(logicalPath => logicalPath !== 'index.js'),
|
|
1060
|
+
]),
|
|
1061
|
+
],
|
|
866
1062
|
uiClient: uiClientPaths.filter(logicalPath =>
|
|
867
1063
|
COMPILED_MODULE_PATTERN.test(logicalPath),
|
|
868
1064
|
),
|
|
@@ -879,7 +1075,9 @@ const createNodeStagedReleaseArtifactInputs = async (
|
|
|
879
1075
|
uiClient: [...uiClientPaths].sort((left, right) =>
|
|
880
1076
|
left.localeCompare(right),
|
|
881
1077
|
),
|
|
882
|
-
ssr:
|
|
1078
|
+
ssr: apiOnly
|
|
1079
|
+
? []
|
|
1080
|
+
: [...ssrPaths].sort((left, right) => left.localeCompare(right)),
|
|
883
1081
|
apiBackend: [...apiBackendPaths].sort((left, right) =>
|
|
884
1082
|
left.localeCompare(right),
|
|
885
1083
|
),
|
|
@@ -892,9 +1090,12 @@ const createNodeStagedReleaseArtifactInputs = async (
|
|
|
892
1090
|
};
|
|
893
1091
|
|
|
894
1092
|
export const emitNodeStagedReleaseEnvelope = async ({
|
|
1093
|
+
declaredPublicAssets = [],
|
|
895
1094
|
distDirectory,
|
|
896
1095
|
outputDirectory,
|
|
897
1096
|
}: {
|
|
1097
|
+
/** Staged `deploy.node.publicAssets` files, relative to `outputDirectory`. */
|
|
1098
|
+
declaredPublicAssets?: readonly string[];
|
|
898
1099
|
distDirectory: string;
|
|
899
1100
|
outputDirectory: string;
|
|
900
1101
|
}) => {
|
|
@@ -905,6 +1106,8 @@ export const emitNodeStagedReleaseEnvelope = async ({
|
|
|
905
1106
|
const { artifacts, surfaces } = await createNodeStagedReleaseArtifactInputs(
|
|
906
1107
|
outputDirectory,
|
|
907
1108
|
source.identity,
|
|
1109
|
+
source.surfaces.uiClient.length === 0,
|
|
1110
|
+
declaredPublicAssets,
|
|
908
1111
|
);
|
|
909
1112
|
const staged = await createMicroVerticalReleaseEnvelope({
|
|
910
1113
|
artifactRoot: outputDirectory,
|
|
@@ -946,14 +1149,21 @@ export const stageCloudflareReleaseEnvelope = async ({
|
|
|
946
1149
|
const createCloudflareStagedReleaseArtifactInputs = async (
|
|
947
1150
|
outputDirectory: string,
|
|
948
1151
|
identity: MicroVerticalReleaseIdentity,
|
|
1152
|
+
apiOnly: boolean,
|
|
1153
|
+
declaredPublicAssets: readonly string[],
|
|
949
1154
|
) => {
|
|
950
1155
|
const files = await collectFiles(outputDirectory);
|
|
1156
|
+
const declaredPublicAssetPaths = resolveDeclaredPublicAssets(
|
|
1157
|
+
files,
|
|
1158
|
+
declaredPublicAssets,
|
|
1159
|
+
'Cloudflare',
|
|
1160
|
+
);
|
|
951
1161
|
for (const requiredPath of [
|
|
952
1162
|
'server/index.mjs',
|
|
953
1163
|
'server/modern-worker-manifest.json',
|
|
954
1164
|
'wrangler.json',
|
|
955
1165
|
'package.json',
|
|
956
|
-
'worker/package.json',
|
|
1166
|
+
...(apiOnly ? [] : ['worker/package.json']),
|
|
957
1167
|
]) {
|
|
958
1168
|
if (!files.includes(requiredPath)) {
|
|
959
1169
|
throw new Error(
|
|
@@ -974,28 +1184,47 @@ const createCloudflareStagedReleaseArtifactInputs = async (
|
|
|
974
1184
|
}
|
|
975
1185
|
const backendManifestPath = `public/${BACKEND_FEDERATION_MANIFEST_FILE}`;
|
|
976
1186
|
const backendContainerPath = `public/${BACKEND_FEDERATION_REMOTE_ENTRY_FILE}`;
|
|
977
|
-
const apiBackendPaths = files.filter(
|
|
978
|
-
|
|
979
|
-
|
|
1187
|
+
const apiBackendPaths = files.filter(isEffectBffWorkerArtifact);
|
|
1188
|
+
if (!files.some(logicalPath => EFFECT_BFF_WORKER_PATTERN.test(logicalPath))) {
|
|
1189
|
+
throw new Error(
|
|
1190
|
+
'[ultramodern-release-envelope] final Cloudflare staging has no actual Effect API/BFF worker artifact.',
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
980
1193
|
const ssrPaths = files.filter(
|
|
981
1194
|
logicalPath =>
|
|
982
1195
|
(logicalPath.startsWith('server/') ||
|
|
983
1196
|
logicalPath.startsWith('worker/')) &&
|
|
984
1197
|
COMPILED_MODULE_PATTERN.test(logicalPath) &&
|
|
985
|
-
!
|
|
1198
|
+
!isEffectBffWorkerArtifact(logicalPath),
|
|
986
1199
|
);
|
|
987
1200
|
const uiClientPaths = files.filter(
|
|
988
1201
|
logicalPath =>
|
|
989
1202
|
logicalPath.startsWith('public/') &&
|
|
1203
|
+
logicalPath !== CRAWLER_POLICY_PATH &&
|
|
1204
|
+
!isPublicMetadataPath(logicalPath) &&
|
|
1205
|
+
!declaredPublicAssetPaths.has(logicalPath) &&
|
|
1206
|
+
logicalPath !== PUBLIC_BUILD_ARTIFACT_PATH &&
|
|
990
1207
|
logicalPath !== backendManifestPath &&
|
|
991
1208
|
logicalPath !== backendContainerPath,
|
|
992
1209
|
);
|
|
1210
|
+
if (
|
|
1211
|
+
apiOnly &&
|
|
1212
|
+
(uiClientPaths.length > 0 ||
|
|
1213
|
+
ssrPaths.some(path => path !== 'server/index.mjs'))
|
|
1214
|
+
) {
|
|
1215
|
+
throw new Error(
|
|
1216
|
+
'[ultramodern-release-envelope] final API-only Cloudflare staging contains an undeclared UI/client or SSR surface.',
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
993
1219
|
for (const [surface, paths] of [
|
|
994
1220
|
['UI/client', uiClientPaths],
|
|
995
|
-
['Cloudflare SSR', ssrPaths],
|
|
1221
|
+
['Cloudflare SSR', apiOnly ? [] : ssrPaths],
|
|
996
1222
|
['Cloudflare API/backend', apiBackendPaths],
|
|
997
1223
|
] as const) {
|
|
998
|
-
if (
|
|
1224
|
+
if (
|
|
1225
|
+
paths.length === 0 &&
|
|
1226
|
+
(!apiOnly || surface === 'Cloudflare API/backend')
|
|
1227
|
+
) {
|
|
999
1228
|
throw new Error(
|
|
1000
1229
|
`[ultramodern-release-envelope] final Cloudflare staging has no ${surface} artifacts.`,
|
|
1001
1230
|
);
|
|
@@ -1009,18 +1238,25 @@ const createCloudflareStagedReleaseArtifactInputs = async (
|
|
|
1009
1238
|
}
|
|
1010
1239
|
}
|
|
1011
1240
|
|
|
1012
|
-
const clientExecutionPaths =
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1241
|
+
const clientExecutionPaths = apiOnly
|
|
1242
|
+
? []
|
|
1243
|
+
: await manifestReferencedClientModules(
|
|
1244
|
+
outputDirectory,
|
|
1245
|
+
files,
|
|
1246
|
+
'public/mf-manifest.json',
|
|
1247
|
+
);
|
|
1248
|
+
const ssrExecutionPaths = apiOnly
|
|
1249
|
+
? []
|
|
1250
|
+
: await routeReferencedSsrModules(
|
|
1251
|
+
outputDirectory,
|
|
1252
|
+
files,
|
|
1253
|
+
'cloudflare',
|
|
1254
|
+
'server/route.json',
|
|
1255
|
+
);
|
|
1256
|
+
if (
|
|
1257
|
+
!apiOnly &&
|
|
1258
|
+
(clientExecutionPaths.length === 0 || ssrExecutionPaths.length === 0)
|
|
1259
|
+
) {
|
|
1024
1260
|
throw new Error(
|
|
1025
1261
|
'[ultramodern-release-envelope] final Cloudflare manifests must reference emitted UI/client and SSR execution modules.',
|
|
1026
1262
|
);
|
|
@@ -1036,7 +1272,12 @@ const createCloudflareStagedReleaseArtifactInputs = async (
|
|
|
1036
1272
|
}
|
|
1037
1273
|
};
|
|
1038
1274
|
add(uiClientPaths, 'browser');
|
|
1039
|
-
add(
|
|
1275
|
+
add([...declaredPublicAssetPaths], DECLARED_PUBLIC_ASSET_RUNTIME);
|
|
1276
|
+
if (files.includes(CRAWLER_POLICY_PATH)) {
|
|
1277
|
+
runtimeByPath.set(CRAWLER_POLICY_PATH, 'crawler-policy');
|
|
1278
|
+
}
|
|
1279
|
+
add(files.filter(isPublicMetadataPath), 'public-metadata');
|
|
1280
|
+
add(apiOnly ? [] : ssrPaths, 'workerd');
|
|
1040
1281
|
add(apiBackendPaths, 'workerd-effect');
|
|
1041
1282
|
add([backendManifestPath], 'module-federation-manifest');
|
|
1042
1283
|
add([backendContainerPath], 'commonjs-module');
|
|
@@ -1047,12 +1288,16 @@ const createCloudflareStagedReleaseArtifactInputs = async (
|
|
|
1047
1288
|
{
|
|
1048
1289
|
apiBackend: apiBackendPaths,
|
|
1049
1290
|
backendFederation: [backendManifestPath, backendContainerPath],
|
|
1050
|
-
ssr:
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1291
|
+
ssr: apiOnly
|
|
1292
|
+
? []
|
|
1293
|
+
: [
|
|
1294
|
+
...new Set([
|
|
1295
|
+
...ssrExecutionPaths,
|
|
1296
|
+
...ssrPaths.filter(logicalPath =>
|
|
1297
|
+
logicalPath.startsWith('worker/'),
|
|
1298
|
+
),
|
|
1299
|
+
]),
|
|
1300
|
+
],
|
|
1056
1301
|
uiClient: uiClientPaths.filter(logicalPath =>
|
|
1057
1302
|
COMPILED_MODULE_PATTERN.test(logicalPath),
|
|
1058
1303
|
),
|
|
@@ -1069,7 +1314,9 @@ const createCloudflareStagedReleaseArtifactInputs = async (
|
|
|
1069
1314
|
uiClient: [...uiClientPaths].sort((left, right) =>
|
|
1070
1315
|
left.localeCompare(right),
|
|
1071
1316
|
),
|
|
1072
|
-
ssr:
|
|
1317
|
+
ssr: apiOnly
|
|
1318
|
+
? []
|
|
1319
|
+
: [...ssrPaths].sort((left, right) => left.localeCompare(right)),
|
|
1073
1320
|
apiBackend: [...apiBackendPaths].sort((left, right) =>
|
|
1074
1321
|
left.localeCompare(right),
|
|
1075
1322
|
),
|
|
@@ -1082,9 +1329,12 @@ const createCloudflareStagedReleaseArtifactInputs = async (
|
|
|
1082
1329
|
};
|
|
1083
1330
|
|
|
1084
1331
|
export const emitCloudflareStagedReleaseEnvelope = async ({
|
|
1332
|
+
declaredPublicAssets = [],
|
|
1085
1333
|
distDirectory,
|
|
1086
1334
|
outputDirectory,
|
|
1087
1335
|
}: {
|
|
1336
|
+
/** Staged `deploy.worker.publicAssets` files, relative to `outputDirectory`. */
|
|
1337
|
+
declaredPublicAssets?: readonly string[];
|
|
1088
1338
|
distDirectory: string;
|
|
1089
1339
|
outputDirectory: string;
|
|
1090
1340
|
}) => {
|
|
@@ -1099,6 +1349,8 @@ export const emitCloudflareStagedReleaseEnvelope = async ({
|
|
|
1099
1349
|
await createCloudflareStagedReleaseArtifactInputs(
|
|
1100
1350
|
outputDirectory,
|
|
1101
1351
|
source.identity,
|
|
1352
|
+
source.surfaces.uiClient.length === 0,
|
|
1353
|
+
declaredPublicAssets,
|
|
1102
1354
|
);
|
|
1103
1355
|
const staged = await createMicroVerticalReleaseEnvelope({
|
|
1104
1356
|
artifactRoot: outputDirectory,
|