@bleedingdev/modern-js-app-tools 3.5.0-ultramodern.3 → 3.5.0-ultramodern.30

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 (32) hide show
  1. package/dist/cjs/builder/generator/getBuilderEnvironments.js +1 -0
  2. package/dist/cjs/index.js +50 -44
  3. package/dist/cjs/plugins/backendFederationBuild.js +347 -0
  4. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-contract.js +91 -0
  5. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-plan.js +93 -0
  6. package/dist/cjs/plugins/deploy/platforms/cloudflare-output-verifier.js +402 -0
  7. package/dist/cjs/plugins/deploy/platforms/cloudflare.js +272 -29
  8. package/dist/cjs/plugins/deploy/platforms/templates/cloudflare-entry.mjs +346 -43
  9. package/dist/esm/builder/generator/getBuilderEnvironments.mjs +1 -0
  10. package/dist/esm/index.mjs +2 -0
  11. package/dist/esm/plugins/backendFederationBuild.mjs +296 -0
  12. package/dist/esm/plugins/deploy/platforms/cloudflare-output-contract.mjs +17 -0
  13. package/dist/esm/plugins/deploy/platforms/cloudflare-output-plan.mjs +45 -0
  14. package/dist/esm/plugins/deploy/platforms/cloudflare-output-verifier.mjs +347 -0
  15. package/dist/esm/plugins/deploy/platforms/cloudflare.mjs +272 -29
  16. package/dist/esm/plugins/deploy/platforms/templates/cloudflare-entry.mjs +346 -43
  17. package/dist/esm-node/builder/generator/getBuilderEnvironments.mjs +1 -0
  18. package/dist/esm-node/index.mjs +2 -0
  19. package/dist/esm-node/plugins/backendFederationBuild.mjs +297 -0
  20. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-contract.mjs +18 -0
  21. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-plan.mjs +46 -0
  22. package/dist/esm-node/plugins/deploy/platforms/cloudflare-output-verifier.mjs +348 -0
  23. package/dist/esm-node/plugins/deploy/platforms/cloudflare.mjs +272 -29
  24. package/dist/esm-node/plugins/deploy/platforms/templates/cloudflare-entry.mjs +346 -43
  25. package/dist/types/plugins/backendFederationBuild.d.ts +11 -0
  26. package/dist/types/plugins/deploy/platforms/cloudflare-output-contract.d.ts +13 -0
  27. package/dist/types/plugins/deploy/platforms/cloudflare-output-plan.d.ts +33 -0
  28. package/dist/types/plugins/deploy/platforms/cloudflare-output-verifier.d.ts +32 -0
  29. package/dist/types/types/config/cloudflareDeploy.d.ts +22 -0
  30. package/dist/types/types/config/deploy.d.ts +15 -2
  31. package/lib/types.d.ts +2 -3
  32. 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 WORKER_ENTRY = 'server/index.mjs';
47
- const WORKER_MANIFEST = 'server/modern-worker-manifest.json';
48
- const WRANGLER_CONFIG_FILE = 'wrangler.json';
49
- const OUTPUT_PACKAGE_FILE = 'package.json';
50
- const ASSETS_BINDING = 'ASSETS';
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 = 'public';
56
- const WORKER_BUNDLE_DIRECTORY = 'worker';
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
@@ -151,6 +150,102 @@ const DEFAULT_CSP_DIRECTIVES = {
151
150
  'blob:'
152
151
  ]
153
152
  };
153
+ const I18N_SERVER_PLUGIN_NAME = 'plugin-i18n/server';
154
+ const DEFAULT_I18NEXT_DETECTION_OPTIONS = {
155
+ order: [
156
+ 'querystring',
157
+ 'cookie',
158
+ 'localStorage',
159
+ 'header',
160
+ 'navigator',
161
+ 'htmlTag',
162
+ 'path',
163
+ 'subdomain'
164
+ ],
165
+ lookupQuerystring: 'lng',
166
+ lookupCookie: 'i18next',
167
+ lookupHeader: 'accept-language'
168
+ };
169
+ const SERVER_SIDE_I18N_DETECTORS = new Set([
170
+ 'querystring',
171
+ 'cookie',
172
+ 'header'
173
+ ]);
174
+ const isRecord = (value)=>null !== value && 'object' == typeof value && !Array.isArray(value);
175
+ const toStringArray = (value)=>Array.isArray(value) ? value.filter((entry)=>'string' == typeof entry) : [];
176
+ const getI18nServerPlugin = (appContext)=>appContext.serverPlugins?.find((plugin)=>plugin.name.includes(I18N_SERVER_PLUGIN_NAME));
177
+ const removeLocaleDetectionByEntry = (config)=>{
178
+ const { localeDetectionByEntry: _localeDetectionByEntry, ...rest } = config;
179
+ return rest;
180
+ };
181
+ const getEntryLocaleDetection = (entryName, localeDetection)=>{
182
+ const byEntry = isRecord(localeDetection.localeDetectionByEntry) ? localeDetection.localeDetectionByEntry : void 0;
183
+ const entryConfig = byEntry?.[entryName];
184
+ const globalConfig = removeLocaleDetectionByEntry(localeDetection);
185
+ return isRecord(entryConfig) ? {
186
+ ...globalConfig,
187
+ ...entryConfig
188
+ } : globalConfig;
189
+ };
190
+ const normalizeI18nDetection = (detection)=>{
191
+ const configured = isRecord(detection) ? detection : {};
192
+ const rawOrder = Array.isArray(configured.order) ? configured.order : DEFAULT_I18NEXT_DETECTION_OPTIONS.order;
193
+ const order = rawOrder.filter((entry)=>'string' == typeof entry && SERVER_SIDE_I18N_DETECTORS.has(entry));
194
+ return {
195
+ order: order.length > 0 ? order : [
196
+ 'querystring',
197
+ 'cookie',
198
+ 'header'
199
+ ],
200
+ lookupQuerystring: 'string' == typeof configured.lookupQuerystring && configured.lookupQuerystring ? configured.lookupQuerystring : DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupQuerystring,
201
+ lookupCookie: 'string' == typeof configured.lookupCookie && configured.lookupCookie ? configured.lookupCookie : DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupCookie,
202
+ lookupHeader: 'string' == typeof configured.lookupHeader && configured.lookupHeader ? configured.lookupHeader : DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupHeader
203
+ };
204
+ };
205
+ const normalizeLocalisedUrls = (localisedUrls)=>{
206
+ if (!isRecord(localisedUrls) || 0 === Object.keys(localisedUrls).length) return;
207
+ const normalized = {};
208
+ for (const [canonicalPath, languageMap] of Object.entries(localisedUrls)){
209
+ if (!isRecord(languageMap)) continue;
210
+ const normalizedLanguageMap = Object.fromEntries(Object.entries(languageMap).filter((entry)=>'string' == typeof entry[1] && entry[1].length > 0));
211
+ if (Object.keys(normalizedLanguageMap).length > 0) normalized[canonicalPath] = normalizedLanguageMap;
212
+ }
213
+ return Object.keys(normalized).length > 0 ? normalized : void 0;
214
+ };
215
+ const normalizeWorkerI18nEntry = (entryName, localeDetection, staticRoutePrefixes)=>{
216
+ const entryConfig = getEntryLocaleDetection(entryName, localeDetection);
217
+ const languages = toStringArray(entryConfig.languages);
218
+ if (true !== entryConfig.localePathRedirect || 0 === languages.length) return;
219
+ const fallbackLanguage = 'string' == typeof entryConfig.fallbackLanguage && entryConfig.fallbackLanguage ? entryConfig.fallbackLanguage : 'en';
220
+ const ignoreRedirectRoutes = Array.isArray(entryConfig.ignoreRedirectRoutes) ? toStringArray(entryConfig.ignoreRedirectRoutes) : [];
221
+ const localisedUrls = normalizeLocalisedUrls(entryConfig.localisedUrls);
222
+ return {
223
+ i18nextDetector: false !== entryConfig.i18nextDetector,
224
+ languages,
225
+ fallbackLanguage,
226
+ detection: normalizeI18nDetection(entryConfig.detection),
227
+ ignoreRedirectRoutes,
228
+ staticRoutePrefixes,
229
+ ...localisedUrls ? {
230
+ localisedUrls
231
+ } : {}
232
+ };
233
+ };
234
+ const createI18nWorkerManifest = (routeSpec, appContext)=>{
235
+ const i18nServerPlugin = getI18nServerPlugin(appContext);
236
+ const localeDetection = i18nServerPlugin?.options?.localeDetection;
237
+ if (!isRecord(localeDetection)) return;
238
+ const staticRoutePrefixes = toStringArray(i18nServerPlugin?.options?.staticRoutePrefixes);
239
+ const entries = {};
240
+ for (const route of routeSpec.routes){
241
+ if ('string' != typeof route.entryName || entries[route.entryName]) continue;
242
+ const entry = normalizeWorkerI18nEntry(route.entryName, localeDetection, staticRoutePrefixes);
243
+ if (entry) entries[route.entryName] = entry;
244
+ }
245
+ return Object.keys(entries).length > 0 ? {
246
+ entries
247
+ } : void 0;
248
+ };
154
249
  const getCompatibilityDate = (modernConfig)=>{
155
250
  const configuredDate = modernConfig.deploy?.worker?.compatibilityDate?.trim();
156
251
  const compatibilityDate = configuredDate || DEFAULT_COMPATIBILITY_DATE;
@@ -165,11 +260,12 @@ const getConfiguredWorkerName = (appDirectory, modernConfig)=>{
165
260
  const configuredName = modernConfig.deploy?.worker?.name?.trim();
166
261
  return configuredName || getWorkerName(appDirectory);
167
262
  };
168
- const normalizeRelativePath = (value, label, scope = 'app output')=>{
263
+ const normalizeRelativePath = (value, label, scope = 'app output', options = {})=>{
169
264
  if ('string' != typeof value) throw new Error(`${label} must be a relative path inside the ${scope}.`);
170
265
  const normalized = value.trim().replace(/\\/g, '/').replace(/^\.\/+/u, '').replace(/\/+$/u, '');
171
266
  const segments = normalized.split('/');
172
- if (!normalized || external_node_path_default().isAbsolute(normalized) || '.' === normalized || segments.includes('..')) throw new Error(`${label} must be a relative path inside the ${scope}.`);
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}.`);
173
269
  return normalized;
174
270
  };
175
271
  const normalizeDirectiveValues = (value)=>{
@@ -324,9 +420,48 @@ const createWranglerD1Databases = (modernConfig, configuredWranglerD1)=>{
324
420
  if (!Array.isArray(d1Databases)) throw new Error('deploy.worker.d1Databases must be an array.');
325
421
  return d1Databases.map(normalizeD1Database);
326
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
+ };
327
460
  const createWranglerConfig = (appDirectory, modernConfig)=>{
328
461
  const wrangler = getConfiguredWrangler(modernConfig);
329
462
  const d1Databases = createWranglerD1Databases(modernConfig, wrangler.d1_databases);
463
+ const serviceBindings = createWorkerServiceBindings(modernConfig, wrangler.services);
464
+ const wranglerServices = createWranglerServices(serviceBindings);
330
465
  return {
331
466
  $schema: 'node_modules/wrangler/config-schema.json',
332
467
  name: getConfiguredWorkerName(appDirectory, modernConfig),
@@ -337,6 +472,9 @@ const createWranglerConfig = (appDirectory, modernConfig)=>{
337
472
  assets: createWranglerAssetsConfig(wrangler.assets),
338
473
  ...void 0 === d1Databases ? {} : {
339
474
  d1_databases: d1Databases
475
+ },
476
+ ...void 0 === wranglerServices ? {} : {
477
+ services: wranglerServices
340
478
  }
341
479
  };
342
480
  };
@@ -353,6 +491,18 @@ const normalizeCloudflareArtifact = (artifact, index)=>{
353
491
  };
354
492
  };
355
493
  const getCloudflareArtifacts = (modernConfig)=>(modernConfig.deploy?.worker?.artifacts ?? []).map(normalizeCloudflareArtifact);
494
+ const normalizeCloudflarePublicAsset = (asset, index)=>{
495
+ const from = normalizeRelativePath(asset.from, `deploy.worker.publicAssets[${index}].from`, 'app root');
496
+ const to = normalizeRelativePath(asset.to, `deploy.worker.publicAssets[${index}].to`, 'Cloudflare public output', {
497
+ allowRoot: true
498
+ });
499
+ return {
500
+ from,
501
+ to,
502
+ index
503
+ };
504
+ };
505
+ const getCloudflarePublicAssets = (modernConfig)=>(modernConfig.deploy?.worker?.publicAssets ?? []).map(normalizeCloudflarePublicAsset);
356
506
  const copyCloudflareArtifacts = async (appDirectory, outputDirectory, artifacts)=>{
357
507
  for (const artifact of artifacts){
358
508
  const sourcePath = external_node_path_default().join(appDirectory, artifact.from);
@@ -360,6 +510,13 @@ const copyCloudflareArtifacts = async (appDirectory, outputDirectory, artifacts)
360
510
  await utils_namespaceObject.fs.copy(sourcePath, external_node_path_default().join(outputDirectory, artifact.to));
361
511
  }
362
512
  };
513
+ const copyCloudflarePublicAssets = async (appDirectory, publicDirectory, publicAssets)=>{
514
+ for (const asset of publicAssets){
515
+ const sourcePath = external_node_path_default().join(appDirectory, asset.from);
516
+ if (!await utils_namespaceObject.fs.pathExists(sourcePath)) throw new Error(`deploy.worker.publicAssets[${asset.index}].from does not exist: ${asset.from}`);
517
+ await utils_namespaceObject.fs.copy(sourcePath, external_node_path_default().join(publicDirectory, asset.to));
518
+ }
519
+ };
363
520
  const copyCloudflareD1Migrations = async (appDirectory, outputDirectory, modernConfig)=>{
364
521
  for (const [index, database] of (modernConfig.deploy?.worker?.d1Databases ?? []).entries()){
365
522
  if (!database.migrationsDir) continue;
@@ -380,8 +537,79 @@ const readRouteSpec = async (outputDirectory)=>{
380
537
  routes: Array.isArray(routeSpec.routes) ? routeSpec.routes : []
381
538
  };
382
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
+ };
383
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}`);
384
- const createWorkerManifest = async (outputDirectory, modernConfig)=>{
612
+ const createWorkerManifest = async (outputDirectory, modernConfig, appContext, deliveryUnitStamp)=>{
385
613
  const routeSpec = await readRouteSpec(outputDirectory);
386
614
  const routes = await Promise.all(routeSpec.routes.map(async (route)=>{
387
615
  const worker = 'string' == typeof route.worker ? route.worker : void 0;
@@ -398,11 +626,12 @@ const createWorkerManifest = async (outputDirectory, modernConfig)=>{
398
626
  const primaryBffPrefix = Array.isArray(bffPrefix) ? bffPrefix[0] : bffPrefix;
399
627
  const isEffectApi = Boolean(modernConfig.bff) && modernConfig.bff?.runtimeFramework !== 'hono';
400
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));
401
630
  if (isEffectApi && primaryBffPrefix && !effectApiWorkerExists) throw createMissingEffectBffWorkerError(outputDirectory, BFF_EFFECT_WORKER_ENTRY);
402
631
  return {
403
632
  version: 1,
404
633
  runtime: {
405
- type: 'cloudflare-module-worker',
634
+ type: external_cloudflare_output_contract_js_namespaceObject.CLOUDFLARE_RUNTIME_TYPE,
406
635
  entry: WORKER_ENTRY,
407
636
  fetchExport: true,
408
637
  nodeListen: false
@@ -418,7 +647,7 @@ const createWorkerManifest = async (outputDirectory, modernConfig)=>{
418
647
  },
419
648
  workerBundles: {
420
649
  directory: WORKER_BUNDLE_DIRECTORY,
421
- format: 'commonjs',
650
+ format: external_cloudflare_output_contract_js_namespaceObject.CLOUDFLARE_WORKER_BUNDLE_FORMAT,
422
651
  importableFromModuleWorker: true,
423
652
  requestHandlerExport: 'requestHandler'
424
653
  },
@@ -427,11 +656,18 @@ const createWorkerManifest = async (outputDirectory, modernConfig)=>{
427
656
  routeManifest: ROUTE_MANIFEST_FILE
428
657
  },
429
658
  security: createCloudflareWorkerSecurityPolicy(modernConfig),
659
+ ...deliveryUnitStamp ? {
660
+ deliveryUnit: deliveryUnitStamp
661
+ } : {},
662
+ i18n: createI18nWorkerManifest(routeSpec, appContext),
430
663
  bff: isEffectApi && primaryBffPrefix && effectApiWorkerExists ? {
431
664
  runtimeFramework: 'effect',
432
665
  prefix: primaryBffPrefix,
433
666
  worker: BFF_EFFECT_WORKER_ENTRY
434
- } : void 0
667
+ } : void 0,
668
+ ...void 0 === serviceBindings ? {} : {
669
+ serviceBindings
670
+ }
435
671
  };
436
672
  };
437
673
  const createWorkerModuleLoaders = (manifest)=>{
@@ -483,11 +719,12 @@ const shouldCopyToWorkerBundle = (src, workerBundleDirectory)=>{
483
719
  const createCloudflarePreset = ({ appContext, modernConfig })=>{
484
720
  const { appDirectory, distDirectory } = appContext;
485
721
  const outputDirectory = external_node_path_default().join(appDirectory, '.output');
486
- const publicDirectory = external_node_path_default().join(outputDirectory, PUBLIC_ASSETS_DIRECTORY);
487
- const workerEntryPath = external_node_path_default().join(outputDirectory, WORKER_ENTRY);
488
- const workerManifestPath = external_node_path_default().join(outputDirectory, WORKER_MANIFEST);
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;
489
726
  const routeSpecOutputPath = external_node_path_default().join(outputDirectory, ROUTE_SPEC_OUTPUT);
490
- const wranglerConfigPath = external_node_path_default().join(outputDirectory, WRANGLER_CONFIG_FILE);
727
+ const wranglerConfigPath = outputPlan.paths.wranglerConfig;
491
728
  const cloudflareArtifacts = getCloudflareArtifacts(modernConfig);
492
729
  const publicAssetExcludes = getPublicAssetExcludes(appDirectory, modernConfig);
493
730
  return {
@@ -503,31 +740,37 @@ const createCloudflarePreset = ({ appContext, modernConfig })=>{
503
740
  const routeSpecSourcePath = external_node_path_default().join(distDirectory, ROUTE_SPEC_FILE);
504
741
  if (await utils_namespaceObject.fs.pathExists(routeSpecSourcePath)) await utils_namespaceObject.fs.copy(routeSpecSourcePath, routeSpecOutputPath);
505
742
  const workerBundleSourceDirectory = external_node_path_default().join(distDirectory, WORKER_BUNDLE_DIRECTORY);
506
- const workerBundleOutputDirectory = external_node_path_default().join(outputDirectory, WORKER_BUNDLE_DIRECTORY);
743
+ const workerBundleOutputDirectory = outputPlan.paths.workerBundle;
507
744
  if (await utils_namespaceObject.fs.pathExists(workerBundleSourceDirectory)) {
508
745
  await utils_namespaceObject.fs.copy(workerBundleSourceDirectory, workerBundleOutputDirectory, {
509
746
  filter: (src)=>shouldCopyToWorkerBundle(src, workerBundleSourceDirectory)
510
747
  });
511
- await utils_namespaceObject.fs.writeJSON(external_node_path_default().join(workerBundleOutputDirectory, 'package.json'), {
512
- type: 'commonjs'
513
- });
748
+ await utils_namespaceObject.fs.writeJSON(outputPlan.paths.workerPackage, outputPlan.packages.worker);
514
749
  }
515
750
  await copyCloudflareArtifacts(appDirectory, outputDirectory, cloudflareArtifacts);
751
+ await copyCloudflarePublicAssets(appDirectory, publicDirectory, getCloudflarePublicAssets(modernConfig));
516
752
  await copyCloudflareD1Migrations(appDirectory, outputDirectory, modernConfig);
517
753
  await utils_namespaceObject.fs.writeJSON(wranglerConfigPath, createWranglerConfig(appDirectory, modernConfig), {
518
754
  spaces: 2
519
755
  });
520
- await utils_namespaceObject.fs.writeJSON(workerManifestPath, await createWorkerManifest(outputDirectory, modernConfig), {
756
+ const deliveryUnitStamp = await resolveWorkerDeliveryUnitStamp(appDirectory);
757
+ await utils_namespaceObject.fs.writeJSON(workerManifestPath, await createWorkerManifest(outputDirectory, modernConfig, appContext, deliveryUnitStamp), {
521
758
  spaces: 2
522
759
  });
523
- await utils_namespaceObject.fs.writeJSON(external_node_path_default().join(outputDirectory, OUTPUT_PACKAGE_FILE), {
524
- type: 'module'
525
- });
760
+ await utils_namespaceObject.fs.writeJSON(outputPlan.paths.outputPackage, outputPlan.packages.output);
526
761
  },
527
762
  async genEntry () {
528
763
  const template = await (0, index_js_namespaceObject.readTemplate)('cloudflare-entry.mjs');
529
764
  const manifest = await utils_namespaceObject.fs.readJSON(workerManifestPath);
530
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
+ });
531
774
  }
532
775
  };
533
776
  };