@bleedingdev/modern-js-create 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.
Files changed (79) hide show
  1. package/dist/cjs/ultramodern-tooling/commands.cjs +497 -155
  2. package/dist/cjs/ultramodern-tooling/config.cjs +149 -8
  3. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +25 -0
  4. package/dist/cjs/ultramodern-workspace/api.cjs +44 -0
  5. package/dist/cjs/ultramodern-workspace/backend-federation.cjs +282 -0
  6. package/dist/cjs/ultramodern-workspace/contracts.cjs +24 -8
  7. package/dist/cjs/ultramodern-workspace/delivery-unit-sync.cjs +157 -0
  8. package/dist/cjs/ultramodern-workspace/delivery-unit.cjs +88 -0
  9. package/dist/cjs/ultramodern-workspace/descriptors.cjs +24 -0
  10. package/dist/cjs/ultramodern-workspace/module-federation.cjs +122 -44
  11. package/dist/cjs/ultramodern-workspace/package-json.cjs +11 -34
  12. package/dist/cjs/ultramodern-workspace/pnpm-workspace-policy-plan.cjs +166 -0
  13. package/dist/cjs/ultramodern-workspace/tooling-command-catalog.cjs +153 -0
  14. package/dist/cjs/ultramodern-workspace/workspace-script-plan.cjs +175 -0
  15. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +43 -62
  16. package/dist/cjs/ultramodern-workspace/workspace-validation-contract.cjs +97 -0
  17. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +9 -3
  18. package/dist/cjs/ultramodern-workspace/zerops.cjs +100 -0
  19. package/dist/esm/ultramodern-tooling/commands.js +500 -158
  20. package/dist/esm/ultramodern-tooling/config.js +144 -9
  21. package/dist/esm/ultramodern-workspace/add-vertical.js +26 -1
  22. package/dist/esm/ultramodern-workspace/api.js +43 -2
  23. package/dist/esm/ultramodern-workspace/backend-federation.js +217 -0
  24. package/dist/esm/ultramodern-workspace/contracts.js +24 -8
  25. package/dist/esm/ultramodern-workspace/delivery-unit-sync.js +108 -0
  26. package/dist/esm/ultramodern-workspace/delivery-unit.js +31 -0
  27. package/dist/esm/ultramodern-workspace/descriptors.js +13 -1
  28. package/dist/esm/ultramodern-workspace/module-federation.js +121 -36
  29. package/dist/esm/ultramodern-workspace/package-json.js +11 -31
  30. package/dist/esm/ultramodern-workspace/pnpm-workspace-policy-plan.js +113 -0
  31. package/dist/esm/ultramodern-workspace/tooling-command-catalog.js +100 -0
  32. package/dist/esm/ultramodern-workspace/workspace-script-plan.js +110 -0
  33. package/dist/esm/ultramodern-workspace/workspace-scripts.js +35 -63
  34. package/dist/esm/ultramodern-workspace/workspace-validation-contract.js +59 -0
  35. package/dist/esm/ultramodern-workspace/write-workspace.js +11 -5
  36. package/dist/esm/ultramodern-workspace/zerops.js +62 -0
  37. package/dist/esm-node/ultramodern-tooling/commands.js +500 -158
  38. package/dist/esm-node/ultramodern-tooling/config.js +144 -9
  39. package/dist/esm-node/ultramodern-workspace/add-vertical.js +26 -1
  40. package/dist/esm-node/ultramodern-workspace/api.js +43 -2
  41. package/dist/esm-node/ultramodern-workspace/backend-federation.js +218 -0
  42. package/dist/esm-node/ultramodern-workspace/contracts.js +24 -8
  43. package/dist/esm-node/ultramodern-workspace/delivery-unit-sync.js +109 -0
  44. package/dist/esm-node/ultramodern-workspace/delivery-unit.js +32 -0
  45. package/dist/esm-node/ultramodern-workspace/descriptors.js +13 -1
  46. package/dist/esm-node/ultramodern-workspace/module-federation.js +121 -36
  47. package/dist/esm-node/ultramodern-workspace/package-json.js +11 -31
  48. package/dist/esm-node/ultramodern-workspace/pnpm-workspace-policy-plan.js +114 -0
  49. package/dist/esm-node/ultramodern-workspace/tooling-command-catalog.js +101 -0
  50. package/dist/esm-node/ultramodern-workspace/workspace-script-plan.js +111 -0
  51. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +35 -63
  52. package/dist/esm-node/ultramodern-workspace/workspace-validation-contract.js +60 -0
  53. package/dist/esm-node/ultramodern-workspace/write-workspace.js +11 -5
  54. package/dist/esm-node/ultramodern-workspace/zerops.js +63 -0
  55. package/dist/types/ultramodern-tooling/config.d.ts +6 -0
  56. package/dist/types/ultramodern-workspace/api.d.ts +1 -0
  57. package/dist/types/ultramodern-workspace/backend-federation.d.ts +11 -0
  58. package/dist/types/ultramodern-workspace/contracts.d.ts +1 -1
  59. package/dist/types/ultramodern-workspace/delivery-unit-sync.d.ts +6 -0
  60. package/dist/types/ultramodern-workspace/delivery-unit.d.ts +27 -0
  61. package/dist/types/ultramodern-workspace/descriptors.d.ts +4 -0
  62. package/dist/types/ultramodern-workspace/module-federation.d.ts +4 -9
  63. package/dist/types/ultramodern-workspace/package-json.d.ts +0 -1
  64. package/dist/types/ultramodern-workspace/pnpm-workspace-policy-plan.d.ts +39 -0
  65. package/dist/types/ultramodern-workspace/tooling-command-catalog.d.ts +18 -0
  66. package/dist/types/ultramodern-workspace/workspace-script-plan.d.ts +72 -0
  67. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +3 -0
  68. package/dist/types/ultramodern-workspace/workspace-validation-contract.d.ts +42 -0
  69. package/dist/types/ultramodern-workspace/zerops.d.ts +2 -0
  70. package/package.json +3 -3
  71. package/template-workspace/.gitignore.handlebars +1 -0
  72. package/template-workspace/scripts/bootstrap-agent-skills.mjs +11 -3
  73. package/templates/workspace-scripts/check-ultramodern-api-boundaries.mts +40 -2
  74. package/templates/workspace-scripts/generate-node-backend-federation.mjs +270 -0
  75. package/templates/workspace-scripts/materialize-zerops-runtime.mjs +448 -0
  76. package/templates/workspace-scripts/proof-cloudflare-version.mjs +234 -8
  77. package/templates/workspace-scripts/proof-node-backend-federation.mjs +729 -0
  78. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +188 -2
  79. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +322 -8
@@ -0,0 +1,108 @@
1
+ import node_fs from "node:fs";
2
+ import node_path from "node:path";
3
+ import { readUltramodernConfig, workspaceAppsFromToolingConfig } from "../ultramodern-tooling/config.js";
4
+ import { createBackendFederationContract } from "./backend-federation.js";
5
+ import { createDeliveryUnitRecord, deliveryUnitContractBlock } from "./delivery-unit.js";
6
+ import { ULTRAMODERN_CONFIG_PATH } from "./descriptors.js";
7
+ import { createUltramodernBuildModule } from "./module-federation.js";
8
+ const REFERENCE_TOPOLOGY_PATH = 'topology/reference-topology.json';
9
+ function readOption(args, name) {
10
+ const prefix = `${name}=`;
11
+ const inline = args.find((arg)=>arg.startsWith(prefix));
12
+ if (inline) {
13
+ const value = inline.slice(prefix.length);
14
+ if (!value) throw new Error(`${name} needs a value.`);
15
+ return value;
16
+ }
17
+ const index = args.indexOf(name);
18
+ if (-1 === index) return;
19
+ const value = args[index + 1];
20
+ if (!value || value.startsWith('--')) throw new Error(`${name} needs a value.`);
21
+ return value;
22
+ }
23
+ function isPlainObject(value) {
24
+ return 'object' == typeof value && null !== value && !Array.isArray(value);
25
+ }
26
+ function writeTextIfChanged(absolutePath, content) {
27
+ if (node_fs.existsSync(absolutePath) && node_fs.readFileSync(absolutePath, 'utf-8') === content) return false;
28
+ node_fs.mkdirSync(node_path.dirname(absolutePath), {
29
+ recursive: true
30
+ });
31
+ node_fs.writeFileSync(absolutePath, content, 'utf-8');
32
+ return true;
33
+ }
34
+ function writeJsonIfChanged(absolutePath, value) {
35
+ return writeTextIfChanged(absolutePath, `${JSON.stringify(value, null, 2)}\n`);
36
+ }
37
+ function stampDeliveryUnitIdentity(entry, scope, app) {
38
+ const block = deliveryUnitContractBlock(createDeliveryUnitRecord(scope, app));
39
+ entry.deliveryUnit = block;
40
+ if (isPlainObject(entry.backendFederation)) {
41
+ entry.backendFederation.deliveryUnit = block;
42
+ if (!isPlainObject(entry.backendFederation.versionBoundary)) entry.backendFederation.versionBoundary = {};
43
+ entry.backendFederation.versionBoundary.identityRoot = 'deliveryUnit';
44
+ } else entry.backendFederation = createBackendFederationContract(scope, app);
45
+ }
46
+ function runSyncDeliveryUnit(args, context) {
47
+ if (args.includes('--help') || args.includes('-h')) {
48
+ process.stdout.write(`Usage:
49
+ modern-js-create ultramodern sync-delivery-unit [--workspace <dir>]
50
+
51
+ Backfills the per-vertical delivery-unit identity blocks required by
52
+ \`ultramodern validate\` onto an existing generated workspace, in place and
53
+ through framework tooling only. For each full-stack vertical it writes the
54
+ delivery-unit block into .modernjs/ultramodern.json topology.apps[] and
55
+ topology/reference-topology.json verticals[] (plus backendFederation.deliveryUnit
56
+ and backendFederation.versionBoundary.identityRoot) and regenerates
57
+ verticals/<id>/shared/ultramodern-build.ts. Idempotent: a second run writes
58
+ nothing.
59
+ `);
60
+ return 0;
61
+ }
62
+ const workspaceOverride = readOption(args, '--workspace');
63
+ const workspaceRoot = workspaceOverride ? node_path.resolve(context.invocationCwd, workspaceOverride) : context.workspaceRoot;
64
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
65
+ if (!node_fs.existsSync(compactPath)) throw new Error(`Missing ${ULTRAMODERN_CONFIG_PATH}. sync-delivery-unit needs the compact UltraModern config; run \`modern-js-create ultramodern migrate-strict-effect\` first.`);
66
+ const config = readUltramodernConfig(workspaceRoot);
67
+ const scope = config.workspace.packageScope;
68
+ const apiApps = workspaceAppsFromToolingConfig(config).filter((app)=>Boolean(app.api));
69
+ const written = [];
70
+ const unchanged = [];
71
+ const track = (relativePath, changed)=>{
72
+ (changed ? written : unchanged).push(relativePath);
73
+ };
74
+ const appById = new Map(apiApps.map((app)=>[
75
+ app.id,
76
+ app
77
+ ]));
78
+ const compact = JSON.parse(node_fs.readFileSync(compactPath, 'utf-8'));
79
+ if (isPlainObject(compact) && Array.isArray(compact.topology?.apps)) for (const entry of compact.topology.apps){
80
+ if (!isPlainObject(entry)) continue;
81
+ const app = appById.get(String(entry.id));
82
+ if (app) stampDeliveryUnitIdentity(entry, scope, app);
83
+ }
84
+ track(ULTRAMODERN_CONFIG_PATH, writeJsonIfChanged(compactPath, compact));
85
+ const topologyPath = node_path.join(workspaceRoot, REFERENCE_TOPOLOGY_PATH);
86
+ if (node_fs.existsSync(topologyPath)) {
87
+ const topology = JSON.parse(node_fs.readFileSync(topologyPath, 'utf-8'));
88
+ if (isPlainObject(topology) && Array.isArray(topology.verticals)) for (const entry of topology.verticals){
89
+ if (!isPlainObject(entry)) continue;
90
+ const app = appById.get(String(entry.id));
91
+ if (app) stampDeliveryUnitIdentity(entry, scope, app);
92
+ }
93
+ track(REFERENCE_TOPOLOGY_PATH, writeJsonIfChanged(topologyPath, topology));
94
+ }
95
+ for (const app of apiApps){
96
+ const relativePath = node_path.join(app.directory, 'shared/ultramodern-build.ts');
97
+ const changed = writeTextIfChanged(node_path.join(workspaceRoot, relativePath), createUltramodernBuildModule(scope, app));
98
+ track(relativePath, changed);
99
+ }
100
+ if (0 === written.length) process.stdout.write('[ultramodern] sync-delivery-unit: already in sync; no files written.\n');
101
+ else {
102
+ process.stdout.write(`[ultramodern] sync-delivery-unit: wrote ${written.length} file(s):\n`);
103
+ for (const relativePath of written)process.stdout.write(` wrote ${relativePath}\n`);
104
+ for (const relativePath of unchanged)process.stdout.write(` in-sync ${relativePath}\n`);
105
+ }
106
+ return 0;
107
+ }
108
+ export { runSyncDeliveryUnit };
@@ -0,0 +1,31 @@
1
+ import node_crypto from "node:crypto";
2
+ import { packageName } from "./naming.js";
3
+ const DELIVERY_UNIT_SCHEMA_VERSION = 1;
4
+ function createBuildMarker(scope, app) {
5
+ return node_crypto.createHash('sha256').update(`${scope}:${app.packageSuffix}:${app.id}:0.1.0`).digest('hex').slice(0, 16);
6
+ }
7
+ function deliveryUnitContractBlock(record) {
8
+ return {
9
+ schemaVersion: record.schemaVersion,
10
+ kind: record.kind,
11
+ unitId: record.unitId,
12
+ packageName: record.packageName,
13
+ version: record.version,
14
+ buildMarker: record.buildMarker,
15
+ sourceRevision: record.sourceRevision
16
+ };
17
+ }
18
+ function createDeliveryUnitRecord(scope, app) {
19
+ return {
20
+ schemaVersion: DELIVERY_UNIT_SCHEMA_VERSION,
21
+ kind: 'microvertical-delivery-unit',
22
+ unitId: `${scope}/${app.domain ?? app.id}`,
23
+ appId: app.id,
24
+ packageName: packageName(scope, app.packageSuffix),
25
+ version: '0.1.0',
26
+ sourceRevision: 'workspace',
27
+ buildMarker: createBuildMarker(scope, app),
28
+ deployProfile: 'cloudflare-ssr-mf-effect-v1'
29
+ };
30
+ }
31
+ export { DELIVERY_UNIT_SCHEMA_VERSION, createBuildMarker, createDeliveryUnitRecord, deliveryUnitContractBlock };
@@ -112,6 +112,18 @@ function resolveRemoteRefs(app, remotes = []) {
112
112
  function createRemoteManifestEnv(remote) {
113
113
  return `VERTICAL_${toEnvSegment(remote.domain ?? remote.id)}_MF_MANIFEST`;
114
114
  }
115
+ function createBackendFederationManifestEnv(remote) {
116
+ return `VERTICAL_${toEnvSegment(remote.domain ?? remote.id)}_BACKEND_MF_MANIFEST`;
117
+ }
118
+ function createBackendFederationName(app) {
119
+ return `${app.mfName}Backend`;
120
+ }
121
+ function createBackendFederationManifestUrl(app) {
122
+ return `http://localhost:${app.port}/backend-mf-manifest.json`;
123
+ }
124
+ function createBackendFederationContainerEntry(app) {
125
+ return `http://localhost:${app.port}/backendRemoteEntry.mjs`;
126
+ }
115
127
  function createModuleFederationRemoteContracts(app, remotes = []) {
116
128
  return resolveRemoteRefs(app, remotes).map((remote)=>({
117
129
  id: remote.id,
@@ -130,4 +142,4 @@ function createCloudflarePublicUrlEnv(app) {
130
142
  function appI18nNamespace(app) {
131
143
  return 'shell' === app.kind ? 'shell' : app.domain ?? app.id;
132
144
  }
133
- export { ULTRAMODERN_CONFIG_PATH, appHasApi, appI18nNamespace, createCloudflarePublicUrlEnv, createCloudflareWorkerName, createModuleFederationRemoteContracts, createNeutralOwnership, createRemoteManifestEnv, createShellHost, createVerticalDescriptor, remoteDependencyAlias, resolveApiPrefix, resolveApiStem, resolveRemoteRefs, sharedPackages, shellApp, verticalApiApps, zephyrRemoteDependency };
145
+ export { ULTRAMODERN_CONFIG_PATH, appHasApi, appI18nNamespace, createBackendFederationContainerEntry, createBackendFederationManifestEnv, createBackendFederationManifestUrl, createBackendFederationName, createCloudflarePublicUrlEnv, createCloudflareWorkerName, createModuleFederationRemoteContracts, createNeutralOwnership, createRemoteManifestEnv, createShellHost, createVerticalDescriptor, remoteDependencyAlias, resolveApiPrefix, resolveApiStem, resolveRemoteRefs, sharedPackages, shellApp, verticalApiApps, zephyrRemoteDependency };
@@ -1,10 +1,11 @@
1
- import node_crypto from "node:crypto";
2
- import { appHasApi, createCloudflarePublicUrlEnv, createCloudflareWorkerName, createRemoteManifestEnv, remoteDependencyAlias, resolveApiPrefix, resolveRemoteRefs, shellApp } from "./descriptors.js";
3
- import { createRspackChunkLoadingGlobal, createRspackUniqueName, packageName } from "./naming.js";
1
+ import { createDispatchWorkerNameEnv, createWorkerBindingEnv, createWorkerBindingName } from "./backend-federation.js";
2
+ import { createBuildMarker, createDeliveryUnitRecord } from "./delivery-unit.js";
3
+ import { appHasApi, createBackendFederationName, createCloudflarePublicUrlEnv, createCloudflareWorkerName, createRemoteManifestEnv, remoteDependencyAlias, resolveApiPrefix, resolveRemoteRefs, shellApp } from "./descriptors.js";
4
+ import { createRspackChunkLoadingGlobal, createRspackUniqueName } from "./naming.js";
4
5
  import { createCloudflareSecurityContract, formatTsJsonValue } from "./policy.js";
5
6
  import { sortJsonValue } from "./types.js";
6
7
  import { CLOUDFLARE_COMPATIBILITY_DATE } from "./versions.js";
7
- function createAppModernConfig(scope, app) {
8
+ function createAppModernConfig(scope, app, remotes = []) {
8
9
  const bffImport = appHasApi(app) ? "import { bffPlugin } from '@modern-js/plugin-bff';\n" : '';
9
10
  const bffConfig = appHasApi(app) ? ` bff: {
10
11
  effect: {
@@ -19,6 +20,19 @@ function createAppModernConfig(scope, app) {
19
20
  },
20
21
  ` : '';
21
22
  const bffPluginEntry = appHasApi(app) ? ' bffPlugin(),\n' : '';
23
+ const serviceBindings = 'shell' === app.kind ? remotes.filter(appHasApi) : [];
24
+ const serviceBindingsConfig = serviceBindings.length > 0 ? ` services: [
25
+ ${serviceBindings.map((service)=>` {
26
+ binding:
27
+ envValue('${createWorkerBindingEnv(service)}') ??
28
+ '${createWorkerBindingName(service)}',
29
+ prefix: '${resolveApiPrefix(service)}',
30
+ service:
31
+ envValue('${createDispatchWorkerNameEnv(service)}') ??
32
+ '${createCloudflareWorkerName(scope, service)}',
33
+ },`).join('\n')}
34
+ ],
35
+ ` : '';
22
36
  const defaultAssetPrefixSource = 'shell' === app.kind ? "const defaultAssetPrefix = '/';" : `const remoteAssetOrigin =
23
37
  configuredCloudflareUrl ||
24
38
  inferredCloudflareUrl ||
@@ -48,6 +62,13 @@ const zephyrEnabled = process.env['ULTRAMODERN_ZEPHYR'] !== 'false';
48
62
  const cloudflareDeployEnabled =
49
63
  process.env['MODERNJS_DEPLOY'] === 'cloudflare';
50
64
 
65
+ const zephyrWarn = (error: unknown) => {
66
+ const message = error instanceof Error ? error.message : String(error);
67
+ console.warn(
68
+ \`[ultramodern] zephyr-rspack-plugin failed; continuing without Zephyr (set ULTRAMODERN_ZEPHYR=false to disable it): \${message}\`,
69
+ );
70
+ };
71
+
51
72
  const zephyrRspackPlugin = () => ({
52
73
  name: 'ultramodern-zephyr-rspack-plugin',
53
74
  pre: ['@modern-js/plugin-module-federation-config'],
@@ -61,7 +82,17 @@ const zephyrRspackPlugin = () => ({
61
82
  if (!zephyrEnabled) {
62
83
  return;
63
84
  }
64
- api.modifyRspackConfig(config => withZephyrRspack()(config));
85
+ api.modifyRspackConfig(config => {
86
+ try {
87
+ return Promise.resolve(withZephyrRspack()(config)).catch(error => {
88
+ zephyrWarn(error);
89
+ return config;
90
+ });
91
+ } catch (error) {
92
+ zephyrWarn(error);
93
+ return config;
94
+ }
95
+ });
65
96
  },
66
97
  });
67
98
 
@@ -122,12 +153,12 @@ ${bffConfig} ...(cloudflareDeployEnabled
122
153
  ? {
123
154
  deploy: {
124
155
  worker: {
125
- compatibilityDate: '${CLOUDFLARE_COMPATIBILITY_DATE}',
126
- name: cloudflareWorkerName,
127
- security: ${formatTsJsonValue(sortJsonValue(createCloudflareSecurityContract()), 16)},
128
- ssr: true,
129
- },
156
+ compatibilityDate: '${CLOUDFLARE_COMPATIBILITY_DATE}',
157
+ name: cloudflareWorkerName,
158
+ security: ${formatTsJsonValue(sortJsonValue(createCloudflareSecurityContract()), 16)},
159
+ ${serviceBindingsConfig} ssr: true,
130
160
  },
161
+ },
131
162
  }
132
163
  : {}),
133
164
  dev: {
@@ -346,6 +377,19 @@ ${remoteEntries}
346
377
  },
347
378
  `;
348
379
  }
380
+ function createModuleFederationDtsConfig(hasExposes) {
381
+ return hasExposes ? ` dts: {
382
+ displayErrorInTerminal: true,
383
+ generateTypes: {
384
+ compilerInstance: 'tsgo',
385
+ },
386
+ tsConfigPath: './tsconfig.mf-types.json',
387
+ },` : ` dts: {
388
+ consumeTypes: true,
389
+ generateTypes: false,
390
+ tsConfigPath: './tsconfig.mf-types.json',
391
+ },`;
392
+ }
349
393
  function createShellModuleFederationConfig(scope, remotes = []) {
350
394
  const shellHost = {
351
395
  ...shellApp,
@@ -374,13 +418,7 @@ const moduleFederationConfig: Parameters<
374
418
  dev: {
375
419
  disableDynamicRemoteTypeHints: true,
376
420
  },
377
- dts: {
378
- displayErrorInTerminal: true,
379
- generateTypes: {
380
- compilerInstance: 'tsgo',
381
- },
382
- tsConfigPath: './tsconfig.mf-types.json',
383
- },
421
+ ${createModuleFederationDtsConfig(false)}
384
422
  filename: 'remoteEntry.js',
385
423
  name: '${shellApp.mfName}',
386
424
  ${createModuleFederationRemotesConfig(scope, shellHost, remotes)}${createSharedModuleFederationConfig()},
@@ -390,25 +428,75 @@ ${createModuleFederationRemotesConfig(scope, shellHost, remotes)}${createSharedM
390
428
  export default moduleFederationConfig;
391
429
  `;
392
430
  }
393
- function createBuildMarker(scope, app) {
394
- return node_crypto.createHash('sha256').update(`${scope}:${app.packageSuffix}:${app.id}:0.1.0`).digest('hex').slice(0, 16);
431
+ function createBackendModuleFederationConfig(app) {
432
+ return `// @effect-diagnostics nodeBuiltinImport:off
433
+ import { createRequire } from 'node:module';
434
+ import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
435
+ import { dependencies } from './package.json';
436
+
437
+ const require = createRequire(import.meta.url);
438
+ const bffVersion = (
439
+ require('@modern-js/plugin-bff/package.json') as { version: string }
440
+ ).version;
441
+ const effectVersion = (
442
+ require('effect/package.json') as { version: string }
443
+ ).version;
444
+
445
+ const moduleFederationConfig: Parameters<
446
+ typeof createModuleFederationConfig
447
+ >[0] = createModuleFederationConfig({
448
+ dts: false,
449
+ exposes: {
450
+ './effect-api': './api/effect-api.ts',
451
+ },
452
+ filename: 'backendRemoteEntry.mjs',
453
+ name: '${createBackendFederationName(app)}',
454
+ shared: {
455
+ '@modern-js/plugin-bff': {
456
+ requiredVersion: bffVersion,
457
+ singleton: true,
458
+ treeShaking: false,
459
+ },
460
+ '@module-federation/runtime': {
461
+ requiredVersion: dependencies['@module-federation/runtime'],
462
+ singleton: true,
463
+ treeShaking: false,
464
+ },
465
+ effect: {
466
+ requiredVersion: effectVersion,
467
+ singleton: true,
468
+ treeShaking: false,
469
+ },
470
+ },
471
+ treeShakingSharedExcludePlugins: ['RspackModuleFederationPlugin'],
472
+ });
473
+
474
+ export default moduleFederationConfig;
475
+ `;
395
476
  }
396
477
  function createUltramodernBuildModule(scope, app) {
397
- return `export const ultramodernVerticalIdentity = {
398
- appId: '${app.id}',
399
- build: '${createBuildMarker(scope, app)}',
400
- deployProfile: 'cloudflare-ssr-mf-effect-v1',
401
- packageName: '${packageName(scope, app.packageSuffix)}',
402
- version: '0.1.0',
478
+ const record = createDeliveryUnitRecord(scope, app);
479
+ return `export const ultramodernDeliveryUnit = {
480
+ appId: '${record.appId}',
481
+ build: '${record.buildMarker}',
482
+ deployProfile: '${record.deployProfile}',
483
+ kind: '${record.kind}',
484
+ packageName: '${record.packageName}',
485
+ schemaVersion: ${record.schemaVersion},
486
+ sourceRevision: '${record.sourceRevision}',
487
+ unitId: '${record.unitId}',
488
+ version: '${record.version}',
403
489
  } as const;
404
490
 
491
+ export const ultramodernVerticalIdentity = ultramodernDeliveryUnit;
492
+
405
493
  export const ultramodernUiMarker = {
406
- ...ultramodernVerticalIdentity,
494
+ ...ultramodernDeliveryUnit,
407
495
  surface: 'ui',
408
496
  } as const;
409
497
 
410
498
  export const ultramodernApiMarker = {
411
- ...ultramodernVerticalIdentity,
499
+ ...ultramodernDeliveryUnit,
412
500
  surface: 'api',
413
501
  } as const;
414
502
  `;
@@ -416,6 +504,7 @@ export const ultramodernApiMarker = {
416
504
  function createUltramodernBuildReexportModule() {
417
505
  return `export {
418
506
  ultramodernApiMarker,
507
+ ultramodernDeliveryUnit,
419
508
  ultramodernUiMarker,
420
509
  ultramodernVerticalIdentity,
421
510
  } from '../shared/ultramodern-build';
@@ -423,7 +512,9 @@ function createUltramodernBuildReexportModule() {
423
512
  }
424
513
  function createRemoteModuleFederationConfig(scope, app, remotes = []) {
425
514
  const exposes = formatTsObjectLiteral(app.exposes ?? {});
426
- return `// @effect-diagnostics nodeBuiltinImport:off
515
+ const hasExposes = Object.keys(app.exposes ?? {}).length > 0;
516
+ const hostOnlyMarker = hasExposes ? '' : '\n// ultramodern-mf: no-exposes';
517
+ return `// @effect-diagnostics nodeBuiltinImport:off${hostOnlyMarker}
427
518
  import { createRequire } from 'node:module';
428
519
  import { createModuleFederationConfig } from '@module-federation/modern-js-v3';
429
520
  import { dependencies } from './package.json';
@@ -445,13 +536,7 @@ const moduleFederationConfig: Parameters<
445
536
  dev: {
446
537
  disableDynamicRemoteTypeHints: true,
447
538
  },
448
- dts: {
449
- displayErrorInTerminal: true,
450
- generateTypes: {
451
- compilerInstance: 'tsgo',
452
- },
453
- tsConfigPath: './tsconfig.mf-types.json',
454
- },
539
+ ${createModuleFederationDtsConfig(hasExposes)}
455
540
  exposes: ${exposes},
456
541
  filename: 'remoteEntry.js',
457
542
  name: '${app.mfName}',
@@ -462,4 +547,4 @@ ${createModuleFederationRemotesConfig(scope, app, remotes)}${createSharedModuleF
462
547
  export default moduleFederationConfig;
463
548
  `;
464
549
  }
465
- export { createAppModernConfig, createBuildMarker, createModuleFederationRemoteUrlHelpers, createModuleFederationRemotesConfig, createRemoteModuleFederationConfig, createSharedModuleFederationConfig, createShellModuleFederationConfig, createUltramodernBuildModule, createUltramodernBuildReexportModule, formatTsObjectLiteral };
550
+ export { createAppModernConfig, createBackendModuleFederationConfig, createBuildMarker, createModuleFederationRemoteUrlHelpers, createModuleFederationRemotesConfig, createRemoteModuleFederationConfig, createSharedModuleFederationConfig, createShellModuleFederationConfig, createUltramodernBuildModule, createUltramodernBuildReexportModule, formatTsObjectLiteral };
@@ -3,9 +3,8 @@ import { WORKSPACE_PACKAGE_VERSION, modernPackageSpecifier } from "../ultramoder
3
3
  import { appHasApi, remoteDependencyAlias, resolveRemoteRefs, sharedPackages, shellApp, verticalApiApps, zephyrRemoteDependency } from "./descriptors.js";
4
4
  import { readFileTemplate } from "./fs-io.js";
5
5
  import { packageName, relativeRootFor } from "./naming.js";
6
- import { createPublicSurfaceGenerationCommand } from "./public-surface.js";
7
6
  import { EFFECT_TSGO_VERSION, I18NEXT_VERSION, LEFTHOOK_VERSION, MODULE_FEDERATION_VERSION, NODE_FETCH_VERSION, OXFMT_VERSION, OXLINT_VERSION, PNPM_VERSION, POSTCSS_VERSION, REACT_DOM_VERSION, REACT_ROUTER_VERSION, REACT_VERSION, TAILWIND_POSTCSS_VERSION, TAILWIND_VERSION, TANSTACK_ROUTER_VERSION, TYPESCRIPT_NATIVE_PREVIEW_VERSION, TYPESCRIPT_VERSION, TYPES_REACT_DOM_VERSION, TYPES_REACT_VERSION, ULTRACITE_VERSION, WRANGLER_VERSION, ZEPHYR_AGENT_VERSION, ZEPHYR_RSPACK_PLUGIN_VERSION } from "./versions.js";
8
- const createStrictTsgoTypecheckCommand = (packageDir)=>`node ${relativeRootFor(packageDir)}/scripts/ultramodern-typecheck.mts --project tsconfig.json`;
7
+ import { createStrictTsgoTypecheckCommand, createWorkspaceAppPackageScripts, createWorkspaceRootPackageScripts } from "./workspace-script-plan.js";
9
8
  const effectDiagnostics = [
10
9
  'anyUnknownInErrorContext',
11
10
  'classSelfMismatch',
@@ -128,11 +127,6 @@ function appDevDependencies(packageSource, enableTailwind) {
128
127
  function createRootPackageJson(scope, packageSource, remotes = [], bridge) {
129
128
  const shellFilter = `--filter ${packageName(scope, shellApp.packageSuffix)}`;
130
129
  const remoteFilters = remotes.map((remote)=>`--filter ${packageName(scope, remote.packageSuffix)}`);
131
- const remoteBuildPrefix = remotes.length > 0 ? 'ULTRAMODERN_ZEPHYR=false pnpm -r --filter "./verticals/*" run build && ' : '';
132
- const remoteCloudflareBuildPrefix = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:build && ' : '';
133
- const remoteCloudflareDeployPrefix = remotes.length > 0 ? 'pnpm -r --filter "./verticals/*" run cloudflare:deploy && ' : '';
134
- const generatedPackageTypecheck = 'pnpm -r --filter "./apps/*" --filter "./verticals/*" --filter "./packages/*" run typecheck';
135
- const typecheck = bridge ? generatedPackageTypecheck : "node ./scripts/ultramodern-typecheck.mts --build tsconfig.json";
136
130
  const bridgeScripts = bridge ? {
137
131
  ...Object.fromEntries(bridge.gates.map((gate)=>[
138
132
  `bridge:${gate.name}`,
@@ -141,6 +135,11 @@ function createRootPackageJson(scope, packageSource, remotes = [], bridge) {
141
135
  'bridge:check': bridge.gates.map((gate)=>`pnpm run bridge:${gate.name}`).join(' && ')
142
136
  } : {};
143
137
  const bridgeCheck = bridge ? ' && pnpm bridge:check' : '';
138
+ const bridgeTypecheck = bridge ? 'pnpm -r --filter "./apps/*" --filter "./verticals/*" --filter "./packages/*" run typecheck' : void 0;
139
+ const rootPackageScripts = createWorkspaceRootPackageScripts(remotes, {
140
+ bridgeCheck,
141
+ typecheck: bridgeTypecheck
142
+ });
144
143
  const workspacePackages = [
145
144
  'apps/*',
146
145
  'verticals/*',
@@ -163,28 +162,19 @@ function createRootPackageJson(scope, packageSource, remotes = [], bridge) {
163
162
  `dev:${remote.packageSuffix}`,
164
163
  `pnpm --filter ${packageName(scope, remote.packageSuffix)} dev`
165
164
  ])),
166
- build: `${remoteBuildPrefix}ULTRAMODERN_ZEPHYR=false pnpm --filter "./apps/shell-super-app" run build && pnpm mf:types && pnpm performance:readiness`,
165
+ ...rootPackageScripts,
167
166
  format: "oxfmt . '!repos/**'",
168
167
  'format:check': "oxfmt --check . '!repos/**'",
169
168
  lint: 'oxlint apps verticals packages',
170
169
  'lint:fix': 'oxlint apps verticals packages --fix',
171
- typecheck,
172
- 'cloudflare:build': `${remoteCloudflareBuildPrefix}pnpm --filter "./apps/shell-super-app" run cloudflare:build && pnpm mf:types`,
173
- 'cloudflare:deploy': `${remoteCloudflareDeployPrefix}pnpm --filter "./apps/shell-super-app" run cloudflare:deploy`,
174
- 'cloudflare:proof': "node ./scripts/proof-cloudflare-version.mts --out .codex/reports/cloudflare-version-proof/public-url-proof.json",
175
170
  'skills:install': "node ./scripts/bootstrap-agent-skills.mts",
176
171
  'skills:check': "node ./scripts/bootstrap-agent-skills.mts --check",
177
172
  'agents:refs:install': "node ./scripts/setup-agent-reference-repos.mts",
178
173
  'agents:refs:check': "node ./scripts/setup-agent-reference-repos.mts --check",
179
- 'mf:types': "node ./scripts/assert-mf-types.mts",
180
- 'performance:readiness': "node ./scripts/ultramodern-performance-readiness.mts",
181
- 'migrate:strict-effect': "node ./scripts/migrate-strict-effect.mts",
182
- 'contract:check': "node ./scripts/validate-ultramodern-workspace.mts",
183
174
  'api:check': "node ./scripts/check-ultramodern-api-boundaries.mts",
184
175
  'i18n:boundaries': "node ./scripts/check-ultramodern-i18n-boundaries.mts",
185
176
  ...bridgeScripts,
186
- postinstall: "oxfmt . '!repos/**' && node ./scripts/bootstrap-agent-skills.mts --postinstall",
187
- check: `pnpm format:check && pnpm lint && pnpm typecheck && pnpm skills:check && pnpm i18n:boundaries && pnpm api:check && pnpm contract:check && pnpm performance:readiness${bridgeCheck}`
177
+ postinstall: "oxfmt . '!repos/**' && node ./scripts/bootstrap-agent-skills.mts --postinstall"
188
178
  },
189
179
  engines: {
190
180
  node: '>=26',
@@ -205,6 +195,7 @@ function createRootPackageJson(scope, packageSource, remotes = [], bridge) {
205
195
  '@effect/tsgo': EFFECT_TSGO_VERSION,
206
196
  '@modern-js/code-tools': modernPackageSpecifier('@modern-js/code-tools', packageSource),
207
197
  '@modern-js/create': modernPackageSpecifier('@modern-js/create', packageSource),
198
+ '@modern-js/plugin-bff': modernPackageSpecifier('@modern-js/plugin-bff', packageSource),
208
199
  "@typescript/native-preview": TYPESCRIPT_NATIVE_PREVIEW_VERSION,
209
200
  lefthook: LEFTHOOK_VERSION,
210
201
  oxlint: OXLINT_VERSION,
@@ -354,8 +345,6 @@ function createRootTsConfig(apps = []) {
354
345
  };
355
346
  }
356
347
  function createAppPackage(scope, app, packageSource, enableTailwind, remotes = [], bridge) {
357
- const publicSurfaceBuildCommand = createPublicSurfaceGenerationCommand(app, 'dist');
358
- const publicSurfaceCloudflareOutputCommand = createPublicSurfaceGenerationCommand(app, 'cloudflare');
359
348
  const packageExports = Object.fromEntries(Object.entries(app.exposes ?? {}).map(([expose, source])=>[
360
349
  expose,
361
350
  source
@@ -364,16 +353,7 @@ function createAppPackage(scope, app, packageSource, enableTailwind, remotes = [
364
353
  private: true,
365
354
  name: packageName(scope, app.packageSuffix),
366
355
  version: '0.1.0',
367
- scripts: {
368
- dev: 'modern dev',
369
- build: app.exposes ? `ULTRAMODERN_ZEPHYR=false modern build && ${publicSurfaceBuildCommand} && node ${relativeRootFor(app.directory)}/scripts/assert-mf-types.mts` : `ULTRAMODERN_ZEPHYR=false modern build && ${publicSurfaceBuildCommand}`,
370
- 'cloudflare:build': `ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern build && ULTRAMODERN_ZEPHYR=false MODERNJS_DEPLOY=cloudflare modern deploy --skip-build && ${publicSurfaceCloudflareOutputCommand}`,
371
- 'cloudflare:deploy': 'ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS=true pnpm run cloudflare:build && wrangler deploy --config .output/wrangler.json',
372
- 'cloudflare:preview': 'pnpm run cloudflare:build && wrangler dev --config .output/wrangler.json',
373
- 'cloudflare:proof': `node ${relativeRootFor(app.directory)}/scripts/proof-cloudflare-version.mts --app ${app.id}`,
374
- serve: 'modern serve',
375
- typecheck: createStrictTsgoTypecheckCommand(app.directory)
376
- },
356
+ scripts: createWorkspaceAppPackageScripts(app),
377
357
  modernjs: {
378
358
  preset: 'presetUltramodern',
379
359
  role: 'shell' === app.kind ? 'shell' : 'module-federation-remote',
@@ -424,4 +404,4 @@ function createSharedPackage(scope, id, description) {
424
404
  function createSharedContractsIndex() {
425
405
  return readFileTemplate('packages/shared-contracts-index.ts');
426
406
  }
427
- export { appDependencies, appDevDependencies, createAppMfTypesTsConfig, createAppPackage, createAppTsConfig, createPackageTsConfig, createRootPackageJson, createRootTsConfig, createSharedContractsIndex, createSharedPackage, createSharedPackageTsConfig, createStrictTsgoTypecheckCommand, createTsConfigBase, createZephyrDependencies, effectDiagnostics };
407
+ export { appDependencies, appDevDependencies, createAppMfTypesTsConfig, createAppPackage, createAppTsConfig, createPackageTsConfig, createRootPackageJson, createRootTsConfig, createSharedContractsIndex, createSharedPackage, createSharedPackageTsConfig, createTsConfigBase, createZephyrDependencies, effectDiagnostics };
@@ -0,0 +1,113 @@
1
+ import { DRIZZLE_ORM_VERSION, EFFECT_VERSION, EFFECT_VITEST_VERSION, MODULE_FEDERATION_VERSION } from "./versions.js";
2
+ const strictEffectPackageVersionPolicyExclusions = [
3
+ `effect@${EFFECT_VERSION}`,
4
+ `@effect/opentelemetry@${EFFECT_VERSION}`
5
+ ];
6
+ const moduleFederationModernJsPatchPath = `patches/@module-federation__modern-js-v3@${MODULE_FEDERATION_VERSION}.patch`;
7
+ const moduleFederationBridgeReactPatchPath = `patches/@module-federation__bridge-react@${MODULE_FEDERATION_VERSION}.patch`;
8
+ const effectDeclarationPatchPath = 'patches/effect-schema-error-type-id.patch';
9
+ const drizzleOrmDeclarationPatchPath = 'patches/drizzle-orm-ts7-strict-declarations.patch';
10
+ function createPnpmWorkspacePolicyPlan(options) {
11
+ return {
12
+ yamlActions: [
13
+ {
14
+ kind: 'replace-line',
15
+ pattern: /^ {4}'@effect\/vitest>effect': .+$/mu,
16
+ replacement: ` '@effect/vitest>effect': '${EFFECT_VERSION}'`
17
+ },
18
+ {
19
+ kind: 'ensure-scalar-map-entry',
20
+ key: 'overrides',
21
+ entryKey: "'@effect/opentelemetry'",
22
+ value: EFFECT_VERSION
23
+ },
24
+ {
25
+ kind: 'ensure-scalar-map-entry',
26
+ key: 'overrides',
27
+ entryKey: "'@effect/vitest'",
28
+ value: EFFECT_VITEST_VERSION
29
+ },
30
+ {
31
+ kind: 'ensure-scalar-map-entry',
32
+ key: 'overrides',
33
+ entryKey: 'effect',
34
+ value: EFFECT_VERSION
35
+ },
36
+ {
37
+ kind: 'ensure-scalar-map-entry',
38
+ key: 'allowBuilds',
39
+ entryKey: "'@parcel/watcher'",
40
+ value: 'true'
41
+ },
42
+ ...strictEffectPackageVersionPolicyExclusions.flatMap((item)=>createStrictEffectPackageVersionPolicyActions(item)),
43
+ {
44
+ kind: 'ensure-map-entry',
45
+ key: 'patchedDependencies',
46
+ entryKey: `effect@${EFFECT_VERSION}`,
47
+ value: effectDeclarationPatchPath
48
+ },
49
+ {
50
+ kind: 'ensure-map-entry',
51
+ key: 'patchedDependencies',
52
+ entryKey: `@module-federation/modern-js-v3@${MODULE_FEDERATION_VERSION}`,
53
+ value: moduleFederationModernJsPatchPath
54
+ },
55
+ {
56
+ kind: 'ensure-map-entry',
57
+ key: 'patchedDependencies',
58
+ entryKey: `@module-federation/bridge-react@${MODULE_FEDERATION_VERSION}`,
59
+ value: moduleFederationBridgeReactPatchPath
60
+ },
61
+ options.usesDrizzleOrm ? {
62
+ kind: 'ensure-map-entry',
63
+ key: 'patchedDependencies',
64
+ entryKey: `drizzle-orm@${DRIZZLE_ORM_VERSION}`,
65
+ value: drizzleOrmDeclarationPatchPath
66
+ } : {
67
+ kind: 'remove-map-entry',
68
+ entryKey: `drizzle-orm@${DRIZZLE_ORM_VERSION}`
69
+ }
70
+ ],
71
+ declarationPatchActions: [
72
+ {
73
+ kind: 'ensure',
74
+ relativePatchPath: moduleFederationModernJsPatchPath
75
+ },
76
+ {
77
+ kind: 'ensure',
78
+ relativePatchPath: moduleFederationBridgeReactPatchPath
79
+ },
80
+ {
81
+ kind: 'ensure',
82
+ relativePatchPath: effectDeclarationPatchPath
83
+ },
84
+ {
85
+ kind: options.usesDrizzleOrm ? 'ensure' : 'remove-if-unchanged',
86
+ relativePatchPath: drizzleOrmDeclarationPatchPath
87
+ }
88
+ ],
89
+ requiresInstallOnChange: true
90
+ };
91
+ }
92
+ function createStrictEffectPackageVersionPolicyActions(item) {
93
+ const packageName = item.slice(0, item.lastIndexOf('@'));
94
+ const escapedPackageName = packageName.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
95
+ return [
96
+ {
97
+ kind: 'replace-line',
98
+ pattern: new RegExp(`^ {2}- '${escapedPackageName}@[^']+'$`, 'gmu'),
99
+ replacement: ` - '${item}'`
100
+ },
101
+ {
102
+ kind: 'ensure-list-item',
103
+ key: 'minimumReleaseAgeExclude',
104
+ item
105
+ },
106
+ {
107
+ kind: 'ensure-list-item',
108
+ key: 'trustPolicyExclude',
109
+ item
110
+ }
111
+ ];
112
+ }
113
+ export { createPnpmWorkspacePolicyPlan, drizzleOrmDeclarationPatchPath, effectDeclarationPatchPath, moduleFederationBridgeReactPatchPath, moduleFederationModernJsPatchPath, strictEffectPackageVersionPolicyExclusions };