@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.9 → 3.9.3-ultramodern.1
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/README.md +23 -0
- package/dist/cjs/backend-federation-build/codegen.js +29 -18
- package/dist/cjs/backend-federation-build/config.js +84 -45
- package/dist/cjs/backend-federation-build/emit.js +63 -22
- package/dist/cjs/build-config/build-environment.js +25 -162
- package/dist/cjs/build-config/internal-effect-discovery.js +143 -0
- package/dist/cjs/build-config/public.js +2 -5
- package/dist/cjs/build-config/remote-address.js +58 -0
- package/dist/cjs/cloudflare/artifacts.js +8 -19
- package/dist/cjs/cloudflare/delivery-unit.js +125 -43
- package/dist/cjs/cloudflare/index.js +14 -4
- package/dist/cjs/cloudflare/native-renderer.js +100 -0
- package/dist/cjs/cloudflare/renderer-identity.js +106 -0
- package/dist/cjs/cloudflare/worker-manifest.js +54 -13
- package/dist/cjs/cloudflare/worker-options.js +48 -0
- package/dist/cjs/cloudflare/wrangler-config.js +210 -13
- package/dist/cjs/cloudflare-builder.js +307 -89
- package/dist/cjs/cloudflare-output-contract.js +36 -4
- package/dist/cjs/cloudflare-output-verifier/identity.js +112 -23
- 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 +10 -5
- package/dist/cjs/config.js +10 -5
- package/dist/cjs/deploy-output/npmAliases.js +92 -6
- package/dist/cjs/deploy-output/plugin.js +27 -6
- package/dist/cjs/deploy-output/public-assets.js +146 -0
- package/dist/cjs/deploy-output/target.js +57 -1
- package/dist/cjs/deploy-output/trace-files.js +99 -0
- package/dist/cjs/module-federation-config/exposes.js +70 -0
- package/dist/cjs/module-federation-config/index.js +73 -0
- package/dist/cjs/module-federation-config/inspect.js +103 -0
- package/dist/cjs/module-federation-config/object-literal.js +87 -0
- package/dist/cjs/module-federation-config/syntax.js +151 -0
- package/dist/cjs/module-federation-config/types.js +18 -0
- package/dist/cjs/native-type-checker.js +130 -13
- package/dist/cjs/policy-defaults.js +38 -29
- package/dist/cjs/release-envelope/canonical.js +2 -9
- package/dist/cjs/release-envelope/contract.js +68 -0
- package/dist/cjs/release-envelope/framework-output.js +296 -105
- package/dist/cjs/release-envelope/index.js +150 -36
- package/dist/cjs/release-envelope/plugin.js +24 -15
- package/dist/cjs/release-envelope/renderer-output-stamp.js +170 -0
- package/dist/cjs/release-envelope/types.js +10 -8
- package/dist/cjs/release-identity.js +8 -5
- package/dist/cjs/renderer-build-identity.js +134 -0
- package/dist/cjs/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
- package/dist/cjs/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
- package/dist/cjs/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
- package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
- package/dist/cjs/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
- package/dist/cjs/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/cjs/workspace-topology.js +84 -0
- package/dist/esm/backend-federation-build/codegen.mjs +29 -18
- package/dist/esm/backend-federation-build/config.mjs +74 -38
- package/dist/esm/backend-federation-build/emit.mjs +64 -23
- package/dist/esm/build-config/build-environment.mjs +26 -160
- package/dist/esm/build-config/internal-effect-discovery.mjs +102 -0
- package/dist/esm/build-config/public.mjs +1 -1
- package/dist/esm/build-config/remote-address.mjs +20 -0
- package/dist/esm/cloudflare/artifacts.mjs +8 -19
- package/dist/esm/cloudflare/delivery-unit.mjs +124 -45
- package/dist/esm/cloudflare/index.mjs +15 -5
- package/dist/esm/cloudflare/native-renderer.mjs +48 -0
- package/dist/esm/cloudflare/renderer-identity.mjs +57 -0
- package/dist/esm/cloudflare/worker-manifest.mjs +55 -14
- package/dist/esm/cloudflare/worker-options.mjs +10 -0
- package/dist/esm/cloudflare/wrangler-config.mjs +203 -12
- package/dist/esm/cloudflare-builder.mjs +301 -89
- package/dist/esm/cloudflare-output-contract.mjs +29 -3
- package/dist/esm/cloudflare-output-verifier/identity.mjs +113 -24
- 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 +11 -6
- package/dist/esm/config.mjs +3 -1
- package/dist/esm/deploy-output/npmAliases.mjs +92 -6
- package/dist/esm/deploy-output/plugin.mjs +24 -6
- package/dist/esm/deploy-output/public-assets.mjs +82 -0
- package/dist/esm/deploy-output/target.mjs +50 -3
- package/dist/esm/deploy-output/trace-files.mjs +47 -0
- package/dist/esm/module-federation-config/exposes.mjs +18 -0
- package/dist/esm/module-federation-config/index.mjs +4 -0
- package/dist/esm/module-federation-config/inspect.mjs +65 -0
- package/dist/esm/module-federation-config/object-literal.mjs +43 -0
- package/dist/esm/module-federation-config/syntax.mjs +101 -0
- package/dist/esm/module-federation-config/types.mjs +0 -0
- package/dist/esm/native-type-checker.mjs +124 -13
- package/dist/esm/policy-defaults.mjs +36 -30
- package/dist/esm/release-envelope/canonical.mjs +2 -9
- package/dist/esm/release-envelope/contract.mjs +21 -0
- package/dist/esm/release-envelope/framework-output.mjs +297 -106
- package/dist/esm/release-envelope/index.mjs +149 -38
- package/dist/esm/release-envelope/plugin.mjs +24 -15
- package/dist/esm/release-envelope/renderer-output-stamp.mjs +112 -0
- package/dist/esm/release-envelope/types.mjs +2 -3
- package/dist/esm/release-identity.mjs +8 -5
- package/dist/esm/renderer-build-identity.mjs +82 -0
- package/dist/esm/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
- package/dist/esm/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
- package/dist/esm/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
- package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
- package/dist/esm/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
- package/dist/esm/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/esm/workspace-topology.mjs +21 -0
- package/dist/esm-node/backend-federation-build/codegen.mjs +29 -18
- package/dist/esm-node/backend-federation-build/config.mjs +74 -38
- package/dist/esm-node/backend-federation-build/emit.mjs +64 -23
- package/dist/esm-node/build-config/build-environment.mjs +26 -160
- package/dist/esm-node/build-config/internal-effect-discovery.mjs +103 -0
- package/dist/esm-node/build-config/public.mjs +1 -1
- 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/delivery-unit.mjs +124 -45
- package/dist/esm-node/cloudflare/index.mjs +15 -5
- package/dist/esm-node/cloudflare/native-renderer.mjs +49 -0
- package/dist/esm-node/cloudflare/renderer-identity.mjs +58 -0
- package/dist/esm-node/cloudflare/worker-manifest.mjs +55 -14
- package/dist/esm-node/cloudflare/worker-options.mjs +11 -0
- package/dist/esm-node/cloudflare/wrangler-config.mjs +203 -12
- package/dist/esm-node/cloudflare-builder.mjs +301 -89
- package/dist/esm-node/cloudflare-output-contract.mjs +29 -3
- package/dist/esm-node/cloudflare-output-verifier/identity.mjs +113 -24
- 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 +11 -6
- package/dist/esm-node/config.mjs +3 -1
- package/dist/esm-node/deploy-output/npmAliases.mjs +92 -6
- package/dist/esm-node/deploy-output/plugin.mjs +24 -6
- package/dist/esm-node/deploy-output/public-assets.mjs +83 -0
- package/dist/esm-node/deploy-output/target.mjs +50 -3
- package/dist/esm-node/deploy-output/trace-files.mjs +48 -0
- package/dist/esm-node/module-federation-config/exposes.mjs +19 -0
- package/dist/esm-node/module-federation-config/index.mjs +5 -0
- package/dist/esm-node/module-federation-config/inspect.mjs +66 -0
- package/dist/esm-node/module-federation-config/object-literal.mjs +44 -0
- package/dist/esm-node/module-federation-config/syntax.mjs +102 -0
- package/dist/esm-node/module-federation-config/types.mjs +1 -0
- package/dist/esm-node/native-type-checker.mjs +124 -13
- package/dist/esm-node/policy-defaults.mjs +36 -30
- package/dist/esm-node/release-envelope/canonical.mjs +2 -9
- package/dist/esm-node/release-envelope/contract.mjs +22 -0
- package/dist/esm-node/release-envelope/framework-output.mjs +297 -106
- package/dist/esm-node/release-envelope/index.mjs +149 -38
- package/dist/esm-node/release-envelope/plugin.mjs +24 -15
- package/dist/esm-node/release-envelope/renderer-output-stamp.mjs +113 -0
- package/dist/esm-node/release-envelope/types.mjs +2 -3
- package/dist/esm-node/release-identity.mjs +8 -5
- package/dist/esm-node/renderer-build-identity.mjs +83 -0
- package/dist/esm-node/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
- package/dist/esm-node/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
- package/dist/esm-node/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
- package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
- package/dist/esm-node/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
- package/dist/esm-node/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/esm-node/workspace-topology.mjs +22 -0
- package/dist/types/backend-federation-build/codegen.d.ts +30 -0
- package/dist/types/backend-federation-build/config.d.ts +10 -57
- package/dist/types/backend-federation-build/emit.d.ts +12 -2
- package/dist/types/backend-federation-build/index.d.ts +1 -1
- package/dist/types/build-config/build-environment.d.ts +1 -9
- package/dist/types/build-config/internal-effect-discovery.d.ts +4 -0
- package/dist/types/build-config/public.d.ts +1 -1
- 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 +38 -7
- package/dist/types/cloudflare/native-renderer.d.ts +30 -0
- package/dist/types/cloudflare/renderer-identity.d.ts +25 -0
- package/dist/types/cloudflare/worker-manifest.d.ts +17 -2
- package/dist/types/cloudflare/worker-options.d.ts +20 -0
- package/dist/types/cloudflare/wrangler-config.d.ts +55 -2
- package/dist/types/cloudflare-builder.d.ts +51 -8
- package/dist/types/cloudflare-output-contract.d.ts +5 -3
- package/dist/types/cloudflare-output-verifier/identity.d.ts +7 -3
- package/dist/types/cloudflare-output-verifier/worker-bundles.d.ts +2 -2
- package/dist/types/config.d.ts +98 -13
- package/dist/types/deploy-output/plugin.d.ts +29 -9
- package/dist/types/deploy-output/public-assets.d.ts +51 -0
- package/dist/types/deploy-output/target.d.ts +28 -5
- package/dist/types/deploy-output/trace-files.d.ts +17 -0
- package/dist/types/module-federation-config/exposes.d.ts +11 -0
- package/dist/types/module-federation-config/index.d.ts +5 -0
- package/dist/types/module-federation-config/inspect.d.ts +2 -0
- package/dist/types/module-federation-config/object-literal.d.ts +4 -0
- package/dist/types/module-federation-config/syntax.d.ts +7 -0
- package/dist/types/module-federation-config/types.d.ts +20 -0
- package/dist/types/native-type-checker.d.ts +26 -3
- package/dist/types/policy-defaults.d.ts +12 -2
- package/dist/types/release-envelope/canonical.d.ts +5 -5
- package/dist/types/release-envelope/contract.d.mts +8 -0
- package/dist/types/release-envelope/framework-output.d.ts +23 -13
- package/dist/types/release-envelope/index.d.ts +17 -5
- package/dist/types/release-envelope/plugin.d.ts +10 -6
- package/dist/types/release-envelope/renderer-output-stamp.d.ts +34 -0
- package/dist/types/release-envelope/types.d.ts +42 -4
- package/dist/types/release-identity.d.ts +3 -2
- package/dist/types/renderer-build-identity.d.ts +30 -0
- package/dist/types/templates/cloudflare-worker-route-data.d.mts +14 -0
- package/dist/types/workspace-topology.d.ts +91 -0
- package/package.json +87 -27
- package/src/backend-federation-build/codegen.ts +28 -19
- package/src/backend-federation-build/config.ts +189 -123
- package/src/backend-federation-build/emit.ts +119 -38
- package/src/backend-federation-build/index.ts +4 -1
- package/src/build-config/build-environment.ts +59 -285
- package/src/build-config/internal-effect-discovery.ts +239 -0
- package/src/build-config/public.ts +0 -1
- package/src/build-config/remote-address.ts +33 -0
- package/src/cloudflare/artifacts.ts +22 -43
- package/src/cloudflare/delivery-unit.ts +283 -60
- package/src/cloudflare/index.ts +35 -19
- package/src/cloudflare/native-renderer.ts +117 -0
- package/src/cloudflare/renderer-identity.ts +171 -0
- package/src/cloudflare/worker-manifest.ts +108 -10
- package/src/cloudflare/worker-options.ts +60 -0
- package/src/cloudflare/wrangler-config.ts +390 -14
- package/src/cloudflare-builder.ts +543 -170
- package/src/cloudflare-output-contract.ts +45 -2
- package/src/cloudflare-output-verifier/identity.ts +138 -7
- 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 +27 -11
- package/src/config.ts +101 -19
- package/src/deploy-output/npmAliases.ts +141 -4
- package/src/deploy-output/plugin.ts +55 -18
- package/src/deploy-output/public-assets.ts +224 -0
- package/src/deploy-output/target.ts +97 -14
- package/src/deploy-output/trace-files.ts +80 -0
- package/src/module-federation-config/exposes.ts +46 -0
- package/src/module-federation-config/index.ts +22 -0
- package/src/module-federation-config/inspect.ts +157 -0
- package/src/module-federation-config/object-literal.ts +76 -0
- package/src/module-federation-config/syntax.ts +212 -0
- package/src/module-federation-config/types.ts +22 -0
- package/src/native-type-checker.ts +233 -20
- package/src/policy-defaults.ts +47 -17
- package/src/release-envelope/canonical.ts +6 -18
- package/src/release-envelope/contract.mjs +28 -0
- package/src/release-envelope/framework-output.ts +681 -156
- package/src/release-envelope/index.ts +315 -60
- package/src/release-envelope/plugin.ts +54 -38
- package/src/release-envelope/renderer-output-stamp.ts +210 -0
- package/src/release-envelope/types.ts +75 -6
- package/src/release-identity.ts +19 -2
- package/src/renderer-build-identity.ts +168 -0
- package/src/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
- package/src/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
- package/src/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
- package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
- package/src/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
- package/src/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/src/workspace-topology.ts +125 -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
|
@@ -2,14 +2,34 @@ import "node:module";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import promises from "node:fs/promises";
|
|
4
4
|
import node_path from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { isDeepStrictEqual } from "node:util";
|
|
6
|
+
import { BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_REMOTE_ENTRY_FILE, ULTRAMODERN_BUILD_ARTIFACT_FILE, ULTRAMODERN_BUILD_ARTIFACT_PATH, assertRendererProfileCompatibility, isUltramodernBuildArtifact } from "@modern-js/backend-federation-contracts";
|
|
7
|
+
import { readBuildIdentity } from "../backend-federation-build/config.mjs";
|
|
8
|
+
import { resolveTopologyDeliveryUnit, resolveTopologyReleaseRole, resolveWorkerDeliveryUnitStamp } from "../cloudflare/delivery-unit.mjs";
|
|
6
9
|
import { canonicalSerializeMicroVerticalReleaseEnvelope, createMicroVerticalReleaseEnvelope, verifyMicroVerticalReleaseEnvelope } from "./index.mjs";
|
|
10
|
+
import { SHELL_RELEASE_ENVELOPE_KIND } from "./types.mjs";
|
|
7
11
|
const MICROVERTICAL_RELEASE_ENVELOPE_PATH = 'release/microvertical-release-envelope.json';
|
|
8
12
|
const MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_PATH = 'release/microvertical-release-identity-carriers.json';
|
|
9
13
|
const MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_KIND = 'ultramodern-release-identity-carriers';
|
|
10
14
|
const MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_SCHEMA_VERSION = 1;
|
|
11
15
|
const COMPILED_MODULE_PATTERN = /\.(?:c|m)?js$/u;
|
|
12
16
|
const EFFECT_BFF_WORKER_PATTERN = /^worker\/__modern_bff_effect\.(?:c|m)?js$/u;
|
|
17
|
+
const EFFECT_BFF_WORKER_SUPPORT_PATTERN = /^worker\/__modern_worker_(?:runtime|shared)_*\.(?:c|m)?js$/u;
|
|
18
|
+
const isEffectBffWorkerArtifact = (logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath) || EFFECT_BFF_WORKER_SUPPORT_PATTERN.test(logicalPath);
|
|
19
|
+
const effectBffWorkerArtifacts = (files)=>files.some((logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath)) ? files.filter(isEffectBffWorkerArtifact) : [];
|
|
20
|
+
const CRAWLER_POLICY_PATH = 'public/robots.txt';
|
|
21
|
+
const isPublicMetadataPath = (logicalPath)=>'public/_headers' === logicalPath || /^public\/\.well-known\/[^/]+\.json$/u.test(logicalPath);
|
|
22
|
+
const PUBLIC_BUILD_ARTIFACT_PATH = `public/${ULTRAMODERN_BUILD_ARTIFACT_FILE}`;
|
|
23
|
+
const DECLARED_PUBLIC_ASSET_RUNTIME = 'public-asset';
|
|
24
|
+
const resolveDeclaredPublicAssets = (files, declaredPublicAssets, target)=>{
|
|
25
|
+
const staged = new Set(files);
|
|
26
|
+
const declared = new Set();
|
|
27
|
+
for (const logicalPath of declaredPublicAssets){
|
|
28
|
+
if (!logicalPath.startsWith('public/') || !staged.has(logicalPath)) throw new Error(`[ultramodern-release-envelope] final ${target} staging has no declared public asset file ${JSON.stringify(logicalPath)}.`);
|
|
29
|
+
declared.add(logicalPath);
|
|
30
|
+
}
|
|
31
|
+
return declared;
|
|
32
|
+
};
|
|
13
33
|
const isRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
|
|
14
34
|
const flattenStrings = (value)=>'string' == typeof value ? [
|
|
15
35
|
value
|
|
@@ -23,7 +43,12 @@ const collectNestedStrings = (value, key)=>{
|
|
|
23
43
|
]);
|
|
24
44
|
};
|
|
25
45
|
const emptyProducerRouteModules = async (distDirectory, files, manifest, manifestLogicalPath)=>{
|
|
26
|
-
if (!isRecord(manifest) || !Array.isArray(manifest.exposes) || 0 !== manifest.exposes.length || !Array.isArray(manifest.remotes) ||
|
|
46
|
+
if (!isRecord(manifest) || !Array.isArray(manifest.exposes) || 0 !== manifest.exposes.length || !Array.isArray(manifest.remotes) || manifest.remotes.some((remote)=>!isRecord(remote) || [
|
|
47
|
+
'federationContainerName',
|
|
48
|
+
'moduleName',
|
|
49
|
+
'alias',
|
|
50
|
+
'entry'
|
|
51
|
+
].some((key)=>'string' != typeof remote[key] || 0 === remote[key].trim().length)) || !isRecord(manifest.metaData) || !isRecord(manifest.metaData.remoteEntry) || '' !== manifest.metaData.remoteEntry.name || '' !== manifest.metaData.remoteEntry.path || 'global' !== manifest.metaData.remoteEntry.type) return [];
|
|
27
52
|
const publicPath = manifest.metaData.publicPath;
|
|
28
53
|
const safeReference = (value)=>'string' == typeof value && value.length > 0 && !/[\\\\%?#\s]/u.test(value) && !value.split('/').some((segment)=>'.' === segment || '..' === segment);
|
|
29
54
|
if (!safeReference(publicPath)) return [];
|
|
@@ -124,11 +149,14 @@ const writeReleaseEnvelope = async (artifactRoot, envelope)=>{
|
|
|
124
149
|
if (!envelopePath) throw new Error('[ultramodern-release-envelope] could not resolve release envelope path.');
|
|
125
150
|
await promises.writeFile(envelopePath, `${canonicalSerializeMicroVerticalReleaseEnvelope(envelope)}\n`);
|
|
126
151
|
};
|
|
127
|
-
const readBuildArtifact = async (distDirectory)=>{
|
|
152
|
+
const readBuildArtifact = async (distDirectory, requirePromotable = true)=>{
|
|
128
153
|
const artifactPath = node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE);
|
|
129
154
|
const artifact = await readJson(artifactPath);
|
|
130
155
|
if (!isUltramodernBuildArtifact(artifact)) throw new Error(`[ultramodern-release-envelope] ${ULTRAMODERN_BUILD_ARTIFACT_FILE} is missing or invalid.`);
|
|
131
|
-
if ('workspace' === artifact.deliveryUnit.sourceRevision)
|
|
156
|
+
if ('workspace' === artifact.deliveryUnit.sourceRevision) {
|
|
157
|
+
if (!requirePromotable) return;
|
|
158
|
+
throw new Error('[ultramodern-release-envelope] sourceRevision "workspace" cannot produce a promotable envelope.');
|
|
159
|
+
}
|
|
132
160
|
return artifact;
|
|
133
161
|
};
|
|
134
162
|
const assertIdentityBlock = (value, identity, location, includeReleaseVersion)=>{
|
|
@@ -197,7 +225,7 @@ const releaseArtifact = (logicalPath, runtime)=>({
|
|
|
197
225
|
const writeReleaseIdentityCarrierMetadata = async (artifactRoot, identity, carrierPaths)=>{
|
|
198
226
|
const surfacesByPath = new Map();
|
|
199
227
|
for (const [surface, logicalPaths] of Object.entries(carrierPaths)){
|
|
200
|
-
if (0 === logicalPaths.length) throw new Error(`[ultramodern-release-envelope] ${surface} has no declared release-identity carrier artifact.`);
|
|
228
|
+
if (0 === logicalPaths.length && 'ssr' !== surface && 'uiClient' !== surface) throw new Error(`[ultramodern-release-envelope] ${surface} has no declared release-identity carrier artifact.`);
|
|
201
229
|
for (const logicalPath of logicalPaths){
|
|
202
230
|
const surfaces = surfacesByPath.get(logicalPath) ?? new Set();
|
|
203
231
|
surfaces.add(surface);
|
|
@@ -237,12 +265,14 @@ const writeReleaseIdentityCarrierMetadata = async (artifactRoot, identity, carri
|
|
|
237
265
|
}, null, 2)}\n`);
|
|
238
266
|
return releaseArtifact(MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_PATH, 'release-identity-metadata');
|
|
239
267
|
};
|
|
240
|
-
const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
|
|
268
|
+
const createReleaseArtifactInputs = async (distDirectory, identity, target, apiOnly, uiOnly, shellConsumer)=>{
|
|
269
|
+
const backendProducer = !uiOnly && !shellConsumer;
|
|
241
270
|
const files = await collectFiles(distDirectory);
|
|
242
|
-
|
|
271
|
+
const effectWorkerPaths = effectBffWorkerArtifacts(files);
|
|
272
|
+
if ('node' === target && files.some(isEffectBffWorkerArtifact)) throw new Error('[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.');
|
|
243
273
|
const uiClientPaths = files.filter((logicalPath)=>{
|
|
244
274
|
const topLevel = logicalPath.split('/')[0];
|
|
245
|
-
return 'static' === topLevel || 'html' === topLevel || 'public' === topLevel || [
|
|
275
|
+
return 'static' === topLevel || 'html' === topLevel || 'public' === topLevel && logicalPath !== CRAWLER_POLICY_PATH && logicalPath !== PUBLIC_BUILD_ARTIFACT_PATH && !isPublicMetadataPath(logicalPath) || [
|
|
246
276
|
'index.html',
|
|
247
277
|
'mf-manifest.json',
|
|
248
278
|
'mf-stats.json',
|
|
@@ -251,16 +281,19 @@ const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
|
|
|
251
281
|
'loadable-stats.json'
|
|
252
282
|
].includes(logicalPath);
|
|
253
283
|
});
|
|
254
|
-
const ssrPaths = 'node' === target ? files.filter((logicalPath)=>logicalPath.startsWith('bundles/') && COMPILED_MODULE_PATTERN.test(logicalPath)) : files.filter((logicalPath)=>logicalPath.startsWith('worker/') && COMPILED_MODULE_PATTERN.test(logicalPath) && !
|
|
284
|
+
const ssrPaths = 'node' === target ? files.filter((logicalPath)=>logicalPath.startsWith('bundles/') && COMPILED_MODULE_PATTERN.test(logicalPath)) : files.filter((logicalPath)=>logicalPath.startsWith('worker/') && COMPILED_MODULE_PATTERN.test(logicalPath) && !effectWorkerPaths.includes(logicalPath));
|
|
255
285
|
const nodeApiEntryPaths = files.filter((logicalPath)=>logicalPath.startsWith('api/') && COMPILED_MODULE_PATTERN.test(logicalPath));
|
|
256
|
-
const apiBackendPaths = 'node' === target ? files.filter((logicalPath)=>(logicalPath.startsWith('api/') || logicalPath.startsWith('shared/')) && COMPILED_MODULE_PATTERN.test(logicalPath)) :
|
|
257
|
-
if (0 ===
|
|
258
|
-
if (0
|
|
259
|
-
if (
|
|
260
|
-
|
|
261
|
-
if (0
|
|
262
|
-
|
|
263
|
-
|
|
286
|
+
const apiBackendPaths = 'node' === target ? files.filter((logicalPath)=>(logicalPath.startsWith('api/') || logicalPath.startsWith('shared/')) && COMPILED_MODULE_PATTERN.test(logicalPath)) : effectWorkerPaths;
|
|
287
|
+
if (backendProducer && 'cloudflare' === target && 0 === effectWorkerPaths.length) throw new Error('[ultramodern-release-envelope] cloudflare MicroVertical has no actual Effect API/BFF worker artifact.');
|
|
288
|
+
if (apiOnly && (uiClientPaths.length > 0 || ssrPaths.length > 0)) throw new Error('[ultramodern-release-envelope] API-only MicroVertical emitted an undeclared UI/client or SSR surface.');
|
|
289
|
+
if (!apiOnly && 0 === uiClientPaths.length) throw new Error(`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no UI/client artifacts.`);
|
|
290
|
+
if (!apiOnly && 0 === ssrPaths.length) throw new Error(`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no ${'node' === target ? 'Node' : 'workerd'} SSR artifacts.`);
|
|
291
|
+
if (uiOnly && (apiBackendPaths.length > 0 || nodeApiEntryPaths.length > 0)) throw new Error('[ultramodern-release-envelope] UI-only application emitted an undeclared API/backend artifact.');
|
|
292
|
+
if (backendProducer && ('node' === target && 0 === nodeApiEntryPaths.length || 0 === apiBackendPaths.length)) throw new Error(`[ultramodern-release-envelope] ${target} MicroVertical has no actual ${'node' === target ? 'compiled Node Effect API artifact' : 'Effect API/BFF worker artifact'}.`);
|
|
293
|
+
const clientExecutionPaths = apiOnly ? [] : await manifestReferencedClientModules(distDirectory, files);
|
|
294
|
+
if (!apiOnly && 0 === clientExecutionPaths.length) throw new Error('[ultramodern-release-envelope] UI/client manifest references no compiled execution module.');
|
|
295
|
+
const ssrExecutionPaths = apiOnly ? [] : await routeReferencedSsrModules(distDirectory, files, target);
|
|
296
|
+
if (!apiOnly && 0 === ssrExecutionPaths.length) throw new Error(`[ultramodern-release-envelope] route manifest references no emitted ${'node' === target ? 'Node' : 'Cloudflare'} SSR execution module.`);
|
|
264
297
|
const runtimeByPath = new Map();
|
|
265
298
|
const add = (paths, runtime)=>{
|
|
266
299
|
for (const logicalPath of paths){
|
|
@@ -270,14 +303,25 @@ const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
|
|
|
270
303
|
}
|
|
271
304
|
};
|
|
272
305
|
add(uiClientPaths, 'browser');
|
|
306
|
+
add(files.filter((logicalPath)=>logicalPath === CRAWLER_POLICY_PATH), 'crawler-policy');
|
|
307
|
+
add(files.filter(isPublicMetadataPath), 'public-metadata');
|
|
273
308
|
add(ssrPaths, 'node' === target ? 'nodejs' : 'workerd');
|
|
274
309
|
add(apiBackendPaths, 'node' === target ? 'nodejs' : 'workerd-effect');
|
|
275
310
|
add([
|
|
276
|
-
|
|
277
|
-
], '
|
|
278
|
-
add(
|
|
279
|
-
|
|
280
|
-
|
|
311
|
+
ULTRAMODERN_BUILD_ARTIFACT_FILE
|
|
312
|
+
], 'release-identity-metadata');
|
|
313
|
+
add(files.filter((logicalPath)=>logicalPath === PUBLIC_BUILD_ARTIFACT_PATH), 'release-identity-metadata');
|
|
314
|
+
if (backendProducer) {
|
|
315
|
+
add([
|
|
316
|
+
BACKEND_FEDERATION_MANIFEST_FILE
|
|
317
|
+
], 'module-federation-manifest');
|
|
318
|
+
add([
|
|
319
|
+
BACKEND_FEDERATION_REMOTE_ENTRY_FILE
|
|
320
|
+
], 'node' === target ? 'nodejs' : 'commonjs-module');
|
|
321
|
+
}
|
|
322
|
+
if (shellConsumer) {
|
|
323
|
+
for (const logicalPath of files)if (!runtimeByPath.has(logicalPath)) runtimeByPath.set(logicalPath, `${target}-deployment`);
|
|
324
|
+
}
|
|
281
325
|
const surfaces = {
|
|
282
326
|
uiClient: [
|
|
283
327
|
...uiClientPaths
|
|
@@ -288,20 +332,26 @@ const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
|
|
|
288
332
|
apiBackend: [
|
|
289
333
|
...apiBackendPaths
|
|
290
334
|
].sort((left, right)=>left.localeCompare(right)),
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
335
|
+
...backendProducer ? {
|
|
336
|
+
backendFederation: {
|
|
337
|
+
manifest: BACKEND_FEDERATION_MANIFEST_FILE,
|
|
338
|
+
container: BACKEND_FEDERATION_REMOTE_ENTRY_FILE
|
|
339
|
+
}
|
|
340
|
+
} : {}
|
|
295
341
|
};
|
|
296
342
|
const artifacts = [
|
|
297
343
|
...runtimeByPath
|
|
298
344
|
].map(([logicalPath, runtime])=>releaseArtifact(logicalPath, runtime)).sort((left, right)=>left.logicalPath.localeCompare(right.logicalPath));
|
|
299
345
|
const identityCarrierMetadata = await writeReleaseIdentityCarrierMetadata(distDirectory, identity, {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
346
|
+
...apiBackendPaths.length > 0 ? {
|
|
347
|
+
apiBackend: apiBackendPaths
|
|
348
|
+
} : {},
|
|
349
|
+
...backendProducer ? {
|
|
350
|
+
backendFederation: [
|
|
351
|
+
BACKEND_FEDERATION_MANIFEST_FILE,
|
|
352
|
+
BACKEND_FEDERATION_REMOTE_ENTRY_FILE
|
|
353
|
+
]
|
|
354
|
+
} : {},
|
|
305
355
|
ssr: [
|
|
306
356
|
...new Set([
|
|
307
357
|
...ssrExecutionPaths,
|
|
@@ -318,6 +368,25 @@ const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
|
|
|
318
368
|
surfaces
|
|
319
369
|
};
|
|
320
370
|
};
|
|
371
|
+
const releaseEnvelopeSurfaces = (surfaces, shellConsumer)=>{
|
|
372
|
+
if (!shellConsumer) return {
|
|
373
|
+
surfaces
|
|
374
|
+
};
|
|
375
|
+
const { uiClient, ssr, apiBackend } = surfaces;
|
|
376
|
+
return {
|
|
377
|
+
kind: SHELL_RELEASE_ENVELOPE_KIND,
|
|
378
|
+
surfaces: {
|
|
379
|
+
uiClient,
|
|
380
|
+
ssr,
|
|
381
|
+
apiBackend
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
};
|
|
385
|
+
const assertShellBuildIdentity = async (appDirectory, distDirectory)=>{
|
|
386
|
+
const source = await readBuildIdentity(appDirectory);
|
|
387
|
+
const finalized = await pathExists(node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE)) ? await resolveWorkerDeliveryUnitStamp(appDirectory, distDirectory) : void 0;
|
|
388
|
+
if (!source.artifact || !finalized || finalized.unitId !== source.unitId) throw new Error('[ultramodern-release-envelope] Shell is missing its native compiled delivery-unit identity.');
|
|
389
|
+
};
|
|
321
390
|
const pathExists = async (filePath)=>{
|
|
322
391
|
try {
|
|
323
392
|
await promises.access(filePath);
|
|
@@ -326,36 +395,79 @@ const pathExists = async (filePath)=>{
|
|
|
326
395
|
return false;
|
|
327
396
|
}
|
|
328
397
|
};
|
|
329
|
-
const emitFrameworkMicroVerticalReleaseEnvelope = async ({ apiOnly, distDirectory, target })=>{
|
|
330
|
-
|
|
398
|
+
const emitFrameworkMicroVerticalReleaseEnvelope = async ({ apiOnly, appDirectory, distDirectory, requirePromotable = true, expectedRendererProfile, role, target })=>{
|
|
399
|
+
const declaredRole = appDirectory ? await resolveTopologyReleaseRole(appDirectory) : void 0;
|
|
400
|
+
if (void 0 !== role && void 0 !== declaredRole && role !== declaredRole) throw new Error('[ultramodern-release-envelope] Configured release role disagrees with the declared topology app.');
|
|
401
|
+
const shellConsumer = (declaredRole ?? role ?? 'microvertical') === 'shell';
|
|
331
402
|
const backendManifestPath = node_path.join(distDirectory, BACKEND_FEDERATION_MANIFEST_FILE);
|
|
332
403
|
const backendContainerPath = node_path.join(distDirectory, BACKEND_FEDERATION_REMOTE_ENTRY_FILE);
|
|
333
404
|
const [hasBackendManifest, hasBackendContainer] = await Promise.all([
|
|
334
405
|
pathExists(backendManifestPath),
|
|
335
406
|
pathExists(backendContainerPath)
|
|
336
407
|
]);
|
|
337
|
-
if (
|
|
338
|
-
|
|
339
|
-
|
|
408
|
+
if (shellConsumer) {
|
|
409
|
+
if (apiOnly || hasBackendManifest || hasBackendContainer || !appDirectory) throw new Error('[ultramodern-release-envelope] Shell requires a full-stack application source and cannot declare a backend federation producer.');
|
|
410
|
+
await assertShellBuildIdentity(appDirectory, distDirectory);
|
|
411
|
+
} else if (!apiOnly && !hasBackendManifest && !hasBackendContainer) {
|
|
412
|
+
const source = appDirectory ? node_path.join(appDirectory, ULTRAMODERN_BUILD_ARTIFACT_PATH) : void 0;
|
|
413
|
+
const participationPaths = [
|
|
414
|
+
node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE),
|
|
415
|
+
backendManifestPath,
|
|
416
|
+
backendContainerPath,
|
|
417
|
+
...source ? [
|
|
418
|
+
source,
|
|
419
|
+
source.replace(/\.json$/u, '.ts')
|
|
420
|
+
] : []
|
|
421
|
+
];
|
|
422
|
+
const hasParticipationEntry = (await Promise.all(participationPaths.map(async (filePath)=>{
|
|
423
|
+
try {
|
|
424
|
+
await promises.lstat(filePath);
|
|
425
|
+
return true;
|
|
426
|
+
} catch (error) {
|
|
427
|
+
if (isMissingPathError(error)) return false;
|
|
428
|
+
throw error;
|
|
429
|
+
}
|
|
430
|
+
}))).some(Boolean);
|
|
431
|
+
if (!hasParticipationEntry && (!appDirectory || !await resolveTopologyDeliveryUnit(appDirectory))) return;
|
|
432
|
+
}
|
|
433
|
+
const buildArtifact = await readBuildArtifact(distDirectory, requirePromotable);
|
|
434
|
+
const declared = appDirectory && buildArtifact ? await resolveWorkerDeliveryUnitStamp(appDirectory, distDirectory) : void 0;
|
|
435
|
+
const uiOnly = Boolean(declared?.surfaces.ui && !declared.surfaces.api);
|
|
436
|
+
if (uiOnly && (apiOnly || hasBackendManifest || hasBackendContainer)) throw new Error('[ultramodern-release-envelope] UI-only application cannot declare API-only configuration or backend federation output.');
|
|
437
|
+
if (!hasBackendManifest && !hasBackendContainer && !apiOnly && !buildArtifact) return;
|
|
438
|
+
if (!shellConsumer && !uiOnly && (!hasBackendManifest || !hasBackendContainer)) throw new Error('[ultramodern-release-envelope] MicroVertical backend federation manifest and container must be emitted together.');
|
|
439
|
+
if (!buildArtifact) return;
|
|
440
|
+
const builtUi = buildArtifact.surfaces.ui;
|
|
441
|
+
if (Boolean(builtUi) === apiOnly) throw new Error('[ultramodern-release-envelope] build artifact UI surface must match the configured application surface.');
|
|
442
|
+
if (void 0 !== expectedRendererProfile) assertRendererProfileCompatibility(expectedRendererProfile, builtUi?.rendererProfile);
|
|
443
|
+
const ui = builtUi ? {
|
|
444
|
+
rendererIdentity: builtUi.rendererIdentity,
|
|
445
|
+
rendererProfile: builtUi.rendererProfile,
|
|
446
|
+
routerBindings: builtUi.routerBindings
|
|
447
|
+
} : void 0;
|
|
340
448
|
const identity = {
|
|
341
449
|
unitId: buildArtifact.deliveryUnit.unitId,
|
|
342
450
|
buildMarker: buildArtifact.deliveryUnit.buildMarker,
|
|
343
451
|
sourceRevision: buildArtifact.deliveryUnit.sourceRevision,
|
|
344
452
|
releaseVersion: buildArtifact.deliveryUnit.version
|
|
345
453
|
};
|
|
346
|
-
await readBackendManifest(distDirectory, identity);
|
|
347
|
-
const { artifacts, surfaces } = await createReleaseArtifactInputs(distDirectory, identity, target);
|
|
454
|
+
if (!shellConsumer && !uiOnly) await readBackendManifest(distDirectory, identity);
|
|
455
|
+
const { artifacts, surfaces } = await createReleaseArtifactInputs(distDirectory, identity, target, apiOnly, uiOnly, shellConsumer);
|
|
348
456
|
const envelope = await createMicroVerticalReleaseEnvelope({
|
|
349
457
|
artifactRoot: distDirectory,
|
|
350
458
|
target,
|
|
351
459
|
identity,
|
|
460
|
+
...ui ? {
|
|
461
|
+
ui
|
|
462
|
+
} : {},
|
|
352
463
|
artifacts,
|
|
353
|
-
surfaces
|
|
464
|
+
...releaseEnvelopeSurfaces(surfaces, shellConsumer)
|
|
354
465
|
});
|
|
466
|
+
await verifyBuildArtifactCarriers(distDirectory, envelope);
|
|
355
467
|
await writeReleaseEnvelope(distDirectory, envelope);
|
|
356
468
|
return envelope;
|
|
357
469
|
};
|
|
358
|
-
const readFrameworkMicroVerticalReleaseEnvelope = async ({ artifactRoot, expectedTarget, logicalPathForArtifact, required = false })=>{
|
|
470
|
+
const readFrameworkMicroVerticalReleaseEnvelope = async ({ artifactRoot, expectedTarget, logicalPathForArtifact, expectedRendererProfile, required = false })=>{
|
|
359
471
|
const envelopePath = await resolveSafeReleaseEnvelopePath({
|
|
360
472
|
artifactRoot,
|
|
361
473
|
createDirectory: false
|
|
@@ -364,30 +476,55 @@ const readFrameworkMicroVerticalReleaseEnvelope = async ({ artifactRoot, expecte
|
|
|
364
476
|
if (required) throw new Error(`[ultramodern-release-envelope] required envelope is missing at ${MICROVERTICAL_RELEASE_ENVELOPE_PATH}.`);
|
|
365
477
|
return;
|
|
366
478
|
}
|
|
367
|
-
|
|
479
|
+
const envelope = await verifyMicroVerticalReleaseEnvelope(await readJson(envelopePath), {
|
|
368
480
|
artifactRoot,
|
|
369
481
|
...expectedTarget ? {
|
|
370
482
|
expectedTarget
|
|
371
483
|
} : {},
|
|
372
484
|
...logicalPathForArtifact ? {
|
|
373
485
|
logicalPathForArtifact
|
|
486
|
+
} : {},
|
|
487
|
+
...expectedRendererProfile ? {
|
|
488
|
+
expectedRendererProfile
|
|
374
489
|
} : {}
|
|
375
490
|
});
|
|
491
|
+
await verifyBuildArtifactCarriers(artifactRoot, envelope, logicalPathForArtifact);
|
|
492
|
+
return envelope;
|
|
493
|
+
};
|
|
494
|
+
const verifyBuildArtifactCarriers = async (artifactRoot, envelope, logicalPathForArtifact)=>{
|
|
495
|
+
const carriers = envelope.artifacts.filter((artifact)=>artifact.logicalPath === ULTRAMODERN_BUILD_ARTIFACT_FILE || artifact.logicalPath === PUBLIC_BUILD_ARTIFACT_PATH);
|
|
496
|
+
if (0 === carriers.length) throw new Error('[ultramodern-release-envelope] immutable build artifact must be bound to the release envelope.');
|
|
497
|
+
for (const artifact of carriers){
|
|
498
|
+
if ('file' !== artifact.kind || 'release-identity-metadata' !== artifact.runtime) throw new Error('[ultramodern-release-envelope] immutable build artifact must be a real file with runtime "release-identity-metadata".');
|
|
499
|
+
const logicalPath = logicalPathForArtifact?.(artifact) ?? artifact.logicalPath;
|
|
500
|
+
const built = await readJson(node_path.join(artifactRoot, logicalPath));
|
|
501
|
+
if (!isUltramodernBuildArtifact(built)) throw new Error('[ultramodern-release-envelope] bound build artifact is invalid.');
|
|
502
|
+
assertIdentityBlock(built.deliveryUnit, envelope.identity, 'buildArtifact.deliveryUnit', true);
|
|
503
|
+
if (Boolean(built.surfaces.ui) !== Boolean(envelope.ui)) throw new Error('[ultramodern-release-envelope] bound build artifact UI surface differs from the release envelope.');
|
|
504
|
+
if (envelope.ui && built.surfaces.ui) {
|
|
505
|
+
if (!isDeepStrictEqual(envelope.ui.routerBindings, built.surfaces.ui.routerBindings)) throw new Error('[ultramodern-release-envelope] buildArtifact.ui.routerBindings differ from the release envelope.');
|
|
506
|
+
assertRendererProfileCompatibility(envelope.ui.rendererProfile, built.surfaces.ui.rendererProfile);
|
|
507
|
+
for (const field of [
|
|
508
|
+
'renderer',
|
|
509
|
+
'protocolVersion',
|
|
510
|
+
'appId',
|
|
511
|
+
'entryName',
|
|
512
|
+
'buildId'
|
|
513
|
+
])if (envelope.ui.rendererIdentity[field] !== built.surfaces.ui.rendererIdentity[field]) throw new Error(`[ultramodern-release-envelope] buildArtifact.ui.rendererIdentity.${field} differs from the release envelope.`);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
376
516
|
};
|
|
377
517
|
const verifyBuildOutputReleaseEnvelope = async (distDirectory, expectedTarget)=>{
|
|
378
|
-
const [hasEnvelope, hasBuildArtifact
|
|
518
|
+
const [hasEnvelope, hasBuildArtifact] = await Promise.all([
|
|
379
519
|
pathExists(node_path.join(distDirectory, MICROVERTICAL_RELEASE_ENVELOPE_PATH)),
|
|
380
|
-
pathExists(node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE))
|
|
381
|
-
pathExists(node_path.join(distDirectory, BACKEND_FEDERATION_MANIFEST_FILE)),
|
|
382
|
-
pathExists(node_path.join(distDirectory, BACKEND_FEDERATION_REMOTE_ENTRY_FILE))
|
|
520
|
+
pathExists(node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE))
|
|
383
521
|
]);
|
|
384
|
-
const isFullStackMicroVertical = hasBuildArtifact && (hasBackendManifest || hasBackendEntry);
|
|
385
522
|
return readFrameworkMicroVerticalReleaseEnvelope({
|
|
386
523
|
artifactRoot: distDirectory,
|
|
387
524
|
...expectedTarget ? {
|
|
388
525
|
expectedTarget
|
|
389
526
|
} : {},
|
|
390
|
-
required: hasEnvelope ||
|
|
527
|
+
required: hasEnvelope || hasBuildArtifact
|
|
391
528
|
});
|
|
392
529
|
};
|
|
393
530
|
const verifyNodeReleaseEnvelopeStaging = async ({ outputDirectory })=>readFrameworkMicroVerticalReleaseEnvelope({
|
|
@@ -395,21 +532,26 @@ const verifyNodeReleaseEnvelopeStaging = async ({ outputDirectory })=>readFramew
|
|
|
395
532
|
expectedTarget: 'node',
|
|
396
533
|
required: true
|
|
397
534
|
});
|
|
398
|
-
const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=>{
|
|
535
|
+
const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity, apiOnly, uiOnly, declaredPublicAssets, shellConsumer)=>{
|
|
536
|
+
const backendProducer = !uiOnly && !shellConsumer;
|
|
399
537
|
const files = await collectFiles(outputDirectory);
|
|
538
|
+
const declaredPublicAssetPaths = resolveDeclaredPublicAssets(files, declaredPublicAssets, 'Node');
|
|
400
539
|
for (const requiredPath of [
|
|
401
540
|
'index.js',
|
|
402
541
|
'package.json',
|
|
403
|
-
|
|
404
|
-
|
|
542
|
+
...backendProducer ? [
|
|
543
|
+
BACKEND_FEDERATION_MANIFEST_FILE,
|
|
544
|
+
BACKEND_FEDERATION_REMOTE_ENTRY_FILE
|
|
545
|
+
] : []
|
|
405
546
|
])if (!files.includes(requiredPath)) throw new Error(`[ultramodern-release-envelope] final Node staging is missing ${requiredPath}.`);
|
|
406
547
|
const backendFederationPaths = new Set([
|
|
407
548
|
BACKEND_FEDERATION_MANIFEST_FILE,
|
|
408
549
|
BACKEND_FEDERATION_REMOTE_ENTRY_FILE
|
|
409
550
|
]);
|
|
551
|
+
if (shellConsumer && files.some((logicalPath)=>backendFederationPaths.has(logicalPath))) throw new Error('[ultramodern-release-envelope] final Shell Node staging cannot contain a backend federation producer artifact.');
|
|
410
552
|
const uiClientPaths = files.filter((logicalPath)=>{
|
|
411
553
|
const topLevel = logicalPath.split('/')[0];
|
|
412
|
-
return 'static' === topLevel || 'html' === topLevel || 'public' === topLevel || [
|
|
554
|
+
return 'static' === topLevel || 'html' === topLevel || 'public' === topLevel && logicalPath !== CRAWLER_POLICY_PATH && logicalPath !== PUBLIC_BUILD_ARTIFACT_PATH && !isPublicMetadataPath(logicalPath) && !declaredPublicAssetPaths.has(logicalPath) || [
|
|
413
555
|
'index.html',
|
|
414
556
|
'mf-manifest.json',
|
|
415
557
|
'mf-stats.json',
|
|
@@ -420,6 +562,8 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
|
|
|
420
562
|
});
|
|
421
563
|
const ssrPaths = files.filter((logicalPath)=>'index.js' === logicalPath || logicalPath.startsWith('bundles/') && COMPILED_MODULE_PATTERN.test(logicalPath));
|
|
422
564
|
const apiBackendPaths = files.filter((logicalPath)=>(logicalPath.startsWith('api/') || logicalPath.startsWith('shared/')) && COMPILED_MODULE_PATTERN.test(logicalPath));
|
|
565
|
+
if (uiOnly && (apiBackendPaths.length > 0 || files.some((logicalPath)=>backendFederationPaths.has(logicalPath)))) throw new Error('[ultramodern-release-envelope] final UI-only Node staging contains an undeclared API/backend or backend federation artifact.');
|
|
566
|
+
if (apiOnly && (uiClientPaths.length > 0 || ssrPaths.some((path)=>'index.js' !== path))) throw new Error('[ultramodern-release-envelope] final API-only Node staging contains an undeclared UI/client or SSR surface.');
|
|
423
567
|
for (const [surface, paths] of [
|
|
424
568
|
[
|
|
425
569
|
'UI/client',
|
|
@@ -427,32 +571,45 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
|
|
|
427
571
|
],
|
|
428
572
|
[
|
|
429
573
|
'Node SSR',
|
|
430
|
-
ssrPaths
|
|
574
|
+
apiOnly ? [] : ssrPaths
|
|
431
575
|
],
|
|
432
576
|
[
|
|
433
577
|
'Node API/backend',
|
|
434
578
|
apiBackendPaths
|
|
435
579
|
]
|
|
436
|
-
])if (0 === paths.length) throw new Error(`[ultramodern-release-envelope] final Node staging has no ${surface} artifacts.`);
|
|
437
|
-
const clientExecutionPaths = await manifestReferencedClientModules(outputDirectory, files);
|
|
438
|
-
const ssrExecutionPaths = await routeReferencedSsrModules(outputDirectory, files, 'node');
|
|
439
|
-
if (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length) throw new Error('[ultramodern-release-envelope] final Node manifests must reference emitted UI/client and SSR execution modules.');
|
|
580
|
+
])if (0 === paths.length && ('Node API/backend' === surface ? backendProducer : !apiOnly)) throw new Error(`[ultramodern-release-envelope] final Node staging has no ${surface} artifacts.`);
|
|
581
|
+
const clientExecutionPaths = apiOnly ? [] : await manifestReferencedClientModules(outputDirectory, files);
|
|
582
|
+
const ssrExecutionPaths = apiOnly ? [] : await routeReferencedSsrModules(outputDirectory, files, 'node');
|
|
583
|
+
if (!apiOnly && (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length)) throw new Error('[ultramodern-release-envelope] final Node manifests must reference emitted UI/client and SSR execution modules.');
|
|
440
584
|
const runtimeByPath = new Map(files.filter((logicalPath)=>!backendFederationPaths.has(logicalPath)).map((logicalPath)=>[
|
|
441
585
|
logicalPath,
|
|
442
586
|
'nodejs-deployment'
|
|
443
587
|
]));
|
|
444
588
|
for (const logicalPath of uiClientPaths)runtimeByPath.set(logicalPath, 'browser');
|
|
445
|
-
for (const logicalPath of
|
|
589
|
+
for (const logicalPath of declaredPublicAssetPaths)runtimeByPath.set(logicalPath, DECLARED_PUBLIC_ASSET_RUNTIME);
|
|
590
|
+
if (files.includes(CRAWLER_POLICY_PATH)) runtimeByPath.set(CRAWLER_POLICY_PATH, 'crawler-policy');
|
|
591
|
+
for (const logicalPath of files.filter(isPublicMetadataPath))runtimeByPath.set(logicalPath, 'public-metadata');
|
|
592
|
+
for (const logicalPath of apiOnly ? [] : ssrPaths)runtimeByPath.set(logicalPath, 'nodejs');
|
|
446
593
|
for (const logicalPath of apiBackendPaths)runtimeByPath.set(logicalPath, 'nodejs');
|
|
447
|
-
|
|
448
|
-
|
|
594
|
+
if (backendProducer) {
|
|
595
|
+
runtimeByPath.set(BACKEND_FEDERATION_MANIFEST_FILE, 'module-federation-manifest');
|
|
596
|
+
runtimeByPath.set(BACKEND_FEDERATION_REMOTE_ENTRY_FILE, 'nodejs');
|
|
597
|
+
}
|
|
598
|
+
for (const logicalPath of [
|
|
599
|
+
ULTRAMODERN_BUILD_ARTIFACT_FILE,
|
|
600
|
+
PUBLIC_BUILD_ARTIFACT_PATH
|
|
601
|
+
])if (files.includes(logicalPath)) runtimeByPath.set(logicalPath, 'release-identity-metadata');
|
|
449
602
|
const identityCarrierMetadata = await writeReleaseIdentityCarrierMetadata(outputDirectory, identity, {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
603
|
+
...apiBackendPaths.length > 0 ? {
|
|
604
|
+
apiBackend: apiBackendPaths
|
|
605
|
+
} : {},
|
|
606
|
+
...backendProducer ? {
|
|
607
|
+
backendFederation: [
|
|
608
|
+
BACKEND_FEDERATION_MANIFEST_FILE,
|
|
609
|
+
BACKEND_FEDERATION_REMOTE_ENTRY_FILE
|
|
610
|
+
]
|
|
611
|
+
} : {},
|
|
612
|
+
ssr: apiOnly ? [] : [
|
|
456
613
|
...new Set([
|
|
457
614
|
...ssrExecutionPaths,
|
|
458
615
|
...ssrPaths.filter((logicalPath)=>'index.js' !== logicalPath)
|
|
@@ -471,30 +628,37 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
|
|
|
471
628
|
uiClient: [
|
|
472
629
|
...uiClientPaths
|
|
473
630
|
].sort((left, right)=>left.localeCompare(right)),
|
|
474
|
-
ssr: [
|
|
631
|
+
ssr: apiOnly ? [] : [
|
|
475
632
|
...ssrPaths
|
|
476
633
|
].sort((left, right)=>left.localeCompare(right)),
|
|
477
634
|
apiBackend: [
|
|
478
635
|
...apiBackendPaths
|
|
479
636
|
].sort((left, right)=>left.localeCompare(right)),
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
637
|
+
...backendProducer ? {
|
|
638
|
+
backendFederation: {
|
|
639
|
+
manifest: BACKEND_FEDERATION_MANIFEST_FILE,
|
|
640
|
+
container: BACKEND_FEDERATION_REMOTE_ENTRY_FILE
|
|
641
|
+
}
|
|
642
|
+
} : {}
|
|
484
643
|
}
|
|
485
644
|
};
|
|
486
645
|
};
|
|
487
|
-
const emitNodeStagedReleaseEnvelope = async ({ distDirectory, outputDirectory })=>{
|
|
646
|
+
const emitNodeStagedReleaseEnvelope = async ({ declaredPublicAssets = [], distDirectory, outputDirectory })=>{
|
|
488
647
|
const source = await verifyBuildOutputReleaseEnvelope(distDirectory, 'node');
|
|
489
648
|
if (!source) return;
|
|
490
|
-
const
|
|
649
|
+
const shellConsumer = source.kind === SHELL_RELEASE_ENVELOPE_KIND;
|
|
650
|
+
const { artifacts, surfaces } = await createNodeStagedReleaseArtifactInputs(outputDirectory, source.identity, 0 === source.surfaces.uiClient.length, 0 === source.surfaces.apiBackend.length, declaredPublicAssets, shellConsumer);
|
|
491
651
|
const staged = await createMicroVerticalReleaseEnvelope({
|
|
492
652
|
artifactRoot: outputDirectory,
|
|
493
653
|
target: 'node',
|
|
494
654
|
identity: source.identity,
|
|
655
|
+
...source.ui ? {
|
|
656
|
+
ui: source.ui
|
|
657
|
+
} : {},
|
|
495
658
|
artifacts,
|
|
496
|
-
surfaces
|
|
659
|
+
...releaseEnvelopeSurfaces(surfaces, shellConsumer)
|
|
497
660
|
});
|
|
661
|
+
await verifyBuildArtifactCarriers(outputDirectory, staged);
|
|
498
662
|
await writeReleaseEnvelope(outputDirectory, staged);
|
|
499
663
|
return staged;
|
|
500
664
|
};
|
|
@@ -505,21 +669,29 @@ const stageCloudflareReleaseEnvelope = async ({ distDirectory, outputDirectory }
|
|
|
505
669
|
if (await pathExists(stagedEnvelopePath)) throw new Error('[ultramodern-release-envelope] Cloudflare staging contains a stale envelope before final output generation.');
|
|
506
670
|
return source;
|
|
507
671
|
};
|
|
508
|
-
const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, identity)=>{
|
|
672
|
+
const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, identity, apiOnly, uiOnly, declaredPublicAssets, shellConsumer)=>{
|
|
673
|
+
const backendProducer = !uiOnly && !shellConsumer;
|
|
509
674
|
const files = await collectFiles(outputDirectory);
|
|
675
|
+
const declaredPublicAssetPaths = resolveDeclaredPublicAssets(files, declaredPublicAssets, 'Cloudflare');
|
|
510
676
|
for (const requiredPath of [
|
|
511
677
|
'server/index.mjs',
|
|
512
678
|
'server/modern-worker-manifest.json',
|
|
513
679
|
'wrangler.json',
|
|
514
680
|
'package.json',
|
|
515
|
-
|
|
681
|
+
...apiOnly ? [] : [
|
|
682
|
+
'worker/package.json'
|
|
683
|
+
]
|
|
516
684
|
])if (!files.includes(requiredPath)) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging is missing ${requiredPath}.`);
|
|
517
685
|
if (files.some((logicalPath)=>'public/release' === logicalPath || logicalPath.startsWith('public/release/'))) throw new Error('[ultramodern-release-envelope] final Cloudflare envelope must remain private and cannot be staged under public/release.');
|
|
518
686
|
const backendManifestPath = `public/${BACKEND_FEDERATION_MANIFEST_FILE}`;
|
|
519
687
|
const backendContainerPath = `public/${BACKEND_FEDERATION_REMOTE_ENTRY_FILE}`;
|
|
520
|
-
const apiBackendPaths = files
|
|
521
|
-
|
|
522
|
-
const
|
|
688
|
+
const apiBackendPaths = effectBffWorkerArtifacts(files);
|
|
689
|
+
if (backendProducer && !files.some((logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath))) throw new Error('[ultramodern-release-envelope] final Cloudflare staging has no actual Effect API/BFF worker artifact.');
|
|
690
|
+
const ssrPaths = files.filter((logicalPath)=>(logicalPath.startsWith('server/') || logicalPath.startsWith('worker/')) && COMPILED_MODULE_PATTERN.test(logicalPath) && !apiBackendPaths.includes(logicalPath));
|
|
691
|
+
const uiClientPaths = files.filter((logicalPath)=>logicalPath.startsWith('public/') && logicalPath !== CRAWLER_POLICY_PATH && !isPublicMetadataPath(logicalPath) && !declaredPublicAssetPaths.has(logicalPath) && logicalPath !== PUBLIC_BUILD_ARTIFACT_PATH && logicalPath !== backendManifestPath && logicalPath !== backendContainerPath);
|
|
692
|
+
if (shellConsumer && files.some((logicalPath)=>logicalPath === backendManifestPath || logicalPath === backendContainerPath)) throw new Error('[ultramodern-release-envelope] final Shell Cloudflare staging cannot contain a backend federation producer artifact.');
|
|
693
|
+
if (uiOnly && (apiBackendPaths.length > 0 || files.some((logicalPath)=>logicalPath === backendManifestPath || logicalPath === backendContainerPath || logicalPath.startsWith('api/') && COMPILED_MODULE_PATTERN.test(logicalPath)))) throw new Error('[ultramodern-release-envelope] final UI-only Cloudflare staging contains an undeclared API/backend or backend federation artifact.');
|
|
694
|
+
if (apiOnly && (uiClientPaths.length > 0 || ssrPaths.some((path)=>'server/index.mjs' !== path))) throw new Error('[ultramodern-release-envelope] final API-only Cloudflare staging contains an undeclared UI/client or SSR surface.');
|
|
523
695
|
for (const [surface, paths] of [
|
|
524
696
|
[
|
|
525
697
|
'UI/client',
|
|
@@ -527,41 +699,53 @@ const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, iden
|
|
|
527
699
|
],
|
|
528
700
|
[
|
|
529
701
|
'Cloudflare SSR',
|
|
530
|
-
ssrPaths
|
|
702
|
+
apiOnly ? [] : ssrPaths
|
|
531
703
|
],
|
|
532
704
|
[
|
|
533
705
|
'Cloudflare API/backend',
|
|
534
706
|
apiBackendPaths
|
|
535
707
|
]
|
|
536
|
-
])if (0 === paths.length) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging has no ${surface} artifacts.`);
|
|
537
|
-
for (const logicalPath of [
|
|
708
|
+
])if (0 === paths.length && ('Cloudflare API/backend' === surface ? backendProducer : !apiOnly)) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging has no ${surface} artifacts.`);
|
|
709
|
+
for (const logicalPath of backendProducer ? [
|
|
538
710
|
backendManifestPath,
|
|
539
711
|
backendContainerPath
|
|
540
|
-
])if (!files.includes(logicalPath)) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging is missing ${logicalPath}.`);
|
|
541
|
-
const clientExecutionPaths = await manifestReferencedClientModules(outputDirectory, files, 'public/mf-manifest.json');
|
|
542
|
-
const ssrExecutionPaths = await routeReferencedSsrModules(outputDirectory, files, 'cloudflare', 'server/route.json');
|
|
543
|
-
if (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length) throw new Error('[ultramodern-release-envelope] final Cloudflare manifests must reference emitted UI/client and SSR execution modules.');
|
|
712
|
+
] : [])if (!files.includes(logicalPath)) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging is missing ${logicalPath}.`);
|
|
713
|
+
const clientExecutionPaths = apiOnly ? [] : await manifestReferencedClientModules(outputDirectory, files, 'public/mf-manifest.json');
|
|
714
|
+
const ssrExecutionPaths = apiOnly ? [] : await routeReferencedSsrModules(outputDirectory, files, 'cloudflare', 'server/route.json');
|
|
715
|
+
if (!apiOnly && (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length)) throw new Error('[ultramodern-release-envelope] final Cloudflare manifests must reference emitted UI/client and SSR execution modules.');
|
|
544
716
|
const runtimeByPath = new Map();
|
|
545
717
|
for (const logicalPath of files)runtimeByPath.set(logicalPath, 'cloudflare-deployment');
|
|
546
718
|
const add = (paths, runtime)=>{
|
|
547
719
|
for (const logicalPath of paths)runtimeByPath.set(logicalPath, runtime);
|
|
548
720
|
};
|
|
549
721
|
add(uiClientPaths, 'browser');
|
|
550
|
-
add(ssrPaths, 'workerd');
|
|
551
|
-
add(apiBackendPaths, 'workerd-effect');
|
|
552
722
|
add([
|
|
553
|
-
|
|
554
|
-
],
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
], '
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
backendManifestPath
|
|
723
|
+
...declaredPublicAssetPaths
|
|
724
|
+
], DECLARED_PUBLIC_ASSET_RUNTIME);
|
|
725
|
+
if (files.includes(CRAWLER_POLICY_PATH)) runtimeByPath.set(CRAWLER_POLICY_PATH, 'crawler-policy');
|
|
726
|
+
add(files.filter(isPublicMetadataPath), 'public-metadata');
|
|
727
|
+
add(apiOnly ? [] : ssrPaths, 'workerd');
|
|
728
|
+
add(apiBackendPaths, 'workerd-effect');
|
|
729
|
+
if (backendProducer) {
|
|
730
|
+
add([
|
|
731
|
+
backendManifestPath
|
|
732
|
+
], 'module-federation-manifest');
|
|
733
|
+
add([
|
|
562
734
|
backendContainerPath
|
|
563
|
-
],
|
|
564
|
-
|
|
735
|
+
], 'commonjs-module');
|
|
736
|
+
}
|
|
737
|
+
add(files.filter((logicalPath)=>logicalPath === ULTRAMODERN_BUILD_ARTIFACT_FILE || logicalPath === PUBLIC_BUILD_ARTIFACT_PATH), 'release-identity-metadata');
|
|
738
|
+
const identityCarrierMetadata = await writeReleaseIdentityCarrierMetadata(outputDirectory, identity, {
|
|
739
|
+
...apiBackendPaths.length > 0 ? {
|
|
740
|
+
apiBackend: apiBackendPaths
|
|
741
|
+
} : {},
|
|
742
|
+
...backendProducer ? {
|
|
743
|
+
backendFederation: [
|
|
744
|
+
backendManifestPath,
|
|
745
|
+
backendContainerPath
|
|
746
|
+
]
|
|
747
|
+
} : {},
|
|
748
|
+
ssr: apiOnly ? [] : [
|
|
565
749
|
...new Set([
|
|
566
750
|
...ssrExecutionPaths,
|
|
567
751
|
...ssrPaths.filter((logicalPath)=>logicalPath.startsWith('worker/'))
|
|
@@ -580,30 +764,37 @@ const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, iden
|
|
|
580
764
|
uiClient: [
|
|
581
765
|
...uiClientPaths
|
|
582
766
|
].sort((left, right)=>left.localeCompare(right)),
|
|
583
|
-
ssr: [
|
|
767
|
+
ssr: apiOnly ? [] : [
|
|
584
768
|
...ssrPaths
|
|
585
769
|
].sort((left, right)=>left.localeCompare(right)),
|
|
586
770
|
apiBackend: [
|
|
587
771
|
...apiBackendPaths
|
|
588
772
|
].sort((left, right)=>left.localeCompare(right)),
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
773
|
+
...backendProducer ? {
|
|
774
|
+
backendFederation: {
|
|
775
|
+
manifest: backendManifestPath,
|
|
776
|
+
container: backendContainerPath
|
|
777
|
+
}
|
|
778
|
+
} : {}
|
|
593
779
|
}
|
|
594
780
|
};
|
|
595
781
|
};
|
|
596
|
-
const emitCloudflareStagedReleaseEnvelope = async ({ distDirectory, outputDirectory })=>{
|
|
782
|
+
const emitCloudflareStagedReleaseEnvelope = async ({ declaredPublicAssets = [], distDirectory, outputDirectory })=>{
|
|
597
783
|
const source = await verifyBuildOutputReleaseEnvelope(distDirectory, 'cloudflare');
|
|
598
784
|
if (!source) return;
|
|
599
|
-
const
|
|
785
|
+
const shellConsumer = source.kind === SHELL_RELEASE_ENVELOPE_KIND;
|
|
786
|
+
const { artifacts, surfaces } = await createCloudflareStagedReleaseArtifactInputs(outputDirectory, source.identity, 0 === source.surfaces.uiClient.length, 0 === source.surfaces.apiBackend.length, declaredPublicAssets, shellConsumer);
|
|
600
787
|
const staged = await createMicroVerticalReleaseEnvelope({
|
|
601
788
|
artifactRoot: outputDirectory,
|
|
602
789
|
target: 'cloudflare',
|
|
603
790
|
identity: source.identity,
|
|
791
|
+
...source.ui ? {
|
|
792
|
+
ui: source.ui
|
|
793
|
+
} : {},
|
|
604
794
|
artifacts,
|
|
605
|
-
surfaces
|
|
795
|
+
...releaseEnvelopeSurfaces(surfaces, shellConsumer)
|
|
606
796
|
});
|
|
797
|
+
await verifyBuildArtifactCarriers(outputDirectory, staged);
|
|
607
798
|
await writeReleaseEnvelope(outputDirectory, staged);
|
|
608
799
|
return staged;
|
|
609
800
|
};
|