@bleedingdev/modern-js-app-tools 3.5.0-ultramodern.28 → 3.5.0-ultramodern.29

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.
Files changed (28) hide show
  1. package/dist/cjs/index.js +50 -44
  2. package/dist/cjs/plugins/backendFederationBuild.js +347 -0
  3. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-contract.js +91 -0
  4. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-plan.js +93 -0
  5. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier.js +402 -0
  6. package/dist/cjs/plugins/deploy/platforms/cloudflare.js +152 -27
  7. package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-entry.mjs +30 -19
  8. package/dist/esm/index.mjs +2 -0
  9. package/dist/esm/plugins/backendFederationBuild.mjs +296 -0
  10. package/dist/esm/plugins/deploy/platforms/cloudflare-output-contract.mjs +17 -0
  11. package/dist/esm/plugins/deploy/platforms/cloudflare-output-plan.mjs +45 -0
  12. package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier.mjs +347 -0
  13. package/dist/esm/plugins/deploy/platforms/cloudflare.mjs +152 -27
  14. package/dist/esm/plugins/deploy/platforms/templates/cloudflare-entry.mjs +30 -19
  15. package/dist/esm-node/index.mjs +2 -0
  16. package/dist/esm-node/plugins/backendFederationBuild.mjs +297 -0
  17. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-contract.mjs +18 -0
  18. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-plan.mjs +46 -0
  19. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier.mjs +348 -0
  20. package/dist/esm-node/plugins/deploy/platforms/cloudflare.mjs +152 -27
  21. package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-entry.mjs +30 -19
  22. package/dist/types/plugins/backendFederationBuild.d.ts +11 -0
  23. package/dist/types/plugins/deploy/platforms/cloudflare-output-contract.d.ts +13 -0
  24. package/dist/types/plugins/deploy/platforms/cloudflare-output-plan.d.ts +33 -0
  25. package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier.d.ts +32 -0
  26. package/dist/types/types/config/cloudflareDeploy.d.ts +11 -0
  27. package/dist/types/types/config/deploy.d.ts +9 -2
  28. package/package.json +17 -11
@@ -2,7 +2,7 @@ const ASSETS_BINDING = 'ASSETS';
2
2
  const MODERN_WORKER_MANIFEST = p_workerManifest;
3
3
  const WORKER_MODULE_LOADERS = p_workerModuleLoaders;
4
4
  const workerModulePromises = new Map();
5
- const effectBffHandlerPromises = new Map();
5
+ const effectBffDispatcherPromises = new Map();
6
6
  let effectEdgeRuntimePromise;
7
7
  const remoteJsonPromises = new Map();
8
8
  const CORS_POLICY = MODERN_WORKER_MANIFEST.security?.cors || {};
@@ -152,17 +152,7 @@ async function createCorsPreflightResponse(request, env) {
152
152
  status: 204
153
153
  });
154
154
  }
155
- if (!ASSET_CORS_ENABLED) return null;
156
- const assets = env?.[ASSETS_BINDING];
157
- if (!assets || 'function' != typeof assets.fetch) return null;
158
- const assetResponse = await assets.fetch(new Request(request.url, {
159
- method: 'HEAD'
160
- }));
161
- if (!assetResponse || 404 === assetResponse.status) return null;
162
- return new Response(null, {
163
- headers: ASSET_CORS_HEADERS,
164
- status: 204
165
- });
155
+ return null;
166
156
  }
167
157
  async function fetchAsset(request, env) {
168
158
  if ('GET' !== request.method && 'HEAD' !== request.method) return null;
@@ -798,17 +788,17 @@ async function dispatchBffRequest(request, env) {
798
788
  const defaultExport = workerModule.default;
799
789
  const runtime = getRuntimeModule(workerModule);
800
790
  if ('effect' === bff.runtimeFramework) {
801
- let effectHandlerPromise = effectBffHandlerPromises.get(bff.worker);
802
- if (!effectHandlerPromise) {
803
- const { createEffectBffEdgeHandler } = await loadEffectEdgeRuntime();
804
- effectHandlerPromise = createEffectBffEdgeHandler({
791
+ let effectDispatcherPromise = effectBffDispatcherPromises.get(bff.worker);
792
+ if (!effectDispatcherPromise) {
793
+ const { createEffectBffEdgeDispatcher } = await loadEffectEdgeRuntime();
794
+ effectDispatcherPromise = createEffectBffEdgeDispatcher({
805
795
  module: runtime,
806
796
  prefix: bff.prefix
807
797
  });
808
- effectBffHandlerPromises.set(bff.worker, effectHandlerPromise);
798
+ effectBffDispatcherPromises.set(bff.worker, effectDispatcherPromise);
809
799
  }
810
- const effectHandler = await effectHandlerPromise;
811
- return effectHandler.handler(request, {
800
+ const effectDispatcher = await effectDispatcherPromise;
801
+ return effectDispatcher.dispatch(request, {
812
802
  env
813
803
  });
814
804
  }
@@ -836,12 +826,33 @@ async function dispatchBffRequest(request, env) {
836
826
  };
837
827
  return handler(mountedRequest, effectContext);
838
828
  }
829
+ async function dispatchServiceBindingRequest(request, env) {
830
+ const serviceBindings = MODERN_WORKER_MANIFEST.serviceBindings;
831
+ if (!Array.isArray(serviceBindings) || 0 === serviceBindings.length) return null;
832
+ const pathname = new URL(request.url).pathname;
833
+ for (const binding of serviceBindings){
834
+ if (!binding?.binding || !binding?.prefix) continue;
835
+ if (!matchesPrefix(pathname, binding.prefix)) continue;
836
+ const service = env?.[binding.binding];
837
+ if (!service || 'function' != typeof service.fetch) return new Response(`Cloudflare service binding not available: ${binding.binding}`, {
838
+ status: 502,
839
+ headers: {
840
+ 'content-type': 'text/plain; charset=utf-8',
841
+ 'x-modern-js-service-binding': binding.binding
842
+ }
843
+ });
844
+ return service.fetch(request);
845
+ }
846
+ return null;
847
+ }
839
848
  export default {
840
849
  async fetch (request, env, ctx) {
841
850
  const corsPreflightResponse = await createCorsPreflightResponse(request, env);
842
851
  if (corsPreflightResponse) return finalizeResponseForRequest(corsPreflightResponse, request);
843
852
  const bffResponse = await dispatchBffRequest(request, env);
844
853
  if (bffResponse) return finalizeResponseForRequest(withAppCorsHeaders(bffResponse, request), request);
854
+ const serviceBindingResponse = await dispatchServiceBindingRequest(request, env);
855
+ if (serviceBindingResponse) return finalizeResponseForRequest(withAppCorsHeaders(serviceBindingResponse, request), request);
845
856
  const route = findRoute(request);
846
857
  const { pathname } = new URL(request.url);
847
858
  if (isAssetLikePathname(pathname) && !routeMatchesExactly(route, pathname)) {
@@ -0,0 +1,11 @@
1
+ import type { AppTools, CliPlugin } from '../types';
2
+ export type BackendFederationArtifactResult = {
3
+ appId: string;
4
+ manifestPath: string;
5
+ containerPath: string;
6
+ remoteName: string;
7
+ remoteType: string;
8
+ };
9
+ export declare const emitBackendFederationArtifacts: (appDirectory: string, distDirectory: string) => Promise<BackendFederationArtifactResult | undefined>;
10
+ export default _default;
11
+ declare function _default(): CliPlugin<AppTools>;
@@ -0,0 +1,13 @@
1
+ export declare const CLOUDFLARE_WORKER_ENTRY = "server/index.mjs";
2
+ export declare const CLOUDFLARE_WORKER_MANIFEST = "server/modern-worker-manifest.json";
3
+ export declare const CLOUDFLARE_WRANGLER_CONFIG_FILE = "wrangler.json";
4
+ export declare const CLOUDFLARE_OUTPUT_PACKAGE_FILE = "package.json";
5
+ export declare const CLOUDFLARE_WORKER_PACKAGE_FILE = "worker/package.json";
6
+ export declare const CLOUDFLARE_ASSETS_BINDING = "ASSETS";
7
+ export declare const CLOUDFLARE_PUBLIC_ASSETS_DIRECTORY = "public";
8
+ export declare const CLOUDFLARE_WORKER_BUNDLE_DIRECTORY = "worker";
9
+ export declare const CLOUDFLARE_RUNTIME_TYPE = "cloudflare-module-worker";
10
+ export declare const CLOUDFLARE_OUTPUT_PACKAGE_TYPE = "module";
11
+ export declare const CLOUDFLARE_WORKER_PACKAGE_TYPE = "commonjs";
12
+ export declare const CLOUDFLARE_WORKER_BUNDLE_FORMAT = "commonjs";
13
+ export declare const CLOUDFLARE_REQUIRED_COMPATIBILITY_FLAGS: readonly ['nodejs_compat', 'global_fetch_strictly_public'];
@@ -0,0 +1,33 @@
1
+ import { CLOUDFLARE_ASSETS_BINDING, CLOUDFLARE_OUTPUT_PACKAGE_TYPE, CLOUDFLARE_PUBLIC_ASSETS_DIRECTORY, CLOUDFLARE_REQUIRED_COMPATIBILITY_FLAGS, CLOUDFLARE_WORKER_ENTRY, CLOUDFLARE_WORKER_PACKAGE_TYPE } from './cloudflare-output-contract';
2
+ export type CloudflareOutputPlan = {
3
+ outputDirectory: string;
4
+ requiredFiles: string[];
5
+ publicLeakDirectories: string[];
6
+ paths: {
7
+ workerEntry: string;
8
+ workerManifest: string;
9
+ wranglerConfig: string;
10
+ outputPackage: string;
11
+ workerPackage: string;
12
+ publicAssets: string;
13
+ workerBundle: string;
14
+ };
15
+ packages: {
16
+ output: {
17
+ type: typeof CLOUDFLARE_OUTPUT_PACKAGE_TYPE;
18
+ };
19
+ worker: {
20
+ type: typeof CLOUDFLARE_WORKER_PACKAGE_TYPE;
21
+ };
22
+ };
23
+ wrangler: {
24
+ main: typeof CLOUDFLARE_WORKER_ENTRY;
25
+ assets: {
26
+ binding: typeof CLOUDFLARE_ASSETS_BINDING;
27
+ directory: `./${typeof CLOUDFLARE_PUBLIC_ASSETS_DIRECTORY}`;
28
+ run_worker_first: true;
29
+ };
30
+ requiredCompatibilityFlags: typeof CLOUDFLARE_REQUIRED_COMPATIBILITY_FLAGS;
31
+ };
32
+ };
33
+ export declare function createCloudflareOutputPlan(outputDirectory: string): CloudflareOutputPlan;
@@ -0,0 +1,32 @@
1
+ export type CloudflareOutputVerifierIssueCode = 'missing-file' | 'invalid-manifest' | 'invalid-wrangler' | 'invalid-package' | 'public-output-leak' | 'missing-worker-bundle' | 'invalid-worker-bundle' | 'worker-import-failed' | 'delivery-unit-drift' | 'missing-delivery-unit' | 'forbidden-mutation-pattern';
2
+ export interface CloudflareOutputVerifierIssue {
3
+ code: CloudflareOutputVerifierIssueCode;
4
+ message: string;
5
+ path?: string;
6
+ }
7
+ export interface CloudflareDeliveryUnitIdentity {
8
+ unitId: string;
9
+ buildMarker: string;
10
+ sourceRevision: string;
11
+ }
12
+ export interface VerifyCloudflareOutputOptions {
13
+ outputDirectory: string;
14
+ importWorker?: boolean;
15
+ /**
16
+ * Topology-declared delivery-unit record (from the workspace compact config).
17
+ * When provided, the Cloudflare worker manifest must carry a matching
18
+ * `deliveryUnit` stamp so the deployed worker snapshot is proven to derive
19
+ * from the same delivery unit as the Node/API surfaces (ADR-0019 lane D).
20
+ */
21
+ deliveryUnit?: CloudflareDeliveryUnitIdentity;
22
+ }
23
+ export interface VerifyCloudflareOutputMutationPolicyOptions {
24
+ scanRoots: string[];
25
+ }
26
+ export interface CloudflareOutputVerifierResult {
27
+ ok: boolean;
28
+ issues: CloudflareOutputVerifierIssue[];
29
+ }
30
+ export declare const verifyCloudflareOutputMutationPolicy: (options: VerifyCloudflareOutputMutationPolicyOptions) => Promise<CloudflareOutputVerifierResult>;
31
+ export declare const verifyCloudflareOutput: (options: VerifyCloudflareOutputOptions) => Promise<CloudflareOutputVerifierResult>;
32
+ export declare const assertCloudflareOutput: (options: VerifyCloudflareOutputOptions) => Promise<void>;
@@ -52,6 +52,17 @@ export interface CloudflareWorkerD1DatabaseConfig {
52
52
  */
53
53
  remote?: boolean;
54
54
  }
55
+ export interface CloudflareWorkerServiceBindingConfig {
56
+ /** Worker binding name exposed on the module worker `env` object. */
57
+ binding: string;
58
+ /** Target Cloudflare Worker service name. */
59
+ service: string;
60
+ /**
61
+ * Optional application path prefix that Modern.js should dispatch to this
62
+ * service binding with `env[binding].fetch(request)`.
63
+ */
64
+ prefix?: string;
65
+ }
55
66
  export interface CloudflareWorkerSecurityCspConfig {
56
67
  mode?: CloudflareWorkerSecurityCspMode;
57
68
  directives?: Record<string, string[] | string | false>;
@@ -1,5 +1,5 @@
1
- import type { CloudflareWorkerArtifactConfig, CloudflareWorkerD1DatabaseConfig, CloudflareWorkerPublicAssetConfig, CloudflareWorkerSecurityConfig, DeployTarget, JsonValue } from './cloudflareDeploy';
2
- export type { CloudflareWorkerArtifactConfig, CloudflareWorkerD1DatabaseConfig, CloudflareWorkerPublicAssetConfig, CloudflareWorkerSecurityConfig, CloudflareWorkerSecurityCorsConfig, CloudflareWorkerSecurityCspConfig, CloudflareWorkerSecurityCspMode, CloudflareWorkerSecurityNoindexConfig, DeployTarget, JsonValue, } from './cloudflareDeploy';
1
+ import type { CloudflareWorkerArtifactConfig, CloudflareWorkerD1DatabaseConfig, CloudflareWorkerPublicAssetConfig, CloudflareWorkerSecurityConfig, CloudflareWorkerServiceBindingConfig, DeployTarget, JsonValue } from './cloudflareDeploy';
2
+ export type { CloudflareWorkerArtifactConfig, CloudflareWorkerD1DatabaseConfig, CloudflareWorkerPublicAssetConfig, CloudflareWorkerSecurityConfig, CloudflareWorkerSecurityCorsConfig, CloudflareWorkerSecurityCspConfig, CloudflareWorkerSecurityCspMode, CloudflareWorkerSecurityNoindexConfig, CloudflareWorkerServiceBindingConfig, DeployTarget, JsonValue, } from './cloudflareDeploy';
3
3
  export interface MicroFrontend {
4
4
  /**
5
5
  * Specifies whether to enable the HTML entry.
@@ -59,6 +59,13 @@ export interface DeployUserConfig {
59
59
  * directories into `.output`.
60
60
  */
61
61
  d1Databases?: CloudflareWorkerD1DatabaseConfig[];
62
+ /**
63
+ * First-class Cloudflare service bindings. Modern.js writes these to
64
+ * `wrangler.json` as `services`; when a binding also has `prefix`,
65
+ * the generated Worker dispatches matching requests through
66
+ * `env[binding].fetch(request)`.
67
+ */
68
+ services?: CloudflareWorkerServiceBindingConfig[];
62
69
  /**
63
70
  * Dist output paths that must not be copied into Cloudflare public assets.
64
71
  * Entries are slash-normalized path prefixes relative to the app dist root.
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.5.0-ultramodern.28",
20
+ "version": "3.5.0-ultramodern.29",
21
21
  "types": "./dist/types/index.d.ts",
22
22
  "main": "./dist/cjs/index.js",
23
23
  "exports": {
@@ -45,6 +45,12 @@
45
45
  "require": "./dist/cjs/run/index.js",
46
46
  "default": "./dist/cjs/run/index.js"
47
47
  },
48
+ "./cloudflare-output-verifier": {
49
+ "types": "./dist/types/plugins/deploy/platforms/cloudflare-output-verifier.d.ts",
50
+ "import": "./dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier.mjs",
51
+ "require": "./dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier.js",
52
+ "default": "./dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier.js"
53
+ },
48
54
  "./types": {
49
55
  "types": "./lib/types.d.ts",
50
56
  "default": "./lib/types.d.ts"
@@ -90,16 +96,16 @@
90
96
  "ndepe": "^0.1.13",
91
97
  "pkg-types": "^2.3.1",
92
98
  "std-env": "4.1.0",
93
- "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.5.0-ultramodern.28",
94
- "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.5.0-ultramodern.28",
95
- "@modern-js/plugin-data-loader": "npm:@bleedingdev/modern-js-plugin-data-loader@3.5.0-ultramodern.28",
96
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.5.0-ultramodern.28",
97
- "@modern-js/server": "npm:@bleedingdev/modern-js-server@3.5.0-ultramodern.28",
98
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.5.0-ultramodern.28",
99
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.28",
100
- "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.5.0-ultramodern.28",
101
- "@modern-js/prod-server": "npm:@bleedingdev/modern-js-prod-server@3.5.0-ultramodern.28",
102
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.28"
99
+ "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.5.0-ultramodern.29",
100
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.5.0-ultramodern.29",
101
+ "@modern-js/prod-server": "npm:@bleedingdev/modern-js-prod-server@3.5.0-ultramodern.29",
102
+ "@modern-js/plugin-data-loader": "npm:@bleedingdev/modern-js-plugin-data-loader@3.5.0-ultramodern.29",
103
+ "@modern-js/server": "npm:@bleedingdev/modern-js-server@3.5.0-ultramodern.29",
104
+ "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.5.0-ultramodern.29",
105
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.5.0-ultramodern.29",
106
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.29",
107
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.29",
108
+ "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.5.0-ultramodern.29"
103
109
  },
104
110
  "devDependencies": {
105
111
  "@rslib/core": "0.23.1",