@bleedingdev/modern-js-app-tools 3.5.0-ultramodern.27 → 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.
- package/dist/cjs/index.js +50 -44
- package/dist/cjs/plugins/backendFederationBuild.js +347 -0
- package/dist/cjs/plugins/deploy/platforms/cloudflare-output-contract.js +91 -0
- package/dist/cjs/plugins/deploy/platforms/cloudflare-output-plan.js +93 -0
- package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier.js +402 -0
- package/dist/cjs/plugins/deploy/platforms/cloudflare.js +158 -30
- package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-entry.mjs +30 -19
- package/dist/esm/index.mjs +2 -0
- package/dist/esm/plugins/backendFederationBuild.mjs +296 -0
- package/dist/esm/plugins/deploy/platforms/cloudflare-output-contract.mjs +17 -0
- package/dist/esm/plugins/deploy/platforms/cloudflare-output-plan.mjs +45 -0
- package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier.mjs +347 -0
- package/dist/esm/plugins/deploy/platforms/cloudflare.mjs +158 -30
- package/dist/esm/plugins/deploy/platforms/templates/cloudflare-entry.mjs +30 -19
- package/dist/esm-node/index.mjs +2 -0
- package/dist/esm-node/plugins/backendFederationBuild.mjs +297 -0
- package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-contract.mjs +18 -0
- package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-plan.mjs +46 -0
- package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier.mjs +348 -0
- package/dist/esm-node/plugins/deploy/platforms/cloudflare.mjs +158 -30
- package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-entry.mjs +30 -19
- package/dist/types/plugins/backendFederationBuild.d.ts +11 -0
- package/dist/types/plugins/deploy/platforms/cloudflare-output-contract.d.ts +13 -0
- package/dist/types/plugins/deploy/platforms/cloudflare-output-plan.d.ts +33 -0
- package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier.d.ts +32 -0
- package/dist/types/types/config/cloudflareDeploy.d.ts +11 -0
- package/dist/types/types/config/deploy.d.ts +11 -3
- package/package.json +17 -11
|
@@ -43,17 +43,19 @@ const external_node_path_namespaceObject = require("node:path");
|
|
|
43
43
|
var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
|
|
44
44
|
const utils_namespaceObject = require("@modern-js/utils");
|
|
45
45
|
const index_js_namespaceObject = require("../utils/index.js");
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
46
|
+
const external_cloudflare_output_contract_js_namespaceObject = require("./cloudflare-output-contract.js");
|
|
47
|
+
const external_cloudflare_output_plan_js_namespaceObject = require("./cloudflare-output-plan.js");
|
|
48
|
+
const external_cloudflare_output_verifier_js_namespaceObject = require("./cloudflare-output-verifier.js");
|
|
49
|
+
const WORKER_ENTRY = external_cloudflare_output_contract_js_namespaceObject.CLOUDFLARE_WORKER_ENTRY;
|
|
50
|
+
const WRANGLER_CONFIG_FILE = external_cloudflare_output_contract_js_namespaceObject.CLOUDFLARE_WRANGLER_CONFIG_FILE;
|
|
51
|
+
const OUTPUT_PACKAGE_FILE = external_cloudflare_output_contract_js_namespaceObject.CLOUDFLARE_OUTPUT_PACKAGE_FILE;
|
|
52
|
+
const ASSETS_BINDING = external_cloudflare_output_contract_js_namespaceObject.CLOUDFLARE_ASSETS_BINDING;
|
|
51
53
|
const ROUTE_SPEC_FILE = 'route.json';
|
|
52
54
|
const ROUTE_SPEC_OUTPUT = `server/${ROUTE_SPEC_FILE}`;
|
|
53
55
|
const LOADABLE_STATS_FILE = 'loadable-stats.json';
|
|
54
56
|
const ROUTE_MANIFEST_FILE = 'routes-manifest.json';
|
|
55
|
-
const PUBLIC_ASSETS_DIRECTORY =
|
|
56
|
-
const WORKER_BUNDLE_DIRECTORY =
|
|
57
|
+
const PUBLIC_ASSETS_DIRECTORY = external_cloudflare_output_contract_js_namespaceObject.CLOUDFLARE_PUBLIC_ASSETS_DIRECTORY;
|
|
58
|
+
const WORKER_BUNDLE_DIRECTORY = external_cloudflare_output_contract_js_namespaceObject.CLOUDFLARE_WORKER_BUNDLE_DIRECTORY;
|
|
57
59
|
const SERVER_BUNDLE_DIRECTORY = 'bundles';
|
|
58
60
|
const SERVER_OUTPUT_DIRECTORY = 'server';
|
|
59
61
|
const DEFAULT_SERVER_ONLY_PUBLIC_ASSET_EXCLUDES = [
|
|
@@ -64,10 +66,7 @@ const BFF_EFFECT_WORKER_ENTRY = `${WORKER_BUNDLE_DIRECTORY}/__modern_bff_effect.
|
|
|
64
66
|
const EFFECT_BFF_CLOUDFLARE_IMPORT_GUIDANCE = 'Ensure the Effect API entry exists at api/index.ts or bff.effect.entry, and import Cloudflare edge handlers from @modern-js/plugin-bff/effect-edge instead of lambda/Hono server helpers.';
|
|
65
67
|
const DEFAULT_COMPATIBILITY_DATE = '2026-06-02';
|
|
66
68
|
const COMPATIBILITY_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/u;
|
|
67
|
-
const REQUIRED_COMPATIBILITY_FLAGS =
|
|
68
|
-
'nodejs_compat',
|
|
69
|
-
'global_fetch_strictly_public'
|
|
70
|
-
];
|
|
69
|
+
const REQUIRED_COMPATIBILITY_FLAGS = external_cloudflare_output_contract_js_namespaceObject.CLOUDFLARE_REQUIRED_COMPATIBILITY_FLAGS;
|
|
71
70
|
const RESERVED_ARTIFACT_DESTINATION_FILES = new Set([
|
|
72
71
|
WRANGLER_CONFIG_FILE,
|
|
73
72
|
OUTPUT_PACKAGE_FILE
|
|
@@ -261,11 +260,12 @@ const getConfiguredWorkerName = (appDirectory, modernConfig)=>{
|
|
|
261
260
|
const configuredName = modernConfig.deploy?.worker?.name?.trim();
|
|
262
261
|
return configuredName || getWorkerName(appDirectory);
|
|
263
262
|
};
|
|
264
|
-
const normalizeRelativePath = (value, label, scope = 'app output')=>{
|
|
263
|
+
const normalizeRelativePath = (value, label, scope = 'app output', options = {})=>{
|
|
265
264
|
if ('string' != typeof value) throw new Error(`${label} must be a relative path inside the ${scope}.`);
|
|
266
265
|
const normalized = value.trim().replace(/\\/g, '/').replace(/^\.\/+/u, '').replace(/\/+$/u, '');
|
|
267
266
|
const segments = normalized.split('/');
|
|
268
|
-
|
|
267
|
+
const isRootDestination = '.' === normalized;
|
|
268
|
+
if (!normalized || external_node_path_default().isAbsolute(normalized) || !options.allowRoot && isRootDestination || segments.includes('..')) throw new Error(`${label} must be a relative path inside the ${scope}.`);
|
|
269
269
|
return normalized;
|
|
270
270
|
};
|
|
271
271
|
const normalizeDirectiveValues = (value)=>{
|
|
@@ -420,9 +420,48 @@ const createWranglerD1Databases = (modernConfig, configuredWranglerD1)=>{
|
|
|
420
420
|
if (!Array.isArray(d1Databases)) throw new Error('deploy.worker.d1Databases must be an array.');
|
|
421
421
|
return d1Databases.map(normalizeD1Database);
|
|
422
422
|
};
|
|
423
|
+
const normalizeServiceBinding = (service, index)=>{
|
|
424
|
+
const binding = assertNonEmptyString(service.binding, `deploy.worker.services[${index}].binding`);
|
|
425
|
+
const serviceName = assertNonEmptyString(service.service, `deploy.worker.services[${index}].service`);
|
|
426
|
+
const prefix = void 0 === service.prefix ? void 0 : assertNonEmptyString(service.prefix, `deploy.worker.services[${index}].prefix`);
|
|
427
|
+
return {
|
|
428
|
+
binding,
|
|
429
|
+
service: serviceName,
|
|
430
|
+
...void 0 === prefix ? {} : {
|
|
431
|
+
prefix
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
};
|
|
435
|
+
const createWorkerServiceBindings = (modernConfig, configuredWranglerServices)=>{
|
|
436
|
+
const services = modernConfig.deploy?.worker?.services;
|
|
437
|
+
if (void 0 === services) return configuredWranglerServices;
|
|
438
|
+
if (void 0 !== configuredWranglerServices) throw new Error('Use deploy.worker.services or deploy.worker.wrangler.services, not both.');
|
|
439
|
+
if (!Array.isArray(services)) throw new Error('deploy.worker.services must be an array.');
|
|
440
|
+
return services.map(normalizeServiceBinding);
|
|
441
|
+
};
|
|
442
|
+
const createWranglerServices = (serviceBindings)=>{
|
|
443
|
+
if (!Array.isArray(serviceBindings)) return serviceBindings;
|
|
444
|
+
if (!serviceBindings.every(isJsonRecord)) return serviceBindings;
|
|
445
|
+
return serviceBindings.map((service)=>{
|
|
446
|
+
const { prefix, ...wranglerService } = service;
|
|
447
|
+
return wranglerService;
|
|
448
|
+
});
|
|
449
|
+
};
|
|
450
|
+
const createWorkerManifestServiceBindings = (serviceBindings)=>{
|
|
451
|
+
if (!Array.isArray(serviceBindings)) return;
|
|
452
|
+
const dispatchBindings = serviceBindings.filter((service)=>isJsonRecord(service) && 'string' == typeof service.binding && 'string' == typeof service.service && 'string' == typeof service.prefix).map((service)=>({
|
|
453
|
+
binding: service.binding,
|
|
454
|
+
service: service.service,
|
|
455
|
+
prefix: service.prefix,
|
|
456
|
+
interface: 'fetch'
|
|
457
|
+
}));
|
|
458
|
+
return dispatchBindings.length > 0 ? dispatchBindings : void 0;
|
|
459
|
+
};
|
|
423
460
|
const createWranglerConfig = (appDirectory, modernConfig)=>{
|
|
424
461
|
const wrangler = getConfiguredWrangler(modernConfig);
|
|
425
462
|
const d1Databases = createWranglerD1Databases(modernConfig, wrangler.d1_databases);
|
|
463
|
+
const serviceBindings = createWorkerServiceBindings(modernConfig, wrangler.services);
|
|
464
|
+
const wranglerServices = createWranglerServices(serviceBindings);
|
|
426
465
|
return {
|
|
427
466
|
$schema: 'node_modules/wrangler/config-schema.json',
|
|
428
467
|
name: getConfiguredWorkerName(appDirectory, modernConfig),
|
|
@@ -433,6 +472,9 @@ const createWranglerConfig = (appDirectory, modernConfig)=>{
|
|
|
433
472
|
assets: createWranglerAssetsConfig(wrangler.assets),
|
|
434
473
|
...void 0 === d1Databases ? {} : {
|
|
435
474
|
d1_databases: d1Databases
|
|
475
|
+
},
|
|
476
|
+
...void 0 === wranglerServices ? {} : {
|
|
477
|
+
services: wranglerServices
|
|
436
478
|
}
|
|
437
479
|
};
|
|
438
480
|
};
|
|
@@ -451,7 +493,9 @@ const normalizeCloudflareArtifact = (artifact, index)=>{
|
|
|
451
493
|
const getCloudflareArtifacts = (modernConfig)=>(modernConfig.deploy?.worker?.artifacts ?? []).map(normalizeCloudflareArtifact);
|
|
452
494
|
const normalizeCloudflarePublicAsset = (asset, index)=>{
|
|
453
495
|
const from = normalizeRelativePath(asset.from, `deploy.worker.publicAssets[${index}].from`, 'app root');
|
|
454
|
-
const to = normalizeRelativePath(asset.to, `deploy.worker.publicAssets[${index}].to`, 'Cloudflare public output'
|
|
496
|
+
const to = normalizeRelativePath(asset.to, `deploy.worker.publicAssets[${index}].to`, 'Cloudflare public output', {
|
|
497
|
+
allowRoot: true
|
|
498
|
+
});
|
|
455
499
|
return {
|
|
456
500
|
from,
|
|
457
501
|
to,
|
|
@@ -493,8 +537,79 @@ const readRouteSpec = async (outputDirectory)=>{
|
|
|
493
537
|
routes: Array.isArray(routeSpec.routes) ? routeSpec.routes : []
|
|
494
538
|
};
|
|
495
539
|
};
|
|
540
|
+
const COMPACT_CONFIG_PATH = '.modernjs/ultramodern.json';
|
|
541
|
+
const ULTRAMODERN_BUILD_MODULE = 'shared/ultramodern-build.ts';
|
|
542
|
+
const nonEmptyString = (value)=>'string' == typeof value && value.length > 0 ? value : void 0;
|
|
543
|
+
const toDeliveryUnitIdentity = (value)=>{
|
|
544
|
+
if (!isRecord(value)) return;
|
|
545
|
+
const unitId = nonEmptyString(value.unitId);
|
|
546
|
+
const buildMarker = nonEmptyString(value.buildMarker);
|
|
547
|
+
const sourceRevision = nonEmptyString(value.sourceRevision);
|
|
548
|
+
if (!unitId || !buildMarker || !sourceRevision) return;
|
|
549
|
+
return {
|
|
550
|
+
unitId,
|
|
551
|
+
buildMarker,
|
|
552
|
+
sourceRevision
|
|
553
|
+
};
|
|
554
|
+
};
|
|
555
|
+
const findWorkspaceRoot = async (appDirectory)=>{
|
|
556
|
+
let current = external_node_path_default().resolve(appDirectory);
|
|
557
|
+
for(;;){
|
|
558
|
+
if (await utils_namespaceObject.fs.pathExists(external_node_path_default().join(current, COMPACT_CONFIG_PATH))) return current;
|
|
559
|
+
const parent = external_node_path_default().dirname(current);
|
|
560
|
+
if (parent === current) return;
|
|
561
|
+
current = parent;
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
const resolveTopologyDeliveryUnit = async (appDirectory)=>{
|
|
565
|
+
const workspaceRoot = await findWorkspaceRoot(appDirectory);
|
|
566
|
+
if (!workspaceRoot) return;
|
|
567
|
+
let compactConfig;
|
|
568
|
+
try {
|
|
569
|
+
compactConfig = await utils_namespaceObject.fs.readJSON(external_node_path_default().join(workspaceRoot, COMPACT_CONFIG_PATH));
|
|
570
|
+
} catch {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
if (!isRecord(compactConfig)) return;
|
|
574
|
+
const topology = isRecord(compactConfig.topology) ? compactConfig.topology : void 0;
|
|
575
|
+
const apps = Array.isArray(topology?.apps) ? topology.apps : [];
|
|
576
|
+
const resolvedAppDirectory = external_node_path_default().resolve(appDirectory);
|
|
577
|
+
for (const app of apps){
|
|
578
|
+
if (!isRecord(app)) continue;
|
|
579
|
+
const appPath = nonEmptyString(app.path);
|
|
580
|
+
if (appPath && external_node_path_default().resolve(workspaceRoot, appPath.replace(/^\.\/+/u, '')) === resolvedAppDirectory) return toDeliveryUnitIdentity(app.deliveryUnit);
|
|
581
|
+
}
|
|
582
|
+
return toDeliveryUnitIdentity(compactConfig.deliveryUnit);
|
|
583
|
+
};
|
|
584
|
+
const resolveWorkerDeliveryUnitStamp = async (appDirectory)=>{
|
|
585
|
+
const buildModulePath = external_node_path_default().join(appDirectory, ULTRAMODERN_BUILD_MODULE);
|
|
586
|
+
if (!await utils_namespaceObject.fs.pathExists(buildModulePath)) return;
|
|
587
|
+
const source = await utils_namespaceObject.fs.readFile(buildModulePath, 'utf8');
|
|
588
|
+
const buildMarker = source.match(/\bbuild:\s*['"]([^'"]+)['"]/u)?.[1];
|
|
589
|
+
const unitId = source.match(/\bunitId:\s*['"]([^'"]+)['"]/u)?.[1];
|
|
590
|
+
const sourceRevision = source.match(/\bsourceRevision:\s*['"]([^'"]+)['"]/u)?.[1];
|
|
591
|
+
if (!buildMarker || !unitId || !sourceRevision) return;
|
|
592
|
+
const identity = {
|
|
593
|
+
unitId,
|
|
594
|
+
buildMarker,
|
|
595
|
+
sourceRevision
|
|
596
|
+
};
|
|
597
|
+
return {
|
|
598
|
+
...identity,
|
|
599
|
+
surfaces: {
|
|
600
|
+
ui: {
|
|
601
|
+
...identity,
|
|
602
|
+
surface: 'ui'
|
|
603
|
+
},
|
|
604
|
+
api: {
|
|
605
|
+
...identity,
|
|
606
|
+
surface: 'api'
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
};
|
|
496
611
|
const createMissingEffectBffWorkerError = (outputDirectory, worker)=>new Error(`Cloudflare Effect API runtime is configured, but the BFF worker bundle is missing: ${external_node_path_default().join(outputDirectory, worker)}. ${EFFECT_BFF_CLOUDFLARE_IMPORT_GUIDANCE}`);
|
|
497
|
-
const createWorkerManifest = async (outputDirectory, modernConfig, appContext)=>{
|
|
612
|
+
const createWorkerManifest = async (outputDirectory, modernConfig, appContext, deliveryUnitStamp)=>{
|
|
498
613
|
const routeSpec = await readRouteSpec(outputDirectory);
|
|
499
614
|
const routes = await Promise.all(routeSpec.routes.map(async (route)=>{
|
|
500
615
|
const worker = 'string' == typeof route.worker ? route.worker : void 0;
|
|
@@ -511,11 +626,12 @@ const createWorkerManifest = async (outputDirectory, modernConfig, appContext)=>
|
|
|
511
626
|
const primaryBffPrefix = Array.isArray(bffPrefix) ? bffPrefix[0] : bffPrefix;
|
|
512
627
|
const isEffectApi = Boolean(modernConfig.bff) && modernConfig.bff?.runtimeFramework !== 'hono';
|
|
513
628
|
const effectApiWorkerExists = await utils_namespaceObject.fs.pathExists(external_node_path_default().join(outputDirectory, BFF_EFFECT_WORKER_ENTRY));
|
|
629
|
+
const serviceBindings = createWorkerManifestServiceBindings(createWorkerServiceBindings(modernConfig, void 0));
|
|
514
630
|
if (isEffectApi && primaryBffPrefix && !effectApiWorkerExists) throw createMissingEffectBffWorkerError(outputDirectory, BFF_EFFECT_WORKER_ENTRY);
|
|
515
631
|
return {
|
|
516
632
|
version: 1,
|
|
517
633
|
runtime: {
|
|
518
|
-
type:
|
|
634
|
+
type: external_cloudflare_output_contract_js_namespaceObject.CLOUDFLARE_RUNTIME_TYPE,
|
|
519
635
|
entry: WORKER_ENTRY,
|
|
520
636
|
fetchExport: true,
|
|
521
637
|
nodeListen: false
|
|
@@ -531,7 +647,7 @@ const createWorkerManifest = async (outputDirectory, modernConfig, appContext)=>
|
|
|
531
647
|
},
|
|
532
648
|
workerBundles: {
|
|
533
649
|
directory: WORKER_BUNDLE_DIRECTORY,
|
|
534
|
-
format:
|
|
650
|
+
format: external_cloudflare_output_contract_js_namespaceObject.CLOUDFLARE_WORKER_BUNDLE_FORMAT,
|
|
535
651
|
importableFromModuleWorker: true,
|
|
536
652
|
requestHandlerExport: 'requestHandler'
|
|
537
653
|
},
|
|
@@ -540,12 +656,18 @@ const createWorkerManifest = async (outputDirectory, modernConfig, appContext)=>
|
|
|
540
656
|
routeManifest: ROUTE_MANIFEST_FILE
|
|
541
657
|
},
|
|
542
658
|
security: createCloudflareWorkerSecurityPolicy(modernConfig),
|
|
659
|
+
...deliveryUnitStamp ? {
|
|
660
|
+
deliveryUnit: deliveryUnitStamp
|
|
661
|
+
} : {},
|
|
543
662
|
i18n: createI18nWorkerManifest(routeSpec, appContext),
|
|
544
663
|
bff: isEffectApi && primaryBffPrefix && effectApiWorkerExists ? {
|
|
545
664
|
runtimeFramework: 'effect',
|
|
546
665
|
prefix: primaryBffPrefix,
|
|
547
666
|
worker: BFF_EFFECT_WORKER_ENTRY
|
|
548
|
-
} : void 0
|
|
667
|
+
} : void 0,
|
|
668
|
+
...void 0 === serviceBindings ? {} : {
|
|
669
|
+
serviceBindings
|
|
670
|
+
}
|
|
549
671
|
};
|
|
550
672
|
};
|
|
551
673
|
const createWorkerModuleLoaders = (manifest)=>{
|
|
@@ -597,11 +719,12 @@ const shouldCopyToWorkerBundle = (src, workerBundleDirectory)=>{
|
|
|
597
719
|
const createCloudflarePreset = ({ appContext, modernConfig })=>{
|
|
598
720
|
const { appDirectory, distDirectory } = appContext;
|
|
599
721
|
const outputDirectory = external_node_path_default().join(appDirectory, '.output');
|
|
600
|
-
const
|
|
601
|
-
const
|
|
602
|
-
const
|
|
722
|
+
const outputPlan = (0, external_cloudflare_output_plan_js_namespaceObject.createCloudflareOutputPlan)(outputDirectory);
|
|
723
|
+
const publicDirectory = outputPlan.paths.publicAssets;
|
|
724
|
+
const workerEntryPath = outputPlan.paths.workerEntry;
|
|
725
|
+
const workerManifestPath = outputPlan.paths.workerManifest;
|
|
603
726
|
const routeSpecOutputPath = external_node_path_default().join(outputDirectory, ROUTE_SPEC_OUTPUT);
|
|
604
|
-
const wranglerConfigPath =
|
|
727
|
+
const wranglerConfigPath = outputPlan.paths.wranglerConfig;
|
|
605
728
|
const cloudflareArtifacts = getCloudflareArtifacts(modernConfig);
|
|
606
729
|
const publicAssetExcludes = getPublicAssetExcludes(appDirectory, modernConfig);
|
|
607
730
|
return {
|
|
@@ -617,14 +740,12 @@ const createCloudflarePreset = ({ appContext, modernConfig })=>{
|
|
|
617
740
|
const routeSpecSourcePath = external_node_path_default().join(distDirectory, ROUTE_SPEC_FILE);
|
|
618
741
|
if (await utils_namespaceObject.fs.pathExists(routeSpecSourcePath)) await utils_namespaceObject.fs.copy(routeSpecSourcePath, routeSpecOutputPath);
|
|
619
742
|
const workerBundleSourceDirectory = external_node_path_default().join(distDirectory, WORKER_BUNDLE_DIRECTORY);
|
|
620
|
-
const workerBundleOutputDirectory =
|
|
743
|
+
const workerBundleOutputDirectory = outputPlan.paths.workerBundle;
|
|
621
744
|
if (await utils_namespaceObject.fs.pathExists(workerBundleSourceDirectory)) {
|
|
622
745
|
await utils_namespaceObject.fs.copy(workerBundleSourceDirectory, workerBundleOutputDirectory, {
|
|
623
746
|
filter: (src)=>shouldCopyToWorkerBundle(src, workerBundleSourceDirectory)
|
|
624
747
|
});
|
|
625
|
-
await utils_namespaceObject.fs.writeJSON(
|
|
626
|
-
type: 'commonjs'
|
|
627
|
-
});
|
|
748
|
+
await utils_namespaceObject.fs.writeJSON(outputPlan.paths.workerPackage, outputPlan.packages.worker);
|
|
628
749
|
}
|
|
629
750
|
await copyCloudflareArtifacts(appDirectory, outputDirectory, cloudflareArtifacts);
|
|
630
751
|
await copyCloudflarePublicAssets(appDirectory, publicDirectory, getCloudflarePublicAssets(modernConfig));
|
|
@@ -632,17 +753,24 @@ const createCloudflarePreset = ({ appContext, modernConfig })=>{
|
|
|
632
753
|
await utils_namespaceObject.fs.writeJSON(wranglerConfigPath, createWranglerConfig(appDirectory, modernConfig), {
|
|
633
754
|
spaces: 2
|
|
634
755
|
});
|
|
635
|
-
|
|
756
|
+
const deliveryUnitStamp = await resolveWorkerDeliveryUnitStamp(appDirectory);
|
|
757
|
+
await utils_namespaceObject.fs.writeJSON(workerManifestPath, await createWorkerManifest(outputDirectory, modernConfig, appContext, deliveryUnitStamp), {
|
|
636
758
|
spaces: 2
|
|
637
759
|
});
|
|
638
|
-
await utils_namespaceObject.fs.writeJSON(
|
|
639
|
-
type: 'module'
|
|
640
|
-
});
|
|
760
|
+
await utils_namespaceObject.fs.writeJSON(outputPlan.paths.outputPackage, outputPlan.packages.output);
|
|
641
761
|
},
|
|
642
762
|
async genEntry () {
|
|
643
763
|
const template = await (0, index_js_namespaceObject.readTemplate)('cloudflare-entry.mjs');
|
|
644
764
|
const manifest = await utils_namespaceObject.fs.readJSON(workerManifestPath);
|
|
645
765
|
await utils_namespaceObject.fs.writeFile(workerEntryPath, template.replace('p_workerManifest', JSON.stringify(manifest, null, 2)).replace('p_workerModuleLoaders', createWorkerModuleLoaders(manifest)));
|
|
766
|
+
const topologyDeliveryUnit = await resolveTopologyDeliveryUnit(appDirectory);
|
|
767
|
+
await (0, external_cloudflare_output_verifier_js_namespaceObject.assertCloudflareOutput)({
|
|
768
|
+
outputDirectory,
|
|
769
|
+
importWorker: false,
|
|
770
|
+
...topologyDeliveryUnit ? {
|
|
771
|
+
deliveryUnit: topologyDeliveryUnit
|
|
772
|
+
} : {}
|
|
773
|
+
});
|
|
646
774
|
}
|
|
647
775
|
};
|
|
648
776
|
};
|
|
@@ -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
|
|
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
|
-
|
|
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
|
|
802
|
-
if (!
|
|
803
|
-
const {
|
|
804
|
-
|
|
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
|
-
|
|
798
|
+
effectBffDispatcherPromises.set(bff.worker, effectDispatcherPromise);
|
|
809
799
|
}
|
|
810
|
-
const
|
|
811
|
-
return
|
|
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)) {
|
package/dist/esm/index.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { compatPlugin } from "./compat/index.mjs";
|
|
|
8
8
|
import { DEFAULT_RUNTIME_CONFIG_FILE } from "./constants.mjs";
|
|
9
9
|
import { i18n } from "./locale/index.mjs";
|
|
10
10
|
import analyze from "./plugins/analyze/index.mjs";
|
|
11
|
+
import backendFederationBuild from "./plugins/backendFederationBuild.mjs";
|
|
11
12
|
import deploy from "./plugins/deploy/index.mjs";
|
|
12
13
|
import initialize from "./plugins/initialize/index.mjs";
|
|
13
14
|
import serverBuild from "./plugins/serverBuild.mjs";
|
|
@@ -28,6 +29,7 @@ const appTools = ()=>({
|
|
|
28
29
|
initialize(),
|
|
29
30
|
analyze(),
|
|
30
31
|
serverBuild(),
|
|
32
|
+
backendFederationBuild(),
|
|
31
33
|
deploy()
|
|
32
34
|
],
|
|
33
35
|
post: [
|