@bleedingdev/modern-js-ultramodern-create 3.9.0-ultramodern.11 → 3.9.0-ultramodern.13
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 +32 -36
- package/dist/cjs/ultramodern-release-cohort.cjs +6 -21
- package/dist/cjs/ultramodern-tooling/commands/cloudflare-output-verify.cjs +29 -53
- package/dist/cjs/ultramodern-tooling/commands/context.cjs +1 -27
- package/dist/cjs/ultramodern-tooling/commands/routes-generate-app.cjs +60 -0
- package/dist/cjs/ultramodern-tooling/commands/routes-generate.cjs +12 -48
- package/dist/cjs/ultramodern-tooling/commands/validate.cjs +5 -58
- package/dist/cjs/ultramodern-tooling/commands.cjs +13 -3
- package/dist/cjs/ultramodern-tooling/config/index.cjs +11 -4
- package/dist/cjs/ultramodern-tooling/config/load.cjs +4 -17
- package/dist/cjs/ultramodern-tooling/config/metadata.cjs +41 -2
- package/dist/cjs/ultramodern-tooling/config/normalize.cjs +121 -198
- package/dist/cjs/ultramodern-workspace/add-shell.cjs +103 -147
- package/dist/cjs/ultramodern-workspace/add-vertical/execute.cjs +61 -94
- package/dist/cjs/ultramodern-workspace/add-vertical/plan.cjs +14 -160
- package/dist/cjs/ultramodern-workspace/add-vertical/preflight.cjs +8 -13
- package/dist/cjs/ultramodern-workspace/add-vertical/preview.cjs +78 -0
- package/dist/cjs/ultramodern-workspace/add-vertical/topology.cjs +0 -32
- package/dist/cjs/ultramodern-workspace/add-vertical/transaction.cjs +2 -2
- package/dist/cjs/ultramodern-workspace/add-vertical/workspace-state.cjs +19 -42
- package/dist/cjs/ultramodern-workspace/add-vertical.cjs +0 -9
- package/dist/cjs/ultramodern-workspace/api/contracts.cjs +2 -2
- package/dist/cjs/ultramodern-workspace/api/rpc.cjs +16 -14
- package/dist/cjs/ultramodern-workspace/api/service.cjs +4 -8
- package/dist/cjs/ultramodern-workspace/api/shared.cjs +1 -1
- package/dist/cjs/ultramodern-workspace/backend-federation.cjs +3 -3
- package/dist/cjs/ultramodern-workspace/contracts.cjs +14 -183
- package/dist/cjs/ultramodern-workspace/delivery-unit-stamp.cjs +10 -5
- package/dist/cjs/ultramodern-workspace/delivery-unit-sync.cjs +27 -17
- package/dist/cjs/ultramodern-workspace/delivery-unit.cjs +11 -6
- package/dist/cjs/ultramodern-workspace/demo-components.cjs +62 -115
- package/dist/cjs/ultramodern-workspace/descriptors.cjs +0 -4
- package/dist/cjs/ultramodern-workspace/generation-result.cjs +1 -1
- package/dist/cjs/ultramodern-workspace/index.cjs +2 -5
- package/dist/cjs/ultramodern-workspace/mf-validation/constants.cjs +0 -6
- package/dist/cjs/ultramodern-workspace/mf-validation/discovery.cjs +6 -6
- package/dist/cjs/ultramodern-workspace/mf-validation/path-utils.cjs +37 -38
- package/dist/cjs/ultramodern-workspace/module-federation/config.cjs +9 -16
- package/dist/cjs/ultramodern-workspace/module-federation/remote-refs.cjs +1 -43
- package/dist/cjs/ultramodern-workspace/package-json.cjs +47 -54
- package/dist/cjs/ultramodern-workspace/patch-inventory.cjs +122 -0
- package/dist/cjs/ultramodern-workspace/policy.cjs +6 -380
- package/dist/cjs/ultramodern-workspace/public-surface.cjs +2 -2
- package/dist/cjs/ultramodern-workspace/routes.cjs +11 -7
- package/dist/cjs/ultramodern-workspace/shared-patches.cjs +12 -2
- package/dist/cjs/ultramodern-workspace/shells.cjs +0 -70
- package/dist/cjs/ultramodern-workspace/tooling-command-catalog.cjs +6 -43
- package/dist/cjs/ultramodern-workspace/tsconfigs.cjs +3 -1
- package/dist/cjs/ultramodern-workspace/validation/architecture.cjs +397 -0
- package/dist/cjs/ultramodern-workspace/validation/assertions.cjs +140 -0
- package/dist/cjs/ultramodern-workspace/validation/types.cjs +18 -0
- package/dist/cjs/ultramodern-workspace/validation/workspace.cjs +343 -0
- package/dist/cjs/ultramodern-workspace/versions.cjs +25 -41
- package/dist/cjs/ultramodern-workspace/workspace-artifact-ownership.cjs +71 -31
- package/dist/cjs/ultramodern-workspace/workspace-script-plan.cjs +7 -7
- package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +17 -143
- package/dist/cjs/ultramodern-workspace/workspace-validation-contract.cjs +62 -371
- package/dist/cjs/ultramodern-workspace/write-app.cjs +5 -4
- package/dist/cjs/ultramodern-workspace/write-workspace.cjs +10 -50
- package/dist/esm/ultramodern-release-cohort.js +5 -14
- package/dist/esm/ultramodern-tooling/commands/cloudflare-output-verify.js +29 -53
- package/dist/esm/ultramodern-tooling/commands/context.js +3 -24
- package/dist/esm/ultramodern-tooling/commands/routes-generate-app.js +28 -0
- package/dist/esm/ultramodern-tooling/commands/routes-generate.js +13 -49
- package/dist/esm/ultramodern-tooling/commands/validate.js +6 -49
- package/dist/esm/ultramodern-tooling/commands.js +12 -3
- package/dist/esm/ultramodern-tooling/config/index.js +1 -0
- package/dist/esm/ultramodern-tooling/config/load.js +5 -17
- package/dist/esm/ultramodern-tooling/config/metadata.js +36 -1
- package/dist/esm/ultramodern-tooling/config/normalize.js +125 -191
- package/dist/esm/ultramodern-workspace/add-shell.js +110 -153
- package/dist/esm/ultramodern-workspace/add-vertical/execute.js +61 -98
- package/dist/esm/ultramodern-workspace/add-vertical/plan.js +16 -150
- package/dist/esm/ultramodern-workspace/add-vertical/preflight.js +6 -11
- package/dist/esm/ultramodern-workspace/add-vertical/preview.js +40 -0
- package/dist/esm/ultramodern-workspace/add-vertical/topology.js +1 -30
- package/dist/esm/ultramodern-workspace/add-vertical/transaction.js +2 -2
- package/dist/esm/ultramodern-workspace/add-vertical/workspace-state.js +15 -32
- package/dist/esm/ultramodern-workspace/add-vertical.js +1 -1
- package/dist/esm/ultramodern-workspace/api/contracts.js +2 -2
- package/dist/esm/ultramodern-workspace/api/rpc.js +16 -14
- package/dist/esm/ultramodern-workspace/api/service.js +4 -8
- package/dist/esm/ultramodern-workspace/api/shared.js +1 -1
- package/dist/esm/ultramodern-workspace/backend-federation.js +3 -3
- package/dist/esm/ultramodern-workspace/contracts.js +14 -180
- package/dist/esm/ultramodern-workspace/delivery-unit-stamp.js +10 -5
- package/dist/esm/ultramodern-workspace/delivery-unit-sync.js +28 -18
- package/dist/esm/ultramodern-workspace/delivery-unit.js +11 -6
- package/dist/esm/ultramodern-workspace/demo-components.js +62 -115
- package/dist/esm/ultramodern-workspace/descriptors.js +1 -2
- package/dist/esm/ultramodern-workspace/generation-result.js +2 -2
- package/dist/esm/ultramodern-workspace/index.js +1 -1
- package/dist/esm/ultramodern-workspace/mf-validation/constants.js +1 -4
- package/dist/esm/ultramodern-workspace/mf-validation/discovery.js +7 -7
- package/dist/esm/ultramodern-workspace/mf-validation/path-utils.js +36 -34
- package/dist/esm/ultramodern-workspace/module-federation/config.js +10 -17
- package/dist/esm/ultramodern-workspace/module-federation/remote-refs.js +1 -43
- package/dist/esm/ultramodern-workspace/package-json.js +48 -55
- package/dist/esm/ultramodern-workspace/patch-inventory.js +84 -0
- package/dist/esm/ultramodern-workspace/policy.js +6 -371
- package/dist/esm/ultramodern-workspace/public-surface.js +2 -2
- package/dist/esm/ultramodern-workspace/routes.js +11 -7
- package/dist/esm/ultramodern-workspace/shared-patches.js +2 -2
- package/dist/esm/ultramodern-workspace/shells.js +3 -67
- package/dist/esm/ultramodern-workspace/tooling-command-catalog.js +7 -41
- package/dist/esm/ultramodern-workspace/tsconfigs.js +4 -2
- package/dist/esm/ultramodern-workspace/validation/architecture.js +348 -0
- package/dist/esm/ultramodern-workspace/validation/assertions.js +65 -0
- package/dist/esm/ultramodern-workspace/validation/types.js +0 -0
- package/dist/esm/ultramodern-workspace/validation/workspace.js +285 -0
- package/dist/esm/ultramodern-workspace/versions.js +24 -28
- package/dist/esm/ultramodern-workspace/workspace-artifact-ownership.js +64 -30
- package/dist/esm/ultramodern-workspace/workspace-script-plan.js +7 -7
- package/dist/esm/ultramodern-workspace/workspace-scripts.js +19 -136
- package/dist/esm/ultramodern-workspace/workspace-validation-contract.js +64 -373
- package/dist/esm/ultramodern-workspace/write-app.js +4 -3
- package/dist/esm/ultramodern-workspace/write-workspace.js +16 -53
- package/dist/esm-node/ultramodern-release-cohort.js +5 -14
- package/dist/esm-node/ultramodern-tooling/commands/cloudflare-output-verify.js +29 -53
- package/dist/esm-node/ultramodern-tooling/commands/context.js +3 -24
- package/dist/esm-node/ultramodern-tooling/commands/routes-generate-app.js +29 -0
- package/dist/esm-node/ultramodern-tooling/commands/routes-generate.js +13 -49
- package/dist/esm-node/ultramodern-tooling/commands/validate.js +6 -49
- package/dist/esm-node/ultramodern-tooling/commands.js +12 -3
- package/dist/esm-node/ultramodern-tooling/config/index.js +1 -0
- package/dist/esm-node/ultramodern-tooling/config/load.js +5 -17
- package/dist/esm-node/ultramodern-tooling/config/metadata.js +36 -1
- package/dist/esm-node/ultramodern-tooling/config/normalize.js +125 -191
- package/dist/esm-node/ultramodern-workspace/add-shell.js +110 -153
- package/dist/esm-node/ultramodern-workspace/add-vertical/execute.js +61 -98
- package/dist/esm-node/ultramodern-workspace/add-vertical/plan.js +16 -150
- package/dist/esm-node/ultramodern-workspace/add-vertical/preflight.js +6 -11
- package/dist/esm-node/ultramodern-workspace/add-vertical/preview.js +41 -0
- package/dist/esm-node/ultramodern-workspace/add-vertical/topology.js +1 -30
- package/dist/esm-node/ultramodern-workspace/add-vertical/transaction.js +2 -2
- package/dist/esm-node/ultramodern-workspace/add-vertical/workspace-state.js +15 -32
- package/dist/esm-node/ultramodern-workspace/add-vertical.js +1 -1
- package/dist/esm-node/ultramodern-workspace/api/contracts.js +2 -2
- package/dist/esm-node/ultramodern-workspace/api/rpc.js +16 -14
- package/dist/esm-node/ultramodern-workspace/api/service.js +4 -8
- package/dist/esm-node/ultramodern-workspace/api/shared.js +1 -1
- package/dist/esm-node/ultramodern-workspace/backend-federation.js +3 -3
- package/dist/esm-node/ultramodern-workspace/contracts.js +14 -180
- package/dist/esm-node/ultramodern-workspace/delivery-unit-stamp.js +10 -5
- package/dist/esm-node/ultramodern-workspace/delivery-unit-sync.js +28 -18
- package/dist/esm-node/ultramodern-workspace/delivery-unit.js +11 -6
- package/dist/esm-node/ultramodern-workspace/demo-components.js +62 -115
- package/dist/esm-node/ultramodern-workspace/descriptors.js +1 -2
- package/dist/esm-node/ultramodern-workspace/generation-result.js +2 -2
- package/dist/esm-node/ultramodern-workspace/index.js +1 -1
- package/dist/esm-node/ultramodern-workspace/mf-validation/constants.js +1 -4
- package/dist/esm-node/ultramodern-workspace/mf-validation/discovery.js +7 -7
- package/dist/esm-node/ultramodern-workspace/mf-validation/path-utils.js +36 -34
- package/dist/esm-node/ultramodern-workspace/module-federation/config.js +10 -17
- package/dist/esm-node/ultramodern-workspace/module-federation/remote-refs.js +1 -43
- package/dist/esm-node/ultramodern-workspace/package-json.js +48 -55
- package/dist/esm-node/ultramodern-workspace/patch-inventory.js +85 -0
- package/dist/esm-node/ultramodern-workspace/policy.js +6 -371
- package/dist/esm-node/ultramodern-workspace/public-surface.js +2 -2
- package/dist/esm-node/ultramodern-workspace/routes.js +11 -7
- package/dist/esm-node/ultramodern-workspace/shared-patches.js +2 -2
- package/dist/esm-node/ultramodern-workspace/shells.js +3 -67
- package/dist/esm-node/ultramodern-workspace/tooling-command-catalog.js +7 -41
- package/dist/esm-node/ultramodern-workspace/tsconfigs.js +4 -2
- package/dist/esm-node/ultramodern-workspace/validation/architecture.js +349 -0
- package/dist/esm-node/ultramodern-workspace/validation/assertions.js +66 -0
- package/dist/esm-node/ultramodern-workspace/validation/types.js +1 -0
- package/dist/esm-node/ultramodern-workspace/validation/workspace.js +286 -0
- package/dist/esm-node/ultramodern-workspace/versions.js +24 -28
- package/dist/esm-node/ultramodern-workspace/workspace-artifact-ownership.js +64 -30
- package/dist/esm-node/ultramodern-workspace/workspace-script-plan.js +7 -7
- package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +19 -136
- package/dist/esm-node/ultramodern-workspace/workspace-validation-contract.js +64 -373
- package/dist/esm-node/ultramodern-workspace/write-app.js +4 -3
- package/dist/esm-node/ultramodern-workspace/write-workspace.js +16 -53
- package/dist/types/ultramodern-release-cohort.d.ts +1 -3
- package/dist/types/ultramodern-tooling/commands/cloudflare-output-verify.d.ts +2 -2
- package/dist/types/ultramodern-tooling/commands/context.d.ts +0 -1
- package/dist/types/ultramodern-tooling/commands/routes-generate-app.d.ts +1 -0
- package/dist/types/ultramodern-tooling/commands/routes-generate.d.ts +1 -1
- package/dist/types/ultramodern-tooling/commands/validate.d.ts +1 -1
- package/dist/types/ultramodern-tooling/config/index.d.ts +1 -0
- package/dist/types/ultramodern-tooling/config/load.d.ts +6 -58
- package/dist/types/ultramodern-tooling/config/metadata.d.ts +3 -0
- package/dist/types/ultramodern-tooling/config/normalize.d.ts +9 -88
- package/dist/types/ultramodern-tooling/config/types.d.ts +1 -9
- package/dist/types/ultramodern-workspace/add-shell.d.ts +1 -1
- package/dist/types/ultramodern-workspace/add-vertical/execute.d.ts +1 -0
- package/dist/types/ultramodern-workspace/add-vertical/preflight.d.ts +4 -3
- package/dist/types/ultramodern-workspace/add-vertical/preview.d.ts +4 -0
- package/dist/types/ultramodern-workspace/add-vertical/topology.d.ts +0 -1
- package/dist/types/ultramodern-workspace/add-vertical/transaction.d.ts +4 -1
- package/dist/types/ultramodern-workspace/add-vertical/workspace-state.d.ts +8 -5
- package/dist/types/ultramodern-workspace/add-vertical.d.ts +1 -1
- package/dist/types/ultramodern-workspace/api/rpc.d.ts +1 -1
- package/dist/types/ultramodern-workspace/contracts.d.ts +1 -3
- package/dist/types/ultramodern-workspace/delivery-unit-stamp.d.ts +2 -2
- package/dist/types/ultramodern-workspace/delivery-unit.d.ts +2 -2
- package/dist/types/ultramodern-workspace/descriptors.d.ts +0 -1
- package/dist/types/ultramodern-workspace/index.d.ts +1 -1
- package/dist/types/ultramodern-workspace/mf-validation/constants.d.ts +0 -1
- package/dist/types/ultramodern-workspace/mf-validation/path-utils.d.ts +2 -3
- package/dist/types/ultramodern-workspace/module-federation/config.d.ts +1 -1
- package/dist/types/ultramodern-workspace/patch-inventory.d.ts +10 -0
- package/dist/types/ultramodern-workspace/policy.d.ts +60 -182
- package/dist/types/ultramodern-workspace/shells.d.ts +1 -14
- package/dist/types/ultramodern-workspace/tooling-command-catalog.d.ts +1 -6
- package/dist/types/ultramodern-workspace/validation/architecture.d.ts +2 -0
- package/dist/types/ultramodern-workspace/validation/assertions.d.ts +14 -0
- package/dist/types/ultramodern-workspace/validation/types.d.ts +9 -0
- package/dist/types/ultramodern-workspace/validation/workspace.d.ts +6 -0
- package/dist/types/ultramodern-workspace/versions.d.ts +23 -27
- package/dist/types/ultramodern-workspace/workspace-artifact-ownership.d.ts +5 -0
- package/dist/types/ultramodern-workspace/workspace-script-plan.d.ts +1 -1
- package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +7 -19
- package/dist/types/ultramodern-workspace/workspace-validation-contract.d.ts +22 -188
- package/dist/types/ultramodern-workspace/write-workspace.d.ts +1 -3
- package/package.json +17 -16
- package/{template-workspace/.modernjs/release-cohort.json → release-cohort.json} +48 -48
- package/template-workspace/README.md.handlebars +9 -16
- package/template-workspace/pnpm-workspace.yaml.handlebars +0 -24
- package/templates/workspace/apps/modern.config.ts.handlebars +6 -6
- package/templates/workspace/apps/shared/src/routes/ultramodern-route-metadata.ts.handlebars +8 -21
- package/templates/workspace/apps/shell-super-app/src/routes/[lang]/page.tsx.handlebars +20 -20
- package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.helpers.tsx.handlebars +1 -1
- package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.tsx.handlebars +11 -11
- package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.worker.helpers.tsx.handlebars +1 -1
- package/templates/workspace-scripts/generate-node-backend-federation.mjs +39 -49
- package/templates/workspace-scripts/generate-public-surface-assets.mjs +142 -206
- package/templates/workspace-scripts/materialize-zerops-runtime.mjs +3 -11
- package/templates/workspace-scripts/proof-cloudflare-version.mjs +53 -444
- package/templates/workspace-scripts/proof-node-backend-federation.mjs +193 -39
- package/templates/workspace-scripts/proof-workerd-ssr.mjs +126 -66
- package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +45 -147
- package/dist/cjs/ultramodern-workspace/cohort-parity.cjs +0 -129
- package/dist/cjs/ultramodern-workspace/patch-parity.cjs +0 -163
- package/dist/esm/ultramodern-workspace/cohort-parity.js +0 -74
- package/dist/esm/ultramodern-workspace/patch-parity.js +0 -99
- package/dist/esm-node/ultramodern-workspace/cohort-parity.js +0 -75
- package/dist/esm-node/ultramodern-workspace/patch-parity.js +0 -100
- package/dist/types/ultramodern-workspace/cohort-parity.d.ts +0 -31
- package/dist/types/ultramodern-workspace/patch-parity.d.ts +0 -39
- package/template-workspace/patches/@module-federation__bridge-react@2.9.0.patch +0 -47
- package/template-workspace/patches/@module-federation__dts-plugin@2.9.0.patch +0 -100
- package/template-workspace/patches/@module-federation__modern-js-v3@2.9.0.patch +0 -268
- package/template-workspace/patches/@module-federation__runtime-core@2.9.0.patch +0 -13
- package/template-workspace/patches/drizzle-orm-ts7-strict-declarations.patch +0 -471
- package/template-workspace/patches/msgpackr@2.1.0.patch +0 -30
- package/template-workspace/patches/zod@4.5.4.patch +0 -56
- package/templates/packages/effect-bff-runtime.ts +0 -2
- package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +0 -4046
|
@@ -9,7 +9,8 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
9
9
|
|
|
10
10
|
const workspaceRoot = path.resolve(process.env.ULTRAMODERN_WORKSPACE_ROOT ?? process.cwd());
|
|
11
11
|
const workspaceRequire = createRequire(path.join(workspaceRoot, 'package.json'));
|
|
12
|
-
const
|
|
12
|
+
const topologyPath = path.join(workspaceRoot, 'topology/reference-topology.json');
|
|
13
|
+
const localOverlayPath = path.join(workspaceRoot, 'topology/local-overlays/development.json');
|
|
13
14
|
const defaultOut = path.join(
|
|
14
15
|
workspaceRoot,
|
|
15
16
|
'.codex/reports/node-backend-federation-proof/proof.json',
|
|
@@ -70,7 +71,6 @@ export async function importBackendFederationRuntime() {
|
|
|
70
71
|
`${effectPath} does not export createEffectBffTestHandler`,
|
|
71
72
|
);
|
|
72
73
|
}
|
|
73
|
-
|
|
74
74
|
return {
|
|
75
75
|
loadBackendFederatedEffectApiFromManifest:
|
|
76
76
|
runtime.loadBackendFederatedEffectApiFromManifest,
|
|
@@ -96,7 +96,6 @@ function createBackendName(app) {
|
|
|
96
96
|
return (
|
|
97
97
|
app.backendFederation?.name ??
|
|
98
98
|
app.backendFederation?.executionSurfaces?.node?.remoteName ??
|
|
99
|
-
app.serverExecution?.node?.remoteName ??
|
|
100
99
|
(typeof app.moduleFederation?.name === 'string'
|
|
101
100
|
? `${app.moduleFederation.name}Backend`
|
|
102
101
|
: `vertical${toPascalCase(app.id)}Backend`)
|
|
@@ -105,7 +104,6 @@ function createBackendName(app) {
|
|
|
105
104
|
|
|
106
105
|
function createBackendManifestUrl(app) {
|
|
107
106
|
return (
|
|
108
|
-
app.backendFederation?.executionSurfaces?.node?.manifestUrl ??
|
|
109
107
|
app.serverExecution?.node?.manifestUrl ??
|
|
110
108
|
`http://localhost:${app.port}/backend-mf-manifest.json`
|
|
111
109
|
);
|
|
@@ -113,7 +111,6 @@ function createBackendManifestUrl(app) {
|
|
|
113
111
|
|
|
114
112
|
function createBackendContainerEntry(app) {
|
|
115
113
|
return (
|
|
116
|
-
app.backendFederation?.executionSurfaces?.node?.containerEntry ??
|
|
117
114
|
app.serverExecution?.node?.containerEntry ??
|
|
118
115
|
`http://localhost:${app.port}/backendRemoteEntry.cjs`
|
|
119
116
|
);
|
|
@@ -121,8 +118,8 @@ function createBackendContainerEntry(app) {
|
|
|
121
118
|
|
|
122
119
|
function resolveRemoteType(app) {
|
|
123
120
|
return (
|
|
124
|
-
app.backendFederation?.executionSurfaces?.node?.remoteType ??
|
|
125
121
|
app.serverExecution?.node?.remoteType ??
|
|
122
|
+
app.backendFederation?.executionSurfaces?.node?.remoteType ??
|
|
126
123
|
'commonjs-module'
|
|
127
124
|
);
|
|
128
125
|
}
|
|
@@ -140,8 +137,8 @@ function collectJsonSmokeChecks(apps, targetApp) {
|
|
|
140
137
|
|
|
141
138
|
const configuredChecks = apps
|
|
142
139
|
.flatMap((app) =>
|
|
143
|
-
Array.isArray(app?.
|
|
144
|
-
? app.
|
|
140
|
+
Array.isArray(app?.cloudflare?.jsonSmokeChecks)
|
|
141
|
+
? app.cloudflare.jsonSmokeChecks
|
|
145
142
|
: [],
|
|
146
143
|
)
|
|
147
144
|
.filter((check) => {
|
|
@@ -178,27 +175,70 @@ function collectJsonSmokeChecks(apps, targetApp) {
|
|
|
178
175
|
];
|
|
179
176
|
}
|
|
180
177
|
|
|
181
|
-
function
|
|
182
|
-
|
|
178
|
+
export function topologyApps(topology, localOverlay, appFilter, env = process.env) {
|
|
179
|
+
if (!Array.isArray(topology?.verticals) || !localOverlay?.ports || !localOverlay?.serverExecution) {
|
|
180
|
+
throw new Error('Node proof requires declared reference topology and development overlay.');
|
|
181
|
+
}
|
|
182
|
+
const apps = topology.verticals;
|
|
183
183
|
const filteredApps = apps
|
|
184
184
|
.filter((app) => app?.kind === 'vertical' && app.api)
|
|
185
185
|
.filter((app) => !appFilter || app.id === appFilter)
|
|
186
|
-
.map((app) =>
|
|
186
|
+
.map((app) => {
|
|
187
|
+
const overlayPort = localOverlay.ports[app.id];
|
|
188
|
+
const domain = app.domain ?? app.id;
|
|
189
|
+
const portEnv = app.portEnv ??
|
|
190
|
+
`VERTICAL_${String(domain).replace(/[^a-zA-Z0-9]/gu, '_').toUpperCase()}_PORT`;
|
|
191
|
+
const configuredPort = env[portEnv];
|
|
192
|
+
const port = configuredPort === undefined ? overlayPort : Number(configuredPort);
|
|
193
|
+
const serverExecution = localOverlay.serverExecution[app.id];
|
|
194
|
+
if (!app.path || !Number.isInteger(overlayPort) || !Number.isInteger(port) || port < 1 || port > 65535 || !serverExecution?.node) {
|
|
195
|
+
throw new Error(`${app.id} is missing its declared path, port or Node server execution.`);
|
|
196
|
+
}
|
|
197
|
+
const appManifest = readJson(path.join(workspaceRoot, app.path, 'package.json'));
|
|
198
|
+
if (app.package !== appManifest.name || typeof appManifest.version !== 'string') {
|
|
199
|
+
throw new Error(`${app.id} topology package identity must match package.json`);
|
|
200
|
+
}
|
|
201
|
+
const api = { ...app.api, prefix: app.api.bff?.prefix };
|
|
202
|
+
const declared = { ...app, api, serverExecution };
|
|
203
|
+
const publicUrlEnv = app.cloudflare?.publicUrlEnv;
|
|
204
|
+
const configuredPublicUrl = publicUrlEnv ? env[publicUrlEnv] : undefined;
|
|
205
|
+
const publicOrigin = configuredPublicUrl
|
|
206
|
+
? new URL(configuredPublicUrl).origin
|
|
207
|
+
: `http://localhost:${port}`;
|
|
208
|
+
if (!/^https?:\/\//u.test(publicOrigin)) {
|
|
209
|
+
throw new Error(`${app.id} has an invalid declared public URL.`);
|
|
210
|
+
}
|
|
211
|
+
const nodeUrl = (declaredUrl) => {
|
|
212
|
+
const url = new URL(declaredUrl);
|
|
213
|
+
return url.origin === new URL(`http://localhost:${overlayPort}`).origin &&
|
|
214
|
+
url.username === '' &&
|
|
215
|
+
url.password === ''
|
|
216
|
+
? `${publicOrigin}${url.pathname}${url.search}${url.hash}`
|
|
217
|
+
: declaredUrl;
|
|
218
|
+
};
|
|
219
|
+
return {
|
|
187
220
|
id: app.id,
|
|
188
|
-
directory:
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
221
|
+
directory: normalizeRelativePath(app.path),
|
|
222
|
+
backendName: createBackendName(declared),
|
|
223
|
+
manifestUrl: nodeUrl(createBackendManifestUrl(declared)),
|
|
224
|
+
containerEntry: nodeUrl(createBackendContainerEntry(declared)),
|
|
225
|
+
port,
|
|
226
|
+
portEnv,
|
|
227
|
+
remoteType: resolveRemoteType(declared),
|
|
228
|
+
apiProtocol: app.api.protocol ?? 'rest',
|
|
229
|
+
apiPrefix: app.api.bff?.prefix,
|
|
230
|
+
rpcPath: app.api.rpcPath,
|
|
231
|
+
rpcSerialization: app.api.rpcSerialization,
|
|
232
|
+
apiOnly: app.surfaceProfile === 'api-only',
|
|
233
|
+
smokeChecks: collectJsonSmokeChecks(apps, declared),
|
|
234
|
+
topologyDeliveryUnit:
|
|
198
235
|
app.deliveryUnit && typeof app.deliveryUnit === 'object'
|
|
199
236
|
? app.deliveryUnit
|
|
200
237
|
: undefined,
|
|
201
|
-
|
|
238
|
+
packageName: appManifest.name,
|
|
239
|
+
version: appManifest.version,
|
|
240
|
+
};
|
|
241
|
+
});
|
|
202
242
|
|
|
203
243
|
if (appFilter && filteredApps.length === 0) {
|
|
204
244
|
throw new Error(`No vertical API app matched --app ${appFilter}`);
|
|
@@ -440,7 +480,7 @@ function sha256(bytes) {
|
|
|
440
480
|
return createHash('sha256').update(bytes).digest('hex');
|
|
441
481
|
}
|
|
442
482
|
|
|
443
|
-
function readBoundReleaseEnvelope(app, target) {
|
|
483
|
+
export function readBoundReleaseEnvelope(app, target) {
|
|
444
484
|
const targetDirectory = path.join(workspaceRoot, app.directory, target);
|
|
445
485
|
const envelopePath = path.join(targetDirectory, releaseEnvelopePath);
|
|
446
486
|
assertFile(envelopePath, app.id, 'Node release envelope');
|
|
@@ -451,6 +491,18 @@ function readBoundReleaseEnvelope(app, target) {
|
|
|
451
491
|
`${app.id} release-envelope schema`,
|
|
452
492
|
);
|
|
453
493
|
assertEqual(envelope.target, 'node', `${app.id} release-envelope target`);
|
|
494
|
+
assertEqual(
|
|
495
|
+
envelope.kind,
|
|
496
|
+
'ultramodern-target-microvertical-release-envelope',
|
|
497
|
+
`${app.id} release-envelope kind`,
|
|
498
|
+
);
|
|
499
|
+
if (app.apiOnly) {
|
|
500
|
+
if (envelope.surfaces?.uiClient?.length !== 0 || envelope.surfaces?.ssr?.length !== 0) {
|
|
501
|
+
throw new Error(`${app.id} API-only release envelope must declare empty UI/client and SSR surfaces`);
|
|
502
|
+
}
|
|
503
|
+
} else if (!envelope.surfaces?.uiClient?.length || !envelope.surfaces?.ssr?.length) {
|
|
504
|
+
throw new Error(`${app.id} full-stack release envelope must bind UI/client and SSR surfaces`);
|
|
505
|
+
}
|
|
454
506
|
|
|
455
507
|
const manifestLogicalPath = envelope.surfaces?.backendFederation?.manifest;
|
|
456
508
|
const containerLogicalPath = envelope.surfaces?.backendFederation?.container;
|
|
@@ -696,6 +748,98 @@ async function proveLiveApi(app, manifest, releaseBinding) {
|
|
|
696
748
|
};
|
|
697
749
|
}
|
|
698
750
|
|
|
751
|
+
export async function proveLiveRpcApi(app, loaded, manifest, releaseBinding) {
|
|
752
|
+
const contract = loaded.contract;
|
|
753
|
+
if (contract?.protocol !== 'rpc' || typeof contract.group !== 'string' ||
|
|
754
|
+
typeof contract.path !== 'string' || contract.serialization !== 'json') {
|
|
755
|
+
throw new Error(`${app.id} backend expose missing native RPC contract`);
|
|
756
|
+
}
|
|
757
|
+
assertEqual(contract.path, app.rpcPath, `${app.id} RPC contract/topology path`);
|
|
758
|
+
assertEqual(contract.path, manifest.backendFederation?.rpcPath, `${app.id} RPC contract/manifest path`);
|
|
759
|
+
assertEqual(contract.serialization, app.rpcSerialization, `${app.id} RPC contract/topology serialization`);
|
|
760
|
+
assertEqual(contract.serialization, manifest.backendFederation?.rpcSerialization, `${app.id} RPC contract/manifest serialization`);
|
|
761
|
+
const check = app.smokeChecks.find((entry) => entry?.body?.method === 'list' && entry.route === contract.path);
|
|
762
|
+
if (!check || !Number.isInteger(check.body?.params?.limit) || check.body.params.limit < 1) {
|
|
763
|
+
throw new Error(`${app.id} has no declared RPC list smoke check`);
|
|
764
|
+
}
|
|
765
|
+
const route = normalizeRoutePath(contract.path);
|
|
766
|
+
const url = new URL(route, app.manifestUrl).href;
|
|
767
|
+
const clientModule = createRequire(
|
|
768
|
+
path.join(workspaceRoot, app.directory, 'package.json'),
|
|
769
|
+
)(`${app.packageName}/api/rpc-client`);
|
|
770
|
+
const clientName = toPascalCase(contract.group);
|
|
771
|
+
const localContract = clientModule[`${clientName}RpcContract`];
|
|
772
|
+
if (localContract?.path !== contract.path ||
|
|
773
|
+
localContract?.group !== contract.group ||
|
|
774
|
+
localContract?.serialization !== contract.serialization ||
|
|
775
|
+
localContract?.protocol !== contract.protocol) {
|
|
776
|
+
throw new Error(`${app.id} public RPC client contract does not match the verified backend expose`);
|
|
777
|
+
}
|
|
778
|
+
const makeClient = clientModule[`make${clientName}RpcClient`];
|
|
779
|
+
if (typeof makeClient !== 'function' ||
|
|
780
|
+
typeof clientModule.Effect?.runPromise !== 'function') {
|
|
781
|
+
throw new Error(`${app.id} has no native public RPC client`);
|
|
782
|
+
}
|
|
783
|
+
const client = await clientModule.Effect.runPromise(makeClient({ url }));
|
|
784
|
+
try {
|
|
785
|
+
const listed = await clientModule.Effect.runPromise(client.list({ limit: check.body.params.limit }));
|
|
786
|
+
const item = listed?.items?.[0];
|
|
787
|
+
if (typeof item?.id !== 'string' || item.id.length === 0) {
|
|
788
|
+
throw new Error(`${app.id} native RPC list returned no item id`);
|
|
789
|
+
}
|
|
790
|
+
for (const expectation of normalizeJsonExpectations(check)) {
|
|
791
|
+
if (expectation.path === 'id') {
|
|
792
|
+
assertJsonEqual(check.body.id, expectation.value, `${app.id} declared RPC request id`);
|
|
793
|
+
} else if (expectation.path.startsWith('result.')) {
|
|
794
|
+
assertJsonEqual(
|
|
795
|
+
jsonPathValue(listed, expectation.path.slice('result.'.length)),
|
|
796
|
+
expectation.value,
|
|
797
|
+
`${app.id} native RPC ${expectation.path}`,
|
|
798
|
+
);
|
|
799
|
+
} else {
|
|
800
|
+
throw new Error(`${app.id} RPC smoke check has unsupported expectation ${expectation.path}`);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
const fetched = await clientModule.Effect.runPromise(client.get({ id: item.id }));
|
|
804
|
+
assertEqual(fetched?.id, item.id, `${app.id} native RPC get item id`);
|
|
805
|
+
const missingId = `__ultramodern-proof-missing-${item.id}__`;
|
|
806
|
+
const missing = await clientModule.Effect.runPromise(
|
|
807
|
+
clientModule.Effect.match(client.get({ id: missingId }), {
|
|
808
|
+
onFailure: (error) => ({ error }),
|
|
809
|
+
onSuccess: (value) => ({ value }),
|
|
810
|
+
}),
|
|
811
|
+
);
|
|
812
|
+
const expectedErrorTag = `${toPascalCase(contract.group)}NotFoundRpc`;
|
|
813
|
+
if (missing?.error?._tag !== expectedErrorTag ||
|
|
814
|
+
missing.error?.id !== missingId) {
|
|
815
|
+
throw new Error(`${app.id} native RPC get did not return ${expectedErrorTag} for a missing item`);
|
|
816
|
+
}
|
|
817
|
+
return {
|
|
818
|
+
method: 'RPC',
|
|
819
|
+
route,
|
|
820
|
+
url,
|
|
821
|
+
protocol: contract.protocol,
|
|
822
|
+
serialization: contract.serialization,
|
|
823
|
+
group: contract.group,
|
|
824
|
+
operations: [
|
|
825
|
+
{ method: 'list', itemId: item.id, status: 'pass' },
|
|
826
|
+
{ method: 'get', itemId: fetched.id, status: 'pass' },
|
|
827
|
+
{ method: 'get', errorTag: missing.error._tag, missingId, status: 'pass' },
|
|
828
|
+
],
|
|
829
|
+
envelopeDigest: releaseBinding.envelope.envelopeDigest,
|
|
830
|
+
apiBackendArtifacts: releaseBinding.apiBackendArtifacts.map((artifact) => ({
|
|
831
|
+
logicalPath: artifact.logicalPath,
|
|
832
|
+
runtime: artifact.runtime,
|
|
833
|
+
byteLength: artifact.byteLength,
|
|
834
|
+
sha256: artifact.sha256,
|
|
835
|
+
})),
|
|
836
|
+
status: 'pass',
|
|
837
|
+
};
|
|
838
|
+
} finally {
|
|
839
|
+
await client.dispose();
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
699
843
|
function jsonPathValue(value, path) {
|
|
700
844
|
const segments = String(path ?? '')
|
|
701
845
|
.split('.')
|
|
@@ -748,12 +892,17 @@ async function runSmokeChecks(app, loaded, createEffectBffTestHandler) {
|
|
|
748
892
|
throw new Error(`${app.id} backend runtime cannot create Effect test handler`);
|
|
749
893
|
}
|
|
750
894
|
|
|
751
|
-
const servicePrefix = loaded.contract?.
|
|
895
|
+
const servicePrefix = loaded.contract?.protocol === 'rpc'
|
|
896
|
+
? app.apiPrefix
|
|
897
|
+
: loaded.contract?.servicePrefix ?? loaded.contract?.apiPrefix;
|
|
752
898
|
if (typeof servicePrefix !== 'string' || servicePrefix.length === 0) {
|
|
753
899
|
throw new Error(
|
|
754
|
-
`${app.id} backend expose missing
|
|
900
|
+
`${app.id} backend expose missing declared API prefix`,
|
|
755
901
|
);
|
|
756
902
|
}
|
|
903
|
+
if (loaded.contract?.protocol === 'rpc') {
|
|
904
|
+
assertEqual(loaded.contract.path, app.rpcPath, `${app.id} RPC smoke route`);
|
|
905
|
+
}
|
|
757
906
|
|
|
758
907
|
const edge = await createEffectBffTestHandler({
|
|
759
908
|
module: loaded.runtime,
|
|
@@ -962,10 +1111,10 @@ function validateManifest(app, manifest, buildIdentity) {
|
|
|
962
1111
|
}
|
|
963
1112
|
}
|
|
964
1113
|
|
|
965
|
-
function
|
|
966
|
-
const
|
|
967
|
-
if (!
|
|
968
|
-
|
|
1114
|
+
function assertTopologyStableIdentityMatchesBuild(app, buildIdentity) {
|
|
1115
|
+
const topologyDeliveryUnit = app.topologyDeliveryUnit;
|
|
1116
|
+
if (!topologyDeliveryUnit) {
|
|
1117
|
+
throw new Error(`${app.id} is missing its declared topology delivery-unit identity`);
|
|
969
1118
|
}
|
|
970
1119
|
|
|
971
1120
|
const mismatches = [];
|
|
@@ -974,20 +1123,22 @@ function assertCompactStableIdentityMatchesBuild(app, buildIdentity) {
|
|
|
974
1123
|
mismatches.push(`${label}: deliveryUnit=${a} vs ultramodern-build=${b}`);
|
|
975
1124
|
}
|
|
976
1125
|
};
|
|
977
|
-
compare('unitId',
|
|
1126
|
+
compare('unitId', topologyDeliveryUnit.unitId, buildIdentity.unitId);
|
|
978
1127
|
compare(
|
|
979
1128
|
'packageName',
|
|
980
|
-
|
|
1129
|
+
topologyDeliveryUnit.packageName,
|
|
981
1130
|
buildIdentity.packageName,
|
|
982
1131
|
);
|
|
983
|
-
compare('version',
|
|
1132
|
+
compare('version', topologyDeliveryUnit.version, buildIdentity.version);
|
|
1133
|
+
compare('packageName', app.packageName, buildIdentity.packageName);
|
|
1134
|
+
compare('version', app.version, buildIdentity.version);
|
|
984
1135
|
|
|
985
1136
|
if (mismatches.length > 0) {
|
|
986
1137
|
throw new Error(
|
|
987
1138
|
`${app.id} delivery-unit identity drift between ${path.relative(
|
|
988
1139
|
workspaceRoot,
|
|
989
|
-
|
|
990
|
-
)} (
|
|
1140
|
+
topologyPath,
|
|
1141
|
+
)} (reference topology) and ${path.relative(
|
|
991
1142
|
workspaceRoot,
|
|
992
1143
|
buildIdentity.artifactPath,
|
|
993
1144
|
)} (stamped target identity): ${mismatches.join('; ')}`,
|
|
@@ -1006,7 +1157,7 @@ async function proveBackend(app, backendRuntime, target) {
|
|
|
1006
1157
|
assertFile(entryPath, app.id, 'backend remote entry');
|
|
1007
1158
|
|
|
1008
1159
|
const buildIdentity = readBuildIdentity(app, target);
|
|
1009
|
-
|
|
1160
|
+
assertTopologyStableIdentityMatchesBuild(app, buildIdentity);
|
|
1010
1161
|
const manifest = readJson(manifestPath);
|
|
1011
1162
|
validateManifest(app, manifest, buildIdentity);
|
|
1012
1163
|
const releaseBinding = readBoundReleaseEnvelope(app, target);
|
|
@@ -1108,7 +1259,9 @@ async function proveBackend(app, backendRuntime, target) {
|
|
|
1108
1259
|
}
|
|
1109
1260
|
|
|
1110
1261
|
const smokeChecks = await runSmokeChecks(app, loaded, createEffectBffTestHandler);
|
|
1111
|
-
const liveApi =
|
|
1262
|
+
const liveApi = loaded.contract?.protocol === 'rpc'
|
|
1263
|
+
? await proveLiveRpcApi(app, loaded, manifest, releaseBinding)
|
|
1264
|
+
: await proveLiveApi(app, manifest, releaseBinding);
|
|
1112
1265
|
|
|
1113
1266
|
return {
|
|
1114
1267
|
appId: app.id,
|
|
@@ -1185,9 +1338,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
1185
1338
|
return 0;
|
|
1186
1339
|
}
|
|
1187
1340
|
|
|
1188
|
-
const
|
|
1189
|
-
const
|
|
1190
|
-
const
|
|
1341
|
+
const topology = readJson(topologyPath);
|
|
1342
|
+
const localOverlay = readJson(localOverlayPath);
|
|
1343
|
+
const runtimeApps = topologyApps(topology, localOverlay);
|
|
1344
|
+
const apps = args.app ? topologyApps(topology, localOverlay, args.app) : runtimeApps;
|
|
1191
1345
|
const results = [];
|
|
1192
1346
|
const backendRuntime =
|
|
1193
1347
|
apps.length > 0 ? await importBackendFederationRuntime() : undefined;
|