@bleedingdev/modern-js-ultramodern-create 3.9.0-ultramodern.2 → 3.9.0-ultramodern.21
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 +43 -74
- package/dist/cjs/cli/package-source.cjs +2 -2
- package/dist/cjs/cli/project-setup.cjs +2 -22
- package/dist/cjs/index.cjs +9 -1
- package/dist/cjs/locale/en.cjs +2 -1
- package/dist/cjs/locale/zh.cjs +2 -1
- package/dist/cjs/ultramodern-package-source.cjs +13 -9
- package/dist/cjs/ultramodern-release-cohort.cjs +6 -21
- package/dist/cjs/ultramodern-tooling/commands/cloudflare-output-verify.cjs +49 -56
- package/dist/cjs/ultramodern-tooling/commands/context.cjs +11 -29
- package/dist/cjs/ultramodern-tooling/commands/routes-generate-app.cjs +67 -0
- package/dist/cjs/ultramodern-tooling/commands/routes-generate.cjs +24 -53
- package/dist/cjs/ultramodern-tooling/commands/validate.cjs +5 -11
- package/dist/cjs/ultramodern-tooling/commands.cjs +15 -8
- package/dist/cjs/ultramodern-tooling/config/index.cjs +10 -24
- package/dist/cjs/ultramodern-tooling/config/load.cjs +12 -10
- package/dist/cjs/ultramodern-tooling/config/metadata.cjs +34 -32
- package/dist/cjs/ultramodern-tooling/config/normalize.cjs +165 -164
- package/dist/cjs/ultramodern-workspace/add-shell.cjs +118 -118
- package/dist/cjs/ultramodern-workspace/add-vertical/execute.cjs +121 -25
- package/dist/cjs/ultramodern-workspace/add-vertical/plan.cjs +14 -160
- package/dist/cjs/ultramodern-workspace/add-vertical/preflight.cjs +16 -48
- package/dist/cjs/ultramodern-workspace/add-vertical/preview.cjs +78 -0
- package/dist/cjs/ultramodern-workspace/add-vertical/shell-files.cjs +87 -24
- package/dist/cjs/ultramodern-workspace/add-vertical/topology.cjs +4 -1
- package/dist/cjs/ultramodern-workspace/add-vertical/transaction.cjs +378 -45
- package/dist/cjs/ultramodern-workspace/add-vertical/workspace-state.cjs +21 -44
- package/dist/cjs/ultramodern-workspace/add-vertical.cjs +0 -9
- package/dist/cjs/ultramodern-workspace/api/client.cjs +5 -5
- package/dist/cjs/ultramodern-workspace/api/contracts.cjs +2 -2
- package/dist/cjs/ultramodern-workspace/api/rpc.cjs +20 -18
- package/dist/cjs/ultramodern-workspace/api/service.cjs +49 -24
- package/dist/cjs/ultramodern-workspace/api/shared.cjs +28 -18
- package/dist/cjs/ultramodern-workspace/app-files.cjs +3 -23
- package/dist/cjs/ultramodern-workspace/backend-federation.cjs +27 -27
- package/dist/cjs/ultramodern-workspace/contracts.cjs +14 -183
- package/dist/cjs/ultramodern-workspace/delivery-unit-schema/surfaces.cjs +85 -0
- package/dist/cjs/ultramodern-workspace/delivery-unit-schema/types.cjs +8 -85
- package/dist/cjs/ultramodern-workspace/delivery-unit-stamp.cjs +16 -4
- package/dist/cjs/ultramodern-workspace/delivery-unit-sync.cjs +27 -17
- package/dist/cjs/ultramodern-workspace/delivery-unit.cjs +16 -10
- package/dist/cjs/ultramodern-workspace/demo-components.cjs +66 -433
- package/dist/cjs/ultramodern-workspace/descriptors.cjs +0 -4
- package/dist/cjs/ultramodern-workspace/fs-io.cjs +37 -2
- package/dist/cjs/ultramodern-workspace/generation-result.cjs +4 -4
- 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-tooling/commands/migrate-strict-effect/generated-patches.cjs → ultramodern-workspace/mf-validation/exposes.cjs} +13 -17
- package/dist/cjs/ultramodern-workspace/mf-validation/inspect.cjs +26 -7
- package/dist/cjs/ultramodern-workspace/mf-validation/object-literal.cjs +21 -122
- package/dist/cjs/ultramodern-workspace/mf-validation/path-utils.cjs +37 -38
- package/dist/cjs/ultramodern-workspace/mf-validation/syntax.cjs +93 -153
- package/dist/cjs/ultramodern-workspace/mf-validation.cjs +4 -0
- package/dist/cjs/ultramodern-workspace/module-federation/config.cjs +24 -29
- package/dist/cjs/ultramodern-workspace/module-federation/reexport-module.cjs +39 -58
- package/dist/cjs/ultramodern-workspace/module-federation/remote-refs.cjs +1 -43
- package/dist/cjs/ultramodern-workspace/package-json.cjs +63 -37
- package/dist/cjs/ultramodern-workspace/patch-inventory.cjs +104 -0
- package/dist/cjs/ultramodern-workspace/policy.cjs +3 -577
- package/dist/cjs/ultramodern-workspace/public-surface.cjs +3 -3
- package/dist/cjs/ultramodern-workspace/routes.cjs +2 -11
- 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 +27 -41
- package/dist/cjs/ultramodern-workspace/tsconfigs.cjs +8 -2
- 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 +344 -0
- package/dist/cjs/ultramodern-workspace/versions.cjs +81 -45
- package/dist/cjs/ultramodern-workspace/workspace-artifact-ownership.cjs +183 -0
- package/dist/cjs/ultramodern-workspace/workspace-script-plan.cjs +36 -25
- package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +11 -200
- package/dist/cjs/ultramodern-workspace/workspace-validation-contract.cjs +62 -373
- package/dist/cjs/ultramodern-workspace/write-app.cjs +19 -11
- package/dist/cjs/ultramodern-workspace/write-workspace.cjs +20 -50
- package/dist/cjs/ultramodern-workspace/zerops.cjs +2 -0
- package/dist/esm/cli/package-source.js +3 -3
- package/dist/esm/cli/project-setup.js +2 -22
- package/dist/esm/index.js +10 -2
- package/dist/esm/locale/en.js +2 -1
- package/dist/esm/locale/zh.js +2 -1
- package/dist/esm/ultramodern-package-source.js +14 -4
- package/dist/esm/ultramodern-release-cohort.js +5 -14
- package/dist/esm/ultramodern-tooling/commands/cloudflare-output-verify.js +49 -56
- package/dist/esm/ultramodern-tooling/commands/context.js +10 -26
- package/dist/esm/ultramodern-tooling/commands/routes-generate-app.js +35 -0
- package/dist/esm/ultramodern-tooling/commands/routes-generate.js +25 -54
- package/dist/esm/ultramodern-tooling/commands/validate.js +6 -12
- package/dist/esm/ultramodern-tooling/commands.js +15 -9
- package/dist/esm/ultramodern-tooling/config/index.js +0 -2
- package/dist/esm/ultramodern-tooling/config/load.js +9 -9
- package/dist/esm/ultramodern-tooling/config/metadata.js +33 -28
- package/dist/esm/ultramodern-tooling/config/normalize.js +159 -156
- package/dist/esm/ultramodern-workspace/add-shell.js +124 -123
- package/dist/esm/ultramodern-workspace/add-vertical/execute.js +119 -27
- package/dist/esm/ultramodern-workspace/add-vertical/plan.js +16 -150
- package/dist/esm/ultramodern-workspace/add-vertical/preflight.js +15 -47
- package/dist/esm/ultramodern-workspace/add-vertical/preview.js +40 -0
- package/dist/esm/ultramodern-workspace/add-vertical/shell-files.js +87 -25
- package/dist/esm/ultramodern-workspace/add-vertical/topology.js +4 -1
- package/dist/esm/ultramodern-workspace/add-vertical/transaction.js +373 -46
- package/dist/esm/ultramodern-workspace/add-vertical/workspace-state.js +17 -34
- package/dist/esm/ultramodern-workspace/add-vertical.js +1 -1
- package/dist/esm/ultramodern-workspace/api/client.js +5 -5
- package/dist/esm/ultramodern-workspace/api/contracts.js +2 -2
- package/dist/esm/ultramodern-workspace/api/rpc.js +20 -18
- package/dist/esm/ultramodern-workspace/api/service.js +49 -24
- package/dist/esm/ultramodern-workspace/api/shared.js +28 -18
- package/dist/esm/ultramodern-workspace/app-files.js +3 -23
- package/dist/esm/ultramodern-workspace/backend-federation.js +5 -5
- package/dist/esm/ultramodern-workspace/contracts.js +14 -180
- package/dist/esm/ultramodern-workspace/delivery-unit-schema/surfaces.js +41 -0
- package/dist/esm/ultramodern-workspace/delivery-unit-schema/types.js +1 -75
- package/dist/esm/ultramodern-workspace/delivery-unit-stamp.js +16 -4
- package/dist/esm/ultramodern-workspace/delivery-unit-sync.js +28 -18
- package/dist/esm/ultramodern-workspace/delivery-unit.js +12 -6
- package/dist/esm/ultramodern-workspace/demo-components.js +67 -428
- package/dist/esm/ultramodern-workspace/descriptors.js +1 -2
- package/dist/esm/ultramodern-workspace/fs-io.js +37 -2
- package/dist/esm/ultramodern-workspace/generation-result.js +3 -3
- 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/exposes.js +14 -0
- package/dist/esm/ultramodern-workspace/mf-validation/inspect.js +27 -8
- package/dist/esm/ultramodern-workspace/mf-validation/object-literal.js +22 -123
- package/dist/esm/ultramodern-workspace/mf-validation/path-utils.js +36 -34
- package/dist/esm/ultramodern-workspace/mf-validation/syntax.js +83 -143
- package/dist/esm/ultramodern-workspace/mf-validation.js +1 -0
- package/dist/esm/ultramodern-workspace/module-federation/config.js +25 -30
- package/dist/esm/ultramodern-workspace/module-federation/reexport-module.js +39 -58
- package/dist/esm/ultramodern-workspace/module-federation/remote-refs.js +1 -43
- package/dist/esm/ultramodern-workspace/package-json.js +58 -32
- package/dist/esm/ultramodern-workspace/patch-inventory.js +66 -0
- package/dist/esm/ultramodern-workspace/policy.js +2 -564
- package/dist/esm/ultramodern-workspace/public-surface.js +3 -3
- package/dist/esm/ultramodern-workspace/routes.js +2 -11
- 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 +22 -37
- package/dist/esm/ultramodern-workspace/tsconfigs.js +9 -3
- 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 +286 -0
- package/dist/esm/ultramodern-workspace/versions.js +74 -29
- package/dist/esm/ultramodern-workspace/workspace-artifact-ownership.js +128 -0
- package/dist/esm/ultramodern-workspace/workspace-script-plan.js +35 -27
- package/dist/esm/ultramodern-workspace/workspace-scripts.js +10 -190
- package/dist/esm/ultramodern-workspace/workspace-validation-contract.js +65 -376
- package/dist/esm/ultramodern-workspace/write-app.js +18 -10
- package/dist/esm/ultramodern-workspace/write-workspace.js +25 -52
- package/dist/esm/ultramodern-workspace/zerops.js +2 -0
- package/dist/esm-node/cli/package-source.js +3 -3
- package/dist/esm-node/cli/project-setup.js +2 -22
- package/dist/esm-node/index.js +10 -2
- package/dist/esm-node/locale/en.js +2 -1
- package/dist/esm-node/locale/zh.js +2 -1
- package/dist/esm-node/ultramodern-package-source.js +14 -4
- package/dist/esm-node/ultramodern-release-cohort.js +5 -14
- package/dist/esm-node/ultramodern-tooling/commands/cloudflare-output-verify.js +49 -56
- package/dist/esm-node/ultramodern-tooling/commands/context.js +10 -26
- package/dist/esm-node/ultramodern-tooling/commands/routes-generate-app.js +36 -0
- package/dist/esm-node/ultramodern-tooling/commands/routes-generate.js +25 -54
- package/dist/esm-node/ultramodern-tooling/commands/validate.js +6 -12
- package/dist/esm-node/ultramodern-tooling/commands.js +15 -9
- package/dist/esm-node/ultramodern-tooling/config/index.js +0 -2
- package/dist/esm-node/ultramodern-tooling/config/load.js +9 -9
- package/dist/esm-node/ultramodern-tooling/config/metadata.js +33 -28
- package/dist/esm-node/ultramodern-tooling/config/normalize.js +159 -156
- package/dist/esm-node/ultramodern-workspace/add-shell.js +124 -123
- package/dist/esm-node/ultramodern-workspace/add-vertical/execute.js +119 -27
- package/dist/esm-node/ultramodern-workspace/add-vertical/plan.js +16 -150
- package/dist/esm-node/ultramodern-workspace/add-vertical/preflight.js +15 -47
- package/dist/esm-node/ultramodern-workspace/add-vertical/preview.js +41 -0
- package/dist/esm-node/ultramodern-workspace/add-vertical/shell-files.js +87 -25
- package/dist/esm-node/ultramodern-workspace/add-vertical/topology.js +4 -1
- package/dist/esm-node/ultramodern-workspace/add-vertical/transaction.js +373 -46
- package/dist/esm-node/ultramodern-workspace/add-vertical/workspace-state.js +17 -34
- package/dist/esm-node/ultramodern-workspace/add-vertical.js +1 -1
- package/dist/esm-node/ultramodern-workspace/api/client.js +5 -5
- package/dist/esm-node/ultramodern-workspace/api/contracts.js +2 -2
- package/dist/esm-node/ultramodern-workspace/api/rpc.js +20 -18
- package/dist/esm-node/ultramodern-workspace/api/service.js +49 -24
- package/dist/esm-node/ultramodern-workspace/api/shared.js +28 -18
- package/dist/esm-node/ultramodern-workspace/app-files.js +3 -23
- package/dist/esm-node/ultramodern-workspace/backend-federation.js +5 -5
- package/dist/esm-node/ultramodern-workspace/contracts.js +14 -180
- package/dist/esm-node/ultramodern-workspace/delivery-unit-schema/surfaces.js +42 -0
- package/dist/esm-node/ultramodern-workspace/delivery-unit-schema/types.js +1 -75
- package/dist/esm-node/ultramodern-workspace/delivery-unit-stamp.js +16 -4
- package/dist/esm-node/ultramodern-workspace/delivery-unit-sync.js +28 -18
- package/dist/esm-node/ultramodern-workspace/delivery-unit.js +12 -6
- package/dist/esm-node/ultramodern-workspace/demo-components.js +67 -428
- package/dist/esm-node/ultramodern-workspace/descriptors.js +1 -2
- package/dist/esm-node/ultramodern-workspace/fs-io.js +37 -2
- package/dist/esm-node/ultramodern-workspace/generation-result.js +3 -3
- 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/exposes.js +15 -0
- package/dist/esm-node/ultramodern-workspace/mf-validation/inspect.js +27 -8
- package/dist/esm-node/ultramodern-workspace/mf-validation/object-literal.js +22 -123
- package/dist/esm-node/ultramodern-workspace/mf-validation/path-utils.js +36 -34
- package/dist/esm-node/ultramodern-workspace/mf-validation/syntax.js +83 -143
- package/dist/esm-node/ultramodern-workspace/mf-validation.js +1 -0
- package/dist/esm-node/ultramodern-workspace/module-federation/config.js +25 -30
- package/dist/esm-node/ultramodern-workspace/module-federation/reexport-module.js +39 -58
- package/dist/esm-node/ultramodern-workspace/module-federation/remote-refs.js +1 -43
- package/dist/esm-node/ultramodern-workspace/package-json.js +58 -32
- package/dist/esm-node/ultramodern-workspace/patch-inventory.js +67 -0
- package/dist/esm-node/ultramodern-workspace/policy.js +2 -564
- package/dist/esm-node/ultramodern-workspace/public-surface.js +3 -3
- package/dist/esm-node/ultramodern-workspace/routes.js +2 -11
- 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 +22 -37
- package/dist/esm-node/ultramodern-workspace/tsconfigs.js +9 -3
- 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 +287 -0
- package/dist/esm-node/ultramodern-workspace/versions.js +74 -29
- package/dist/esm-node/ultramodern-workspace/workspace-artifact-ownership.js +129 -0
- package/dist/esm-node/ultramodern-workspace/workspace-script-plan.js +35 -27
- package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +10 -190
- package/dist/esm-node/ultramodern-workspace/workspace-validation-contract.js +65 -376
- package/dist/esm-node/ultramodern-workspace/write-app.js +18 -10
- package/dist/esm-node/ultramodern-workspace/write-workspace.js +25 -52
- package/dist/esm-node/ultramodern-workspace/zerops.js +2 -0
- package/dist/types/cli/project-setup.d.ts +1 -1
- package/dist/types/locale/en.d.ts +1 -0
- package/dist/types/locale/index.d.ts +2 -0
- package/dist/types/locale/zh.d.ts +1 -0
- package/dist/types/ultramodern-package-source.d.ts +2 -4
- 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 +4 -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 +0 -2
- package/dist/types/ultramodern-tooling/config/load.d.ts +10 -2
- package/dist/types/ultramodern-tooling/config/metadata.d.ts +2 -2
- package/dist/types/ultramodern-tooling/config/normalize.d.ts +19 -19
- package/dist/types/ultramodern-tooling/config/types.d.ts +2 -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/shell-files.d.ts +7 -2
- package/dist/types/ultramodern-workspace/add-vertical/transaction.d.ts +32 -6
- package/dist/types/ultramodern-workspace/add-vertical/workspace-state.d.ts +10 -7
- package/dist/types/ultramodern-workspace/add-vertical.d.ts +1 -1
- package/dist/types/ultramodern-workspace/api/client.d.ts +3 -1
- package/dist/types/ultramodern-workspace/api/rpc.d.ts +1 -1
- package/dist/types/ultramodern-workspace/api/service.d.ts +3 -1
- package/dist/types/ultramodern-workspace/api/shared.d.ts +2 -0
- package/dist/types/ultramodern-workspace/backend-federation.d.ts +1 -1
- package/dist/types/ultramodern-workspace/contracts.d.ts +1 -3
- package/dist/types/ultramodern-workspace/delivery-unit-schema/surfaces.d.ts +17 -0
- package/dist/types/ultramodern-workspace/delivery-unit-schema/types.d.ts +3 -68
- package/dist/types/ultramodern-workspace/delivery-unit-stamp.d.ts +2 -2
- package/dist/types/ultramodern-workspace/delivery-unit.d.ts +3 -3
- package/dist/types/ultramodern-workspace/demo-components.d.ts +0 -3
- package/dist/types/ultramodern-workspace/descriptors.d.ts +0 -1
- package/dist/types/ultramodern-workspace/fs-io.d.ts +1 -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/exposes.d.ts +11 -0
- package/dist/types/ultramodern-workspace/mf-validation/path-utils.d.ts +2 -3
- package/dist/types/ultramodern-workspace/mf-validation/syntax.d.ts +4 -3
- package/dist/types/ultramodern-workspace/mf-validation/types.d.ts +1 -0
- package/dist/types/ultramodern-workspace/mf-validation.d.ts +1 -0
- package/dist/types/ultramodern-workspace/module-federation/config.d.ts +1 -1
- package/dist/types/ultramodern-workspace/module-federation/reexport-module.d.ts +2 -2
- package/dist/types/ultramodern-workspace/package-json.d.ts +1 -1
- package/dist/types/ultramodern-workspace/patch-inventory.d.ts +10 -0
- package/dist/types/ultramodern-workspace/policy.d.ts +31 -209
- package/dist/types/ultramodern-workspace/routes.d.ts +6 -1
- package/dist/types/ultramodern-workspace/shells.d.ts +2 -15
- package/dist/types/ultramodern-workspace/tooling-command-catalog.d.ts +8 -7
- package/dist/types/ultramodern-workspace/types.d.ts +9 -9
- 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 +73 -28
- package/dist/types/ultramodern-workspace/workspace-artifact-ownership.d.ts +18 -0
- package/dist/types/ultramodern-workspace/workspace-script-plan.d.ts +4 -5
- package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +8 -13
- 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 +18 -11
- package/{template-workspace/.modernjs/release-cohort.json → release-cohort.json} +88 -40
- package/template-workspace/.codex/skills/rspack-tracing/scripts/analyze_trace.js +1 -1
- package/template-workspace/README.md.handlebars +20 -35
- package/template-workspace/oxfmt.config.ts +3 -0
- package/template-workspace/pnpm-workspace.yaml.handlebars +0 -24
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +4 -4
- package/templates/packages/shared-contracts-index.ts +4 -332
- package/templates/workspace/apps/modern.config.shared-module-federation.ts.handlebars +10 -0
- package/templates/workspace/apps/modern.config.ts.handlebars +13 -27
- package/templates/workspace/apps/shared/src/routes/ultramodern-route-metadata.ts.handlebars +21 -11
- 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 +4 -6
- package/templates/workspace/apps/shell-super-app/src/routes/vertical-components.imports.tsx.handlebars +2 -2
- 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/apps/shell-super-app/src/routes/vertical-components.worker.imports.tsx.handlebars +1 -1
- package/templates/workspace-scripts/generate-node-backend-federation.mjs +68 -77
- package/templates/workspace-scripts/generate-public-surface-assets.mjs +107 -207
- package/templates/workspace-scripts/materialize-zerops-runtime.mjs +12 -19
- package/templates/workspace-scripts/proof-cloudflare-version.mjs +53 -444
- package/templates/workspace-scripts/proof-node-backend-federation.mjs +212 -91
- package/templates/workspace-scripts/{proof-workerd-ssr.mts → proof-workerd-ssr.mjs} +434 -239
- package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +25 -7
- package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +45 -147
- package/templates/workspace-scripts/ultramodern-typecheck.mjs +1 -1
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/api-metadata.cjs +0 -212
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/declaration-patches.cjs +0 -47
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/dependency-usage.cjs +0 -112
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-backend-federation.cjs +0 -55
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.cjs +0 -102
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.cjs +0 -195
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxfmt.cjs +0 -146
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxlint.cjs +0 -110
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-removal.cjs +0 -67
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.cjs +0 -307
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-zerops.cjs +0 -58
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts.cjs +0 -71
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/generated-ui-source.cjs +0 -151
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/help.cjs +0 -67
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/install.cjs +0 -52
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/io.cjs +0 -447
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.cjs +0 -323
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/package-source.cjs +0 -87
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.cjs +0 -612
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.cjs +0 -376
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/policy-constants.cjs +0 -73
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/react-router-retirement.cjs +0 -215
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.cjs +0 -135
- package/dist/cjs/ultramodern-tooling/commands/migrate-strict-effect.cjs +0 -454
- package/dist/cjs/ultramodern-tooling/config/constants.cjs +0 -48
- package/dist/cjs/ultramodern-tooling/config/synthesize.cjs +0 -157
- package/dist/cjs/ultramodern-workspace/delivery-unit-schema/up-projection.cjs +0 -188
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/api-metadata.js +0 -154
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/declaration-patches.js +0 -9
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/dependency-usage.js +0 -63
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-backend-federation.js +0 -7
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.js +0 -47
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.js +0 -146
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxfmt.js +0 -97
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxlint.js +0 -61
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-removal.js +0 -16
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.js +0 -258
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-zerops.js +0 -10
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts.js +0 -8
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-patches.js +0 -15
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/generated-ui-source.js +0 -100
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/help.js +0 -29
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/install.js +0 -14
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/io.js +0 -385
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.js +0 -276
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/package-source.js +0 -49
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.js +0 -559
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.js +0 -329
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/policy-constants.js +0 -19
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/react-router-retirement.js +0 -148
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.js +0 -80
- package/dist/esm/ultramodern-tooling/commands/migrate-strict-effect.js +0 -405
- package/dist/esm/ultramodern-tooling/config/constants.js +0 -4
- package/dist/esm/ultramodern-tooling/config/synthesize.js +0 -108
- package/dist/esm/ultramodern-workspace/delivery-unit-schema/up-projection.js +0 -129
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/api-metadata.js +0 -155
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/declaration-patches.js +0 -10
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/dependency-usage.js +0 -64
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-backend-federation.js +0 -8
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.js +0 -48
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.js +0 -147
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxfmt.js +0 -98
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxlint.js +0 -62
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-removal.js +0 -17
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.js +0 -259
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-zerops.js +0 -11
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts.js +0 -9
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-patches.js +0 -16
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/generated-ui-source.js +0 -101
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/help.js +0 -30
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/install.js +0 -15
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/io.js +0 -386
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.js +0 -277
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/package-source.js +0 -50
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.js +0 -560
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.js +0 -330
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/policy-constants.js +0 -20
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/react-router-retirement.js +0 -149
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.js +0 -81
- package/dist/esm-node/ultramodern-tooling/commands/migrate-strict-effect.js +0 -406
- package/dist/esm-node/ultramodern-tooling/config/constants.js +0 -5
- package/dist/esm-node/ultramodern-tooling/config/synthesize.js +0 -109
- package/dist/esm-node/ultramodern-workspace/delivery-unit-schema/up-projection.js +0 -130
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/api-metadata.d.ts +0 -12
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/declaration-patches.d.ts +0 -4
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/dependency-usage.d.ts +0 -1
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-backend-federation.d.ts +0 -3
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-build-identity.d.ts +0 -5
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-modern-configs.d.ts +0 -3
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxfmt.d.ts +0 -2
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-oxlint.d.ts +0 -2
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-removal.d.ts +0 -4
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-typescript.d.ts +0 -3
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts-zerops.d.ts +0 -3
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-artifacts.d.ts +0 -8
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-patches.d.ts +0 -3
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/generated-ui-source.d.ts +0 -3
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/help.d.ts +0 -1
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/install.d.ts +0 -2
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/io.d.ts +0 -26
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/package-cohort.d.ts +0 -25
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/package-source.d.ts +0 -3
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/pnpm-policy.d.ts +0 -26
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/pnpm-yaml.d.ts +0 -26
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/policy-constants.d.ts +0 -6
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/react-router-retirement.d.ts +0 -37
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect/toolchain-pins.d.ts +0 -4
- package/dist/types/ultramodern-tooling/commands/migrate-strict-effect.d.ts +0 -2
- package/dist/types/ultramodern-tooling/config/constants.d.ts +0 -3
- package/dist/types/ultramodern-tooling/config/synthesize.d.ts +0 -5
- package/dist/types/ultramodern-workspace/delivery-unit-schema/up-projection.d.ts +0 -189
- 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 -26
- 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/workspace-scripts/check-ultramodern-api-boundaries.mts +0 -779
- package/templates/workspace-scripts/check-ultramodern-i18n-boundaries.mts +0 -9
- package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +0 -4109
|
@@ -3,14 +3,12 @@ import crypto from "node:crypto";
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import http from "node:http";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import {
|
|
6
|
+
import { parseEnv } from "node:util";
|
|
7
|
+
import { convertV4MiniflareOptions, Log, LogLevel, Miniflare } from "miniflare";
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
workspaceRoot,
|
|
12
|
-
".codex/reports/cloudflare-workerd-ssr/composition-proof.json",
|
|
13
|
-
);
|
|
9
|
+
const reportRelativePath = ".codex/reports/cloudflare-workerd-ssr/composition-proof.json";
|
|
10
|
+
// Mirrors the public website quality gate default for server-rendered HTML.
|
|
11
|
+
const defaultSsrHtmlMaxBytes = 250_000;
|
|
14
12
|
|
|
15
13
|
const assert = (condition, message) => {
|
|
16
14
|
if (!condition) {
|
|
@@ -120,69 +118,164 @@ const bindExecutedModule = (app, envelope, module) => {
|
|
|
120
118
|
};
|
|
121
119
|
};
|
|
122
120
|
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
121
|
+
const normalizeRoutes = (routes) => [
|
|
122
|
+
...new Set(
|
|
123
|
+
(Array.isArray(routes) ? routes : []).filter(
|
|
124
|
+
(route) => typeof route === "string" && route.startsWith("/"),
|
|
125
|
+
),
|
|
126
|
+
),
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
// The generator's distributed SSR contract: every expose except the page
|
|
130
|
+
// route is a fragment a host can server-render (see distributedSsrExposes).
|
|
131
|
+
const declaresDistributedSsrExpose = (rawApp) =>
|
|
132
|
+
Array.isArray(rawApp?.moduleFederation?.exposes) &&
|
|
133
|
+
rawApp.moduleFederation.exposes.some(
|
|
134
|
+
(expose) => typeof expose === "string" && expose !== "./Route",
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Derive what the proof must execute from topology/reference-topology.json.
|
|
139
|
+
*
|
|
140
|
+
* - `ssrRoutes`: every route the app itself must server-render on its own
|
|
141
|
+
* Worker (`cloudflare.routes.ssr` plus `cloudflare.distributedSsrProofRoutes`).
|
|
142
|
+
* - `serverRenderedRoutes`: shell routes that must compose distributed SSR
|
|
143
|
+
* boundaries. `cloudflare.distributedSsrProofRoutes` declares them; without
|
|
144
|
+
* it the shell's `cloudflare.routes.ssr` is the composition route. An empty
|
|
145
|
+
* array declares a shell whose MicroVerticals load on the client only.
|
|
146
|
+
* - `serverRenderedRemoteIds`: shell `verticalRefs` whose topology declares a
|
|
147
|
+
* distributed SSR expose, i.e. the boundaries the shell must server-render.
|
|
148
|
+
*/
|
|
149
|
+
export const planWorkerdSsrProof = (topology) => {
|
|
144
150
|
assert(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
);
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
151
|
+
topology?.schemaVersion === 1 && topology.shell && Array.isArray(topology.verticals),
|
|
152
|
+
"Invalid topology/reference-topology.json",
|
|
153
|
+
);
|
|
154
|
+
const rawApps = [topology.shell, ...topology.verticals, ...(topology.shells ?? [])];
|
|
155
|
+
const rawAppsById = new Map(rawApps.map((rawApp) => [String(rawApp.id), rawApp]));
|
|
156
|
+
const plans = rawApps.map((rawApp) => {
|
|
157
|
+
const id = String(rawApp.id);
|
|
158
|
+
const kind = rawApp.kind === "vertical" ? "vertical" : "shell";
|
|
159
|
+
const cloudflare = rawApp.cloudflare ?? {};
|
|
160
|
+
const declaredServerRenderedRoutes = cloudflare.distributedSsrProofRoutes;
|
|
161
|
+
const rawVerticalRefs = rawApp.verticalRefs ?? rawApp.moduleFederation?.verticalRefs;
|
|
162
|
+
const verticalRefs = Array.isArray(rawVerticalRefs)
|
|
163
|
+
? rawVerticalRefs.filter((ref) => typeof ref === "string")
|
|
164
|
+
: [];
|
|
165
|
+
for (const ref of verticalRefs) {
|
|
166
|
+
assert(rawAppsById.has(ref), `${id} references missing MicroVertical ${ref}`);
|
|
167
|
+
}
|
|
168
|
+
const serverRenderedRemoteIds =
|
|
169
|
+
kind === "shell"
|
|
170
|
+
? verticalRefs.filter((ref) => declaresDistributedSsrExpose(rawAppsById.get(ref)))
|
|
171
|
+
: [];
|
|
172
|
+
assert(
|
|
173
|
+
declaredServerRenderedRoutes === undefined ||
|
|
174
|
+
(Array.isArray(declaredServerRenderedRoutes) &&
|
|
175
|
+
normalizeRoutes(declaredServerRenderedRoutes).length ===
|
|
176
|
+
declaredServerRenderedRoutes.length),
|
|
177
|
+
`${id} cloudflare.distributedSsrProofRoutes must list unique absolute routes`,
|
|
178
|
+
);
|
|
179
|
+
const serverRenderedRoutes =
|
|
180
|
+
kind !== "shell"
|
|
181
|
+
? []
|
|
182
|
+
: Array.isArray(declaredServerRenderedRoutes)
|
|
183
|
+
? normalizeRoutes(declaredServerRenderedRoutes)
|
|
184
|
+
: normalizeRoutes([cloudflare.routes?.ssr]);
|
|
185
|
+
const ssrRoutes = normalizeRoutes([
|
|
186
|
+
...(Array.isArray(declaredServerRenderedRoutes) ? declaredServerRenderedRoutes : []),
|
|
187
|
+
cloudflare.routes?.ssr,
|
|
188
|
+
]);
|
|
189
|
+
const apiOnly = rawApp.surfaceProfile === "api-only";
|
|
190
|
+
assert(
|
|
191
|
+
apiOnly || normalizeRoutes([cloudflare.routes?.ssr]).length === 1,
|
|
192
|
+
`${id} declares no cloudflare.routes.ssr to prove on its own Worker`,
|
|
193
|
+
);
|
|
194
|
+
return {
|
|
195
|
+
apiOnly,
|
|
196
|
+
id,
|
|
197
|
+
kind,
|
|
198
|
+
serverRenderedRemoteIds:
|
|
199
|
+
serverRenderedRoutes.length > 0 ? serverRenderedRemoteIds : [],
|
|
200
|
+
serverRenderedRoutes,
|
|
201
|
+
ssrRoutes,
|
|
202
|
+
verticalRefs,
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
assert(
|
|
206
|
+
plans.some((plan) => plan.kind === "shell"),
|
|
207
|
+
"Workerd SSR proof requires at least one shell",
|
|
208
|
+
);
|
|
209
|
+
assert(
|
|
210
|
+
plans.some((plan) => plan.kind === "vertical"),
|
|
211
|
+
"Workerd SSR proof requires at least one MicroVertical or headless API",
|
|
212
|
+
);
|
|
213
|
+
return { plans, rawApps };
|
|
214
|
+
};
|
|
157
215
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
verticalRefs: Array.isArray(moduleFederation.verticalRefs)
|
|
164
|
-
? moduleFederation.verticalRefs.filter((ref) => typeof ref === "string")
|
|
165
|
-
: [],
|
|
166
|
-
apiPrefix:
|
|
167
|
-
typeof rawApp.api?.prefix === "string"
|
|
168
|
-
? rawApp.api.prefix.replace(/\/+$/u, "")
|
|
169
|
-
: undefined,
|
|
170
|
-
proofRoutes: proofRoutes.length > 0 ? proofRoutes : defaultProofRoutes,
|
|
171
|
-
jsonSmokeChecks: Array.isArray(rawApp.deploy?.cloudflare?.jsonSmokeChecks)
|
|
172
|
-
? rawApp.deploy.cloudflare.jsonSmokeChecks
|
|
173
|
-
: [],
|
|
174
|
-
...executedEnvelope,
|
|
175
|
-
outputRoot,
|
|
176
|
-
port: Number(rawApp.port),
|
|
177
|
-
wrangler,
|
|
178
|
-
};
|
|
179
|
-
});
|
|
216
|
+
const readDevVars = (outputRoot) => {
|
|
217
|
+
// Wrangler's local-secret convention: `.dev.vars` beside the executed config.
|
|
218
|
+
const devVarsPath = path.join(outputRoot, ".dev.vars");
|
|
219
|
+
return fs.existsSync(devVarsPath) ? parseEnv(fs.readFileSync(devVarsPath, "utf8")) : {};
|
|
220
|
+
};
|
|
180
221
|
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
222
|
+
const loadApps = (workspaceRoot, topology, overlay) => {
|
|
223
|
+
const { plans, rawApps } = planWorkerdSsrProof(topology);
|
|
224
|
+
return rawApps.map((rawApp, index) => {
|
|
225
|
+
const plan = plans[index];
|
|
226
|
+
const hasApiSurface = plan.kind === "vertical" && rawApp.surfaceProfile !== "ui-only";
|
|
227
|
+
const appPath = rawApp.path;
|
|
228
|
+
assert(typeof appPath === "string" && appPath.length > 0, `${plan.id} topology path is missing`);
|
|
229
|
+
const outputRoot = path.join(workspaceRoot, appPath, ".output");
|
|
230
|
+
const wranglerPath = path.join(outputRoot, "wrangler.json");
|
|
231
|
+
assert(
|
|
232
|
+
fs.existsSync(wranglerPath),
|
|
233
|
+
`${plan.id} Cloudflare output is missing; run pnpm cloudflare:build first`,
|
|
234
|
+
);
|
|
235
|
+
const wrangler = readJson(wranglerPath);
|
|
236
|
+
const executedEnvelope = hasApiSurface
|
|
237
|
+
? readExecutionEnvelope(plan.id, outputRoot, rawApp.deliveryUnit?.unitId)
|
|
238
|
+
: {};
|
|
239
|
+
const domain = rawApp.domain ?? rawApp.id;
|
|
240
|
+
const portEnv = rawApp.portEnv ??
|
|
241
|
+
(plan.kind === "vertical"
|
|
242
|
+
? `VERTICAL_${String(domain).replace(/[^a-zA-Z0-9]/gu, "_").toUpperCase()}_PORT`
|
|
243
|
+
: undefined);
|
|
244
|
+
assert(typeof portEnv === "string" && portEnv.length > 0, `${plan.id} has no declared port environment name`);
|
|
245
|
+
const configuredPort = process.env[portEnv];
|
|
246
|
+
const port = configuredPort === undefined
|
|
247
|
+
? Number(overlay.ports?.[plan.id])
|
|
248
|
+
: Number(configuredPort);
|
|
249
|
+
assert(
|
|
250
|
+
Number.isInteger(port) && port > 0 && port <= 65535,
|
|
251
|
+
`${plan.id} has an invalid local proof port from ${portEnv}`,
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
...plan,
|
|
256
|
+
hasApiSurface,
|
|
257
|
+
path: appPath,
|
|
258
|
+
apiPrefix:
|
|
259
|
+
typeof rawApp.api?.bff?.prefix === "string"
|
|
260
|
+
? rawApp.api.bff.prefix.replace(/\/+$/u, "")
|
|
261
|
+
: undefined,
|
|
262
|
+
apiProtocol: rawApp.api?.protocol ?? "rest",
|
|
263
|
+
rpcRoute: rawApp.api?.protocol === "rpc" ? rawApp.cloudflare?.routes?.rpc : undefined,
|
|
264
|
+
ssrHtmlMaxBytes: Number(
|
|
265
|
+
rawApp.cloudflare?.qualityGates?.budgets?.ssrHtmlMaxBytes ??
|
|
266
|
+
defaultSsrHtmlMaxBytes,
|
|
267
|
+
),
|
|
268
|
+
jsonSmokeChecks: Array.isArray(rawApp.cloudflare?.jsonSmokeChecks)
|
|
269
|
+
? rawApp.cloudflare.jsonSmokeChecks
|
|
270
|
+
: [],
|
|
271
|
+
...executedEnvelope,
|
|
272
|
+
devVars: readDevVars(outputRoot),
|
|
273
|
+
outputRoot,
|
|
274
|
+
port,
|
|
275
|
+
wrangler,
|
|
276
|
+
};
|
|
277
|
+
});
|
|
278
|
+
};
|
|
186
279
|
|
|
187
280
|
const workerName = (app) => {
|
|
188
281
|
assert(
|
|
@@ -192,7 +285,7 @@ const workerName = (app) => {
|
|
|
192
285
|
return app.wrangler.name;
|
|
193
286
|
};
|
|
194
287
|
|
|
195
|
-
const createWorkerOptions = (app, extra = {}) => {
|
|
288
|
+
const createWorkerOptions = (app, workspaceRoot, extra = {}) => {
|
|
196
289
|
const main = typeof app.wrangler.main === "string" ? app.wrangler.main : "server/index.mjs";
|
|
197
290
|
const assets =
|
|
198
291
|
app.wrangler.assets && typeof app.wrangler.assets === "object" ? app.wrangler.assets : {};
|
|
@@ -222,7 +315,7 @@ const createWorkerOptions = (app, extra = {}) => {
|
|
|
222
315
|
const apiBackend = app.envelope?.surfaces?.apiBackend;
|
|
223
316
|
const ssr = app.envelope?.surfaces?.ssr;
|
|
224
317
|
assert(
|
|
225
|
-
app.
|
|
318
|
+
!app.hasApiSurface ||
|
|
226
319
|
(Array.isArray(apiBackend) &&
|
|
227
320
|
apiBackend.length > 0 &&
|
|
228
321
|
apiBackend.every((logicalPath) =>
|
|
@@ -231,13 +324,16 @@ const createWorkerOptions = (app, extra = {}) => {
|
|
|
231
324
|
`${app.id} BFF worker surface is not selected by Miniflare`,
|
|
232
325
|
);
|
|
233
326
|
assert(
|
|
234
|
-
app.
|
|
327
|
+
!app.hasApiSurface ||
|
|
235
328
|
(Array.isArray(ssr) &&
|
|
236
|
-
|
|
329
|
+
(app.apiOnly
|
|
330
|
+
? ssr.length === 0 && mainLogicalPath === "server/index.mjs"
|
|
331
|
+
: ssr.includes(mainLogicalPath)) &&
|
|
237
332
|
boundModules.every((module) =>
|
|
238
|
-
[...ssr, ...(apiBackend ?? [])
|
|
333
|
+
[...ssr, ...(apiBackend ?? []), ...(app.apiOnly ? [mainLogicalPath] : [])]
|
|
334
|
+
.includes(module.logicalPath),
|
|
239
335
|
)),
|
|
240
|
-
`${app.id} Miniflare main/SSR modules
|
|
336
|
+
`${app.id} Miniflare main/SSR modules do not match its envelope surfaces`,
|
|
241
337
|
);
|
|
242
338
|
|
|
243
339
|
return {
|
|
@@ -259,8 +355,8 @@ const createWorkerOptions = (app, extra = {}) => {
|
|
|
259
355
|
modulesRoot: app.outputRoot,
|
|
260
356
|
compatibilityDate: app.wrangler.compatibility_date,
|
|
261
357
|
compatibilityFlags: app.wrangler.compatibility_flags,
|
|
358
|
+
bindings: { ...app.wrangler.vars, ...app.devVars },
|
|
262
359
|
assets: {
|
|
263
|
-
workerName: workerName(app),
|
|
264
360
|
binding: typeof assets.binding === "string" ? assets.binding : "ASSETS",
|
|
265
361
|
directory: path.resolve(app.outputRoot, directory),
|
|
266
362
|
routerConfig: {
|
|
@@ -295,7 +391,10 @@ const findReleaseMarkers = (value, markers = []) => {
|
|
|
295
391
|
return markers;
|
|
296
392
|
};
|
|
297
393
|
|
|
298
|
-
const
|
|
394
|
+
const jsonPathValue = (value, key) =>
|
|
395
|
+
String(key).split(".").reduce((current, segment) => current?.[segment], value);
|
|
396
|
+
|
|
397
|
+
const responseEvidence = async (app, response, check) => {
|
|
299
398
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
300
399
|
let body;
|
|
301
400
|
try {
|
|
@@ -303,19 +402,36 @@ const responseEvidence = async (app, response) => {
|
|
|
303
402
|
} catch (error) {
|
|
304
403
|
throw new Error(`${app.id} API response is not JSON: ${error.message}`);
|
|
305
404
|
}
|
|
306
|
-
const markers = findReleaseMarkers(body);
|
|
307
|
-
const marker = markers.find(
|
|
308
|
-
(candidate) =>
|
|
309
|
-
candidate.appId === app.id &&
|
|
310
|
-
candidate.build === app.envelope.identity.buildMarker &&
|
|
311
|
-
candidate.version === app.envelope.identity.releaseVersion,
|
|
312
|
-
);
|
|
313
|
-
assert(marker, `${app.id} API response is not tied to its executed release identity`);
|
|
314
405
|
assert(response.ok, `${app.id} API response returned HTTP ${response.status}`);
|
|
406
|
+
let marker;
|
|
407
|
+
if (app.apiProtocol === "rpc") {
|
|
408
|
+
assert(
|
|
409
|
+
typeof app.rpcRoute === "string" && app.rpcRoute.startsWith("/") &&
|
|
410
|
+
check.route === app.rpcRoute && String(check.method).toUpperCase() === "POST" &&
|
|
411
|
+
check.body?.jsonrpc === "2.0" && check.body?.id !== undefined &&
|
|
412
|
+
body?.jsonrpc === "2.0" && body.id === check.body.id,
|
|
413
|
+
`${app.id} RPC response does not match its declared POST request`,
|
|
414
|
+
);
|
|
415
|
+
assert(
|
|
416
|
+
check.expect && Object.keys(check.expect).length > 0 &&
|
|
417
|
+
Object.entries(check.expect).every(([key, expected]) =>
|
|
418
|
+
JSON.stringify(jsonPathValue(body, key)) === JSON.stringify(expected)),
|
|
419
|
+
`${app.id} RPC response does not satisfy its declared JSON smoke check`,
|
|
420
|
+
);
|
|
421
|
+
} else {
|
|
422
|
+
const markers = findReleaseMarkers(body);
|
|
423
|
+
marker = markers.find(
|
|
424
|
+
(candidate) =>
|
|
425
|
+
candidate.appId === app.id &&
|
|
426
|
+
candidate.build === app.envelope.identity.buildMarker &&
|
|
427
|
+
candidate.version === app.envelope.identity.releaseVersion,
|
|
428
|
+
);
|
|
429
|
+
assert(marker, `${app.id} API response is not tied to its executed release identity`);
|
|
430
|
+
}
|
|
315
431
|
return {
|
|
316
432
|
bodyBase64: bytes.toString("base64"),
|
|
317
433
|
byteLength: bytes.byteLength,
|
|
318
|
-
releaseMarker: marker,
|
|
434
|
+
...(marker ? { releaseMarker: marker } : {}),
|
|
319
435
|
sha256: sha256(bytes),
|
|
320
436
|
status: response.status,
|
|
321
437
|
};
|
|
@@ -348,9 +464,9 @@ const resolveApiSmokeChecks = (app, shell) => {
|
|
|
348
464
|
return [...uniqueChecks.values()];
|
|
349
465
|
};
|
|
350
466
|
|
|
351
|
-
const runApiProofs = async (miniflare, shell, executionByAppId) => {
|
|
467
|
+
const runApiProofs = async (apps, miniflare, shell, executionByAppId) => {
|
|
352
468
|
const results = [];
|
|
353
|
-
for (const app of apps.filter((candidate) => candidate.
|
|
469
|
+
for (const app of apps.filter((candidate) => candidate.hasApiSurface)) {
|
|
354
470
|
const jsonSmokeChecks = resolveApiSmokeChecks(app, shell);
|
|
355
471
|
assert(
|
|
356
472
|
jsonSmokeChecks.length > 0,
|
|
@@ -359,7 +475,10 @@ const runApiProofs = async (miniflare, shell, executionByAppId) => {
|
|
|
359
475
|
const binding = (shell.wrangler.services ?? []).find(
|
|
360
476
|
(candidate) => candidate.service === workerName(app),
|
|
361
477
|
);
|
|
362
|
-
assert(
|
|
478
|
+
assert(
|
|
479
|
+
binding || (app.apiOnly && !shell.verticalRefs.includes(app.id)),
|
|
480
|
+
`${shell.id} has no service binding for ${app.id}`,
|
|
481
|
+
);
|
|
363
482
|
for (const check of jsonSmokeChecks) {
|
|
364
483
|
const method = String(check.method ?? "GET").toUpperCase();
|
|
365
484
|
const headers = {};
|
|
@@ -373,31 +492,39 @@ const runApiProofs = async (miniflare, shell, executionByAppId) => {
|
|
|
373
492
|
await (
|
|
374
493
|
await miniflare.getWorker(workerName(app))
|
|
375
494
|
).fetch(`https://${workerName(app)}.invalid${check.route}`, init),
|
|
495
|
+
check,
|
|
376
496
|
);
|
|
377
|
-
const throughShell =
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
)
|
|
497
|
+
const throughShell = binding
|
|
498
|
+
? await responseEvidence(
|
|
499
|
+
app,
|
|
500
|
+
await miniflare.dispatchFetch(
|
|
501
|
+
`https://${workerName(shell)}.invalid${check.route}`,
|
|
502
|
+
init,
|
|
503
|
+
),
|
|
504
|
+
check,
|
|
505
|
+
)
|
|
506
|
+
: undefined;
|
|
507
|
+
if (throughShell) {
|
|
508
|
+
assert(
|
|
509
|
+
direct.sha256 === throughShell.sha256,
|
|
510
|
+
`${app.id} direct and service-binding API responses differ`,
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
const target = {
|
|
514
|
+
appId: app.id,
|
|
515
|
+
envelopeDigest: executionByAppId.get(app.id).envelopeDigest,
|
|
516
|
+
worker: workerName(app),
|
|
517
|
+
};
|
|
388
518
|
results.push({
|
|
389
519
|
appId: app.id,
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
worker: workerName(app),
|
|
395
|
-
},
|
|
520
|
+
shellId: shell.id,
|
|
521
|
+
...(binding
|
|
522
|
+
? { binding: binding.binding, bindingTarget: target, throughShell }
|
|
523
|
+
: { directTarget: target }),
|
|
396
524
|
direct,
|
|
397
525
|
id: check.id,
|
|
398
526
|
method,
|
|
399
527
|
route: check.route,
|
|
400
|
-
throughShell,
|
|
401
528
|
});
|
|
402
529
|
}
|
|
403
530
|
}
|
|
@@ -427,6 +554,7 @@ const closeServer = (server) =>
|
|
|
427
554
|
});
|
|
428
555
|
|
|
429
556
|
const startWorkerdTargetServers = async (
|
|
557
|
+
apps,
|
|
430
558
|
miniflare,
|
|
431
559
|
failedServices,
|
|
432
560
|
workerConfigurations,
|
|
@@ -440,16 +568,9 @@ const startWorkerdTargetServers = async (
|
|
|
440
568
|
Number.isInteger(app.port) && app.port > 0,
|
|
441
569
|
`${app.id} requires a configured local port for all-workerd browser proof`,
|
|
442
570
|
);
|
|
443
|
-
// Miniflare 4 currently assigns one shared assets storage service inside
|
|
444
|
-
// a multi-worker instance. Keep the shell composition runtime intact,
|
|
445
|
-
// but give every directly browsed MicroVertical its own runtime so its
|
|
446
|
-
// static assets and MF manifest cannot resolve from a sibling Worker.
|
|
447
571
|
const runtime =
|
|
448
572
|
app.kind === "vertical"
|
|
449
|
-
?
|
|
450
|
-
log: new Log(LogLevel.ERROR),
|
|
451
|
-
workers: [workerConfigurations[index]],
|
|
452
|
-
})
|
|
573
|
+
? createIsolatedRuntime(workerConfigurations[index])
|
|
453
574
|
: miniflare;
|
|
454
575
|
if (runtime !== miniflare) {
|
|
455
576
|
isolatedVerticalRuntimes.push(runtime);
|
|
@@ -597,9 +718,22 @@ const decodeDistributedSsrFragmentRequest = (request) => {
|
|
|
597
718
|
};
|
|
598
719
|
};
|
|
599
720
|
|
|
721
|
+
// Miniflare assigns one shared assets storage service inside a multi-worker
|
|
722
|
+
// instance. Keep the shell composition runtime intact, but give every directly
|
|
723
|
+
// requested MicroVertical its own runtime so its static assets and MF manifest
|
|
724
|
+
// cannot resolve from a sibling Worker. Its service bindings still resolve
|
|
725
|
+
// through the shared composition runtime.
|
|
726
|
+
const createIsolatedRuntime = (workerOptions) =>
|
|
727
|
+
new Miniflare(
|
|
728
|
+
convertV4MiniflareOptions({
|
|
729
|
+
log: new Log(LogLevel.ERROR),
|
|
730
|
+
workers: [workerOptions],
|
|
731
|
+
}),
|
|
732
|
+
);
|
|
733
|
+
|
|
600
734
|
const createServiceBindings = (
|
|
601
735
|
caller,
|
|
602
|
-
{ apiBindingRequests, failedServices, fragmentBindingRequests },
|
|
736
|
+
{ apiBindingRequests, composition, failedServices, fragmentBindingRequests },
|
|
603
737
|
) => {
|
|
604
738
|
const services = Array.isArray(caller.wrangler.services) ? caller.wrangler.services : [];
|
|
605
739
|
return Object.fromEntries(
|
|
@@ -610,7 +744,7 @@ const createServiceBindings = (
|
|
|
610
744
|
);
|
|
611
745
|
return [
|
|
612
746
|
service.binding,
|
|
613
|
-
async (request
|
|
747
|
+
async (request) => {
|
|
614
748
|
if (failedServices.has(service.service)) {
|
|
615
749
|
throw new Error(
|
|
616
750
|
`Injected unavailable service binding ${service.binding} -> ${service.service}`,
|
|
@@ -647,7 +781,7 @@ const createServiceBindings = (
|
|
|
647
781
|
};
|
|
648
782
|
apiBindingRequests.push(apiBindingRequest);
|
|
649
783
|
}
|
|
650
|
-
const target = await miniflare.getWorker(service.service);
|
|
784
|
+
const target = await composition.miniflare.getWorker(service.service);
|
|
651
785
|
const response = await target.fetch(request);
|
|
652
786
|
if (apiBindingRequest !== undefined) {
|
|
653
787
|
apiBindingRequest.response = {
|
|
@@ -662,11 +796,96 @@ const createServiceBindings = (
|
|
|
662
796
|
);
|
|
663
797
|
};
|
|
664
798
|
|
|
665
|
-
const
|
|
666
|
-
const
|
|
667
|
-
|
|
799
|
+
const proveRoute = async (app, route, fetchRoute, apps, recorders) => {
|
|
800
|
+
const apiBindingRequestStart = recorders.apiBindingRequests.length;
|
|
801
|
+
const fragmentBindingRequestStart = recorders.fragmentBindingRequests.length;
|
|
802
|
+
const outboundRequestStart = recorders.outboundRequests.length;
|
|
803
|
+
const response = await fetchRoute(`https://${workerName(app)}.invalid${route}`, {
|
|
804
|
+
headers: { accept: "text/html" },
|
|
805
|
+
});
|
|
806
|
+
const html = await response.text();
|
|
807
|
+
const htmlBytes = Buffer.byteLength(html);
|
|
808
|
+
const routeOutboundRequests = recorders.outboundRequests.slice(outboundRequestStart);
|
|
809
|
+
assert(
|
|
810
|
+
response.status === 200,
|
|
811
|
+
`${app.id} returned HTTP ${response.status} for ${route} in workerd; outbound requests: ${JSON.stringify(routeOutboundRequests)}; response: ${html.slice(0, 500)}`,
|
|
812
|
+
);
|
|
813
|
+
assert(
|
|
814
|
+
response.headers.get("content-type")?.includes("text/html") === true,
|
|
815
|
+
`${app.id} did not return HTML for ${route} in workerd`,
|
|
816
|
+
);
|
|
817
|
+
// Runaway server rendering (for example a worker re-rendering its own
|
|
818
|
+
// route data requests) can still end in an oversized document.
|
|
819
|
+
assert(
|
|
820
|
+
htmlBytes <= app.ssrHtmlMaxBytes,
|
|
821
|
+
`${app.id} rendered ${htmlBytes} bytes for ${route} in workerd, over the ${app.ssrHtmlMaxBytes} byte ssrHtmlMaxBytes budget`,
|
|
822
|
+
);
|
|
823
|
+
assert(
|
|
824
|
+
!html.includes('data-modern-distributed-ssr-status="degraded"'),
|
|
825
|
+
`${app.id} rendered a degraded MicroVertical fallback for ${route} in workerd`,
|
|
826
|
+
);
|
|
827
|
+
|
|
828
|
+
const boundaries = collectDistributedBoundaries(html);
|
|
829
|
+
const routeApiBindingRequests = recorders.apiBindingRequests.slice(apiBindingRequestStart);
|
|
830
|
+
const routeFragmentBindingRequests = recorders.fragmentBindingRequests.slice(
|
|
831
|
+
fragmentBindingRequestStart,
|
|
832
|
+
);
|
|
833
|
+
for (const boundary of boundaries) {
|
|
834
|
+
assert(
|
|
835
|
+
boundary.status === "ready",
|
|
836
|
+
`${app.id} did not mark ${boundary.key} as ready for ${route}`,
|
|
837
|
+
);
|
|
838
|
+
assert(
|
|
839
|
+
typeof boundary.buildMarker === "string" && boundary.buildMarker.length > 0,
|
|
840
|
+
`${app.id} ${boundary.key} is missing immutable build provenance`,
|
|
841
|
+
);
|
|
842
|
+
assert(
|
|
843
|
+
/^[a-f\d]{64}$/u.test(boundary.digest ?? ""),
|
|
844
|
+
`${app.id} ${boundary.key} is missing a verified SHA-256 digest`,
|
|
845
|
+
);
|
|
846
|
+
const remote = apps.find((candidate) => candidate.id === boundary.remote);
|
|
847
|
+
assert(remote, `${app.id} rendered unknown remote ${boundary.remote}`);
|
|
848
|
+
const requests = routeFragmentBindingRequests.filter(
|
|
849
|
+
(request) =>
|
|
850
|
+
request.service === workerName(remote) &&
|
|
851
|
+
request.remote === boundary.remote &&
|
|
852
|
+
request.expose === boundary.expose,
|
|
853
|
+
);
|
|
854
|
+
const renderedCount = boundaries.filter(
|
|
855
|
+
(candidate) => candidate.key === boundary.key,
|
|
856
|
+
).length;
|
|
857
|
+
assert(
|
|
858
|
+
requests.length === renderedCount &&
|
|
859
|
+
requests.every((request) => request.pathname.includes("/_mf/fragment/")),
|
|
860
|
+
`${app.id} must compose each ${boundary.key} occurrence through its remote service binding`,
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const stylesheetHrefs = collectStylesheetHrefs(html);
|
|
865
|
+
assert(
|
|
866
|
+
new Set(stylesheetHrefs).size === stylesheetHrefs.length,
|
|
867
|
+
`${app.id} rendered duplicate distributed SSR stylesheets for ${route}`,
|
|
868
|
+
);
|
|
869
|
+
assert(
|
|
870
|
+
!routeOutboundRequests.some(({ url }) => /(?:remoteEntry|\.m?js(?:\?|$))/u.test(url)),
|
|
871
|
+
`${app.id} attempted to fetch remote JavaScript during ${route} server rendering`,
|
|
872
|
+
);
|
|
668
873
|
|
|
669
|
-
|
|
874
|
+
return {
|
|
875
|
+
worker: workerName(app),
|
|
876
|
+
route,
|
|
877
|
+
status: response.status,
|
|
878
|
+
htmlBytes,
|
|
879
|
+
boundaries,
|
|
880
|
+
fragmentBindingRequests: routeFragmentBindingRequests,
|
|
881
|
+
apiBindingRequests: routeApiBindingRequests,
|
|
882
|
+
outboundRequests: routeOutboundRequests,
|
|
883
|
+
stylesheetHrefs,
|
|
884
|
+
degradedBoundaryCount: count(html, 'data-modern-distributed-ssr-status="degraded"'),
|
|
885
|
+
};
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
const writeReport = (reportPath, report) => {
|
|
670
889
|
fs.mkdirSync(path.dirname(reportPath), { recursive: true });
|
|
671
890
|
fs.writeFileSync(
|
|
672
891
|
reportPath,
|
|
@@ -674,10 +893,8 @@ const writeReport = () => {
|
|
|
674
893
|
{
|
|
675
894
|
schemaVersion: 3,
|
|
676
895
|
runtime: "workerd",
|
|
677
|
-
routes: [...new Set(proofs.map((proof) => proof.route))],
|
|
678
|
-
|
|
679
|
-
apiProofs,
|
|
680
|
-
proofs,
|
|
896
|
+
routes: [...new Set(report.proofs.map((proof) => proof.route))],
|
|
897
|
+
...report,
|
|
681
898
|
},
|
|
682
899
|
null,
|
|
683
900
|
2,
|
|
@@ -685,150 +902,125 @@ const writeReport = () => {
|
|
|
685
902
|
);
|
|
686
903
|
};
|
|
687
904
|
|
|
688
|
-
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
905
|
+
const main = async () => {
|
|
906
|
+
const workspaceRoot = path.resolve(process.env.ULTRAMODERN_WORKSPACE_ROOT ?? process.cwd());
|
|
907
|
+
const reportPath = path.join(workspaceRoot, reportRelativePath);
|
|
908
|
+
const keepWorkerd = process.env.ULTRAMODERN_KEEP_WORKERD === "1";
|
|
909
|
+
const apps = loadApps(
|
|
910
|
+
workspaceRoot,
|
|
911
|
+
readJson(path.join(workspaceRoot, "topology/reference-topology.json")),
|
|
912
|
+
readJson(path.join(workspaceRoot, "topology/local-overlays/development.json")),
|
|
913
|
+
);
|
|
914
|
+
const shells = apps.filter((app) => app.kind === "shell");
|
|
915
|
+
assert(!keepWorkerd || shells.length === 1, "Browser workerd proof requires exactly one shell");
|
|
695
916
|
|
|
696
|
-
const
|
|
917
|
+
const recorders = {
|
|
918
|
+
apiBindingRequests: [],
|
|
919
|
+
fragmentBindingRequests: [],
|
|
920
|
+
outboundRequests: [],
|
|
921
|
+
};
|
|
697
922
|
const failedServices = new Set();
|
|
698
|
-
const
|
|
699
|
-
const outboundRequests = [];
|
|
923
|
+
const composition = {};
|
|
700
924
|
const workerConfigurations = apps.map((app) =>
|
|
701
|
-
createWorkerOptions(app, {
|
|
702
|
-
serviceBindings: createServiceBindings(app, {
|
|
703
|
-
apiBindingRequests,
|
|
704
|
-
failedServices,
|
|
705
|
-
fragmentBindingRequests,
|
|
706
|
-
}),
|
|
925
|
+
createWorkerOptions(app, workspaceRoot, {
|
|
926
|
+
serviceBindings: createServiceBindings(app, { ...recorders, composition, failedServices }),
|
|
707
927
|
async outboundService(request) {
|
|
708
928
|
const requestUrl = new URL(request.url);
|
|
709
|
-
outboundRequests.push({ callerId: app.id, url: requestUrl.href });
|
|
929
|
+
recorders.outboundRequests.push({ callerId: app.id, url: requestUrl.href });
|
|
710
930
|
return new Response("External network disabled by SSR proof", {
|
|
711
931
|
status: 502,
|
|
712
932
|
});
|
|
713
933
|
},
|
|
714
934
|
}),
|
|
715
935
|
);
|
|
716
|
-
executions = workerConfigurations.map((configuration) => configuration.executionEvidence);
|
|
717
|
-
const executionByAppId = new Map(
|
|
718
|
-
apps.map((app, index) => [app.id, executions[index]]),
|
|
719
|
-
);
|
|
936
|
+
const executions = workerConfigurations.map((configuration) => configuration.executionEvidence);
|
|
937
|
+
const executionByAppId = new Map(apps.map((app, index) => [app.id, executions[index]]));
|
|
720
938
|
const workers = workerConfigurations.map(
|
|
721
939
|
({ executionEvidence: _executionEvidence, ...configuration }) => configuration,
|
|
722
940
|
);
|
|
723
|
-
const miniflare = new Miniflare(
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
941
|
+
const miniflare = new Miniflare(
|
|
942
|
+
convertV4MiniflareOptions({
|
|
943
|
+
log: new Log(LogLevel.ERROR),
|
|
944
|
+
workers,
|
|
945
|
+
}),
|
|
946
|
+
);
|
|
947
|
+
composition.miniflare = miniflare;
|
|
948
|
+
const report = { executions, apiProofs: [], proofs: [], verticalProofs: [] };
|
|
728
949
|
|
|
729
950
|
try {
|
|
730
|
-
for (const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
const
|
|
734
|
-
const
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
`${shell.id} returned HTTP ${response.status} for ${route} in workerd`,
|
|
742
|
-
);
|
|
743
|
-
assert(
|
|
744
|
-
!html.includes('data-modern-distributed-ssr-status="degraded"'),
|
|
745
|
-
`${shell.id} rendered a degraded MicroVertical fallback for ${route} in workerd`,
|
|
746
|
-
);
|
|
747
|
-
|
|
748
|
-
const boundaries = collectDistributedBoundaries(html);
|
|
749
|
-
assert(
|
|
750
|
-
boundaries.length > 0,
|
|
751
|
-
`${shell.id} rendered no distributed SSR boundaries for ${route}`,
|
|
752
|
-
);
|
|
753
|
-
const routeApiBindingRequests = apiBindingRequests.slice(
|
|
754
|
-
apiBindingRequestStart,
|
|
755
|
-
);
|
|
756
|
-
const routeFragmentBindingRequests = fragmentBindingRequests.slice(
|
|
757
|
-
fragmentBindingRequestStart,
|
|
758
|
-
);
|
|
759
|
-
const routeOutboundRequests = outboundRequests.slice(outboundRequestStart);
|
|
760
|
-
for (const boundary of boundaries) {
|
|
761
|
-
renderedRemoteIds.add(boundary.remote);
|
|
762
|
-
assert(
|
|
763
|
-
boundary.status === "ready",
|
|
764
|
-
`${shell.id} did not mark ${boundary.key} as ready for ${route}`,
|
|
951
|
+
for (const shell of shells) {
|
|
952
|
+
// Shell routes compose declared server-rendered boundaries through the
|
|
953
|
+
// shell's entry Worker exactly as a browser request would.
|
|
954
|
+
const renderedRemoteIds = new Set();
|
|
955
|
+
for (const route of shell.ssrRoutes) {
|
|
956
|
+
const proof = await proveRoute(
|
|
957
|
+
shell,
|
|
958
|
+
route,
|
|
959
|
+
(url, init) => miniflare.dispatchFetch(url, init),
|
|
960
|
+
apps,
|
|
961
|
+
recorders,
|
|
765
962
|
);
|
|
766
963
|
assert(
|
|
767
|
-
|
|
768
|
-
`${shell.id}
|
|
964
|
+
shell.serverRenderedRoutes.length > 0 || proof.boundaries.length === 0,
|
|
965
|
+
`${shell.id} declares client composition but rendered distributed SSR boundaries for ${route}`,
|
|
769
966
|
);
|
|
967
|
+
if (shell.serverRenderedRoutes.includes(route)) {
|
|
968
|
+
assert(
|
|
969
|
+
shell.serverRenderedRemoteIds.length === 0 || proof.boundaries.length > 0,
|
|
970
|
+
`${shell.id} rendered no distributed SSR boundaries for ${route}`,
|
|
971
|
+
);
|
|
972
|
+
for (const boundary of proof.boundaries) {
|
|
973
|
+
renderedRemoteIds.add(boundary.remote);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
report.proofs.push({ shellId: shell.id, ...proof });
|
|
977
|
+
}
|
|
978
|
+
for (const remoteId of shell.serverRenderedRemoteIds) {
|
|
770
979
|
assert(
|
|
771
|
-
|
|
772
|
-
`${shell.id}
|
|
773
|
-
);
|
|
774
|
-
const remote = apps.find((app) => app.id === boundary.remote);
|
|
775
|
-
assert(remote, `${shell.id} rendered unknown remote ${boundary.remote}`);
|
|
776
|
-
const requests = routeFragmentBindingRequests.filter(
|
|
777
|
-
(request) =>
|
|
778
|
-
request.service === workerName(remote) &&
|
|
779
|
-
request.remote === boundary.remote &&
|
|
780
|
-
request.expose === boundary.expose,
|
|
781
|
-
);
|
|
782
|
-
const renderedCount = boundaries.filter(
|
|
783
|
-
(candidate) => candidate.key === boundary.key,
|
|
784
|
-
).length;
|
|
785
|
-
assert(
|
|
786
|
-
requests.length === renderedCount &&
|
|
787
|
-
requests.every((request) => request.pathname.includes("/_mf/fragment/")),
|
|
788
|
-
`${shell.id} must compose each ${boundary.key} occurrence through its remote service binding`,
|
|
980
|
+
renderedRemoteIds.has(remoteId),
|
|
981
|
+
`${shell.id} proof routes are missing independently rendered ${remoteId} content`,
|
|
789
982
|
);
|
|
790
983
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
assert(
|
|
794
|
-
new Set(stylesheetHrefs).size === stylesheetHrefs.length,
|
|
795
|
-
`${shell.id} rendered duplicate distributed SSR stylesheets for ${route}`,
|
|
984
|
+
report.apiProofs.push(
|
|
985
|
+
...(await runApiProofs(apps, miniflare, shell, executionByAppId)),
|
|
796
986
|
);
|
|
797
|
-
assert(
|
|
798
|
-
!routeOutboundRequests.some(({ url }) => /(?:remoteEntry|\.m?js(?:\?|$))/u.test(url)),
|
|
799
|
-
`${shell.id} attempted to fetch remote JavaScript during ${route} server composition`,
|
|
800
|
-
);
|
|
801
|
-
|
|
802
|
-
proofs.push({
|
|
803
|
-
shellId: shell.id,
|
|
804
|
-
worker: workerName(shell),
|
|
805
|
-
route,
|
|
806
|
-
status: response.status,
|
|
807
|
-
boundaries,
|
|
808
|
-
fragmentBindingRequests: routeFragmentBindingRequests,
|
|
809
|
-
apiBindingRequests: routeApiBindingRequests,
|
|
810
|
-
outboundRequests: routeOutboundRequests,
|
|
811
|
-
stylesheetHrefs,
|
|
812
|
-
degradedBoundaryCount: count(html, 'data-modern-distributed-ssr-status="degraded"'),
|
|
813
|
-
});
|
|
814
987
|
}
|
|
815
988
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
989
|
+
// Every UI MicroVertical proves its own Worker at its declared SSR routes,
|
|
990
|
+
// whether or not a shell also composes it on the server.
|
|
991
|
+
for (const [index, vertical] of apps.entries()) {
|
|
992
|
+
if (vertical.kind !== "vertical" || vertical.ssrRoutes.length === 0) {
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
const runtime = createIsolatedRuntime(workers[index]);
|
|
996
|
+
try {
|
|
997
|
+
for (const route of vertical.ssrRoutes) {
|
|
998
|
+
report.verticalProofs.push({
|
|
999
|
+
appId: vertical.id,
|
|
1000
|
+
...(await proveRoute(
|
|
1001
|
+
vertical,
|
|
1002
|
+
route,
|
|
1003
|
+
(url, init) => runtime.dispatchFetch(url, init),
|
|
1004
|
+
apps,
|
|
1005
|
+
recorders,
|
|
1006
|
+
)),
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
} finally {
|
|
1010
|
+
await runtime.dispose();
|
|
1011
|
+
}
|
|
821
1012
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1013
|
+
|
|
1014
|
+
writeReport(reportPath, report);
|
|
1015
|
+
if (keepWorkerd) {
|
|
825
1016
|
const targetServers = await startWorkerdTargetServers(
|
|
1017
|
+
apps,
|
|
826
1018
|
miniflare,
|
|
827
1019
|
failedServices,
|
|
828
1020
|
workers,
|
|
829
1021
|
);
|
|
830
1022
|
console.log(`WORKERD_TARGET_URLS=${JSON.stringify(targetServers.targetUrls)}`);
|
|
831
|
-
console.log(`WORKERD_URL=${targetServers.targetUrls[
|
|
1023
|
+
console.log(`WORKERD_URL=${targetServers.targetUrls[shells[0].id]}`);
|
|
832
1024
|
try {
|
|
833
1025
|
await new Promise((resolve) => {
|
|
834
1026
|
process.once("SIGINT", resolve);
|
|
@@ -841,7 +1033,10 @@ for (const shell of shells) {
|
|
|
841
1033
|
} finally {
|
|
842
1034
|
await miniflare.dispose();
|
|
843
1035
|
}
|
|
844
|
-
}
|
|
845
1036
|
|
|
846
|
-
|
|
847
|
-
|
|
1037
|
+
console.log(`Workerd SSR composition proof passed for ${shells.length} shell(s): ${reportPath}`);
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
if (import.meta.main) {
|
|
1041
|
+
await main();
|
|
1042
|
+
}
|