@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.9 → 3.9.3-ultramodern.2
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
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
5
|
+
import {
|
|
6
|
+
assertUltramodernBuildArtifact,
|
|
7
|
+
formatBackendFederationValidationErrors,
|
|
8
|
+
stampUltramodernBuildArtifactIdentity,
|
|
9
|
+
ULTRAMODERN_BUILD_ARTIFACT_FILE,
|
|
10
|
+
ULTRAMODERN_BUILD_ARTIFACT_PATH,
|
|
11
|
+
type UltramodernBuildArtifact,
|
|
12
|
+
type UltramodernBuildUiSurface,
|
|
13
|
+
validateRendererIdentity,
|
|
14
|
+
validateRendererProfile,
|
|
15
|
+
validateRendererRouterBindings,
|
|
16
|
+
} from '@modern-js/backend-federation-contracts';
|
|
17
|
+
|
|
18
|
+
export type RendererBuildOutputContext = {
|
|
19
|
+
appDirectory: string;
|
|
20
|
+
distDirectory: string;
|
|
21
|
+
entrypoints: readonly { entryName: string; isMainEntry?: boolean }[];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type FinalizedRendererBuildOutput = {
|
|
25
|
+
buildMarker: string;
|
|
26
|
+
sourceRevision: string;
|
|
27
|
+
ui: Pick<
|
|
28
|
+
UltramodernBuildUiSurface,
|
|
29
|
+
'rendererIdentity' | 'rendererProfile' | 'routerBindings'
|
|
30
|
+
>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type RendererBuildOutputOptions = {
|
|
34
|
+
resolveRendererBuild: (
|
|
35
|
+
context: RendererBuildOutputContext,
|
|
36
|
+
) => Promise<FinalizedRendererBuildOutput>;
|
|
37
|
+
/** Exact metadata producer selected by the owning renderer composition. */
|
|
38
|
+
rendererBuildPlugin: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const validateFinalizedRendererBuild = (
|
|
42
|
+
output: FinalizedRendererBuildOutput,
|
|
43
|
+
context: RendererBuildOutputContext,
|
|
44
|
+
appId: string,
|
|
45
|
+
) => {
|
|
46
|
+
const entryNames = context.entrypoints.map(entry => entry.entryName);
|
|
47
|
+
const primary =
|
|
48
|
+
context.entrypoints.find(entry => entry.isMainEntry) ??
|
|
49
|
+
context.entrypoints[0];
|
|
50
|
+
const errors = [
|
|
51
|
+
...validateRendererIdentity(output.ui.rendererIdentity).errors,
|
|
52
|
+
...validateRendererProfile(output.ui.rendererProfile).errors,
|
|
53
|
+
...validateRendererRouterBindings(
|
|
54
|
+
output.ui.routerBindings,
|
|
55
|
+
entryNames,
|
|
56
|
+
'routerBindings',
|
|
57
|
+
).errors,
|
|
58
|
+
];
|
|
59
|
+
if (errors.length)
|
|
60
|
+
throw new Error(formatBackendFederationValidationErrors(errors));
|
|
61
|
+
if (
|
|
62
|
+
!primary ||
|
|
63
|
+
new Set(entryNames).size !== entryNames.length ||
|
|
64
|
+
context.entrypoints.filter(entry => entry.isMainEntry).length > 1 ||
|
|
65
|
+
output.ui.rendererIdentity.entryName !== primary.entryName ||
|
|
66
|
+
output.ui.rendererIdentity.appId !== appId ||
|
|
67
|
+
output.ui.rendererIdentity.buildId !== output.buildMarker ||
|
|
68
|
+
output.ui.rendererIdentity.renderer !== output.ui.rendererProfile.renderer
|
|
69
|
+
)
|
|
70
|
+
throw new Error(
|
|
71
|
+
'Finalized renderer output conflicts with the actual application entries and release identity.',
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const stampFinalizedRendererBuildArtifact = (
|
|
76
|
+
artifact: UltramodernBuildArtifact,
|
|
77
|
+
output: FinalizedRendererBuildOutput,
|
|
78
|
+
context: RendererBuildOutputContext,
|
|
79
|
+
) => {
|
|
80
|
+
assertUltramodernBuildArtifact(artifact);
|
|
81
|
+
const ui = artifact.surfaces.ui;
|
|
82
|
+
if (!ui)
|
|
83
|
+
throw new Error(
|
|
84
|
+
'Finalized renderer output requires a declared UI artifact',
|
|
85
|
+
);
|
|
86
|
+
validateFinalizedRendererBuild(output, context, artifact.deliveryUnit.appId);
|
|
87
|
+
const drifted = [
|
|
88
|
+
!isDeepStrictEqual(
|
|
89
|
+
{ ...ui.rendererIdentity, buildId: output.ui.rendererIdentity.buildId },
|
|
90
|
+
output.ui.rendererIdentity,
|
|
91
|
+
) &&
|
|
92
|
+
`rendererIdentity (captured entry ${ui.rendererIdentity.entryName}, built entry ${output.ui.rendererIdentity.entryName})`,
|
|
93
|
+
!isDeepStrictEqual(ui.rendererProfile, output.ui.rendererProfile) &&
|
|
94
|
+
'rendererProfile',
|
|
95
|
+
!isDeepStrictEqual(ui.routerBindings, output.ui.routerBindings) &&
|
|
96
|
+
`routerBindings (captured entries ${Object.keys(ui.routerBindings).join(', ')}, built entries ${Object.keys(output.ui.routerBindings).join(', ')})`,
|
|
97
|
+
].filter(Boolean);
|
|
98
|
+
if (drifted.length)
|
|
99
|
+
throw new Error(
|
|
100
|
+
`Finalized renderer output conflicts with the captured application profile or router bindings: ${drifted.join('; ')} differ from ${ULTRAMODERN_BUILD_ARTIFACT_PATH}. After changing modern.config entries, renderer or router, recapture it with \`ultramodern-create ultramodern sync-delivery-unit\`.`,
|
|
101
|
+
);
|
|
102
|
+
const identity = {
|
|
103
|
+
buildMarker: output.buildMarker,
|
|
104
|
+
sourceRevision: output.sourceRevision,
|
|
105
|
+
};
|
|
106
|
+
const stamped = stampUltramodernBuildArtifactIdentity(artifact, identity);
|
|
107
|
+
return stampUltramodernBuildArtifactIdentity(
|
|
108
|
+
{
|
|
109
|
+
...stamped,
|
|
110
|
+
surfaces: {
|
|
111
|
+
...stamped.surfaces,
|
|
112
|
+
ui: { ...stamped.surfaces.ui!, ...output.ui },
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
identity,
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
async function writeOwnedArtifact(file: string, bytes: string) {
|
|
120
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
121
|
+
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
122
|
+
let created = false;
|
|
123
|
+
try {
|
|
124
|
+
const handle = await fs.open(temporary, 'wx');
|
|
125
|
+
created = true;
|
|
126
|
+
try {
|
|
127
|
+
await handle.writeFile(bytes);
|
|
128
|
+
} finally {
|
|
129
|
+
await handle.close();
|
|
130
|
+
}
|
|
131
|
+
await fs.rename(temporary, file);
|
|
132
|
+
} finally {
|
|
133
|
+
if (created) await fs.rm(temporary, { force: true });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function emitRendererBuildArtifact(
|
|
138
|
+
context: RendererBuildOutputContext,
|
|
139
|
+
options: RendererBuildOutputOptions,
|
|
140
|
+
) {
|
|
141
|
+
const source = path.join(
|
|
142
|
+
context.appDirectory,
|
|
143
|
+
ULTRAMODERN_BUILD_ARTIFACT_PATH,
|
|
144
|
+
);
|
|
145
|
+
const bytes = await fs.readFile(source).catch(error => {
|
|
146
|
+
if (error.code === 'ENOENT') return undefined;
|
|
147
|
+
throw error;
|
|
148
|
+
});
|
|
149
|
+
if (!bytes) {
|
|
150
|
+
const declaration = await fs
|
|
151
|
+
.stat(source.replace(/\.json$/u, '.ts'))
|
|
152
|
+
.catch(error => {
|
|
153
|
+
if (error.code === 'ENOENT') return undefined;
|
|
154
|
+
throw error;
|
|
155
|
+
});
|
|
156
|
+
if (declaration)
|
|
157
|
+
throw new Error(
|
|
158
|
+
'Generated UI artifact declaration is missing its build carrier',
|
|
159
|
+
);
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
const artifact: unknown = JSON.parse(bytes.toString('utf8'));
|
|
163
|
+
assertUltramodernBuildArtifact(artifact);
|
|
164
|
+
const finalized = await options.resolveRendererBuild(context);
|
|
165
|
+
const stamped = stampFinalizedRendererBuildArtifact(
|
|
166
|
+
artifact,
|
|
167
|
+
finalized,
|
|
168
|
+
context,
|
|
169
|
+
);
|
|
170
|
+
const serialized = `${JSON.stringify(stamped, null, 2)}\n`;
|
|
171
|
+
for (const directory of [
|
|
172
|
+
context.distDirectory,
|
|
173
|
+
path.join(context.distDirectory, 'public'),
|
|
174
|
+
])
|
|
175
|
+
await writeOwnedArtifact(
|
|
176
|
+
path.join(directory, ULTRAMODERN_BUILD_ARTIFACT_FILE),
|
|
177
|
+
serialized,
|
|
178
|
+
);
|
|
179
|
+
return stamped;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Stamp generated UI carriers after their actual compiler manifest is committed. */
|
|
183
|
+
export function rendererBuildArtifactStampPlugin(
|
|
184
|
+
options: RendererBuildOutputOptions,
|
|
185
|
+
) {
|
|
186
|
+
if (
|
|
187
|
+
typeof options.rendererBuildPlugin !== 'string' ||
|
|
188
|
+
!options.rendererBuildPlugin ||
|
|
189
|
+
options.rendererBuildPlugin.trim() !== options.rendererBuildPlugin ||
|
|
190
|
+
typeof options.resolveRendererBuild !== 'function'
|
|
191
|
+
)
|
|
192
|
+
throw new Error(
|
|
193
|
+
'Renderer artifact stamping requires its owning metadata producer',
|
|
194
|
+
);
|
|
195
|
+
return {
|
|
196
|
+
name: '@modern-js/renderer-build-artifact-stamp',
|
|
197
|
+
pre: [options.rendererBuildPlugin],
|
|
198
|
+
required: [options.rendererBuildPlugin],
|
|
199
|
+
setup(api: {
|
|
200
|
+
getAppContext(): RendererBuildOutputContext & { apiOnly?: boolean };
|
|
201
|
+
onAfterBuild(handler: () => Promise<void>): void;
|
|
202
|
+
}) {
|
|
203
|
+
api.onAfterBuild(async () => {
|
|
204
|
+
const context = api.getAppContext();
|
|
205
|
+
if (context.apiOnly) return;
|
|
206
|
+
await emitRendererBuildArtifact(context, options);
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
@@ -1,7 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
import type {
|
|
2
|
+
RendererIdentity,
|
|
3
|
+
RendererProfile,
|
|
4
|
+
RendererRouterBindings,
|
|
5
|
+
} from '@modern-js/backend-federation-contracts';
|
|
2
6
|
|
|
3
|
-
|
|
4
|
-
|
|
7
|
+
import {
|
|
8
|
+
MICROVERTICAL_RELEASE_ENVELOPE_KIND,
|
|
9
|
+
MICROVERTICAL_RELEASE_ENVELOPE_SCHEMA_VERSION,
|
|
10
|
+
SHELL_RELEASE_ENVELOPE_KIND,
|
|
11
|
+
} from './contract.mjs';
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
MICROVERTICAL_RELEASE_ENVELOPE_KIND,
|
|
15
|
+
MICROVERTICAL_RELEASE_ENVELOPE_SCHEMA_VERSION,
|
|
16
|
+
SHELL_RELEASE_ENVELOPE_KIND,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type ReleaseEnvelopeKind =
|
|
20
|
+
| typeof MICROVERTICAL_RELEASE_ENVELOPE_KIND
|
|
21
|
+
| typeof SHELL_RELEASE_ENVELOPE_KIND;
|
|
5
22
|
|
|
6
23
|
export const MICROVERTICAL_RELEASE_TARGETS = ['node', 'cloudflare'] as const;
|
|
7
24
|
|
|
@@ -48,17 +65,35 @@ export type MicroVerticalReleaseSurfaces = {
|
|
|
48
65
|
uiClient: string[];
|
|
49
66
|
ssr: string[];
|
|
50
67
|
apiBackend: string[];
|
|
51
|
-
backendFederation
|
|
68
|
+
backendFederation?: {
|
|
52
69
|
manifest: string;
|
|
53
70
|
container: string;
|
|
54
71
|
};
|
|
55
72
|
};
|
|
56
73
|
|
|
74
|
+
export type MicroVerticalReleaseUi = {
|
|
75
|
+
rendererIdentity: RendererIdentity;
|
|
76
|
+
rendererProfile: RendererProfile;
|
|
77
|
+
routerBindings: RendererRouterBindings;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** A Shell consumes remotes; it never publishes a backend federation pair. */
|
|
81
|
+
export type ShellReleaseSurfaces = {
|
|
82
|
+
uiClient: string[];
|
|
83
|
+
ssr: string[];
|
|
84
|
+
apiBackend: string[];
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export type ReleaseSurfaces =
|
|
88
|
+
| MicroVerticalReleaseSurfaces
|
|
89
|
+
| ShellReleaseSurfaces;
|
|
90
|
+
|
|
57
91
|
export type MicroVerticalReleaseEnvelopePayload = {
|
|
58
92
|
schemaVersion: typeof MICROVERTICAL_RELEASE_ENVELOPE_SCHEMA_VERSION;
|
|
59
93
|
kind: typeof MICROVERTICAL_RELEASE_ENVELOPE_KIND;
|
|
60
94
|
target: MicroVerticalReleaseTarget;
|
|
61
95
|
identity: MicroVerticalReleaseIdentity;
|
|
96
|
+
ui?: MicroVerticalReleaseUi;
|
|
62
97
|
artifacts: MicroVerticalReleaseArtifact[];
|
|
63
98
|
surfaces: MicroVerticalReleaseSurfaces;
|
|
64
99
|
};
|
|
@@ -68,16 +103,50 @@ export type MicroVerticalReleaseEnvelope =
|
|
|
68
103
|
envelopeDigest: string;
|
|
69
104
|
};
|
|
70
105
|
|
|
106
|
+
export type ShellReleaseEnvelopePayload = {
|
|
107
|
+
schemaVersion: typeof MICROVERTICAL_RELEASE_ENVELOPE_SCHEMA_VERSION;
|
|
108
|
+
kind: typeof SHELL_RELEASE_ENVELOPE_KIND;
|
|
109
|
+
target: MicroVerticalReleaseTarget;
|
|
110
|
+
identity: MicroVerticalReleaseIdentity;
|
|
111
|
+
ui?: MicroVerticalReleaseUi;
|
|
112
|
+
artifacts: MicroVerticalReleaseArtifact[];
|
|
113
|
+
surfaces: ShellReleaseSurfaces;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export type ShellReleaseEnvelope = ShellReleaseEnvelopePayload & {
|
|
117
|
+
envelopeDigest: string;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export type ReleaseEnvelopePayload =
|
|
121
|
+
| MicroVerticalReleaseEnvelopePayload
|
|
122
|
+
| ShellReleaseEnvelopePayload;
|
|
123
|
+
|
|
124
|
+
export type ReleaseEnvelope =
|
|
125
|
+
| MicroVerticalReleaseEnvelope
|
|
126
|
+
| ShellReleaseEnvelope;
|
|
127
|
+
|
|
71
128
|
export type CreateMicroVerticalReleaseEnvelopeInput = {
|
|
72
129
|
artifactRoot: string;
|
|
73
130
|
target: MicroVerticalReleaseTarget;
|
|
74
131
|
identity: MicroVerticalReleaseIdentity;
|
|
132
|
+
ui?: MicroVerticalReleaseUi;
|
|
75
133
|
artifacts: MicroVerticalReleaseArtifactInput[];
|
|
76
|
-
|
|
77
|
-
|
|
134
|
+
} & (
|
|
135
|
+
| {
|
|
136
|
+
kind?: typeof MICROVERTICAL_RELEASE_ENVELOPE_KIND;
|
|
137
|
+
surfaces: MicroVerticalReleaseSurfaces;
|
|
138
|
+
}
|
|
139
|
+
| {
|
|
140
|
+
kind: typeof SHELL_RELEASE_ENVELOPE_KIND;
|
|
141
|
+
surfaces: ShellReleaseSurfaces;
|
|
142
|
+
}
|
|
143
|
+
);
|
|
78
144
|
|
|
79
145
|
export type VerifyMicroVerticalReleaseEnvelopeOptions = {
|
|
80
146
|
artifactRoot: string;
|
|
81
147
|
logicalPathForArtifact?: (artifact: MicroVerticalReleaseArtifact) => string;
|
|
82
148
|
expectedTarget?: MicroVerticalReleaseTarget;
|
|
149
|
+
expectedKind?: ReleaseEnvelopeKind;
|
|
150
|
+
expectedRendererIdentity?: RendererIdentity;
|
|
151
|
+
expectedRendererProfile?: RendererProfile;
|
|
83
152
|
};
|
package/src/release-identity.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
|
|
4
|
+
// The framework initializes this fixed reader before authored config observation.
|
|
5
|
+
// Its commands remain private; authored subprocess APIs keep their own guards.
|
|
6
|
+
const invokeGit = execFileSync;
|
|
4
7
|
const BUILD_MARKER_NAMESPACE =
|
|
5
8
|
'ultramodern-delivery-unit-release-build-marker:v1';
|
|
6
9
|
const PROMOTABLE_SOURCE_REVISION_PATTERN = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u;
|
|
@@ -10,14 +13,24 @@ export type UltramodernReleaseIdentity = {
|
|
|
10
13
|
sourceRevision: string;
|
|
11
14
|
};
|
|
12
15
|
|
|
16
|
+
// Inherited GIT_* variables (git hooks export GIT_DIR and GIT_INDEX_FILE)
|
|
17
|
+
// would make git describe some other repository than `workspaceRoot`.
|
|
18
|
+
const workspaceGitEnv = (): NodeJS.ProcessEnv =>
|
|
19
|
+
Object.fromEntries(
|
|
20
|
+
Object.entries(process.env).filter(
|
|
21
|
+
([name]) => !name.toUpperCase().startsWith('GIT_'),
|
|
22
|
+
),
|
|
23
|
+
);
|
|
24
|
+
|
|
13
25
|
const gitOutput = (
|
|
14
26
|
workspaceRoot: string,
|
|
15
27
|
args: string[],
|
|
16
28
|
): string | undefined => {
|
|
17
29
|
try {
|
|
18
|
-
return
|
|
30
|
+
return invokeGit('git', args, {
|
|
19
31
|
cwd: workspaceRoot,
|
|
20
32
|
encoding: 'utf8',
|
|
33
|
+
env: workspaceGitEnv(),
|
|
21
34
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
22
35
|
}).trim();
|
|
23
36
|
} catch {
|
|
@@ -44,10 +57,11 @@ export const createUltramodernReleaseBuildMarker = ({
|
|
|
44
57
|
export const resolveUltramodernSourceRevision = (
|
|
45
58
|
workspaceRoot = process.cwd(),
|
|
46
59
|
configuredSourceRevision?: string,
|
|
60
|
+
environment: Readonly<NodeJS.ProcessEnv> = process.env,
|
|
47
61
|
): string => {
|
|
48
62
|
const configuredArgumentValue = configuredSourceRevision?.trim();
|
|
49
63
|
const configuredEnvironmentValue =
|
|
50
|
-
|
|
64
|
+
environment.ULTRAMODERN_SOURCE_REVISION?.trim();
|
|
51
65
|
const configuredArgument =
|
|
52
66
|
configuredArgumentValue && configuredArgumentValue !== 'workspace'
|
|
53
67
|
? configuredArgumentValue
|
|
@@ -110,16 +124,19 @@ export const resolveUltramodernReleaseIdentity = ({
|
|
|
110
124
|
sourceRevision: configuredSourceRevision,
|
|
111
125
|
unitId,
|
|
112
126
|
workspaceRoot,
|
|
127
|
+
environment,
|
|
113
128
|
}: {
|
|
114
129
|
generationBuildMarker: string;
|
|
115
130
|
sourceRevision?: string;
|
|
116
131
|
unitId: string;
|
|
117
132
|
workspaceRoot?: string;
|
|
133
|
+
environment?: Readonly<NodeJS.ProcessEnv>;
|
|
118
134
|
}): UltramodernReleaseIdentity => {
|
|
119
135
|
const explicitSourceRevision = configuredSourceRevision?.trim();
|
|
120
136
|
const sourceRevision = resolveUltramodernSourceRevision(
|
|
121
137
|
workspaceRoot,
|
|
122
138
|
explicitSourceRevision,
|
|
139
|
+
environment,
|
|
123
140
|
);
|
|
124
141
|
return {
|
|
125
142
|
buildMarker:
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {
|
|
5
|
+
immutableRendererRouterBindings,
|
|
6
|
+
type RendererIdentity,
|
|
7
|
+
type RendererName,
|
|
8
|
+
type RendererProfile,
|
|
9
|
+
type RendererRouterBindings,
|
|
10
|
+
type RouterFramework,
|
|
11
|
+
validateRendererProfile,
|
|
12
|
+
validateRendererRouterBindings,
|
|
13
|
+
} from '@modern-js/backend-federation-contracts';
|
|
14
|
+
import { resolveUltramodernSourceRevision } from './release-identity';
|
|
15
|
+
|
|
16
|
+
export type RendererProfileKeyInput = RendererProfile & {
|
|
17
|
+
readonly dependencies?: Readonly<Record<string, string>>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export interface RendererBuildIdentityOptions {
|
|
21
|
+
projectRoot: string;
|
|
22
|
+
renderer: RendererName;
|
|
23
|
+
profile: RendererProfileKeyInput;
|
|
24
|
+
entryNames: readonly string[];
|
|
25
|
+
mode: 'development' | 'production';
|
|
26
|
+
/** Delivery-unit app id; defaults to the application package name. */
|
|
27
|
+
appId?: string;
|
|
28
|
+
/** Externally authenticated source revision (delivery unit or CI). */
|
|
29
|
+
sourceRevision?: string;
|
|
30
|
+
/** Actual final entry ownership, supplied by the selected renderer composition. */
|
|
31
|
+
routerBindings: RendererRouterBindings;
|
|
32
|
+
/** Admitted router frameworks, supplied by the selected renderer owner. */
|
|
33
|
+
routerFrameworks?: readonly RouterFramework[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface RendererBuildIdentities {
|
|
37
|
+
readonly identities: Readonly<Record<string, RendererIdentity>>;
|
|
38
|
+
readonly buildId: string;
|
|
39
|
+
readonly profileKey: string;
|
|
40
|
+
readonly sourceRevision: string;
|
|
41
|
+
readonly routerBindings: RendererRouterBindings;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const LOCKFILES = [
|
|
45
|
+
'pnpm-lock.yaml',
|
|
46
|
+
'package-lock.json',
|
|
47
|
+
'yarn.lock',
|
|
48
|
+
'bun.lock',
|
|
49
|
+
'bun.lockb',
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
/** Stable per process: one dev session or dirty build keeps one buildId. */
|
|
53
|
+
const processNonce = randomUUID();
|
|
54
|
+
|
|
55
|
+
const sha256 = (value: string | Uint8Array) =>
|
|
56
|
+
createHash('sha256').update(value).digest('hex');
|
|
57
|
+
|
|
58
|
+
/** Renderer, protocol and installed renderer/framework/compiler versions. */
|
|
59
|
+
export function rendererProfileKey(profile: RendererProfileKeyInput): string {
|
|
60
|
+
return sha256(
|
|
61
|
+
JSON.stringify([
|
|
62
|
+
profile.renderer,
|
|
63
|
+
profile.protocolVersion,
|
|
64
|
+
profile.compiler,
|
|
65
|
+
profile.hydration,
|
|
66
|
+
profile.router,
|
|
67
|
+
Object.entries(profile.dependencies ?? {}).sort(([left], [right]) =>
|
|
68
|
+
left < right ? -1 : left > right ? 1 : 0,
|
|
69
|
+
),
|
|
70
|
+
]),
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Hash of the nearest lockfile, or an empty marker when there is none. */
|
|
75
|
+
function lockfileHash(projectRoot: string): string {
|
|
76
|
+
let directory = path.resolve(projectRoot);
|
|
77
|
+
for (;;) {
|
|
78
|
+
for (const name of LOCKFILES) {
|
|
79
|
+
try {
|
|
80
|
+
return sha256(fs.readFileSync(path.join(directory, name)));
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const parent = path.dirname(directory);
|
|
86
|
+
if (parent === directory) return '';
|
|
87
|
+
directory = parent;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Resolve once per build before emitting entries; never rehashes sources. */
|
|
92
|
+
export function resolveRendererBuildIdentities(
|
|
93
|
+
options: RendererBuildIdentityOptions,
|
|
94
|
+
): RendererBuildIdentities {
|
|
95
|
+
const profile: RendererProfile = {
|
|
96
|
+
renderer: options.profile.renderer,
|
|
97
|
+
protocolVersion: options.profile.protocolVersion,
|
|
98
|
+
compiler: options.profile.compiler,
|
|
99
|
+
hydration: options.profile.hydration,
|
|
100
|
+
router: options.profile.router,
|
|
101
|
+
};
|
|
102
|
+
const validation = validateRendererProfile(profile);
|
|
103
|
+
if (!validation.ok || options.renderer !== profile.renderer)
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Renderer/profile identity mismatch: ${JSON.stringify(validation.errors)}.`,
|
|
106
|
+
);
|
|
107
|
+
const entries = [...options.entryNames].sort();
|
|
108
|
+
if (
|
|
109
|
+
entries.length === 0 ||
|
|
110
|
+
new Set(entries).size !== entries.length ||
|
|
111
|
+
entries.some(value => !value || value === '__proto__')
|
|
112
|
+
)
|
|
113
|
+
throw new Error(
|
|
114
|
+
'Renderer build identity requires unique nonempty entry names.',
|
|
115
|
+
);
|
|
116
|
+
const routerValidation = validateRendererRouterBindings(
|
|
117
|
+
options.routerBindings,
|
|
118
|
+
entries,
|
|
119
|
+
'routerBindings',
|
|
120
|
+
options.routerFrameworks,
|
|
121
|
+
);
|
|
122
|
+
if (!routerValidation.ok)
|
|
123
|
+
throw new Error(
|
|
124
|
+
`Invalid renderer router bindings: ${JSON.stringify(routerValidation.errors)}.`,
|
|
125
|
+
);
|
|
126
|
+
const projectRoot = path.resolve(options.projectRoot);
|
|
127
|
+
const appId =
|
|
128
|
+
options.appId ??
|
|
129
|
+
JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'))
|
|
130
|
+
.name;
|
|
131
|
+
if (typeof appId !== 'string' || !appId || appId.trim() !== appId)
|
|
132
|
+
throw new Error(
|
|
133
|
+
'Renderer application identity requires a delivery artifact appId or a package name.',
|
|
134
|
+
);
|
|
135
|
+
const profileKey = rendererProfileKey(options.profile);
|
|
136
|
+
const sourceRevision = resolveUltramodernSourceRevision(
|
|
137
|
+
projectRoot,
|
|
138
|
+
options.sourceRevision,
|
|
139
|
+
);
|
|
140
|
+
const buildId = sha256(
|
|
141
|
+
JSON.stringify([
|
|
142
|
+
profileKey,
|
|
143
|
+
lockfileHash(projectRoot),
|
|
144
|
+
options.mode === 'development' || sourceRevision === 'workspace'
|
|
145
|
+
? processNonce
|
|
146
|
+
: sourceRevision,
|
|
147
|
+
]),
|
|
148
|
+
);
|
|
149
|
+
const identities = Object.fromEntries(
|
|
150
|
+
entries.map(entryName => [
|
|
151
|
+
entryName,
|
|
152
|
+
Object.freeze({
|
|
153
|
+
renderer: options.renderer,
|
|
154
|
+
appId,
|
|
155
|
+
entryName,
|
|
156
|
+
protocolVersion: 1 as const,
|
|
157
|
+
buildId,
|
|
158
|
+
}),
|
|
159
|
+
]),
|
|
160
|
+
);
|
|
161
|
+
return Object.freeze({
|
|
162
|
+
identities: Object.freeze(identities),
|
|
163
|
+
buildId,
|
|
164
|
+
profileKey,
|
|
165
|
+
sourceRevision,
|
|
166
|
+
routerBindings: immutableRendererRouterBindings(options.routerBindings),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
@@ -3,8 +3,12 @@ const MODERN_WORKER_MANIFEST = p_workerManifest;
|
|
|
3
3
|
export const modernWorkerManifest = MODERN_WORKER_MANIFEST;
|
|
4
4
|
const WORKER_MODULE_LOADERS = p_workerModuleLoaders;
|
|
5
5
|
const workerModulePromises = new Map();
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
// workerd forbids awaiting I/O (a `fetch()` Promise) that was started by a
|
|
7
|
+
// different request's execution context. This worker isolate outlives any
|
|
8
|
+
// single request, so this map may only ever hold settled, plain JSON values
|
|
9
|
+
// — never a live fetch Promise shared across requests. In-flight dedupe is
|
|
10
|
+
// instead scoped per request; see `createRemoteJsonFetchScope`.
|
|
11
|
+
const remoteJsonCache = new Map();
|
|
8
12
|
const CORS_POLICY = MODERN_WORKER_MANIFEST.security?.cors || {};
|
|
9
13
|
const ASSET_CORS_ENABLED = CORS_POLICY.assets !== false;
|
|
10
14
|
const APP_CORS_ALLOWED_ORIGINS = (CORS_POLICY.allowedOrigins || []).map(
|
|
@@ -24,9 +28,6 @@ const ASSET_CORS_HEADERS = {
|
|
|
24
28
|
'access-control-allow-origin': '*',
|
|
25
29
|
};
|
|
26
30
|
|
|
27
|
-
globalThis.__dirname ??= '/';
|
|
28
|
-
globalThis.__filename ??= '/index.js';
|
|
29
|
-
|
|
30
31
|
function getAllowedAppCorsOrigin(request) {
|
|
31
32
|
if (APP_CORS_ALLOWED_ORIGINS.length === 0) {
|
|
32
33
|
return null;
|
|
@@ -226,7 +227,11 @@ function withCloudflareSecurityHeaders(response, request) {
|
|
|
226
227
|
}
|
|
227
228
|
|
|
228
229
|
function createRenderableRequest(request) {
|
|
229
|
-
if (
|
|
230
|
+
if (
|
|
231
|
+
request.method !== 'HEAD' ||
|
|
232
|
+
request.headers.get('x-rsc-action') ||
|
|
233
|
+
request.headers.get('x-rsc-tree')
|
|
234
|
+
) {
|
|
230
235
|
return request;
|
|
231
236
|
}
|
|
232
237
|
|
|
@@ -242,6 +247,9 @@ function finalizeResponseForRequest(response, request) {
|
|
|
242
247
|
|
|
243
248
|
const headers = new Headers(securedResponse.headers);
|
|
244
249
|
headers.delete('content-length');
|
|
250
|
+
// A HEAD response sends no body, so release the producer's stream instead of abandoning it:
|
|
251
|
+
// a stream nobody reads never finishes, and whatever waits for its end never runs.
|
|
252
|
+
securedResponse.body?.cancel().catch(() => undefined);
|
|
245
253
|
|
|
246
254
|
return new Response(null, {
|
|
247
255
|
headers,
|
|
@@ -20,7 +20,7 @@ function withAssetHeaders(response, request) {
|
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
async function createCorsPreflightResponse(request,
|
|
23
|
+
async function createCorsPreflightResponse(request, _env) {
|
|
24
24
|
if (request.method !== 'OPTIONS') {
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
@@ -71,7 +71,15 @@ async function fetchAssetByPath(pathname, request, env) {
|
|
|
71
71
|
const url = new URL(request.url);
|
|
72
72
|
url.pathname = `/${pathname.replace(/^\/+/u, '')}`;
|
|
73
73
|
|
|
74
|
-
return fetchAsset(
|
|
74
|
+
return fetchAsset(
|
|
75
|
+
new Request(url, {
|
|
76
|
+
method: 'GET',
|
|
77
|
+
headers: request.headers,
|
|
78
|
+
redirect: request.redirect,
|
|
79
|
+
signal: request.signal,
|
|
80
|
+
}),
|
|
81
|
+
env,
|
|
82
|
+
);
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
async function fetchAssetByPathFollowingRedirects(
|