@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.11 → 3.9.0-ultramodern.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/cjs/backend-federation-build/codegen.js +7 -2
  2. package/dist/cjs/backend-federation-build/config.js +86 -36
  3. package/dist/cjs/backend-federation-build/emit.js +17 -13
  4. package/dist/cjs/build-config/remote-address.js +56 -0
  5. package/dist/cjs/cloudflare/delivery-unit.js +16 -26
  6. package/dist/cjs/cloudflare/index.js +7 -1
  7. package/dist/cjs/cloudflare-builder.js +13 -3
  8. package/dist/cjs/cloudflare-output-contract.js +1 -0
  9. package/dist/cjs/cloudflare-output-verifier/identity.js +26 -24
  10. package/dist/cjs/cloudflare-output-verifier/index.js +10 -0
  11. package/dist/cjs/cloudflare-output-verifier/manifest.js +46 -0
  12. package/dist/cjs/cloudflare-output-verifier/mutation-policy.js +7 -1
  13. package/dist/cjs/config.js +4 -0
  14. package/dist/cjs/deploy-output/npmAliases.js +28 -3
  15. package/dist/cjs/release-envelope/framework-output.js +62 -49
  16. package/dist/cjs/release-envelope/index.js +10 -4
  17. package/dist/esm/backend-federation-build/codegen.mjs +7 -2
  18. package/dist/esm/backend-federation-build/config.mjs +78 -31
  19. package/dist/esm/backend-federation-build/emit.mjs +18 -14
  20. package/dist/esm/build-config/remote-address.mjs +18 -0
  21. package/dist/esm/cloudflare/delivery-unit.mjs +16 -26
  22. package/dist/esm/cloudflare/index.mjs +7 -1
  23. package/dist/esm/cloudflare-builder.mjs +13 -3
  24. package/dist/esm/cloudflare-output-contract.mjs +1 -0
  25. package/dist/esm/cloudflare-output-verifier/identity.mjs +26 -24
  26. package/dist/esm/cloudflare-output-verifier/index.mjs +10 -0
  27. package/dist/esm/cloudflare-output-verifier/manifest.mjs +46 -0
  28. package/dist/esm/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  29. package/dist/esm/config.mjs +1 -0
  30. package/dist/esm/deploy-output/npmAliases.mjs +28 -3
  31. package/dist/esm/release-envelope/framework-output.mjs +62 -49
  32. package/dist/esm/release-envelope/index.mjs +10 -4
  33. package/dist/esm-node/backend-federation-build/codegen.mjs +7 -2
  34. package/dist/esm-node/backend-federation-build/config.mjs +78 -31
  35. package/dist/esm-node/backend-federation-build/emit.mjs +18 -14
  36. package/dist/esm-node/build-config/remote-address.mjs +19 -0
  37. package/dist/esm-node/cloudflare/delivery-unit.mjs +16 -26
  38. package/dist/esm-node/cloudflare/index.mjs +7 -1
  39. package/dist/esm-node/cloudflare-builder.mjs +13 -3
  40. package/dist/esm-node/cloudflare-output-contract.mjs +1 -0
  41. package/dist/esm-node/cloudflare-output-verifier/identity.mjs +26 -24
  42. package/dist/esm-node/cloudflare-output-verifier/index.mjs +10 -0
  43. package/dist/esm-node/cloudflare-output-verifier/manifest.mjs +46 -0
  44. package/dist/esm-node/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  45. package/dist/esm-node/config.mjs +1 -0
  46. package/dist/esm-node/deploy-output/npmAliases.mjs +28 -3
  47. package/dist/esm-node/release-envelope/framework-output.mjs +62 -49
  48. package/dist/esm-node/release-envelope/index.mjs +10 -4
  49. package/dist/types/backend-federation-build/codegen.d.ts +30 -0
  50. package/dist/types/backend-federation-build/config.d.ts +32 -13
  51. package/dist/types/build-config/remote-address.d.ts +3 -0
  52. package/dist/types/cloudflare/delivery-unit.d.ts +1 -1
  53. package/dist/types/cloudflare-builder.d.ts +1 -0
  54. package/dist/types/cloudflare-output-contract.d.ts +1 -1
  55. package/dist/types/config.d.ts +1 -0
  56. package/package.json +20 -19
  57. package/src/backend-federation-build/codegen.ts +6 -2
  58. package/src/backend-federation-build/config.ts +207 -71
  59. package/src/backend-federation-build/emit.ts +33 -22
  60. package/src/build-config/remote-address.ts +31 -0
  61. package/src/cloudflare/delivery-unit.ts +39 -33
  62. package/src/cloudflare/index.ts +20 -6
  63. package/src/cloudflare-builder.ts +18 -4
  64. package/src/cloudflare-output-contract.ts +1 -0
  65. package/src/cloudflare-output-verifier/identity.ts +4 -0
  66. package/src/cloudflare-output-verifier/index.ts +20 -0
  67. package/src/cloudflare-output-verifier/manifest.ts +77 -0
  68. package/src/cloudflare-output-verifier/mutation-policy.ts +11 -1
  69. package/src/config.ts +1 -0
  70. package/src/deploy-output/npmAliases.ts +52 -3
  71. package/src/release-envelope/framework-output.ts +163 -90
  72. package/src/release-envelope/index.ts +26 -5
@@ -10,6 +10,11 @@ const MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_KIND = 'ultramodern-release-identi
10
10
  const MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_SCHEMA_VERSION = 1;
11
11
  const COMPILED_MODULE_PATTERN = /\.(?:c|m)?js$/u;
12
12
  const EFFECT_BFF_WORKER_PATTERN = /^worker\/__modern_bff_effect\.(?:c|m)?js$/u;
13
+ const EFFECT_BFF_WORKER_SUPPORT_PATTERN = /^worker\/__modern_worker_(?:runtime|shared)_*\.(?:c|m)?js$/u;
14
+ const isEffectBffWorkerArtifact = (logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath) || EFFECT_BFF_WORKER_SUPPORT_PATTERN.test(logicalPath);
15
+ const CRAWLER_POLICY_PATH = 'public/robots.txt';
16
+ const isPublicMetadataPath = (logicalPath)=>'public/_headers' === logicalPath || /^public\/\.well-known\/[^/]+\.json$/u.test(logicalPath);
17
+ const PUBLIC_BUILD_ARTIFACT_PATH = `public/${ULTRAMODERN_BUILD_ARTIFACT_FILE}`;
13
18
  const isRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
14
19
  const flattenStrings = (value)=>'string' == typeof value ? [
15
20
  value
@@ -130,7 +135,7 @@ const readBuildArtifact = async (distDirectory, requirePromotable = true)=>{
130
135
  if (!isUltramodernBuildArtifact(artifact)) throw new Error(`[ultramodern-release-envelope] ${ULTRAMODERN_BUILD_ARTIFACT_FILE} is missing or invalid.`);
131
136
  if ('workspace' === artifact.deliveryUnit.sourceRevision) {
132
137
  if (!requirePromotable) return;
133
- throw new Error('[ultramodern-release-envelope] sourceRevision "workspace" cannot produce a promotable full-stack envelope.');
138
+ throw new Error('[ultramodern-release-envelope] sourceRevision "workspace" cannot produce a promotable envelope.');
134
139
  }
135
140
  return artifact;
136
141
  };
@@ -200,7 +205,7 @@ const releaseArtifact = (logicalPath, runtime)=>({
200
205
  const writeReleaseIdentityCarrierMetadata = async (artifactRoot, identity, carrierPaths)=>{
201
206
  const surfacesByPath = new Map();
202
207
  for (const [surface, logicalPaths] of Object.entries(carrierPaths)){
203
- if (0 === logicalPaths.length) throw new Error(`[ultramodern-release-envelope] ${surface} has no declared release-identity carrier artifact.`);
208
+ if (0 === logicalPaths.length && 'ssr' !== surface && 'uiClient' !== surface) throw new Error(`[ultramodern-release-envelope] ${surface} has no declared release-identity carrier artifact.`);
204
209
  for (const logicalPath of logicalPaths){
205
210
  const surfaces = surfacesByPath.get(logicalPath) ?? new Set();
206
211
  surfaces.add(surface);
@@ -240,12 +245,12 @@ const writeReleaseIdentityCarrierMetadata = async (artifactRoot, identity, carri
240
245
  }, null, 2)}\n`);
241
246
  return releaseArtifact(MICROVERTICAL_RELEASE_IDENTITY_CARRIERS_PATH, 'release-identity-metadata');
242
247
  };
243
- const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
248
+ const createReleaseArtifactInputs = async (distDirectory, identity, target, apiOnly)=>{
244
249
  const files = await collectFiles(distDirectory);
245
- if ('node' === target && files.some((logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath))) throw new Error('[ultramodern-release-envelope] Node target conflicts with a Cloudflare Effect API/BFF worker artifact; resolve the canonical deploy target instead of emitting a Node envelope for Cloudflare output.');
250
+ if ('node' === target && files.some(isEffectBffWorkerArtifact)) throw new Error('[ultramodern-release-envelope] Node target conflicts with a Cloudflare Effect API/BFF worker artifact; resolve the canonical deploy target instead of emitting a Node envelope for Cloudflare output.');
246
251
  const uiClientPaths = files.filter((logicalPath)=>{
247
252
  const topLevel = logicalPath.split('/')[0];
248
- return 'static' === topLevel || 'html' === topLevel || 'public' === topLevel || [
253
+ return 'static' === topLevel || 'html' === topLevel || 'public' === topLevel && logicalPath !== CRAWLER_POLICY_PATH && !isPublicMetadataPath(logicalPath) || [
249
254
  'index.html',
250
255
  'mf-manifest.json',
251
256
  'mf-stats.json',
@@ -254,16 +259,17 @@ const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
254
259
  'loadable-stats.json'
255
260
  ].includes(logicalPath);
256
261
  });
257
- const ssrPaths = 'node' === target ? files.filter((logicalPath)=>logicalPath.startsWith('bundles/') && COMPILED_MODULE_PATTERN.test(logicalPath)) : files.filter((logicalPath)=>logicalPath.startsWith('worker/') && COMPILED_MODULE_PATTERN.test(logicalPath) && !EFFECT_BFF_WORKER_PATTERN.test(logicalPath));
262
+ const ssrPaths = 'node' === target ? files.filter((logicalPath)=>logicalPath.startsWith('bundles/') && COMPILED_MODULE_PATTERN.test(logicalPath)) : files.filter((logicalPath)=>logicalPath.startsWith('worker/') && COMPILED_MODULE_PATTERN.test(logicalPath) && !isEffectBffWorkerArtifact(logicalPath));
258
263
  const nodeApiEntryPaths = files.filter((logicalPath)=>logicalPath.startsWith('api/') && COMPILED_MODULE_PATTERN.test(logicalPath));
259
- const apiBackendPaths = 'node' === target ? files.filter((logicalPath)=>(logicalPath.startsWith('api/') || logicalPath.startsWith('shared/')) && COMPILED_MODULE_PATTERN.test(logicalPath)) : files.filter((logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath));
260
- if (0 === uiClientPaths.length) throw new Error(`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no UI/client artifacts.`);
261
- if (0 === ssrPaths.length) throw new Error(`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no ${'node' === target ? 'Node' : 'workerd'} SSR artifacts.`);
262
- if ('node' === target && 0 === nodeApiEntryPaths.length || 0 === apiBackendPaths.length) throw new Error(`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no actual ${'node' === target ? 'compiled Node Effect API artifact' : 'Effect API/BFF worker artifact'}.`);
263
- const clientExecutionPaths = await manifestReferencedClientModules(distDirectory, files);
264
- if (0 === clientExecutionPaths.length) throw new Error('[ultramodern-release-envelope] UI/client manifest references no compiled execution module.');
265
- const ssrExecutionPaths = await routeReferencedSsrModules(distDirectory, files, target);
266
- if (0 === ssrExecutionPaths.length) throw new Error(`[ultramodern-release-envelope] route manifest references no emitted ${'node' === target ? 'Node' : 'Cloudflare'} SSR execution module.`);
264
+ const apiBackendPaths = 'node' === target ? files.filter((logicalPath)=>(logicalPath.startsWith('api/') || logicalPath.startsWith('shared/')) && COMPILED_MODULE_PATTERN.test(logicalPath)) : files.filter(isEffectBffWorkerArtifact);
265
+ if (apiOnly && (uiClientPaths.length > 0 || ssrPaths.length > 0)) throw new Error('[ultramodern-release-envelope] API-only MicroVertical emitted an undeclared UI/client or SSR surface.');
266
+ if (!apiOnly && 0 === uiClientPaths.length) throw new Error(`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no UI/client artifacts.`);
267
+ if (!apiOnly && 0 === ssrPaths.length) throw new Error(`[ultramodern-release-envelope] ${target} full-stack MicroVertical has no ${'node' === target ? 'Node' : 'workerd'} SSR artifacts.`);
268
+ if ('node' === target && 0 === nodeApiEntryPaths.length || 'cloudflare' === target && !files.some((logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath)) || 0 === apiBackendPaths.length) throw new Error(`[ultramodern-release-envelope] ${target} MicroVertical has no actual ${'node' === target ? 'compiled Node Effect API artifact' : 'Effect API/BFF worker artifact'}.`);
269
+ const clientExecutionPaths = apiOnly ? [] : await manifestReferencedClientModules(distDirectory, files);
270
+ if (!apiOnly && 0 === clientExecutionPaths.length) throw new Error('[ultramodern-release-envelope] UI/client manifest references no compiled execution module.');
271
+ const ssrExecutionPaths = apiOnly ? [] : await routeReferencedSsrModules(distDirectory, files, target);
272
+ if (!apiOnly && 0 === ssrExecutionPaths.length) throw new Error(`[ultramodern-release-envelope] route manifest references no emitted ${'node' === target ? 'Node' : 'Cloudflare'} SSR execution module.`);
267
273
  const runtimeByPath = new Map();
268
274
  const add = (paths, runtime)=>{
269
275
  for (const logicalPath of paths){
@@ -273,6 +279,8 @@ const createReleaseArtifactInputs = async (distDirectory, identity, target)=>{
273
279
  }
274
280
  };
275
281
  add(uiClientPaths, 'browser');
282
+ add(files.filter((logicalPath)=>logicalPath === CRAWLER_POLICY_PATH), 'crawler-policy');
283
+ add(files.filter(isPublicMetadataPath), 'public-metadata');
276
284
  add(ssrPaths, 'node' === target ? 'nodejs' : 'workerd');
277
285
  add(apiBackendPaths, 'node' === target ? 'nodejs' : 'workerd-effect');
278
286
  add([
@@ -330,15 +338,14 @@ const pathExists = async (filePath)=>{
330
338
  }
331
339
  };
332
340
  const emitFrameworkMicroVerticalReleaseEnvelope = async ({ apiOnly, distDirectory, requirePromotable = true, target })=>{
333
- if (apiOnly) return;
334
341
  const backendManifestPath = node_path.join(distDirectory, BACKEND_FEDERATION_MANIFEST_FILE);
335
342
  const backendContainerPath = node_path.join(distDirectory, BACKEND_FEDERATION_REMOTE_ENTRY_FILE);
336
343
  const [hasBackendManifest, hasBackendContainer] = await Promise.all([
337
344
  pathExists(backendManifestPath),
338
345
  pathExists(backendContainerPath)
339
346
  ]);
340
- if (!hasBackendManifest && !hasBackendContainer) return;
341
- if (!hasBackendManifest || !hasBackendContainer) throw new Error('[ultramodern-release-envelope] full-stack MicroVertical backend federation manifest and container must be emitted together.');
347
+ if (!hasBackendManifest && !hasBackendContainer && !apiOnly) return;
348
+ if (!hasBackendManifest || !hasBackendContainer) throw new Error('[ultramodern-release-envelope] MicroVertical backend federation manifest and container must be emitted together.');
342
349
  const buildArtifact = await readBuildArtifact(distDirectory, requirePromotable);
343
350
  if (!buildArtifact) return;
344
351
  const identity = {
@@ -348,7 +355,7 @@ const emitFrameworkMicroVerticalReleaseEnvelope = async ({ apiOnly, distDirector
348
355
  releaseVersion: buildArtifact.deliveryUnit.version
349
356
  };
350
357
  await readBackendManifest(distDirectory, identity);
351
- const { artifacts, surfaces } = await createReleaseArtifactInputs(distDirectory, identity, target);
358
+ const { artifacts, surfaces } = await createReleaseArtifactInputs(distDirectory, identity, target, apiOnly);
352
359
  const envelope = await createMicroVerticalReleaseEnvelope({
353
360
  artifactRoot: distDirectory,
354
361
  target,
@@ -379,19 +386,16 @@ const readFrameworkMicroVerticalReleaseEnvelope = async ({ artifactRoot, expecte
379
386
  });
380
387
  };
381
388
  const verifyBuildOutputReleaseEnvelope = async (distDirectory, expectedTarget)=>{
382
- const [hasEnvelope, hasBuildArtifact, hasBackendManifest, hasBackendEntry] = await Promise.all([
389
+ const [hasEnvelope, hasBuildArtifact] = await Promise.all([
383
390
  pathExists(node_path.join(distDirectory, MICROVERTICAL_RELEASE_ENVELOPE_PATH)),
384
- pathExists(node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE)),
385
- pathExists(node_path.join(distDirectory, BACKEND_FEDERATION_MANIFEST_FILE)),
386
- pathExists(node_path.join(distDirectory, BACKEND_FEDERATION_REMOTE_ENTRY_FILE))
391
+ pathExists(node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE))
387
392
  ]);
388
- const isFullStackMicroVertical = hasBuildArtifact && (hasBackendManifest || hasBackendEntry);
389
393
  return readFrameworkMicroVerticalReleaseEnvelope({
390
394
  artifactRoot: distDirectory,
391
395
  ...expectedTarget ? {
392
396
  expectedTarget
393
397
  } : {},
394
- required: hasEnvelope || isFullStackMicroVertical
398
+ required: hasEnvelope || hasBuildArtifact
395
399
  });
396
400
  };
397
401
  const verifyNodeReleaseEnvelopeStaging = async ({ outputDirectory })=>readFrameworkMicroVerticalReleaseEnvelope({
@@ -399,7 +403,7 @@ const verifyNodeReleaseEnvelopeStaging = async ({ outputDirectory })=>readFramew
399
403
  expectedTarget: 'node',
400
404
  required: true
401
405
  });
402
- const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=>{
406
+ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity, apiOnly)=>{
403
407
  const files = await collectFiles(outputDirectory);
404
408
  for (const requiredPath of [
405
409
  'index.js',
@@ -413,7 +417,7 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
413
417
  ]);
414
418
  const uiClientPaths = files.filter((logicalPath)=>{
415
419
  const topLevel = logicalPath.split('/')[0];
416
- return 'static' === topLevel || 'html' === topLevel || 'public' === topLevel || [
420
+ return 'static' === topLevel || 'html' === topLevel || 'public' === topLevel && logicalPath !== CRAWLER_POLICY_PATH && !isPublicMetadataPath(logicalPath) || [
417
421
  'index.html',
418
422
  'mf-manifest.json',
419
423
  'mf-stats.json',
@@ -424,6 +428,7 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
424
428
  });
425
429
  const ssrPaths = files.filter((logicalPath)=>'index.js' === logicalPath || logicalPath.startsWith('bundles/') && COMPILED_MODULE_PATTERN.test(logicalPath));
426
430
  const apiBackendPaths = files.filter((logicalPath)=>(logicalPath.startsWith('api/') || logicalPath.startsWith('shared/')) && COMPILED_MODULE_PATTERN.test(logicalPath));
431
+ if (apiOnly && (uiClientPaths.length > 0 || ssrPaths.some((path)=>'index.js' !== path))) throw new Error('[ultramodern-release-envelope] final API-only Node staging contains an undeclared UI/client or SSR surface.');
427
432
  for (const [surface, paths] of [
428
433
  [
429
434
  'UI/client',
@@ -431,22 +436,24 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
431
436
  ],
432
437
  [
433
438
  'Node SSR',
434
- ssrPaths
439
+ apiOnly ? [] : ssrPaths
435
440
  ],
436
441
  [
437
442
  'Node API/backend',
438
443
  apiBackendPaths
439
444
  ]
440
- ])if (0 === paths.length) throw new Error(`[ultramodern-release-envelope] final Node staging has no ${surface} artifacts.`);
441
- const clientExecutionPaths = await manifestReferencedClientModules(outputDirectory, files);
442
- const ssrExecutionPaths = await routeReferencedSsrModules(outputDirectory, files, 'node');
443
- if (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length) throw new Error('[ultramodern-release-envelope] final Node manifests must reference emitted UI/client and SSR execution modules.');
445
+ ])if (0 === paths.length && (!apiOnly || 'Node API/backend' === surface)) throw new Error(`[ultramodern-release-envelope] final Node staging has no ${surface} artifacts.`);
446
+ const clientExecutionPaths = apiOnly ? [] : await manifestReferencedClientModules(outputDirectory, files);
447
+ const ssrExecutionPaths = apiOnly ? [] : await routeReferencedSsrModules(outputDirectory, files, 'node');
448
+ if (!apiOnly && (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length)) throw new Error('[ultramodern-release-envelope] final Node manifests must reference emitted UI/client and SSR execution modules.');
444
449
  const runtimeByPath = new Map(files.filter((logicalPath)=>!backendFederationPaths.has(logicalPath)).map((logicalPath)=>[
445
450
  logicalPath,
446
451
  'nodejs-deployment'
447
452
  ]));
448
453
  for (const logicalPath of uiClientPaths)runtimeByPath.set(logicalPath, 'browser');
449
- for (const logicalPath of ssrPaths)runtimeByPath.set(logicalPath, 'nodejs');
454
+ if (files.includes(CRAWLER_POLICY_PATH)) runtimeByPath.set(CRAWLER_POLICY_PATH, 'crawler-policy');
455
+ for (const logicalPath of files.filter(isPublicMetadataPath))runtimeByPath.set(logicalPath, 'public-metadata');
456
+ for (const logicalPath of apiOnly ? [] : ssrPaths)runtimeByPath.set(logicalPath, 'nodejs');
450
457
  for (const logicalPath of apiBackendPaths)runtimeByPath.set(logicalPath, 'nodejs');
451
458
  runtimeByPath.set(BACKEND_FEDERATION_MANIFEST_FILE, 'module-federation-manifest');
452
459
  runtimeByPath.set(BACKEND_FEDERATION_REMOTE_ENTRY_FILE, 'nodejs');
@@ -456,7 +463,7 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
456
463
  BACKEND_FEDERATION_MANIFEST_FILE,
457
464
  BACKEND_FEDERATION_REMOTE_ENTRY_FILE
458
465
  ],
459
- ssr: [
466
+ ssr: apiOnly ? [] : [
460
467
  ...new Set([
461
468
  ...ssrExecutionPaths,
462
469
  ...ssrPaths.filter((logicalPath)=>'index.js' !== logicalPath)
@@ -475,7 +482,7 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
475
482
  uiClient: [
476
483
  ...uiClientPaths
477
484
  ].sort((left, right)=>left.localeCompare(right)),
478
- ssr: [
485
+ ssr: apiOnly ? [] : [
479
486
  ...ssrPaths
480
487
  ].sort((left, right)=>left.localeCompare(right)),
481
488
  apiBackend: [
@@ -491,7 +498,7 @@ const createNodeStagedReleaseArtifactInputs = async (outputDirectory, identity)=
491
498
  const emitNodeStagedReleaseEnvelope = async ({ distDirectory, outputDirectory })=>{
492
499
  const source = await verifyBuildOutputReleaseEnvelope(distDirectory, 'node');
493
500
  if (!source) return;
494
- const { artifacts, surfaces } = await createNodeStagedReleaseArtifactInputs(outputDirectory, source.identity);
501
+ const { artifacts, surfaces } = await createNodeStagedReleaseArtifactInputs(outputDirectory, source.identity, 0 === source.surfaces.uiClient.length);
495
502
  const staged = await createMicroVerticalReleaseEnvelope({
496
503
  artifactRoot: outputDirectory,
497
504
  target: 'node',
@@ -509,21 +516,25 @@ const stageCloudflareReleaseEnvelope = async ({ distDirectory, outputDirectory }
509
516
  if (await pathExists(stagedEnvelopePath)) throw new Error('[ultramodern-release-envelope] Cloudflare staging contains a stale envelope before final output generation.');
510
517
  return source;
511
518
  };
512
- const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, identity)=>{
519
+ const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, identity, apiOnly)=>{
513
520
  const files = await collectFiles(outputDirectory);
514
521
  for (const requiredPath of [
515
522
  'server/index.mjs',
516
523
  'server/modern-worker-manifest.json',
517
524
  'wrangler.json',
518
525
  'package.json',
519
- 'worker/package.json'
526
+ ...apiOnly ? [] : [
527
+ 'worker/package.json'
528
+ ]
520
529
  ])if (!files.includes(requiredPath)) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging is missing ${requiredPath}.`);
521
530
  if (files.some((logicalPath)=>'public/release' === logicalPath || logicalPath.startsWith('public/release/'))) throw new Error('[ultramodern-release-envelope] final Cloudflare envelope must remain private and cannot be staged under public/release.');
522
531
  const backendManifestPath = `public/${BACKEND_FEDERATION_MANIFEST_FILE}`;
523
532
  const backendContainerPath = `public/${BACKEND_FEDERATION_REMOTE_ENTRY_FILE}`;
524
- const apiBackendPaths = files.filter((logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath));
525
- const ssrPaths = files.filter((logicalPath)=>(logicalPath.startsWith('server/') || logicalPath.startsWith('worker/')) && COMPILED_MODULE_PATTERN.test(logicalPath) && !EFFECT_BFF_WORKER_PATTERN.test(logicalPath));
526
- const uiClientPaths = files.filter((logicalPath)=>logicalPath.startsWith('public/') && logicalPath !== backendManifestPath && logicalPath !== backendContainerPath);
533
+ const apiBackendPaths = files.filter(isEffectBffWorkerArtifact);
534
+ if (!files.some((logicalPath)=>EFFECT_BFF_WORKER_PATTERN.test(logicalPath))) throw new Error('[ultramodern-release-envelope] final Cloudflare staging has no actual Effect API/BFF worker artifact.');
535
+ const ssrPaths = files.filter((logicalPath)=>(logicalPath.startsWith('server/') || logicalPath.startsWith('worker/')) && COMPILED_MODULE_PATTERN.test(logicalPath) && !isEffectBffWorkerArtifact(logicalPath));
536
+ const uiClientPaths = files.filter((logicalPath)=>logicalPath.startsWith('public/') && logicalPath !== CRAWLER_POLICY_PATH && !isPublicMetadataPath(logicalPath) && logicalPath !== PUBLIC_BUILD_ARTIFACT_PATH && logicalPath !== backendManifestPath && logicalPath !== backendContainerPath);
537
+ if (apiOnly && (uiClientPaths.length > 0 || ssrPaths.some((path)=>'server/index.mjs' !== path))) throw new Error('[ultramodern-release-envelope] final API-only Cloudflare staging contains an undeclared UI/client or SSR surface.');
527
538
  for (const [surface, paths] of [
528
539
  [
529
540
  'UI/client',
@@ -531,27 +542,29 @@ const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, iden
531
542
  ],
532
543
  [
533
544
  'Cloudflare SSR',
534
- ssrPaths
545
+ apiOnly ? [] : ssrPaths
535
546
  ],
536
547
  [
537
548
  'Cloudflare API/backend',
538
549
  apiBackendPaths
539
550
  ]
540
- ])if (0 === paths.length) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging has no ${surface} artifacts.`);
551
+ ])if (0 === paths.length && (!apiOnly || 'Cloudflare API/backend' === surface)) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging has no ${surface} artifacts.`);
541
552
  for (const logicalPath of [
542
553
  backendManifestPath,
543
554
  backendContainerPath
544
555
  ])if (!files.includes(logicalPath)) throw new Error(`[ultramodern-release-envelope] final Cloudflare staging is missing ${logicalPath}.`);
545
- const clientExecutionPaths = await manifestReferencedClientModules(outputDirectory, files, 'public/mf-manifest.json');
546
- const ssrExecutionPaths = await routeReferencedSsrModules(outputDirectory, files, 'cloudflare', 'server/route.json');
547
- if (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length) throw new Error('[ultramodern-release-envelope] final Cloudflare manifests must reference emitted UI/client and SSR execution modules.');
556
+ const clientExecutionPaths = apiOnly ? [] : await manifestReferencedClientModules(outputDirectory, files, 'public/mf-manifest.json');
557
+ const ssrExecutionPaths = apiOnly ? [] : await routeReferencedSsrModules(outputDirectory, files, 'cloudflare', 'server/route.json');
558
+ if (!apiOnly && (0 === clientExecutionPaths.length || 0 === ssrExecutionPaths.length)) throw new Error('[ultramodern-release-envelope] final Cloudflare manifests must reference emitted UI/client and SSR execution modules.');
548
559
  const runtimeByPath = new Map();
549
560
  for (const logicalPath of files)runtimeByPath.set(logicalPath, 'cloudflare-deployment');
550
561
  const add = (paths, runtime)=>{
551
562
  for (const logicalPath of paths)runtimeByPath.set(logicalPath, runtime);
552
563
  };
553
564
  add(uiClientPaths, 'browser');
554
- add(ssrPaths, 'workerd');
565
+ if (files.includes(CRAWLER_POLICY_PATH)) runtimeByPath.set(CRAWLER_POLICY_PATH, 'crawler-policy');
566
+ add(files.filter(isPublicMetadataPath), 'public-metadata');
567
+ add(apiOnly ? [] : ssrPaths, 'workerd');
555
568
  add(apiBackendPaths, 'workerd-effect');
556
569
  add([
557
570
  backendManifestPath
@@ -565,7 +578,7 @@ const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, iden
565
578
  backendManifestPath,
566
579
  backendContainerPath
567
580
  ],
568
- ssr: [
581
+ ssr: apiOnly ? [] : [
569
582
  ...new Set([
570
583
  ...ssrExecutionPaths,
571
584
  ...ssrPaths.filter((logicalPath)=>logicalPath.startsWith('worker/'))
@@ -584,7 +597,7 @@ const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, iden
584
597
  uiClient: [
585
598
  ...uiClientPaths
586
599
  ].sort((left, right)=>left.localeCompare(right)),
587
- ssr: [
600
+ ssr: apiOnly ? [] : [
588
601
  ...ssrPaths
589
602
  ].sort((left, right)=>left.localeCompare(right)),
590
603
  apiBackend: [
@@ -600,7 +613,7 @@ const createCloudflareStagedReleaseArtifactInputs = async (outputDirectory, iden
600
613
  const emitCloudflareStagedReleaseEnvelope = async ({ distDirectory, outputDirectory })=>{
601
614
  const source = await verifyBuildOutputReleaseEnvelope(distDirectory, 'cloudflare');
602
615
  if (!source) return;
603
- const { artifacts, surfaces } = await createCloudflareStagedReleaseArtifactInputs(outputDirectory, source.identity);
616
+ const { artifacts, surfaces } = await createCloudflareStagedReleaseArtifactInputs(outputDirectory, source.identity, 0 === source.surfaces.uiClient.length);
604
617
  const staged = await createMicroVerticalReleaseEnvelope({
605
618
  artifactRoot: outputDirectory,
606
619
  target: 'cloudflare',
@@ -185,7 +185,7 @@ const assertUniqueSortedArtifacts = (artifacts, location)=>{
185
185
  if (paths.some((logicalPath, index)=>logicalPath !== sorted[index])) throw new Error(`${location} must be sorted by logicalPath.`);
186
186
  };
187
187
  const assertSurfacePaths = (value, location)=>{
188
- if (!Array.isArray(value) || 0 === value.length) throw new Error(`${location} must contain at least one artifact path.`);
188
+ if (!Array.isArray(value)) throw new Error(`${location} must be an array of artifact paths.`);
189
189
  const paths = value.map((item, index)=>assertNormalizedLogicalPath(item, `${location}[${index}]`));
190
190
  const sorted = [
191
191
  ...paths
@@ -207,10 +207,16 @@ const assertSurfaces = (value)=>{
207
207
  'manifest',
208
208
  'container'
209
209
  ], 'surfaces.backendFederation');
210
+ const uiClient = assertSurfacePaths(surfaces.uiClient, 'surfaces.uiClient');
211
+ const ssr = assertSurfacePaths(surfaces.ssr, 'surfaces.ssr');
212
+ const apiBackend = assertSurfacePaths(surfaces.apiBackend, 'surfaces.apiBackend');
213
+ if (uiClient.length > 0 && 0 === ssr.length) throw new Error('surfaces.ssr must contain at least one artifact path when UI/client is declared.');
214
+ if (ssr.length > 0 && 0 === uiClient.length) throw new Error('surfaces.uiClient must contain at least one artifact path when SSR is declared.');
215
+ if (0 === apiBackend.length) throw new Error('surfaces.apiBackend must contain at least one artifact path.');
210
216
  return {
211
- uiClient: assertSurfacePaths(surfaces.uiClient, 'surfaces.uiClient'),
212
- ssr: assertSurfacePaths(surfaces.ssr, 'surfaces.ssr'),
213
- apiBackend: assertSurfacePaths(surfaces.apiBackend, 'surfaces.apiBackend'),
217
+ uiClient,
218
+ ssr,
219
+ apiBackend,
214
220
  backendFederation: {
215
221
  manifest: assertNormalizedLogicalPath(backendFederation.manifest, 'surfaces.backendFederation.manifest'),
216
222
  container: assertNormalizedLogicalPath(backendFederation.container, 'surfaces.backendFederation.container')
@@ -53,6 +53,36 @@ export declare const createBackendManifest: (workspaceRoot: string, distDirector
53
53
  }[];
54
54
  shared: never[];
55
55
  backendFederation: {
56
+ role: string;
57
+ appId: string;
58
+ name: string;
59
+ runtimeFramework: string;
60
+ strictEffectApproach: boolean;
61
+ contractVersion: string;
62
+ nodeAdapterVersion: string;
63
+ remoteType: string;
64
+ expose: string;
65
+ manifestUrl: string;
66
+ containerEntry: string;
67
+ source: {
68
+ module: string;
69
+ };
70
+ rpcPath: string | undefined;
71
+ rpcSerialization: string;
72
+ deliveryUnit?: import("@modern-js/backend-federation-contracts").DeliveryUnitContractBlock | undefined;
73
+ versionBoundary: {
74
+ invariant: string;
75
+ packageName: string | undefined;
76
+ version: string | undefined;
77
+ buildVersion: string | undefined;
78
+ uiManifestUrl?: string | undefined;
79
+ deliveryUnit?: {
80
+ unitId: string;
81
+ buildMarker: string;
82
+ sourceRevision: string;
83
+ } | undefined;
84
+ };
85
+ } | {
56
86
  role: string;
57
87
  appId: string;
58
88
  name: string;
@@ -1,13 +1,23 @@
1
1
  import { type DeliveryUnitContractBlock, type UltramodernBuildArtifact } from '@modern-js/backend-federation-contracts';
2
- export declare const COMPACT_CONFIG_PATH = ".modernjs/ultramodern.json";
3
- export type CompactApp = {
2
+ export declare const REFERENCE_TOPOLOGY_PATH = "topology/reference-topology.json";
3
+ export declare const DEVELOPMENT_OVERLAY_PATH = "topology/local-overlays/development.json";
4
+ export type TopologyApp = {
4
5
  id?: unknown;
6
+ domain?: unknown;
5
7
  kind?: unknown;
6
8
  path?: unknown;
7
9
  package?: unknown;
8
- port?: unknown;
10
+ portEnv?: unknown;
11
+ cloudflare?: {
12
+ publicUrlEnv?: unknown;
13
+ };
9
14
  api?: {
10
- prefix?: unknown;
15
+ bff?: {
16
+ prefix?: unknown;
17
+ };
18
+ protocol?: unknown;
19
+ rpcPath?: unknown;
20
+ rpcSerialization?: unknown;
11
21
  stem?: unknown;
12
22
  };
13
23
  moduleFederation?: {
@@ -37,19 +47,23 @@ export type CompactApp = {
37
47
  packageName?: unknown;
38
48
  version?: unknown;
39
49
  };
40
- serverExecution?: {
50
+ };
51
+ export type DevelopmentOverlay = {
52
+ ports?: Record<string, unknown>;
53
+ manifests?: Record<string, unknown>;
54
+ serverExecution?: Record<string, {
41
55
  node?: {
42
56
  remoteName?: unknown;
43
57
  manifestUrl?: unknown;
44
58
  containerEntry?: unknown;
45
59
  remoteType?: unknown;
46
60
  };
47
- };
61
+ }>;
48
62
  };
49
- export type CompactConfig = {
50
- topology?: {
51
- apps?: CompactApp[];
52
- };
63
+ export type ReferenceTopology = {
64
+ shell?: TopologyApp;
65
+ shells?: TopologyApp[];
66
+ verticals?: TopologyApp[];
53
67
  };
54
68
  export type BackendFederationApp = {
55
69
  id: string;
@@ -62,13 +76,15 @@ export type BackendFederationApp = {
62
76
  deliveryUnit?: DeliveryUnitContractBlock;
63
77
  port: number;
64
78
  apiPrefix: string;
79
+ apiProtocol: 'rest' | 'rpc';
80
+ rpcPath?: string;
65
81
  apiStem: string;
66
82
  backendName: string;
67
83
  manifestUrl: string;
68
84
  containerEntry: string;
69
85
  remoteType: string;
70
86
  uiManifestUrl?: string;
71
- compactDeliveryUnit?: {
87
+ topologyDeliveryUnit?: {
72
88
  unitId?: string;
73
89
  buildMarker?: string;
74
90
  sourceRevision?: string;
@@ -98,5 +114,8 @@ export declare const createStampedDeliveryUnit: (input: {
98
114
  version?: string;
99
115
  sourceRevision: string;
100
116
  }) => DeliveryUnitContractBlock | undefined;
101
- export declare const createAppFromCompactMetadata: (workspaceRoot: string, appDirectory: string, compactApp: CompactApp) => BackendFederationApp | undefined;
102
- export declare const findBackendFederationApp: (workspaceRoot: string, appDirectory: string) => Promise<BackendFederationApp | undefined>;
117
+ export declare const createAppFromTopology: (workspaceRoot: string, appDirectory: string, topologyApp: TopologyApp, overlay: DevelopmentOverlay, packageManifest: {
118
+ name?: unknown;
119
+ version?: unknown;
120
+ }) => BackendFederationApp | undefined;
121
+ export declare const findBackendFederationApp: (workspaceRoot: string, appDirectory: string) => Promise<BackendFederationApp>;
@@ -0,0 +1,3 @@
1
+ import { type BrowserManifestAddressOptions } from '@modern-js/surface-resolution';
2
+ /** Adapt build environment leases and MF's name@URL syntax to discovery policy. */
3
+ export declare function createRemoteManifestUrl(options: BrowserManifestAddressOptions): string;
@@ -11,7 +11,7 @@ export type DeliveryUnitStamp = DeliveryUnitIdentity & {
11
11
  };
12
12
  /**
13
13
  * Resolve the delivery-unit record declared for this app by the workspace
14
- * compact config (`.modernjs/ultramodern.json`). This is the topology source
14
+ * reference topology. This is the topology source
15
15
  * of truth the Cloudflare worker snapshot is verified against.
16
16
  */
17
17
  export declare const resolveTopologyDeliveryUnit: (appDirectory: string) => Promise<DeliveryUnitStamp | undefined>;
@@ -7,6 +7,7 @@ export interface CloudflareBuilderNormalizedConfig {
7
7
  };
8
8
  }
9
9
  export interface CloudflareBuilderAppContext {
10
+ apiOnly?: boolean;
10
11
  apiDirectory: string;
11
12
  appDirectory: string;
12
13
  }
@@ -10,6 +10,6 @@ export declare const CLOUDFLARE_RUNTIME_TYPE = "cloudflare-module-worker";
10
10
  export declare const CLOUDFLARE_OUTPUT_PACKAGE_TYPE = "module";
11
11
  export declare const CLOUDFLARE_WORKER_PACKAGE_TYPE = "commonjs";
12
12
  export declare const CLOUDFLARE_WORKER_BUNDLE_FORMAT = "commonjs";
13
- export declare const CLOUDFLARE_WORKER_NODE_BUILTINS: readonly ['assert', 'assert/strict', 'async_hooks', 'buffer', 'child_process', 'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'fs/promises', 'http', 'http2', 'inspector', 'module', 'net', 'os', 'path', 'process', 'readline', 'readline/promises', 'repl', 'sqlite', 'stream', 'stream/consumers', 'stream/promises', 'stream/web', 'string_decoder', 'timers', 'timers/promises', 'tls', 'trace_events', 'tty', 'url', 'util', 'util/types', 'v8', 'vm', 'wasi', 'worker_threads', 'zlib'];
13
+ export declare const CLOUDFLARE_WORKER_NODE_BUILTINS: readonly ['assert', 'assert/strict', 'async_hooks', 'buffer', 'child_process', 'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'fs/promises', 'http', 'http2', 'https', 'inspector', 'module', 'net', 'os', 'path', 'process', 'readline', 'readline/promises', 'repl', 'sqlite', 'stream', 'stream/consumers', 'stream/promises', 'stream/web', 'string_decoder', 'timers', 'timers/promises', 'tls', 'trace_events', 'tty', 'url', 'util', 'util/types', 'v8', 'vm', 'wasi', 'worker_threads', 'zlib'];
14
14
  export declare const CLOUDFLARE_WORKER_PLATFORM_MODULES: readonly ['cloudflare:sockets'];
15
15
  export declare const CLOUDFLARE_REQUIRED_COMPATIBILITY_FLAGS: readonly ['nodejs_compat', 'global_fetch_strictly_public'];
@@ -1,5 +1,6 @@
1
1
  export type { ResolveEffectTsgoCompilerOptions } from './build-config/public.js';
2
2
  export { getBuildConfigEnvironment, resolveEffectTsgoCompiler, withBuildConfigEnvironment, } from './build-config/public.js';
3
+ export { createRemoteManifestUrl } from './build-config/remote-address.js';
3
4
  export type DeployTarget = 'node' | 'vercel' | 'netlify' | 'ghPages' | 'cloudflare';
4
5
  export type CloudflareWorkerSecurityCspMode = 'enforce' | 'report-only' | 'off';
5
6
  export type JsonValue = string | number | boolean | null | JsonValue[] | {
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "directory": "packages/solutions/app-tools-extensions"
12
12
  },
13
13
  "license": "MIT",
14
- "version": "3.9.0-ultramodern.11",
14
+ "version": "3.9.0-ultramodern.13",
15
15
  "files": [
16
16
  "dist",
17
17
  "src"
@@ -142,37 +142,38 @@
142
142
  "node": ">=26.7.0"
143
143
  },
144
144
  "dependencies": {
145
- "@babel/parser": "^8.0.4",
146
- "@babel/traverse": "^8.0.4",
145
+ "@babel/parser": "^8.0.6",
146
+ "@babel/traverse": "^8.0.6",
147
147
  "@loadable/component": "5.16.7",
148
- "@modern-js/backend-federation-contracts": "npm:@bleedingdev/modern-js-backend-federation-contracts@3.9.0-ultramodern.11",
149
- "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.0-ultramodern.11",
150
- "@modern-js/prod-server": "npm:@bleedingdev/modern-js-prod-server@3.9.0-ultramodern.11",
151
- "@modern-js/runtime-renderer-extensions": "npm:@bleedingdev/modern-js-runtime-renderer-extensions@3.9.0-ultramodern.11",
152
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.11",
153
- "@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.0-ultramodern.11",
154
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.11",
155
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.11",
148
+ "@modern-js/backend-federation-contracts": "npm:@bleedingdev/modern-js-backend-federation-contracts@3.9.0-ultramodern.13",
149
+ "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.0-ultramodern.13",
150
+ "@modern-js/prod-server": "npm:@bleedingdev/modern-js-prod-server@3.9.0-ultramodern.13",
151
+ "@modern-js/runtime-renderer-extensions": "npm:@bleedingdev/modern-js-runtime-renderer-extensions@3.9.0-ultramodern.13",
152
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.13",
153
+ "@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.0-ultramodern.13",
154
+ "@modern-js/surface-resolution": "npm:@bleedingdev/modern-js-surface-resolution@3.9.0-ultramodern.13",
155
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.13",
156
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.13",
156
157
  "@swc/helpers": "^0.5.23",
157
158
  "compression-webpack-plugin": "^12.0.0",
158
159
  "esbuild": "^0.28.2",
159
160
  "std-env": "4.2.0"
160
161
  },
161
162
  "peerDependencies": {
162
- "@rsbuild/core": "2.2.3"
163
+ "@rsbuild/core": "2.2.9"
163
164
  },
164
165
  "devDependencies": {
165
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.11",
166
- "@modern-js/plugin-bff-extensions": "npm:@bleedingdev/modern-js-plugin-bff-extensions@3.9.0-ultramodern.11",
167
- "@module-federation/runtime-tools": "2.9.0",
168
- "@rsbuild/core": "2.2.3",
169
- "@rslib/core": "1.0.0",
166
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.13",
167
+ "@modern-js/plugin-bff-extensions": "npm:@bleedingdev/modern-js-plugin-bff-extensions@3.9.0-ultramodern.13",
168
+ "@module-federation/runtime-tools": "npm:@bleedingdev/mf-runtime-tools@2.9.1",
169
+ "@rsbuild/core": "2.2.9",
170
+ "@rslib/core": "1.0.2",
170
171
  "@scripts/rstest-config": "2.66.0",
171
172
  "@types/babel__traverse": "7.28.0",
172
- "@types/node": "^26.4.1",
173
+ "@types/node": "^26.6.2",
173
174
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
174
175
  "core-js": "^3.50.0",
175
- "effect": "4.0.0-rc.112",
176
+ "effect": "npm:@bleedingdev/effect@4.0.0-rc.117",
176
177
  "ndepe": "^0.1.13"
177
178
  },
178
179
  "sideEffects": false,
@@ -204,8 +204,12 @@ export const createBackendManifest = (
204
204
  source: {
205
205
  module: sourceModule,
206
206
  },
207
- readinessPath: `${app.apiPrefix}/${app.apiStem}/readiness`,
208
- openapiPath: `${app.apiPrefix}/openapi.json`,
207
+ ...(app.apiProtocol === 'rpc'
208
+ ? { rpcPath: app.rpcPath, rpcSerialization: 'json' }
209
+ : {
210
+ readinessPath: `${app.apiPrefix}/${app.apiStem}/readiness`,
211
+ openapiPath: `${app.apiPrefix}/openapi.json`,
212
+ }),
209
213
  ...(app.deliveryUnit ? { deliveryUnit: app.deliveryUnit } : {}),
210
214
  versionBoundary: {
211
215
  invariant: 'web-and-api-same-build',