@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.9 → 3.9.3-ultramodern.10
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 +30 -165
- package/dist/cjs/build-config/internal-effect-discovery.js +231 -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 +180 -17
- 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 +9 -6
- package/dist/cjs/renderer-build-identity.js +144 -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 +547 -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 +31 -163
- package/dist/esm/build-config/internal-effect-discovery.mjs +187 -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 +175 -18
- 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 +9 -6
- package/dist/esm/renderer-build-identity.mjs +92 -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 +547 -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 +31 -163
- package/dist/esm-node/build-config/internal-effect-discovery.mjs +188 -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 +175 -18
- 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 +9 -6
- package/dist/esm-node/renderer-build-identity.mjs +93 -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 +547 -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 +4 -10
- package/dist/types/build-config/internal-effect-discovery.d.ts +6 -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 +20 -4
- 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 +102 -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 +28 -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 +13 -2
- package/dist/types/renderer-build-identity.d.ts +41 -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 +69 -293
- package/src/build-config/internal-effect-discovery.ts +458 -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 +115 -13
- 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 +105 -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 +284 -27
- 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 +30 -4
- package/src/renderer-build-identity.ts +208 -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 +547 -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,188 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { lstatSync, readFileSync, realpathSync } from "node:fs";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { dirname, join, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
const EFFECT_TSGO_PACKAGE = '@effect/tsgo';
|
|
8
|
+
const EFFECT_TSGO_BIN = 'effect-tsgo';
|
|
9
|
+
const NATIVE_TYPESCRIPT_PACKAGES = [
|
|
10
|
+
"typescript",
|
|
11
|
+
"@typescript/native"
|
|
12
|
+
];
|
|
13
|
+
const EFFECT_TSRS_PROVIDER_VERSION = '0.59.0';
|
|
14
|
+
const EFFECT_TSRS_TARGETS = new Set([
|
|
15
|
+
'@tsrs/darwin-arm64',
|
|
16
|
+
'@tsrs/linux-x64-gnu',
|
|
17
|
+
'@tsrs/linux-arm64-gnu'
|
|
18
|
+
]);
|
|
19
|
+
const failureStages = new WeakMap();
|
|
20
|
+
const reportedUnsupportedTsrsTargets = new Set();
|
|
21
|
+
function effectCompilerDiscoveryFailureStage(error) {
|
|
22
|
+
return error && 'object' == typeof error ? failureStages.get(error) : void 0;
|
|
23
|
+
}
|
|
24
|
+
function resolveEffectTsgoPackage(from) {
|
|
25
|
+
const projectRequire = createRequire(from);
|
|
26
|
+
const packageJsonPath = projectRequire.resolve(`${EFFECT_TSGO_PACKAGE}/package.json`);
|
|
27
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
28
|
+
const bin = 'string' == typeof packageJson.bin ? packageJson.bin : packageJson.bin?.[EFFECT_TSGO_BIN];
|
|
29
|
+
if (!bin) throw new Error(`Resolved ${EFFECT_TSGO_PACKAGE} package has no ${EFFECT_TSGO_BIN} bin: ${packageJsonPath}`);
|
|
30
|
+
const cliPath = realpathSync(resolve(dirname(packageJsonPath), bin));
|
|
31
|
+
if (!lstatSync(cliPath).isFile()) throw new Error(`Effect TS-Go CLI is not a regular file: ${cliPath}`);
|
|
32
|
+
return {
|
|
33
|
+
packageJsonPath,
|
|
34
|
+
packageJson,
|
|
35
|
+
cliPath
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function isAbsentPackage(projectRequire, from, packageName, error) {
|
|
39
|
+
const filename = 'string' != typeof from || from.startsWith('file:') ? fileURLToPath(from) : from;
|
|
40
|
+
if (!(error instanceof Error) || !('code' in error) || 'MODULE_NOT_FOUND' !== error.code || 'path' in error && void 0 !== error.path || error.message.split('\n')[0] !== `Cannot find module '${packageName}/package.json'` || !('requireStack' in error) || !Array.isArray(error.requireStack) || error.requireStack[0] !== filename) return false;
|
|
41
|
+
for (const searchDirectory of projectRequire.resolve.paths(packageName) ?? [])try {
|
|
42
|
+
lstatSync(join(searchDirectory, packageName));
|
|
43
|
+
return false;
|
|
44
|
+
} catch (presenceError) {
|
|
45
|
+
if (!(presenceError instanceof Error) || !('code' in presenceError) || ![
|
|
46
|
+
'ENOENT',
|
|
47
|
+
'ENOTDIR'
|
|
48
|
+
].includes(String(presenceError.code))) throw presenceError;
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
function resolveNativeTypeScriptPackage(from) {
|
|
53
|
+
const projectRequire = createRequire(from);
|
|
54
|
+
for (const packageName of NATIVE_TYPESCRIPT_PACKAGES){
|
|
55
|
+
let packageJsonPath;
|
|
56
|
+
try {
|
|
57
|
+
packageJsonPath = projectRequire.resolve(`${packageName}/package.json`);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (isAbsentPackage(projectRequire, from, packageName, error)) continue;
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
63
|
+
const major = packageJson.version?.match(/^(\d+)\.\d+\.\d+(?:[-+].*)?$/u)?.[1];
|
|
64
|
+
if (!major) throw new Error(`Resolved TypeScript package has no valid version: ${packageJsonPath}`);
|
|
65
|
+
if (Number(major) >= 7) return packageJsonPath;
|
|
66
|
+
}
|
|
67
|
+
throw new Error(`No native TypeScript backend is installed for ${String(from)}. Install typescript >=7 or @typescript/native.`);
|
|
68
|
+
}
|
|
69
|
+
function resolveNativeTypeScriptManifest(from) {
|
|
70
|
+
const packageJsonPath = resolveNativeTypeScriptPackage(from);
|
|
71
|
+
const directory = dirname(packageJsonPath);
|
|
72
|
+
const discoveryPackage = resolveNativeTypeScriptPackage(join(directory, '__ultramodern_effect_backend__.cjs'));
|
|
73
|
+
if (realpathSync(discoveryPackage) !== realpathSync(packageJsonPath)) throw new Error(`Effect discovery would select a different native TypeScript installation from ${directory}: ${discoveryPackage}`);
|
|
74
|
+
return packageJsonPath;
|
|
75
|
+
}
|
|
76
|
+
function readCompilerArtifact(filename) {
|
|
77
|
+
const canonicalPath = realpathSync(filename);
|
|
78
|
+
const stats = lstatSync(canonicalPath);
|
|
79
|
+
if (!stats.isFile()) throw new Error(`Compiler artifact is not a regular file: ${filename}`);
|
|
80
|
+
if (0 === stats.size) throw new Error(`Compiler artifact is empty: ${filename}`);
|
|
81
|
+
return canonicalPath;
|
|
82
|
+
}
|
|
83
|
+
function resolveTsrsManifest(from) {
|
|
84
|
+
const projectRequire = createRequire(from);
|
|
85
|
+
try {
|
|
86
|
+
return projectRequire.resolve('tsrs/package.json');
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (isAbsentPackage(projectRequire, from, 'tsrs', error)) return;
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function compilerVersion(compiler) {
|
|
93
|
+
return execFileSync(compiler, [
|
|
94
|
+
'--version'
|
|
95
|
+
], {
|
|
96
|
+
encoding: 'utf8',
|
|
97
|
+
env: {
|
|
98
|
+
...process.env,
|
|
99
|
+
TSRS_CHECKER_ASSIGNMENT: process.env.TSRS_CHECKER_ASSIGNMENT || 'go'
|
|
100
|
+
},
|
|
101
|
+
timeout: 10000,
|
|
102
|
+
maxBuffer: 1048576
|
|
103
|
+
}).trim();
|
|
104
|
+
}
|
|
105
|
+
function tsrsPlatform(manifestPath) {
|
|
106
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
107
|
+
const { getPlatformPackageName } = createRequire(manifestPath)(join(dirname(manifestPath), 'lib/platform.js'));
|
|
108
|
+
const nativeName = getPlatformPackageName();
|
|
109
|
+
const embedded = manifest.tsrs;
|
|
110
|
+
if ('string' != typeof nativeName || !nativeName || !manifest.optionalDependencies || 'object' != typeof manifest.optionalDependencies || Array.isArray(manifest.optionalDependencies) || 0 === Object.keys(manifest.optionalDependencies).length) throw new Error(`Invalid native tsrs capability metadata: ${manifestPath}`);
|
|
111
|
+
if ('string' != typeof manifest.name || !manifest.name || 'string' != typeof manifest.version || !/^\d+\.\d+\.\d+-ts[\w.-]+$/u.test(manifest.version) || 'string' != typeof embedded?.typescriptVersion || !/^7\.\d+\.\d+(?:[-+][\w.-]+)?$/u.test(embedded.typescriptVersion) || !manifest.version.endsWith(`-ts${embedded.typescriptVersion}`) || 'string' != typeof embedded.typescriptCommit || !/^[a-f\d]{40}$/u.test(embedded.typescriptCommit)) throw new Error(`Invalid native tsrs metadata: ${manifestPath}`);
|
|
112
|
+
const supported = Object.hasOwn(manifest.optionalDependencies, nativeName);
|
|
113
|
+
if (supported && manifest.optionalDependencies[nativeName] !== manifest.version) throw new Error(`Native tsrs target version does not match its selected package: ${manifestPath}`);
|
|
114
|
+
return {
|
|
115
|
+
manifest: manifest,
|
|
116
|
+
nativeName,
|
|
117
|
+
supported
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function resolveTsrsCompiler(manifestPath, effect, configuredCompiler, configuredVersion) {
|
|
121
|
+
const { manifest } = tsrsPlatform(manifestPath);
|
|
122
|
+
const embedded = manifest.tsrs;
|
|
123
|
+
const effectName = `@effect/tsgo-${process.platform}-${process.arch}`;
|
|
124
|
+
const effectManifest = createRequire(effect.cliPath).resolve(`${effectName}/package.json`);
|
|
125
|
+
const platform = JSON.parse(readFileSync(effectManifest, 'utf8'));
|
|
126
|
+
const metadataPath = join(dirname(effectManifest), 'lib/upstream.json');
|
|
127
|
+
const metadata = JSON.parse(readFileSync(metadataPath, 'utf8'));
|
|
128
|
+
const component = metadata.components?.tsrs?.[manifest.version];
|
|
129
|
+
if (platform.name !== effectName || platform.version !== effect.packageJson.version || 5 !== metadata.schemaVersion || !/^[a-f\d]{40}$/u.test(component?.gitHead ?? '') || component?.typescript?.version !== embedded.typescriptVersion || component.typescript.gitHead !== embedded.typescriptCommit) throw new Error(`Effect replacement metadata does not match tsrs: ${metadataPath}`);
|
|
130
|
+
const compiler = readCompilerArtifact(configuredCompiler ?? join(dirname(effectManifest), 'artifacts/tsrs', manifest.version, 'win32' === process.platform ? 'tsrs.exe' : 'tsrs'));
|
|
131
|
+
const version = configuredVersion ?? compilerVersion(compiler);
|
|
132
|
+
const stamp = /\+effect-tsgo\.([\d.]+)\s+\(tsrs ([^,]+), microsoft\/TypeScript@([a-f\d]+)\)$/u.exec(version);
|
|
133
|
+
if (!stamp || stamp[1] !== effect.packageJson.version || stamp[2] !== manifest.version || stamp[3].length < 12 || !embedded.typescriptCommit.startsWith(stamp[3])) throw new Error(`Effect-enabled tsrs matching ${manifest.version} and @effect/tsgo@${effect.packageJson.version} is required: ${compiler}. Install matching packages or select a matching Effect-enabled candidate with EFFECT_TSGO_BIN.`);
|
|
134
|
+
return compiler;
|
|
135
|
+
}
|
|
136
|
+
function validateConfiguredEffectCompiler(compiler, from) {
|
|
137
|
+
const version = compilerVersion(compiler);
|
|
138
|
+
if (!/^Version \d+\.\d+\.\d+[\w.-]*\+effect-tsgo\.\d+\.\d+\.\d+(?:\s|$)/u.test(version)) throw new Error(`EFFECT_TSGO_BIN must select an Effect-enabled native compiler: ${compiler}`);
|
|
139
|
+
if (version.includes('(tsrs ')) {
|
|
140
|
+
const manifest = resolveTsrsManifest(from);
|
|
141
|
+
if (!manifest) throw new Error(`Cannot validate tsrs without its installed package metadata for ${String(from)}`);
|
|
142
|
+
resolveTsrsCompiler(manifest, resolveEffectTsgoPackage(from), compiler, version);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function resolveInstalledEffectCompiler(from) {
|
|
146
|
+
let stage = 'Package CLI resolution';
|
|
147
|
+
try {
|
|
148
|
+
const effect = resolveEffectTsgoPackage(from);
|
|
149
|
+
stage = 'Native tsrs package resolution';
|
|
150
|
+
const tsrsManifest = resolveTsrsManifest(from);
|
|
151
|
+
const tsrsTarget = tsrsManifest ? tsrsPlatform(tsrsManifest) : void 0;
|
|
152
|
+
if (tsrsTarget && effect.packageJson.version !== EFFECT_TSRS_PROVIDER_VERSION) throw new Error(`Unreviewed Effect tsrs capability contract: ${effect.packageJson.version}`);
|
|
153
|
+
const configuredTsrsCompiler = process.env.TSRS_BINARY || void 0;
|
|
154
|
+
if (tsrsManifest && tsrsTarget?.supported && (configuredTsrsCompiler || EFFECT_TSRS_TARGETS.has(tsrsTarget.nativeName))) {
|
|
155
|
+
stage = 'Native tsrs validation';
|
|
156
|
+
return resolveTsrsCompiler(tsrsManifest, effect, configuredTsrsCompiler);
|
|
157
|
+
}
|
|
158
|
+
if (tsrsTarget && !reportedUnsupportedTsrsTargets.has(tsrsTarget.nativeName)) {
|
|
159
|
+
reportedUnsupportedTsrsTargets.add(tsrsTarget.nativeName);
|
|
160
|
+
console.warn(`[UltraModern] ${tsrsTarget.supported ? `@effect/tsgo@${effect.packageJson.version} does not provide a tsrs replacement for ${tsrsTarget.nativeName}` : `tsrs does not declare ${tsrsTarget.nativeName}`}; selecting the Effect-enabled TypeScript-Go compiler.`);
|
|
161
|
+
}
|
|
162
|
+
stage = 'Native TypeScript package resolution';
|
|
163
|
+
const typeScriptManifest = resolveNativeTypeScriptManifest(from);
|
|
164
|
+
const typeScript = JSON.parse(readFileSync(typeScriptManifest, 'utf8'));
|
|
165
|
+
const nativeName = `@typescript/typescript-${process.platform}-${process.arch}`;
|
|
166
|
+
const nativeManifest = createRequire(typeScriptManifest).resolve(`${nativeName}/package.json`);
|
|
167
|
+
const native = JSON.parse(readFileSync(nativeManifest, 'utf8'));
|
|
168
|
+
if (native.name !== nativeName || native.version !== typeScript.version || 'string' != typeof native.version || !/^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/u.test(native.version) || 'string' != typeof native.gitHead || !/^[a-f\d]{40}$/u.test(native.gitHead) || native.gitHead !== typeScript.gitHead) throw new Error(`Native TypeScript platform does not match its selected package: ${nativeManifest}`);
|
|
169
|
+
const binaryName = 'win32' === process.platform ? 'tsc.exe' : 'tsc';
|
|
170
|
+
readCompilerArtifact(join(dirname(nativeManifest), 'lib', binaryName));
|
|
171
|
+
stage = 'Compiler backend lookup';
|
|
172
|
+
const effectName = `@effect/tsgo-${process.platform}-${process.arch}`;
|
|
173
|
+
const effectManifest = createRequire(effect.cliPath).resolve(`${effectName}/package.json`);
|
|
174
|
+
const platform = JSON.parse(readFileSync(effectManifest, 'utf8'));
|
|
175
|
+
if (platform.name !== effectName || 'string' != typeof effect.packageJson.version || platform.version !== effect.packageJson.version) throw new Error(`Effect platform does not match its selected provider: ${effectManifest}`);
|
|
176
|
+
const metadataPath = join(dirname(effectManifest), 'lib', 'upstream.json');
|
|
177
|
+
const metadata = JSON.parse(readFileSync(metadataPath, 'utf8'));
|
|
178
|
+
const component = metadata.components?.typescript?.[native.version];
|
|
179
|
+
const latestTypeScript = metadata.tags?.typescript?.latest;
|
|
180
|
+
if (5 !== metadata.schemaVersion || 'string' != typeof latestTypeScript || component?.provider !== "typescript-go" || component.gitHead !== native.gitHead) throw new Error(`Effect replacement metadata does not match the selected native TypeScript backend: ${metadataPath}`);
|
|
181
|
+
const compilerDirectory = native.version === latestTypeScript ? join(dirname(effectManifest), 'lib') : join(dirname(effectManifest), 'artifacts', "typescript", native.version);
|
|
182
|
+
return readCompilerArtifact(join(compilerDirectory, binaryName));
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (error && 'object' == typeof error && !failureStages.has(error)) failureStages.set(error, stage);
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
export { effectCompilerDiscoveryFailureStage, resolveInstalledEffectCompiler, validateConfiguredEffectCompiler };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
export { getBuildConfigEnvironment, resolveEffectTsgoCompiler
|
|
2
|
+
export { getBuildConfigEnvironment, resolveEffectTsgoCompiler } from "./build-environment.mjs";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { resolveBrowserManifestAddress } from "@modern-js/surface-resolution";
|
|
3
|
+
import { resolveDeployTarget } from "../deploy-output/target.mjs";
|
|
4
|
+
import { getBuildConfigEnvironment } from "./build-environment.mjs";
|
|
5
|
+
function createRemoteManifestUrl(options) {
|
|
6
|
+
const env = {
|
|
7
|
+
...Object.fromEntries([
|
|
8
|
+
options.manifestEnv,
|
|
9
|
+
options.publicUrlEnv,
|
|
10
|
+
'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN'
|
|
11
|
+
].map((name)=>[
|
|
12
|
+
name,
|
|
13
|
+
getBuildConfigEnvironment(name)
|
|
14
|
+
])),
|
|
15
|
+
MODERNJS_DEPLOY: resolveDeployTarget().target
|
|
16
|
+
};
|
|
17
|
+
const address = resolveBrowserManifestAddress(env, options, getBuildConfigEnvironment('NODE_ENV') ?? 'production');
|
|
18
|
+
if (!address.ok) throw new Error(`Remote ${options.mfName}: ${address.reason}`);
|
|
19
|
+
return env[options.manifestEnv]?.trim() || `${options.mfName}@${address.manifestUrl}`;
|
|
20
|
+
}
|
|
21
|
+
export { createRemoteManifestUrl };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import node_path from "node:path";
|
|
3
3
|
import { fs } from "@modern-js/utils";
|
|
4
|
+
import { CLOUDFLARE_PUBLIC_ASSET_SCOPE, normalizeDeclaredPublicAssets, stageDeclaredPublicAssets } from "../deploy-output/public-assets.mjs";
|
|
4
5
|
import { RESERVED_ARTIFACT_DESTINATION_DIRECTORIES, RESERVED_ARTIFACT_DESTINATION_FILES, ROUTE_SPEC_OUTPUT } from "./constants.mjs";
|
|
5
6
|
import { normalizeRelativePath } from "./utils.mjs";
|
|
6
7
|
const normalizeCloudflareArtifact = (artifact, index)=>{
|
|
@@ -16,18 +17,7 @@ const normalizeCloudflareArtifact = (artifact, index)=>{
|
|
|
16
17
|
};
|
|
17
18
|
};
|
|
18
19
|
const getCloudflareArtifacts = (modernConfig)=>(modernConfig.deploy?.worker?.artifacts ?? []).map(normalizeCloudflareArtifact);
|
|
19
|
-
const
|
|
20
|
-
const from = normalizeRelativePath(asset.from, `deploy.worker.publicAssets[${index}].from`, 'app root');
|
|
21
|
-
const to = normalizeRelativePath(asset.to, `deploy.worker.publicAssets[${index}].to`, 'Cloudflare public output', {
|
|
22
|
-
allowRoot: true
|
|
23
|
-
});
|
|
24
|
-
return {
|
|
25
|
-
from,
|
|
26
|
-
to,
|
|
27
|
-
index
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
const getCloudflarePublicAssets = (modernConfig)=>(modernConfig.deploy?.worker?.publicAssets ?? []).map(normalizeCloudflarePublicAsset);
|
|
20
|
+
const getCloudflarePublicAssets = (modernConfig)=>normalizeDeclaredPublicAssets(modernConfig.deploy?.worker?.publicAssets, CLOUDFLARE_PUBLIC_ASSET_SCOPE);
|
|
31
21
|
const copyCloudflareArtifacts = async (appDirectory, outputDirectory, artifacts)=>{
|
|
32
22
|
for (const artifact of artifacts){
|
|
33
23
|
const sourcePath = node_path.join(appDirectory, artifact.from);
|
|
@@ -35,13 +25,12 @@ const copyCloudflareArtifacts = async (appDirectory, outputDirectory, artifacts)
|
|
|
35
25
|
await fs.copy(sourcePath, node_path.join(outputDirectory, artifact.to));
|
|
36
26
|
}
|
|
37
27
|
};
|
|
38
|
-
const copyCloudflarePublicAssets = async (appDirectory,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
};
|
|
28
|
+
const copyCloudflarePublicAssets = async (appDirectory, outputDirectory, publicAssets)=>stageDeclaredPublicAssets({
|
|
29
|
+
appDirectory,
|
|
30
|
+
outputDirectory,
|
|
31
|
+
assets: publicAssets,
|
|
32
|
+
scope: CLOUDFLARE_PUBLIC_ASSET_SCOPE
|
|
33
|
+
});
|
|
45
34
|
const copyCloudflareD1Migrations = async (appDirectory, outputDirectory, modernConfig)=>{
|
|
46
35
|
for (const [index, database] of (modernConfig.deploy?.worker?.d1Databases ?? []).entries()){
|
|
47
36
|
if (!database.migrationsDir) continue;
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import node_path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { isDeepStrictEqual } from "node:util";
|
|
4
|
+
import { ULTRAMODERN_BUILD_ARTIFACT_FILE, ULTRAMODERN_BUILD_ARTIFACT_PATH, assertRendererProfileCompatibility, assertUltramodernBuildArtifact, formatBackendFederationValidationErrors, immutableRendererRouterBindings, nonEmptyString, toDeliveryUnitIdentity, validateRendererIdentity, validateRendererProfile, validateRendererRouterBindings } from "@modern-js/backend-federation-contracts";
|
|
4
5
|
import { fs } from "@modern-js/utils";
|
|
5
6
|
import { resolveUltramodernReleaseIdentity } from "../release-identity.mjs";
|
|
6
7
|
import { isRecord } from "./utils.mjs";
|
|
7
|
-
const
|
|
8
|
+
const TOPOLOGY_PATH = 'topology/reference-topology.json';
|
|
9
|
+
const RENDERER_PROJECTION_FIELDS = [
|
|
10
|
+
'renderer',
|
|
11
|
+
'rendererIdentity',
|
|
12
|
+
'rendererIdentities',
|
|
13
|
+
'rendererProfile',
|
|
14
|
+
'routerBindings'
|
|
15
|
+
];
|
|
8
16
|
const findWorkspaceRoot = async (appDirectory)=>{
|
|
9
17
|
let current = node_path.resolve(appDirectory);
|
|
10
18
|
for(;;){
|
|
11
|
-
if (await fs.pathExists(node_path.join(current,
|
|
19
|
+
if (await fs.pathExists(node_path.join(current, TOPOLOGY_PATH))) return current;
|
|
12
20
|
const parent = node_path.dirname(current);
|
|
13
21
|
if (parent === current) return;
|
|
14
22
|
current = parent;
|
|
@@ -25,51 +33,83 @@ const stampReleaseIdentity = (identity, workspaceRoot)=>{
|
|
|
25
33
|
})
|
|
26
34
|
};
|
|
27
35
|
};
|
|
28
|
-
const
|
|
36
|
+
const resolveTopologyApp = async (appDirectory)=>{
|
|
29
37
|
const workspaceRoot = await findWorkspaceRoot(appDirectory);
|
|
30
38
|
if (!workspaceRoot) return;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
if (!isRecord(compactConfig)) return {
|
|
40
|
-
workspaceRoot
|
|
41
|
-
};
|
|
42
|
-
const topology = isRecord(compactConfig.topology) ? compactConfig.topology : void 0;
|
|
43
|
-
const apps = Array.isArray(topology?.apps) ? topology.apps : void 0;
|
|
44
|
-
if (!apps) return {
|
|
45
|
-
app: compactConfig,
|
|
46
|
-
workspaceRoot
|
|
47
|
-
};
|
|
39
|
+
const topology = await fs.readJSON(node_path.join(workspaceRoot, TOPOLOGY_PATH));
|
|
40
|
+
if (!isRecord(topology) || !isRecord(topology.shell) || !Array.isArray(topology.verticals) || void 0 !== topology.shells && !Array.isArray(topology.shells)) throw new Error('[cloudflare-delivery-unit] Invalid declared reference topology.');
|
|
41
|
+
const apps = [
|
|
42
|
+
topology.shell,
|
|
43
|
+
...topology.verticals,
|
|
44
|
+
...Array.isArray(topology.shells) ? topology.shells : []
|
|
45
|
+
];
|
|
48
46
|
const resolvedAppDirectory = node_path.resolve(appDirectory);
|
|
49
47
|
const app = apps.find((candidate)=>{
|
|
50
48
|
if (!isRecord(candidate)) return false;
|
|
51
49
|
const appPath = nonEmptyString(candidate.path);
|
|
52
50
|
return void 0 !== appPath && node_path.resolve(workspaceRoot, appPath.replace(/^\.\/+/u, '')) === resolvedAppDirectory;
|
|
53
51
|
});
|
|
52
|
+
if (!isRecord(app)) throw new Error(`[cloudflare-delivery-unit] ${appDirectory} is missing from the declared reference topology.`);
|
|
54
53
|
return {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
} : {},
|
|
54
|
+
app,
|
|
55
|
+
role: app === topology.shell || Array.isArray(topology.shells) && topology.shells.includes(app) ? 'shell' : 'microvertical',
|
|
58
56
|
workspaceRoot
|
|
59
57
|
};
|
|
60
58
|
};
|
|
61
|
-
const
|
|
62
|
-
const
|
|
59
|
+
const resolveTopologyReleaseRole = async (appDirectory)=>{
|
|
60
|
+
const resolved = await resolveTopologyApp(appDirectory);
|
|
61
|
+
if (!resolved?.app) return;
|
|
62
|
+
const kind = 'shell' === resolved.role ? 'shell' : 'vertical';
|
|
63
|
+
if (resolved.app.kind !== kind) throw new Error('[cloudflare-delivery-unit] Declared app kind disagrees with its topology membership.');
|
|
64
|
+
return resolved.role;
|
|
65
|
+
};
|
|
66
|
+
const createTopologyDeliveryUnitStamp = (identity, app)=>{
|
|
67
|
+
const surfaceProfile = nonEmptyString(app.surfaceProfile);
|
|
68
|
+
if (void 0 !== surfaceProfile && ![
|
|
69
|
+
'api-only',
|
|
70
|
+
'ui-only',
|
|
71
|
+
'full-stack'
|
|
72
|
+
].includes(surfaceProfile)) throw new Error('[cloudflare-delivery-unit] Declared app has an invalid surface profile.');
|
|
63
73
|
const emitsUi = 'api-only' !== surfaceProfile;
|
|
64
74
|
const emitsApi = 'ui-only' !== surfaceProfile;
|
|
75
|
+
const appId = nonEmptyString(app.id);
|
|
76
|
+
if (!appId) throw new Error('[cloudflare-delivery-unit] Declared app requires an id.');
|
|
77
|
+
let ui;
|
|
78
|
+
if (emitsUi) if (RENDERER_PROJECTION_FIELDS.every((field)=>!Object.hasOwn(app, field))) ui = {
|
|
79
|
+
...identity,
|
|
80
|
+
surface: 'ui'
|
|
81
|
+
};
|
|
82
|
+
else {
|
|
83
|
+
const routerBindings = isRecord(app.routerBindings) ? app.routerBindings : {};
|
|
84
|
+
const errors = [
|
|
85
|
+
...validateRendererIdentity(app.rendererIdentity).errors,
|
|
86
|
+
...validateRendererProfile(app.rendererProfile).errors,
|
|
87
|
+
...validateRendererRouterBindings(app.routerBindings, Object.keys(routerBindings), 'routerBindings').errors
|
|
88
|
+
];
|
|
89
|
+
if (errors.length) throw new Error(`[cloudflare-delivery-unit] ${formatBackendFederationValidationErrors(errors)}`);
|
|
90
|
+
const rendererIdentity = app.rendererIdentity;
|
|
91
|
+
const rendererProfile = app.rendererProfile;
|
|
92
|
+
if (!Object.hasOwn(routerBindings, rendererIdentity.entryName)) throw new Error('[cloudflare-delivery-unit] Topology routerBindings must include the primary renderer identity entry.');
|
|
93
|
+
const declaredIdentity = toDeliveryUnitIdentity(app.deliveryUnit);
|
|
94
|
+
if (rendererIdentity.renderer !== app.renderer || rendererIdentity.renderer !== rendererProfile.renderer || rendererIdentity.appId !== app.id || rendererIdentity.buildId !== declaredIdentity?.buildMarker) throw new Error('[cloudflare-delivery-unit] Topology UI renderer metadata must match its app and delivery-unit identity.');
|
|
95
|
+
ui = {
|
|
96
|
+
...identity,
|
|
97
|
+
surface: 'ui',
|
|
98
|
+
rendererIdentity: {
|
|
99
|
+
...rendererIdentity,
|
|
100
|
+
buildId: identity.buildMarker
|
|
101
|
+
},
|
|
102
|
+
rendererProfile,
|
|
103
|
+
routerBindings: immutableRendererRouterBindings(routerBindings)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
else if (void 0 !== app.rendererIdentity || void 0 !== app.rendererProfile || void 0 !== app.routerBindings) throw new Error('[cloudflare-delivery-unit] API-only topology must not declare a UI renderer identity or profile.');
|
|
65
107
|
return {
|
|
66
108
|
...identity,
|
|
109
|
+
appId,
|
|
67
110
|
surfaces: {
|
|
68
|
-
...
|
|
69
|
-
ui
|
|
70
|
-
...identity,
|
|
71
|
-
surface: 'ui'
|
|
72
|
-
}
|
|
111
|
+
...ui ? {
|
|
112
|
+
ui
|
|
73
113
|
} : {},
|
|
74
114
|
...emitsApi ? {
|
|
75
115
|
api: {
|
|
@@ -81,22 +121,61 @@ const createDeliveryUnitStamp = (identity, app)=>{
|
|
|
81
121
|
};
|
|
82
122
|
};
|
|
83
123
|
const resolveTopologyDeliveryUnit = async (appDirectory)=>{
|
|
84
|
-
const resolved = await
|
|
124
|
+
const resolved = await resolveTopologyApp(appDirectory);
|
|
85
125
|
if (!resolved?.app) return;
|
|
86
126
|
const identity = toDeliveryUnitIdentity(resolved.app.deliveryUnit);
|
|
87
|
-
|
|
127
|
+
if (!identity) throw new Error('[cloudflare-delivery-unit] Declared app is missing a valid delivery-unit identity.');
|
|
128
|
+
return createTopologyDeliveryUnitStamp(stampReleaseIdentity(identity, resolved.workspaceRoot), resolved.app);
|
|
88
129
|
};
|
|
89
|
-
const resolveWorkerDeliveryUnitStamp = async (appDirectory)=>{
|
|
90
|
-
const buildArtifactPath = node_path.join(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
130
|
+
const resolveWorkerDeliveryUnitStamp = async (appDirectory, distDirectory)=>{
|
|
131
|
+
const buildArtifactPath = node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE);
|
|
132
|
+
if (!await fs.pathExists(buildArtifactPath)) {
|
|
133
|
+
if (await fs.pathExists(node_path.join(appDirectory, ULTRAMODERN_BUILD_ARTIFACT_PATH))) throw new Error('[cloudflare-delivery-unit] Finalized build artifact is required before worker output stamping.');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const artifact = await fs.readJSON(buildArtifactPath);
|
|
137
|
+
assertUltramodernBuildArtifact(artifact);
|
|
138
|
+
const identity = {
|
|
139
|
+
unitId: artifact.deliveryUnit.unitId,
|
|
140
|
+
buildMarker: artifact.deliveryUnit.buildMarker,
|
|
141
|
+
sourceRevision: artifact.deliveryUnit.sourceRevision
|
|
142
|
+
};
|
|
143
|
+
const resolved = await resolveTopologyApp(appDirectory);
|
|
144
|
+
const stampedArtifact = artifact;
|
|
145
|
+
let emitsApi = true;
|
|
146
|
+
if (resolved?.app) {
|
|
147
|
+
if (stampedArtifact.deliveryUnit.appId !== resolved.app.id) throw new Error('[cloudflare-delivery-unit] Build artifact appId must match the declared topology app.');
|
|
148
|
+
const declaredIdentity = toDeliveryUnitIdentity(resolved.app.deliveryUnit);
|
|
149
|
+
if (!declaredIdentity) throw new Error('[cloudflare-delivery-unit] Declared app is missing a valid delivery-unit identity.');
|
|
150
|
+
const expected = createTopologyDeliveryUnitStamp(identity, resolved.app);
|
|
151
|
+
for (const field of [
|
|
152
|
+
'unitId'
|
|
153
|
+
])if (declaredIdentity[field] !== identity[field]) throw new Error(`[cloudflare-delivery-unit] Build artifact ${field} must match the declared topology delivery-unit identity.`);
|
|
154
|
+
const ui = stampedArtifact.surfaces.ui;
|
|
155
|
+
if (Boolean(expected.surfaces.ui) !== Boolean(ui)) throw new Error('[cloudflare-delivery-unit] Build artifact UI surface must match the declared topology surface profile.');
|
|
156
|
+
if (expected.surfaces.ui?.rendererIdentity && ui) {
|
|
157
|
+
if (!isDeepStrictEqual(expected.surfaces.ui.routerBindings, ui.routerBindings)) throw new Error('[cloudflare-delivery-unit] Finalized build routerBindings must match the declared topology router bindings.');
|
|
158
|
+
for (const field of [
|
|
159
|
+
'renderer',
|
|
160
|
+
'appId',
|
|
161
|
+
'entryName',
|
|
162
|
+
'protocolVersion',
|
|
163
|
+
'buildId'
|
|
164
|
+
])if (expected.surfaces.ui.rendererIdentity[field] !== ui.rendererIdentity[field]) throw new Error(`[cloudflare-delivery-unit] Build artifact UI rendererIdentity.${field} must match the declared topology renderer identity.`);
|
|
165
|
+
assertRendererProfileCompatibility(expected.surfaces.ui.rendererProfile, ui.rendererProfile);
|
|
166
|
+
}
|
|
167
|
+
emitsApi = void 0 !== expected.surfaces.api;
|
|
96
168
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
169
|
+
return {
|
|
170
|
+
...stampedArtifact.deliveryUnit,
|
|
171
|
+
surfaces: {
|
|
172
|
+
...emitsApi ? {
|
|
173
|
+
api: stampedArtifact.surfaces.api
|
|
174
|
+
} : {},
|
|
175
|
+
...stampedArtifact.surfaces.ui ? {
|
|
176
|
+
ui: stampedArtifact.surfaces.ui
|
|
177
|
+
} : {}
|
|
178
|
+
}
|
|
179
|
+
};
|
|
101
180
|
};
|
|
102
|
-
export { resolveTopologyDeliveryUnit, resolveWorkerDeliveryUnitStamp };
|
|
181
|
+
export { resolveTopologyDeliveryUnit, resolveTopologyReleaseRole, resolveWorkerDeliveryUnitStamp };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import node_path from "node:path";
|
|
3
|
+
import { BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_REMOTE_ENTRY_FILE, ULTRAMODERN_BUILD_ARTIFACT_FILE } from "@modern-js/backend-federation-contracts";
|
|
3
4
|
import { fs } from "@modern-js/utils";
|
|
4
5
|
import { createCloudflareOutputPlan } from "../cloudflare-output-plan.mjs";
|
|
5
6
|
import { assertCloudflareOutput } from "../cloudflare-output-verifier/index.mjs";
|
|
@@ -7,7 +8,7 @@ import { readTemplate } from "../read-template.mjs";
|
|
|
7
8
|
import { MICROVERTICAL_RELEASE_ENVELOPE_PATH, emitCloudflareStagedReleaseEnvelope, emitFrameworkMicroVerticalReleaseEnvelope, stageCloudflareReleaseEnvelope, verifyBuildOutputReleaseEnvelope, verifyCloudflareReleaseEnvelopeStaging } from "../release-envelope/framework-output.mjs";
|
|
8
9
|
import { copyCloudflareArtifacts, copyCloudflareD1Migrations, copyCloudflarePublicAssets, getCloudflareArtifacts, getCloudflarePublicAssets } from "./artifacts.mjs";
|
|
9
10
|
import { ROUTE_SPEC_FILE, ROUTE_SPEC_OUTPUT, WORKER_BUNDLE_DIRECTORY } from "./constants.mjs";
|
|
10
|
-
import {
|
|
11
|
+
import { resolveWorkerDeliveryUnitStamp } from "./delivery-unit.mjs";
|
|
11
12
|
import { createWorkerManifest, createWorkerModuleLoaders, getPublicAssetExcludes, shouldCopyToPublicAssets, shouldCopyToWorkerBundle } from "./worker-manifest.mjs";
|
|
12
13
|
import { createWranglerConfig } from "./wrangler-config.mjs";
|
|
13
14
|
const CLOUDFLARE_ENTRY_TEMPLATE_FRAGMENTS = [
|
|
@@ -32,20 +33,28 @@ const createCloudflarePreset = ({ api, appContext, modernConfig })=>{
|
|
|
32
33
|
const publicAssetExcludes = getPublicAssetExcludes(appDirectory, modernConfig);
|
|
33
34
|
const releaseEnvelopeEnabled = api.isPluginExists('@modern-js/ultramodern-release-envelope');
|
|
34
35
|
let hasReleaseEnvelope = false;
|
|
36
|
+
let declaredPublicAssets = [];
|
|
35
37
|
return {
|
|
36
38
|
async prepare () {
|
|
37
39
|
await fs.remove(outputDirectory);
|
|
38
40
|
},
|
|
39
41
|
async writeOutput () {
|
|
40
|
-
if (releaseEnvelopeEnabled
|
|
42
|
+
if (releaseEnvelopeEnabled) await emitFrameworkMicroVerticalReleaseEnvelope({
|
|
41
43
|
apiOnly,
|
|
44
|
+
appDirectory,
|
|
42
45
|
distDirectory,
|
|
46
|
+
role: modernConfig.deploy?.releaseEnvelopeRole,
|
|
43
47
|
target: 'cloudflare'
|
|
44
48
|
});
|
|
45
49
|
if (releaseEnvelopeEnabled) await verifyBuildOutputReleaseEnvelope(distDirectory, 'cloudflare');
|
|
46
50
|
await fs.copy(distDirectory, publicDirectory, {
|
|
47
51
|
filter: (src)=>{
|
|
48
52
|
const relativePath = node_path.relative(distDirectory, src).replace(/\\/gu, '/');
|
|
53
|
+
if (apiOnly && '' !== relativePath && ![
|
|
54
|
+
BACKEND_FEDERATION_MANIFEST_FILE,
|
|
55
|
+
BACKEND_FEDERATION_REMOTE_ENTRY_FILE,
|
|
56
|
+
ULTRAMODERN_BUILD_ARTIFACT_FILE
|
|
57
|
+
].includes(relativePath)) return false;
|
|
49
58
|
return 'release' !== relativePath && !relativePath.startsWith('release/') && 'public' !== relativePath && !relativePath.startsWith('public/') && shouldCopyToPublicAssets(src, distDirectory, publicAssetExcludes);
|
|
50
59
|
}
|
|
51
60
|
});
|
|
@@ -69,12 +78,12 @@ const createCloudflarePreset = ({ api, appContext, modernConfig })=>{
|
|
|
69
78
|
await fs.writeJSON(outputPlan.paths.workerPackage, outputPlan.packages.worker);
|
|
70
79
|
}
|
|
71
80
|
await copyCloudflareArtifacts(appDirectory, outputDirectory, cloudflareArtifacts);
|
|
72
|
-
await copyCloudflarePublicAssets(appDirectory,
|
|
81
|
+
declaredPublicAssets = await copyCloudflarePublicAssets(appDirectory, outputDirectory, getCloudflarePublicAssets(modernConfig));
|
|
73
82
|
await copyCloudflareD1Migrations(appDirectory, outputDirectory, modernConfig);
|
|
74
83
|
await fs.writeJSON(wranglerConfigPath, createWranglerConfig(appDirectory, modernConfig), {
|
|
75
84
|
spaces: 2
|
|
76
85
|
});
|
|
77
|
-
const deliveryUnitStamp = await resolveWorkerDeliveryUnitStamp(appDirectory);
|
|
86
|
+
const deliveryUnitStamp = await resolveWorkerDeliveryUnitStamp(appDirectory, distDirectory);
|
|
78
87
|
await fs.writeJSON(workerManifestPath, await createWorkerManifest(outputDirectory, modernConfig, appContext, deliveryUnitStamp), {
|
|
79
88
|
spaces: 2
|
|
80
89
|
});
|
|
@@ -88,7 +97,7 @@ const createCloudflarePreset = ({ api, appContext, modernConfig })=>{
|
|
|
88
97
|
const template = await readCloudflareEntryTemplate();
|
|
89
98
|
const manifest = await fs.readJSON(workerManifestPath);
|
|
90
99
|
await fs.writeFile(workerEntryPath, template.replace('p_workerManifest', JSON.stringify(manifest, null, 2)).replace('p_workerModuleLoaders', createWorkerModuleLoaders(manifest)));
|
|
91
|
-
const topologyDeliveryUnit = await
|
|
100
|
+
const topologyDeliveryUnit = await resolveWorkerDeliveryUnitStamp(appDirectory, distDirectory);
|
|
92
101
|
await assertCloudflareOutput({
|
|
93
102
|
outputDirectory,
|
|
94
103
|
importWorker: false,
|
|
@@ -98,6 +107,7 @@ const createCloudflarePreset = ({ api, appContext, modernConfig })=>{
|
|
|
98
107
|
});
|
|
99
108
|
if (hasReleaseEnvelope) {
|
|
100
109
|
await emitCloudflareStagedReleaseEnvelope({
|
|
110
|
+
declaredPublicAssets,
|
|
101
111
|
distDirectory,
|
|
102
112
|
outputDirectory
|
|
103
113
|
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import promises from "node:fs/promises";
|
|
3
|
+
import node_path from "node:path";
|
|
4
|
+
import { WORKER_BUNDLE_DIRECTORY } from "./constants.mjs";
|
|
5
|
+
import { isRecord } from "./utils.mjs";
|
|
6
|
+
const NATIVE_RENDERER_WORKER_RESOURCES_FILE = `${WORKER_BUNDLE_DIRECTORY}/native-renderer.json`;
|
|
7
|
+
const invalid = (detail)=>new Error(`Invalid native renderer worker resources (${NATIVE_RENDERER_WORKER_RESOURCES_FILE}): ${detail}. Rebuild the application with deploy.worker.ssr before deploying.`);
|
|
8
|
+
function validateEntry(entryName, value) {
|
|
9
|
+
if (!isRecord(value)) throw invalid(`entry ${entryName} is not an object`);
|
|
10
|
+
const { assets, hydrationBuildId, serverConfig, csrFallbackHeader, nonce } = value;
|
|
11
|
+
if (!Array.isArray(assets) || !assets.length) throw invalid(`entry ${entryName} has no document assets`);
|
|
12
|
+
if (void 0 !== hydrationBuildId && ('string' != typeof hydrationBuildId || !hydrationBuildId.trim())) throw invalid(`entry ${entryName} has an empty hydration build ID`);
|
|
13
|
+
if (!isRecord(serverConfig) || ![
|
|
14
|
+
true,
|
|
15
|
+
false,
|
|
16
|
+
'stream'
|
|
17
|
+
].includes(serverConfig.ssr) || void 0 !== serverConfig.ssrByRouteIds && (!Array.isArray(serverConfig.ssrByRouteIds) || serverConfig.ssrByRouteIds.some((id)=>'string' != typeof id))) throw invalid(`entry ${entryName} has an invalid server config`);
|
|
18
|
+
for (const [field, text] of [
|
|
19
|
+
[
|
|
20
|
+
'csrFallbackHeader',
|
|
21
|
+
csrFallbackHeader
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
'nonce',
|
|
25
|
+
nonce
|
|
26
|
+
]
|
|
27
|
+
])if (void 0 !== text && ('string' != typeof text || !text)) throw invalid(`entry ${entryName} has an invalid ${field}`);
|
|
28
|
+
}
|
|
29
|
+
async function readNativeRendererWorkerResources(distDirectory, build) {
|
|
30
|
+
let bytes;
|
|
31
|
+
try {
|
|
32
|
+
bytes = await promises.readFile(node_path.join(distDirectory, NATIVE_RENDERER_WORKER_RESOURCES_FILE), 'utf8');
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (error instanceof Error && 'code' in error && 'ENOENT' === error.code) return;
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
const value = JSON.parse(bytes);
|
|
38
|
+
if (!isRecord(value) || 'ultramodern-native-worker-resources' !== value.schema || 1 !== value.version || 'string' != typeof value.renderer || !value.renderer || !isRecord(value.entries)) throw invalid('unknown schema');
|
|
39
|
+
if (value.renderer !== build?.renderer.name || !build.renderer.nativeDocuments) throw invalid(`renderer ${value.renderer} is not the built native-document renderer`);
|
|
40
|
+
const entries = value.entries;
|
|
41
|
+
const nativeEntries = Object.entries(build.identities).filter(([, identity])=>identity.renderer === value.renderer);
|
|
42
|
+
for (const [entryName] of nativeEntries)if (!Object.hasOwn(entries, entryName)) throw invalid(`entry ${entryName} is missing`);
|
|
43
|
+
for (const [entryName, entry] of Object.entries(entries)){
|
|
44
|
+
if (!nativeEntries.some(([name])=>name === entryName)) throw invalid(`entry ${entryName} has no built ${value.renderer} renderer identity`);
|
|
45
|
+
validateEntry(entryName, entry);
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
export { NATIVE_RENDERER_WORKER_RESOURCES_FILE, readNativeRendererWorkerResources };
|