@bleedingdev/modern-js-create 3.4.0-ultramodern.12 → 3.4.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 (104) hide show
  1. package/README.md +13 -11
  2. package/dist/cjs/index.cjs +36 -3
  3. package/dist/cjs/locale/en.cjs +10 -0
  4. package/dist/cjs/locale/zh.cjs +10 -0
  5. package/dist/cjs/ultramodern-tooling/commands.cjs +185 -0
  6. package/dist/cjs/ultramodern-tooling/config.cjs +248 -0
  7. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +39 -18
  8. package/dist/cjs/ultramodern-workspace/bridge-config.cjs +327 -0
  9. package/dist/cjs/ultramodern-workspace/codesmith.cjs +1 -0
  10. package/dist/cjs/ultramodern-workspace/contracts.cjs +119 -653
  11. package/dist/cjs/ultramodern-workspace/descriptors.cjs +4 -0
  12. package/dist/cjs/ultramodern-workspace/generation-result.cjs +1 -1
  13. package/dist/cjs/ultramodern-workspace/index.cjs +4 -0
  14. package/dist/cjs/ultramodern-workspace/mf-validation.cjs +572 -0
  15. package/dist/cjs/ultramodern-workspace/module-federation.cjs +14 -0
  16. package/dist/cjs/ultramodern-workspace/package-json.cjs +26 -12
  17. package/dist/cjs/ultramodern-workspace/public-api.cjs +4 -0
  18. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +74 -44
  19. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +37 -8
  20. package/dist/esm/index.js +36 -3
  21. package/dist/esm/locale/en.js +10 -0
  22. package/dist/esm/locale/zh.js +10 -0
  23. package/dist/esm/ultramodern-tooling/commands.js +132 -0
  24. package/dist/esm/ultramodern-tooling/config.js +196 -0
  25. package/dist/esm/ultramodern-workspace/add-vertical.js +39 -21
  26. package/dist/esm/ultramodern-workspace/bridge-config.js +270 -0
  27. package/dist/esm/ultramodern-workspace/codesmith.js +1 -0
  28. package/dist/esm/ultramodern-workspace/contracts.js +122 -608
  29. package/dist/esm/ultramodern-workspace/descriptors.js +2 -1
  30. package/dist/esm/ultramodern-workspace/generation-result.js +2 -2
  31. package/dist/esm/ultramodern-workspace/index.js +1 -0
  32. package/dist/esm/ultramodern-workspace/mf-validation.js +517 -0
  33. package/dist/esm/ultramodern-workspace/module-federation.js +14 -0
  34. package/dist/esm/ultramodern-workspace/package-json.js +26 -12
  35. package/dist/esm/ultramodern-workspace/public-api.js +1 -0
  36. package/dist/esm/ultramodern-workspace/workspace-scripts.js +75 -27
  37. package/dist/esm/ultramodern-workspace/write-workspace.js +38 -12
  38. package/dist/esm-node/index.js +36 -3
  39. package/dist/esm-node/locale/en.js +10 -0
  40. package/dist/esm-node/locale/zh.js +10 -0
  41. package/dist/esm-node/ultramodern-tooling/commands.js +133 -0
  42. package/dist/esm-node/ultramodern-tooling/config.js +197 -0
  43. package/dist/esm-node/ultramodern-workspace/add-vertical.js +39 -21
  44. package/dist/esm-node/ultramodern-workspace/bridge-config.js +271 -0
  45. package/dist/esm-node/ultramodern-workspace/codesmith.js +1 -0
  46. package/dist/esm-node/ultramodern-workspace/contracts.js +122 -608
  47. package/dist/esm-node/ultramodern-workspace/descriptors.js +2 -1
  48. package/dist/esm-node/ultramodern-workspace/generation-result.js +2 -2
  49. package/dist/esm-node/ultramodern-workspace/index.js +1 -0
  50. package/dist/esm-node/ultramodern-workspace/mf-validation.js +518 -0
  51. package/dist/esm-node/ultramodern-workspace/module-federation.js +14 -0
  52. package/dist/esm-node/ultramodern-workspace/package-json.js +26 -12
  53. package/dist/esm-node/ultramodern-workspace/public-api.js +1 -0
  54. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +75 -27
  55. package/dist/esm-node/ultramodern-workspace/write-workspace.js +38 -12
  56. package/dist/types/locale/en.d.ts +10 -0
  57. package/dist/types/locale/index.d.ts +20 -0
  58. package/dist/types/locale/zh.d.ts +10 -0
  59. package/dist/types/ultramodern-tooling/commands.d.ts +1 -0
  60. package/dist/types/ultramodern-tooling/config.d.ts +43 -0
  61. package/dist/types/ultramodern-workspace/add-vertical.d.ts +5 -2
  62. package/dist/types/ultramodern-workspace/bridge-config.d.ts +64 -0
  63. package/dist/types/ultramodern-workspace/codesmith.d.ts +1 -0
  64. package/dist/types/ultramodern-workspace/contracts.d.ts +2 -17
  65. package/dist/types/ultramodern-workspace/descriptors.d.ts +1 -0
  66. package/dist/types/ultramodern-workspace/index.d.ts +2 -0
  67. package/dist/types/ultramodern-workspace/mf-validation.d.ts +27 -0
  68. package/dist/types/ultramodern-workspace/package-json.d.ts +4 -3
  69. package/dist/types/ultramodern-workspace/public-api.d.ts +2 -0
  70. package/dist/types/ultramodern-workspace/types.d.ts +2 -0
  71. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +1 -7
  72. package/dist/types/ultramodern-workspace/write-workspace.d.ts +4 -2
  73. package/package.json +3 -3
  74. package/template-workspace/{.agents → .codex}/skills-lock.json +12 -12
  75. package/template-workspace/.gitignore.handlebars +3 -0
  76. package/template-workspace/AGENTS.md.handlebars +11 -5
  77. package/template-workspace/README.md.handlebars +15 -12
  78. package/template-workspace/oxfmt.config.ts +1 -0
  79. package/template-workspace/oxlint.config.ts +1 -0
  80. package/template-workspace/scripts/bootstrap-agent-skills.mjs +77 -67
  81. package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +56 -3
  82. package/templates/workspace-scripts/generate-public-surface-assets.mjs +239 -4
  83. package/templates/workspace-scripts/proof-cloudflare-version.mjs +282 -4
  84. package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +172 -1
  85. package/templates/workspace-scripts/ultramodern-typecheck.mjs +4 -2
  86. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +783 -26
  87. /package/template-workspace/{.agents → .codex}/rstackjs-agent-skills-LICENSE +0 -0
  88. /package/template-workspace/{.agents → .codex}/skills/rsbuild-best-practices/SKILL.md +0 -0
  89. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/SKILL.md +0 -0
  90. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/command-map.md +0 -0
  91. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/common-analysis-patterns.md +0 -0
  92. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor-common.md +0 -0
  93. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor-rspack.md +0 -0
  94. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor-webpack.md +0 -0
  95. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor.md +0 -0
  96. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/rsdoctor-data-types.md +0 -0
  97. /package/template-workspace/{.agents → .codex}/skills/rslib-best-practices/SKILL.md +0 -0
  98. /package/template-workspace/{.agents → .codex}/skills/rslib-modern-package/SKILL.md +0 -0
  99. /package/template-workspace/{.agents → .codex}/skills/rspack-best-practices/SKILL.md +0 -0
  100. /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/SKILL.md +0 -0
  101. /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/references/bottlenecks.md +0 -0
  102. /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/references/tracing-guide.md +0 -0
  103. /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/scripts/analyze_trace.js +0 -0
  104. /package/template-workspace/{.agents → .codex}/skills/rstest-best-practices/SKILL.md +0 -0
@@ -0,0 +1,197 @@
1
+ import "node:module";
2
+ import node_fs from "node:fs";
3
+ import node_path from "node:path";
4
+ import { normalizeUltramodernBridgeConfig } from "../ultramodern-workspace/bridge-config.js";
5
+ import { GENERATED_CONTRACT_PATH, ULTRAMODERN_CONFIG_PATH, createNeutralOwnership, shellApp } from "../ultramodern-workspace/descriptors.js";
6
+ import { toKebabCase } from "../ultramodern-workspace/naming.js";
7
+ const PACKAGE_SOURCE_METADATA_PATH = '.modernjs/ultramodern-package-source.json';
8
+ const DEVELOPMENT_OVERLAY_PATH = 'topology/local-overlays/development.json';
9
+ function readJsonObject(filePath) {
10
+ const value = JSON.parse(node_fs.readFileSync(filePath, 'utf-8'));
11
+ if (null === value || 'object' != typeof value || Array.isArray(value)) throw new Error(`UltraModern config must contain a JSON object: ${filePath}`);
12
+ return value;
13
+ }
14
+ function readOptionalJsonObject(filePath) {
15
+ return node_fs.existsSync(filePath) ? readJsonObject(filePath) : {};
16
+ }
17
+ function packageScopeFromRoot(workspaceRoot) {
18
+ const rootPackage = readOptionalJsonObject(node_path.join(workspaceRoot, 'package.json'));
19
+ return 'string' == typeof rootPackage.name && rootPackage.name.length > 0 ? rootPackage.name : node_path.basename(workspaceRoot);
20
+ }
21
+ function packageSourceFromMetadata(workspaceRoot) {
22
+ const metadataPath = node_path.join(workspaceRoot, PACKAGE_SOURCE_METADATA_PATH);
23
+ if (!node_fs.existsSync(metadataPath)) return;
24
+ const metadata = readJsonObject(metadataPath);
25
+ const aliases = metadata.modernPackages?.aliases ?? {};
26
+ const firstAlias = Object.values(aliases).find((value)=>'string' == typeof value);
27
+ const firstPackage = Object.keys(aliases)[0];
28
+ const aliasScope = firstAlias?.match(/^@([^/]+)\//)?.[1];
29
+ const unscopedName = firstPackage?.split('/').at(-1) ?? '';
30
+ const aliasUnscopedName = firstAlias?.split('/').at(-1) ?? '';
31
+ const aliasPackageNamePrefix = aliasUnscopedName && unscopedName && aliasUnscopedName.endsWith(unscopedName) ? aliasUnscopedName.slice(0, -unscopedName.length) : void 0;
32
+ return {
33
+ strategy: 'install' === metadata.strategy ? 'install' : 'workspace',
34
+ modernPackageVersion: 'string' == typeof metadata.modernPackages?.specifier ? metadata.modernPackages.specifier : 'workspace:*',
35
+ registry: 'string' == typeof metadata.modernPackages?.registry ? metadata.modernPackages.registry : void 0,
36
+ aliasScope,
37
+ aliasPackageNamePrefix
38
+ };
39
+ }
40
+ function readOverlayPorts(workspaceRoot) {
41
+ const overlayPath = node_path.join(workspaceRoot, DEVELOPMENT_OVERLAY_PATH);
42
+ if (!node_fs.existsSync(overlayPath)) return {};
43
+ const overlay = readJsonObject(overlayPath);
44
+ const ports = overlay.ports;
45
+ if (null === ports || 'object' != typeof ports || Array.isArray(ports)) return {};
46
+ return Object.fromEntries(Object.entries(ports).filter((entry)=>'number' == typeof entry[1]));
47
+ }
48
+ function normalizeCompactConfig(workspaceRoot, sourcePath, config) {
49
+ const packageSource = config.packageSource && 'object' == typeof config.packageSource ? {
50
+ strategy: 'install' === config.packageSource.strategy ? 'install' : 'workspace',
51
+ modernPackageVersion: 'string' == typeof config.packageSource.modernPackageVersion ? config.packageSource.modernPackageVersion : 'workspace:*',
52
+ registry: 'string' == typeof config.packageSource.registry ? config.packageSource.registry : void 0,
53
+ aliasScope: 'string' == typeof config.packageSource.aliasScope ? config.packageSource.aliasScope : void 0,
54
+ aliasPackageNamePrefix: 'string' == typeof config.packageSource.aliasPackageNamePrefix ? config.packageSource.aliasPackageNamePrefix : void 0
55
+ } : packageSourceFromMetadata(workspaceRoot);
56
+ return {
57
+ schemaVersion: 'number' == typeof config.schemaVersion ? config.schemaVersion : 1,
58
+ profile: 'string' == typeof config.profile ? config.profile : void 0,
59
+ source: 'compact',
60
+ sourcePath,
61
+ workspace: {
62
+ packageScope: 'string' == typeof config.workspace?.packageScope ? config.workspace.packageScope : packageScopeFromRoot(workspaceRoot)
63
+ },
64
+ packageSource,
65
+ features: {
66
+ tailwind: config.features?.tailwind !== false
67
+ },
68
+ bridge: normalizeUltramodernBridgeConfig(config.bridge),
69
+ topology: {
70
+ apps: Array.isArray(config.topology?.apps) ? config.topology.apps.map((app)=>({
71
+ id: String(app.id),
72
+ kind: 'vertical' === app.kind ? 'vertical' : 'shell',
73
+ path: 'string' == typeof app.path ? app.path : '.',
74
+ package: 'string' == typeof app.package ? app.package : void 0,
75
+ packageSuffix: 'string' == typeof app.packageSuffix ? app.packageSuffix : void 0,
76
+ displayName: 'string' == typeof app.displayName ? app.displayName : void 0,
77
+ domain: 'string' == typeof app.domain ? app.domain : void 0,
78
+ port: 'number' == typeof app.port ? app.port : void 0,
79
+ portEnv: 'string' == typeof app.portEnv ? app.portEnv : void 0,
80
+ moduleFederation: app.moduleFederation && 'object' == typeof app.moduleFederation ? {
81
+ role: 'remote' === app.moduleFederation.role ? 'remote' : 'host',
82
+ name: 'string' == typeof app.moduleFederation.name ? app.moduleFederation.name : void 0,
83
+ exposes: Array.isArray(app.moduleFederation.exposes) ? app.moduleFederation.exposes.filter((expose)=>'string' == typeof expose) : void 0,
84
+ verticalRefs: Array.isArray(app.moduleFederation.verticalRefs) ? app.moduleFederation.verticalRefs.filter((ref)=>'string' == typeof ref) : void 0,
85
+ hostOnly: true === app.moduleFederation.hostOnly,
86
+ noExposes: true === app.moduleFederation.noExposes
87
+ } : void 0,
88
+ effectApi: app.effectApi && 'object' == typeof app.effectApi ? {
89
+ stem: 'string' == typeof app.effectApi.stem ? app.effectApi.stem : String(app.id),
90
+ prefix: 'string' == typeof app.effectApi.prefix ? app.effectApi.prefix : `/${String(app.id)}-api`,
91
+ consumedBy: Array.isArray(app.effectApi.consumedBy) ? app.effectApi.consumedBy.filter((consumer)=>'string' == typeof consumer) : [
92
+ shellApp.id,
93
+ String(app.id)
94
+ ]
95
+ } : void 0
96
+ })) : []
97
+ }
98
+ };
99
+ }
100
+ function adaptLegacyContract(workspaceRoot, sourcePath, contract) {
101
+ const ports = readOverlayPorts(workspaceRoot);
102
+ const apps = Array.isArray(contract.apps) ? contract.apps : [];
103
+ const shell = apps.find((app)=>app?.id === shellApp.id);
104
+ return {
105
+ schemaVersion: 'number' == typeof contract.schemaVersion ? contract.schemaVersion : 1,
106
+ profile: 'string' == typeof contract.profile ? contract.profile : void 0,
107
+ source: 'legacy',
108
+ sourcePath,
109
+ workspace: {
110
+ packageScope: packageScopeFromRoot(workspaceRoot)
111
+ },
112
+ packageSource: packageSourceFromMetadata(workspaceRoot),
113
+ features: {
114
+ tailwind: shell?.styling?.tailwind !== false
115
+ },
116
+ topology: {
117
+ apps: apps.map((app)=>{
118
+ const id = String(app.id);
119
+ const appPath = 'string' == typeof app.path ? app.path : id === shellApp.id ? shellApp.directory : `verticals/${toKebabCase(id)}`;
120
+ const domain = 'string' == typeof app.i18n?.namespace && 'shell' !== app.i18n.namespace ? app.i18n.namespace : appPath.split('/').at(-1);
121
+ return {
122
+ id,
123
+ kind: 'vertical' === app.kind ? 'vertical' : 'shell',
124
+ path: appPath,
125
+ package: 'string' == typeof app.package ? app.package : void 0,
126
+ packageSuffix: 'string' == typeof app.package ? app.package.split('/').at(-1) : appPath.split('/').at(-1),
127
+ displayName: id === shellApp.id ? shellApp.displayName : void 0,
128
+ domain,
129
+ port: ports[id],
130
+ moduleFederation: app.moduleFederation && 'object' == typeof app.moduleFederation ? {
131
+ role: 'vertical' === app.kind ? 'remote' : 'host',
132
+ name: 'string' == typeof app.moduleFederation.name ? app.moduleFederation.name : void 0,
133
+ exposes: Array.isArray(app.moduleFederation.exposes) ? app.moduleFederation.exposes.filter((expose)=>'string' == typeof expose) : void 0,
134
+ verticalRefs: Array.isArray(app.moduleFederation.verticalRefs) ? app.moduleFederation.verticalRefs.filter((ref)=>'string' == typeof ref) : void 0,
135
+ hostOnly: 'vertical' !== app.kind
136
+ } : void 0,
137
+ effectApi: app.effect && 'object' == typeof app.effect ? {
138
+ stem: 'string' == typeof app.effect.prefix ? app.effect.prefix.split('/').filter(Boolean).at(-1) ?? domain ?? id : domain ?? id,
139
+ prefix: 'string' == typeof app.effect.prefix ? app.effect.prefix : `/${domain ?? id}-api`,
140
+ consumedBy: [
141
+ shellApp.id,
142
+ id
143
+ ]
144
+ } : void 0
145
+ };
146
+ })
147
+ },
148
+ legacyContract: contract
149
+ };
150
+ }
151
+ function readUltramodernConfig(workspaceRoot = process.cwd()) {
152
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
153
+ if (node_fs.existsSync(compactPath)) return normalizeCompactConfig(workspaceRoot, compactPath, readJsonObject(compactPath));
154
+ const legacyPath = node_path.join(workspaceRoot, GENERATED_CONTRACT_PATH);
155
+ if (node_fs.existsSync(legacyPath)) return adaptLegacyContract(workspaceRoot, legacyPath, readJsonObject(legacyPath));
156
+ throw new Error(`Missing UltraModern config. Expected ${ULTRAMODERN_CONFIG_PATH} or ${GENERATED_CONTRACT_PATH}.`);
157
+ }
158
+ function workspaceAppsFromToolingConfig(config) {
159
+ return config.topology.apps.map((app)=>{
160
+ if ('shell' === app.kind) return {
161
+ ...shellApp,
162
+ directory: app.path,
163
+ packageSuffix: app.packageSuffix ?? shellApp.packageSuffix,
164
+ displayName: app.displayName ?? shellApp.displayName,
165
+ port: app.port ?? shellApp.port,
166
+ portEnv: app.portEnv ?? shellApp.portEnv,
167
+ mfName: app.moduleFederation?.name ?? shellApp.mfName,
168
+ verticalRefs: app.moduleFederation?.verticalRefs ?? []
169
+ };
170
+ const domain = app.domain ?? app.id;
171
+ const packageSuffix = app.packageSuffix ?? domain;
172
+ const exposes = Object.fromEntries((app.moduleFederation?.exposes ?? []).map((expose)=>[
173
+ expose,
174
+ './Route' === expose ? './src/federation-entry.tsx' : './Widget' === expose ? `./src/components/${domain}-widget.tsx` : ''
175
+ ]));
176
+ return {
177
+ id: app.id,
178
+ directory: app.path,
179
+ packageSuffix,
180
+ displayName: app.displayName ?? `${domain} Vertical`,
181
+ kind: 'vertical',
182
+ domain,
183
+ portEnv: app.portEnv ?? `VERTICAL_${domain.replace(/[^a-zA-Z0-9]+/g, '_').toUpperCase()}_PORT`,
184
+ port: app.port ?? 0,
185
+ mfName: app.moduleFederation?.name ?? app.id,
186
+ exposes,
187
+ ...app.moduleFederation?.verticalRefs ? {
188
+ verticalRefs: app.moduleFederation.verticalRefs
189
+ } : {},
190
+ ...app.effectApi ? {
191
+ effectApi: app.effectApi
192
+ } : {},
193
+ ownership: createNeutralOwnership(app.id)
194
+ };
195
+ });
196
+ }
197
+ export { readUltramodernConfig, workspaceAppsFromToolingConfig };
@@ -3,10 +3,10 @@ import node_fs from "node:fs";
3
3
  import node_os from "node:os";
4
4
  import node_path from "node:path";
5
5
  import { WORKSPACE_PACKAGE_VERSION } from "../ultramodern-package-source.js";
6
+ import { readUltramodernConfig } from "../ultramodern-tooling/config.js";
6
7
  import { createAppEnvDts, createAppRuntimeConfig, createShellFrameComponent } from "./app-files.js";
7
- import { createGeneratedContract } from "./contracts.js";
8
8
  import { createShellPage, createShellRemoteComponents } from "./demo-components.js";
9
- import { GENERATED_CONTRACT_PATH, appHasEffectApi, appI18nNamespace, createModuleFederationRemoteContracts, createNeutralOwnership, createRemoteManifestEnv, createShellHost, createVerticalDescriptor, effectApiPrefix, remoteDependencyAlias, shellApp, zephyrRemoteDependency } from "./descriptors.js";
9
+ import { GENERATED_CONTRACT_PATH, ULTRAMODERN_CONFIG_PATH, appHasEffectApi, appI18nNamespace, createModuleFederationRemoteContracts, createNeutralOwnership, createRemoteManifestEnv, createShellHost, createVerticalDescriptor, effectApiPrefix, remoteDependencyAlias, shellApp, zephyrRemoteDependency } from "./descriptors.js";
10
10
  import { createShellEffectClient, effectApiTopologyMetadata } from "./effect-api.js";
11
11
  import { readJsonFile, writeFileReplacing, writeJsonFile } from "./fs-io.js";
12
12
  import { createFileSnapshot, createGenerationResult, diffFileSnapshots } from "./generation-result.js";
@@ -20,7 +20,7 @@ import { createCloudflareDeployContract } from "./policy.js";
20
20
  import { createPublicWebAppArtifacts, rewriteWorkspaceAssetsForApp } from "./public-surface.js";
21
21
  import { isRecord } from "./types.js";
22
22
  import { writeGeneratedWorkspaceScripts } from "./workspace-scripts.js";
23
- import { writeApp } from "./write-workspace.js";
23
+ import { createCompactUltramodernConfig, writeApp } from "./write-workspace.js";
24
24
  const FIRST_VERTICAL_PORT = 4101;
25
25
  const TOPOLOGY_PATH = 'topology/reference-topology.json';
26
26
  const OWNERSHIP_PATH = 'topology/ownership.json';
@@ -33,7 +33,12 @@ function existingPackageSource(workspaceRoot, modernVersion, packageSource) {
33
33
  modernVersion,
34
34
  packageSource
35
35
  });
36
- const metadataPath = node_path.join(workspaceRoot, '.modernjs/ultramodern-package-source.json');
36
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
37
+ if (node_fs.existsSync(compactPath)) {
38
+ const compactConfig = readUltramodernConfig(workspaceRoot);
39
+ if (compactConfig.packageSource) return compactConfig.packageSource;
40
+ }
41
+ const metadataPath = node_path.join(workspaceRoot, PACKAGE_SOURCE_METADATA_PATH);
37
42
  if (!node_fs.existsSync(metadataPath)) return resolvePackageSource({
38
43
  targetDir: workspaceRoot,
39
44
  packageName: node_path.basename(workspaceRoot),
@@ -56,6 +61,8 @@ function existingPackageSource(workspaceRoot, modernVersion, packageSource) {
56
61
  };
57
62
  }
58
63
  function existingTailwindEnabled(workspaceRoot) {
64
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
65
+ if (node_fs.existsSync(compactPath)) return readUltramodernConfig(workspaceRoot).features.tailwind;
59
66
  const contractPath = node_path.join(workspaceRoot, GENERATED_CONTRACT_PATH);
60
67
  if (!node_fs.existsSync(contractPath)) return true;
61
68
  const contract = readJsonFile(contractPath);
@@ -63,6 +70,10 @@ function existingTailwindEnabled(workspaceRoot) {
63
70
  const shell = apps.find((app)=>isRecord(app) && app.id === shellApp.id);
64
71
  return shell?.styling && isRecord(shell.styling) ? false !== shell.styling.tailwind : true;
65
72
  }
73
+ function existingBridgeConfig(workspaceRoot) {
74
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
75
+ return node_fs.existsSync(compactPath) ? readUltramodernConfig(workspaceRoot).bridge : void 0;
76
+ }
66
77
  function assertValidVerticalName(name) {
67
78
  const normalized = toKebabCase(name);
68
79
  if (!normalized || normalized !== name) throw new Error(`Invalid Vertical name "${name}". Use lowercase kebab-case.`);
@@ -75,17 +86,17 @@ function nextAvailablePort(ports) {
75
86
  function assertCanCreate(workspaceRoot, relativePath) {
76
87
  if (node_fs.existsSync(node_path.join(workspaceRoot, relativePath))) throw new Error(`Refusing to overwrite existing path: ${relativePath}`);
77
88
  }
78
- function updateRootWorkspaceScripts(workspaceRoot, scope, packageSource, remotes) {
89
+ function updateRootWorkspaceScripts(workspaceRoot, scope, packageSource, remotes, bridge) {
79
90
  const packagePath = node_path.join(workspaceRoot, 'package.json');
80
91
  const rootPackage = readJsonFile(packagePath);
81
- const generatedRootPackage = createRootPackageJson(scope, packageSource, remotes);
92
+ const generatedRootPackage = createRootPackageJson(scope, packageSource, remotes, bridge);
82
93
  rootPackage.scripts = generatedRootPackage.scripts;
83
94
  writeJsonFile(packagePath, rootPackage);
84
95
  }
85
- function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwind, remotes) {
96
+ function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwind, remotes, bridge) {
86
97
  const shellHost = createShellHost(remotes);
87
98
  const publicWeb = createPublicWebAppArtifacts(shellHost);
88
- writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/package.json`), createAppPackage(scope, shellHost, packageSource, enableTailwind, remotes));
99
+ writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/package.json`), createAppPackage(scope, shellHost, packageSource, enableTailwind, remotes, bridge));
89
100
  writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/tsconfig.json`), createAppTsConfig(shellHost, remotes));
90
101
  writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/tsconfig.mf-types.json`), createAppMfTypesTsConfig(shellHost));
91
102
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern-app-env.d.ts`, createAppEnvDts(shellHost, remotes));
@@ -204,8 +215,7 @@ function prepareAddUltramodernVertical(options) {
204
215
  const topology = readRequiredJsonObject(topologyPath);
205
216
  const ownership = readRequiredJsonObject(ownershipPath);
206
217
  const overlay = readRequiredJsonObject(overlayPath);
207
- readRequiredJsonObject(node_path.join(options.workspaceRoot, GENERATED_CONTRACT_PATH));
208
- readRequiredJsonObject(node_path.join(options.workspaceRoot, PACKAGE_SOURCE_METADATA_PATH));
218
+ readRequiredWorkspaceConfig(options.workspaceRoot);
209
219
  assertOptionalJsonObject(topology.shell, 'topology.shell', topologyPath);
210
220
  assertOptionalJsonArray(topology.verticals, 'topology.verticals', topologyPath);
211
221
  assertOptionalJsonArray(ownership.owners, 'ownership.owners', ownershipPath);
@@ -216,6 +226,7 @@ function prepareAddUltramodernVertical(options) {
216
226
  const scope = toPackageScope(String(rootPackage.name ?? node_path.basename(options.workspaceRoot)));
217
227
  const packageSource = existingPackageSource(options.workspaceRoot, options.modernVersion, options.packageSource);
218
228
  const enableTailwind = options.enableTailwind ?? existingTailwindEnabled(options.workspaceRoot);
229
+ const bridge = existingBridgeConfig(options.workspaceRoot);
219
230
  const existingVerticals = verticalsFromTopology(topology, overlay.ports);
220
231
  const port = nextAvailablePort(overlay.ports);
221
232
  const vertical = createVerticalDescriptor(name, port);
@@ -243,6 +254,7 @@ function prepareAddUltramodernVertical(options) {
243
254
  overlay,
244
255
  packageSource,
245
256
  enableTailwind,
257
+ bridge,
246
258
  vertical,
247
259
  updatedVerticals
248
260
  };
@@ -253,6 +265,12 @@ function readRequiredJsonObject(filePath) {
253
265
  if (!isRecord(value)) throw new Error(`UltraModern workspace file must contain a JSON object: ${filePath}`);
254
266
  return value;
255
267
  }
268
+ function readRequiredWorkspaceConfig(workspaceRoot) {
269
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
270
+ if (node_fs.existsSync(compactPath)) return void readRequiredJsonObject(compactPath);
271
+ readRequiredJsonObject(node_path.join(workspaceRoot, GENERATED_CONTRACT_PATH));
272
+ readRequiredJsonObject(node_path.join(workspaceRoot, PACKAGE_SOURCE_METADATA_PATH));
273
+ }
256
274
  function assertOptionalJsonObject(value, label, filePath) {
257
275
  if (void 0 !== value && !isRecord(value)) throw new Error(`${label} in ${filePath} must be a JSON object`);
258
276
  }
@@ -357,7 +375,7 @@ function createVerticalPlan(preflight, result) {
357
375
  shellDependencyChanges: createShellDependencyChanges(scope, vertical),
358
376
  generatedContractChanges: [
359
377
  {
360
- path: GENERATED_CONTRACT_PATH,
378
+ path: ULTRAMODERN_CONFIG_PATH,
361
379
  addedAppIds: [
362
380
  vertical.id
363
381
  ],
@@ -444,9 +462,9 @@ function createDryRunJsonMutations(preflight, manifestUrl) {
444
462
  description: 'Keep shell Module Federation DTS compilation scoped'
445
463
  },
446
464
  {
447
- path: GENERATED_CONTRACT_PATH,
448
- pointer: '/apps',
449
- description: `Regenerate contract with ${vertical.id}`
465
+ path: ULTRAMODERN_CONFIG_PATH,
466
+ pointer: '/topology/apps',
467
+ description: `Regenerate compact config with ${vertical.id}`
450
468
  }
451
469
  ];
452
470
  }
@@ -470,8 +488,8 @@ function createShellDependencyChanges(scope, vertical) {
470
488
  }
471
489
  function addUltramodernVertical(options) {
472
490
  const beforeFiles = createFileSnapshot(options.workspaceRoot);
473
- const { scope, topologyPath, ownershipPath, overlayPath, topology, ownership, overlay, packageSource, enableTailwind, vertical, updatedVerticals } = prepareAddUltramodernVertical(options);
474
- writeApp(options.workspaceRoot, scope, vertical, packageSource, enableTailwind);
491
+ const { scope, topologyPath, ownershipPath, overlayPath, topology, ownership, overlay, packageSource, enableTailwind, bridge, vertical, updatedVerticals } = prepareAddUltramodernVertical(options);
492
+ writeApp(options.workspaceRoot, scope, vertical, packageSource, enableTailwind, updatedVerticals, bridge);
475
493
  topology.shell ??= {};
476
494
  topology.shell.verticalRefs ??= [];
477
495
  topology.shell.verticalRefs.push(vertical.id);
@@ -494,18 +512,18 @@ function addUltramodernVertical(options) {
494
512
  writeJsonFile(topologyPath, topology);
495
513
  writeJsonFile(ownershipPath, ownership);
496
514
  writeJsonFile(overlayPath, overlay);
497
- writeJsonFile(node_path.join(options.workspaceRoot, GENERATED_CONTRACT_PATH), createGeneratedContract(scope, [
515
+ writeJsonFile(node_path.join(options.workspaceRoot, ULTRAMODERN_CONFIG_PATH), createCompactUltramodernConfig(scope, options.modernVersion, packageSource, [
498
516
  {
499
517
  ...shellApp,
500
518
  verticalRefs: updatedVerticals.map((vertical)=>vertical.id)
501
519
  },
502
520
  ...updatedVerticals
503
- ], enableTailwind));
504
- rewriteShellAppFiles(options.workspaceRoot, scope, packageSource, enableTailwind, updatedVerticals);
521
+ ], enableTailwind, bridge));
522
+ rewriteShellAppFiles(options.workspaceRoot, scope, packageSource, enableTailwind, updatedVerticals, bridge);
505
523
  writeGeneratedWorkspaceScripts(options.workspaceRoot, scope, enableTailwind, updatedVerticals);
506
524
  addShellZephyrDependency(options.workspaceRoot, scope, vertical);
507
525
  addShellWorkspaceDependency(options.workspaceRoot, scope, vertical);
508
- updateRootWorkspaceScripts(options.workspaceRoot, scope, packageSource, updatedVerticals);
526
+ updateRootWorkspaceScripts(options.workspaceRoot, scope, packageSource, updatedVerticals, bridge);
509
527
  writeJsonFile(node_path.join(options.workspaceRoot, 'tsconfig.json'), createRootTsConfig([
510
528
  {
511
529
  ...shellApp,
@@ -533,4 +551,4 @@ function addUltramodernVertical(options) {
533
551
  });
534
552
  return result;
535
553
  }
536
- export { addShellWorkspaceDependency, addShellZephyrDependency, addUltramodernVertical, assertCanCreate, assertValidVerticalName, existingPackageSource, existingTailwindEnabled, nextAvailablePort, ownershipEntry, planUltramodernVertical, prepareAddUltramodernVertical, rewriteShellAppFiles, updateRootWorkspaceScripts, verticalTopologyEntry, verticalsFromTopology };
554
+ export { addShellWorkspaceDependency, addShellZephyrDependency, addUltramodernVertical, assertCanCreate, assertValidVerticalName, existingBridgeConfig, existingPackageSource, existingTailwindEnabled, nextAvailablePort, ownershipEntry, planUltramodernVertical, prepareAddUltramodernVertical, rewriteShellAppFiles, updateRootWorkspaceScripts, verticalTopologyEntry, verticalsFromTopology };
@@ -0,0 +1,271 @@
1
+ import "node:module";
2
+ const ultramodernBridgeCliFlags = {
3
+ enabled: '--bridge',
4
+ parentRoot: '--bridge-parent-root',
5
+ workspacePackage: '--bridge-workspace-package',
6
+ workspacePackageName: '--bridge-workspace-package-name',
7
+ testAlias: '--bridge-test-alias',
8
+ dependency: '--bridge-dependency',
9
+ lockfilePolicy: '--bridge-lockfile-policy',
10
+ gate: '--bridge-gate',
11
+ gateCwd: '--bridge-gate-cwd',
12
+ reactSingleton: '--bridge-react-singleton'
13
+ };
14
+ const ultramodernBridgeCliValueFlags = [
15
+ ultramodernBridgeCliFlags.parentRoot,
16
+ ultramodernBridgeCliFlags.workspacePackage,
17
+ ultramodernBridgeCliFlags.workspacePackageName,
18
+ ultramodernBridgeCliFlags.testAlias,
19
+ ultramodernBridgeCliFlags.dependency,
20
+ ultramodernBridgeCliFlags.lockfilePolicy,
21
+ ultramodernBridgeCliFlags.gate,
22
+ ultramodernBridgeCliFlags.gateCwd,
23
+ ultramodernBridgeCliFlags.reactSingleton
24
+ ];
25
+ const ultramodernBridgeCliBooleanFlags = [
26
+ ultramodernBridgeCliFlags.enabled
27
+ ];
28
+ const ultramodernBridgeLockfilePolicies = [
29
+ 'nested',
30
+ 'parent'
31
+ ];
32
+ const defaultReactSingletons = [
33
+ 'react',
34
+ 'react-dom'
35
+ ];
36
+ function hasUltramodernBridgeCliOptions(args) {
37
+ return args.some((arg)=>[
38
+ ...ultramodernBridgeCliBooleanFlags,
39
+ ...ultramodernBridgeCliValueFlags
40
+ ].some((flag)=>arg === flag || arg.startsWith(`${flag}=`)));
41
+ }
42
+ function parseUltramodernBridgeCliOptions(args) {
43
+ rejectBooleanFlagValues(args);
44
+ if (!hasUltramodernBridgeCliOptions(args)) return;
45
+ const parentRoot = readSingleValue(args, ultramodernBridgeCliFlags.parentRoot);
46
+ const lockfilePolicy = readSingleValue(args, ultramodernBridgeCliFlags.lockfilePolicy) ?? 'nested';
47
+ const workspacePackages = readWorkspacePackages(args);
48
+ const gates = readBridgeGates(args);
49
+ const reactSingletons = readCsvOptionValues(args, ultramodernBridgeCliFlags.reactSingleton);
50
+ return normalizeUltramodernBridgeConfig({
51
+ enabled: true,
52
+ parentRoot: requireNonEmptyValue(parentRoot, ultramodernBridgeCliFlags.parentRoot),
53
+ workspacePackages,
54
+ dependencies: readCsvOptionValues(args, ultramodernBridgeCliFlags.dependency),
55
+ lockfilePolicy: parseLockfilePolicy(lockfilePolicy),
56
+ gates,
57
+ reactSingletons: reactSingletons.length > 0 ? reactSingletons : void 0
58
+ });
59
+ }
60
+ function normalizeUltramodernBridgeConfig(bridge) {
61
+ if (!bridge || false === bridge.enabled) return;
62
+ const parentRoot = requireNonEmptyValue(bridge.parentRoot, 'bridge.parentRoot');
63
+ const workspacePackages = normalizeWorkspacePackages(bridge.workspacePackages);
64
+ const dependencies = uniqueNonEmptyValues(bridge.dependencies, 'bridge.dependencies');
65
+ const gates = normalizeBridgeGates(bridge.gates);
66
+ const reactSingletons = bridge.reactSingletons && bridge.reactSingletons.length > 0 ? uniqueNonEmptyValues(bridge.reactSingletons, 'bridge.reactSingletons') : [
67
+ ...defaultReactSingletons
68
+ ];
69
+ if (0 === workspacePackages.length) throw new Error('Bridge mode requires at least one bridge.workspacePackages entry.');
70
+ if (0 === dependencies.length) throw new Error('Bridge mode requires at least one explicit bridge.dependencies package name.');
71
+ if (0 === gates.length) throw new Error('Bridge mode requires at least one bridge.gates delegated parent command.');
72
+ validateParentPackageCoverage(workspacePackages, dependencies);
73
+ validateReactSingletons(reactSingletons);
74
+ return {
75
+ enabled: true,
76
+ parentRoot,
77
+ workspacePackages,
78
+ dependencies,
79
+ lockfilePolicy: parseLockfilePolicy(bridge.lockfilePolicy ?? 'nested'),
80
+ gates,
81
+ reactSingletons
82
+ };
83
+ }
84
+ function rejectBooleanFlagValues(args) {
85
+ for (const flag of ultramodernBridgeCliBooleanFlags)if (args.some((arg)=>arg.startsWith(`${flag}=`))) throw new Error(`${flag} does not accept a value.`);
86
+ }
87
+ function readRepeatedOptionValues(args, flag) {
88
+ const values = [];
89
+ for(let index = 0; index < args.length; index++){
90
+ const arg = args[index];
91
+ if (arg === flag) {
92
+ const value = args[index + 1];
93
+ if (!value || value.startsWith('-')) throw new Error(`${flag} requires a value.`);
94
+ values.push(value);
95
+ index += 1;
96
+ continue;
97
+ }
98
+ if (arg.startsWith(`${flag}=`)) values.push(arg.slice(flag.length + 1));
99
+ }
100
+ return values;
101
+ }
102
+ function readSingleValue(args, flag) {
103
+ const values = readRepeatedOptionValues(args, flag);
104
+ if (values.length > 1) throw new Error(`${flag} can be provided only once.`);
105
+ return values[0];
106
+ }
107
+ function readCsvOptionValues(args, flag) {
108
+ return readRepeatedOptionValues(args, flag).flatMap((value)=>value.split(',').map((entry)=>entry.trim()).filter(Boolean));
109
+ }
110
+ function requireNonEmptyValue(value, label) {
111
+ if ('string' != typeof value || 0 === value.trim().length) throw new Error(`${label} must be a non-empty string.`);
112
+ return value.trim();
113
+ }
114
+ function parseLockfilePolicy(value) {
115
+ if (!ultramodernBridgeLockfilePolicies.includes(value)) throw new Error(`${ultramodernBridgeCliFlags.lockfilePolicy} must be "nested" or "parent".`);
116
+ return value;
117
+ }
118
+ function readWorkspacePackages(args) {
119
+ const drafts = new Map();
120
+ const getDraft = (pattern)=>{
121
+ const normalizedPattern = requireNonEmptyValue(pattern, ultramodernBridgeCliFlags.workspacePackage);
122
+ const existing = drafts.get(normalizedPattern);
123
+ if (existing) return existing;
124
+ const draft = {
125
+ pattern: normalizedPattern,
126
+ packageNames: [],
127
+ testAliases: []
128
+ };
129
+ drafts.set(normalizedPattern, draft);
130
+ return draft;
131
+ };
132
+ for (const pattern of readRepeatedOptionValues(args, ultramodernBridgeCliFlags.workspacePackage))getDraft(pattern);
133
+ for (const value of readRepeatedOptionValues(args, ultramodernBridgeCliFlags.workspacePackageName)){
134
+ const { left: pattern, right: packageNames } = splitAssignment(value, ultramodernBridgeCliFlags.workspacePackageName, '<pattern>=<package-name>[,<package-name>]');
135
+ getDraft(pattern).packageNames.push(...packageNames.split(',').map((packageName)=>packageName.trim()).filter(Boolean));
136
+ }
137
+ for (const value of readRepeatedOptionValues(args, ultramodernBridgeCliFlags.testAlias)){
138
+ const separator = value.indexOf(':');
139
+ if (-1 === separator) throw new Error(`${ultramodernBridgeCliFlags.testAlias} must use <pattern>:<alias>=<target>.`);
140
+ const pattern = value.slice(0, separator);
141
+ const aliasDefinition = value.slice(separator + 1);
142
+ const { left: alias, right: target } = splitAssignment(aliasDefinition, ultramodernBridgeCliFlags.testAlias, '<pattern>:<alias>=<target>');
143
+ getDraft(pattern).testAliases.push({
144
+ alias: requireNonEmptyValue(alias, `${ultramodernBridgeCliFlags.testAlias} alias`),
145
+ target: requireNonEmptyValue(target, `${ultramodernBridgeCliFlags.testAlias} target`)
146
+ });
147
+ }
148
+ return normalizeWorkspacePackages([
149
+ ...drafts.values()
150
+ ]);
151
+ }
152
+ function readBridgeGates(args) {
153
+ const gates = new Map();
154
+ const getGate = (name)=>{
155
+ const normalizedName = requireNonEmptyValue(name, `${ultramodernBridgeCliFlags.gate} name`);
156
+ const existing = gates.get(normalizedName);
157
+ if (existing) return existing;
158
+ const draft = {
159
+ name: normalizedName
160
+ };
161
+ gates.set(normalizedName, draft);
162
+ return draft;
163
+ };
164
+ for (const value of readRepeatedOptionValues(args, ultramodernBridgeCliFlags.gate)){
165
+ const { left: name, right: command } = splitAssignment(value, ultramodernBridgeCliFlags.gate, '<name>=<command>');
166
+ getGate(name).command = requireNonEmptyValue(command, `${ultramodernBridgeCliFlags.gate} command`);
167
+ }
168
+ for (const value of readRepeatedOptionValues(args, ultramodernBridgeCliFlags.gateCwd)){
169
+ const { left: name, right: cwd } = splitAssignment(value, ultramodernBridgeCliFlags.gateCwd, '<name>=<cwd>');
170
+ getGate(name).cwd = requireNonEmptyValue(cwd, `${ultramodernBridgeCliFlags.gateCwd} cwd`);
171
+ }
172
+ const missingCommand = [
173
+ ...gates.values()
174
+ ].find((gate)=>!gate.command);
175
+ if (missingCommand) throw new Error(`${ultramodernBridgeCliFlags.gateCwd} references "${missingCommand.name}" without a matching ${ultramodernBridgeCliFlags.gate}.`);
176
+ return normalizeBridgeGates([
177
+ ...gates.values()
178
+ ].map((gate)=>({
179
+ name: gate.name,
180
+ command: gate.command,
181
+ cwd: gate.cwd
182
+ })));
183
+ }
184
+ function splitAssignment(value, flag, usage) {
185
+ const separator = value.indexOf('=');
186
+ if (-1 === separator) throw new Error(`${flag} must use ${usage}.`);
187
+ return {
188
+ left: value.slice(0, separator),
189
+ right: value.slice(separator + 1)
190
+ };
191
+ }
192
+ function normalizeWorkspacePackages(workspacePackages) {
193
+ return workspacePackages.map((entry, index)=>{
194
+ const label = `bridge.workspacePackages[${index}]`;
195
+ const packageNames = entry.packageNames && entry.packageNames.length > 0 ? uniqueNonEmptyValues(entry.packageNames, `${label}.packageNames`) : void 0;
196
+ const testAliases = entry.testAliases && entry.testAliases.length > 0 ? normalizeTestAliases(entry.testAliases, label) : void 0;
197
+ return {
198
+ pattern: requireNonEmptyValue(entry.pattern, `${label}.pattern`),
199
+ ...packageNames ? {
200
+ packageNames
201
+ } : {},
202
+ ...testAliases ? {
203
+ testAliases
204
+ } : {}
205
+ };
206
+ });
207
+ }
208
+ function validateParentPackageCoverage(workspacePackages, dependencies) {
209
+ const declaredPackages = new Set();
210
+ for (const [index, entry] of workspacePackages.entries()){
211
+ const label = `bridge.workspacePackages[${index}]`;
212
+ const packageNames = entry.packageNames ?? [];
213
+ if (0 === packageNames.length && (entry.testAliases ?? []).length > 0) throw new Error(`${label}.packageNames must declare the parent package names covered by "${entry.pattern}" before testAliases can be checked.`);
214
+ for (const packageName of packageNames)declaredPackages.add(packageName);
215
+ for (const alias of entry.testAliases ?? []){
216
+ if (!packageNames.some((packageName)=>aliasMatchesPackage(alias.alias, packageName))) throw new Error(`${label}.testAliases entry "${alias.alias}" must match one of ${label}.packageNames.`);
217
+ if (pointsAtBuildOutput(alias.target)) throw new Error(`${label}.testAliases target "${alias.target}" must point at source files, not dist output.`);
218
+ }
219
+ }
220
+ const missingDependencies = dependencies.filter((dependency)=>!declaredPackages.has(dependency));
221
+ if (declaredPackages.size > 0 && missingDependencies.length > 0) throw new Error(`Bridge mode dependencies must be declared by bridge.workspacePackages[].packageNames: ${missingDependencies.join(', ')}.`);
222
+ }
223
+ function aliasMatchesPackage(alias, packageName) {
224
+ return alias === packageName || alias.startsWith(`${packageName}/`);
225
+ }
226
+ function pointsAtBuildOutput(target) {
227
+ return target.split(/[\\/]/u).includes('dist');
228
+ }
229
+ function validateReactSingletons(reactSingletons) {
230
+ const missing = defaultReactSingletons.filter((packageName)=>!reactSingletons.includes(packageName));
231
+ if (missing.length > 0) throw new Error(`Bridge mode React singleton/dedupe declarations must include ${defaultReactSingletons.join(' and ')}. Missing: ${missing.join(', ')}.`);
232
+ }
233
+ function normalizeTestAliases(testAliases, parentLabel) {
234
+ const aliases = new Map();
235
+ for (const [index, alias] of testAliases.entries()){
236
+ const label = `${parentLabel}.testAliases[${index}]`;
237
+ const normalizedAlias = requireNonEmptyValue(alias.alias, `${label}.alias`);
238
+ aliases.set(normalizedAlias, {
239
+ alias: normalizedAlias,
240
+ target: requireNonEmptyValue(alias.target, `${label}.target`)
241
+ });
242
+ }
243
+ return [
244
+ ...aliases.values()
245
+ ];
246
+ }
247
+ function normalizeBridgeGates(gates) {
248
+ const normalized = new Map();
249
+ for (const [index, gate] of gates.entries()){
250
+ const label = `bridge.gates[${index}]`;
251
+ const name = requireNonEmptyValue(gate.name, `${label}.name`);
252
+ normalized.set(name, {
253
+ name,
254
+ command: requireNonEmptyValue(gate.command, `${label}.command`),
255
+ ...gate.cwd ? {
256
+ cwd: requireNonEmptyValue(gate.cwd, `${label}.cwd`)
257
+ } : {}
258
+ });
259
+ }
260
+ return [
261
+ ...normalized.values()
262
+ ];
263
+ }
264
+ function uniqueNonEmptyValues(values, label) {
265
+ const unique = new Set();
266
+ for (const [index, value] of values.entries())unique.add(requireNonEmptyValue(value, `${label}[${index}]`));
267
+ return [
268
+ ...unique
269
+ ];
270
+ }
271
+ export { hasUltramodernBridgeCliOptions, normalizeUltramodernBridgeConfig, parseUltramodernBridgeCliOptions, ultramodernBridgeCliBooleanFlags, ultramodernBridgeCliFlags, ultramodernBridgeCliValueFlags, ultramodernBridgeLockfilePolicies };
@@ -15,6 +15,7 @@ async function ultramodernCodeSmithAdapter(context, runtime) {
15
15
  packageName: name,
16
16
  modernVersion,
17
17
  enableTailwind,
18
+ bridge: config.bridge,
18
19
  overlays: config.overlays,
19
20
  packageSource
20
21
  }) : runVerticalMode({