@bleedingdev/modern-js-create 3.4.0-ultramodern.2 → 3.4.0-ultramodern.20

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 (128) hide show
  1. package/README.md +13 -11
  2. package/dist/cjs/index.cjs +37 -4
  3. package/dist/cjs/locale/en.cjs +12 -2
  4. package/dist/cjs/locale/zh.cjs +12 -2
  5. package/dist/cjs/ultramodern-tooling/commands.cjs +185 -0
  6. package/dist/cjs/ultramodern-tooling/config.cjs +172 -0
  7. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +79 -61
  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 +121 -648
  11. package/dist/cjs/ultramodern-workspace/demo-components.cjs +26 -33
  12. package/dist/cjs/ultramodern-workspace/descriptors.cjs +4 -4
  13. package/dist/cjs/ultramodern-workspace/effect-api.cjs +230 -100
  14. package/dist/cjs/ultramodern-workspace/fs-io.cjs +117 -0
  15. package/dist/cjs/ultramodern-workspace/generation-result.cjs +1 -1
  16. package/dist/cjs/ultramodern-workspace/index.cjs +4 -0
  17. package/dist/cjs/ultramodern-workspace/mf-validation.cjs +571 -0
  18. package/dist/cjs/ultramodern-workspace/module-federation.cjs +51 -20
  19. package/dist/cjs/ultramodern-workspace/package-json.cjs +62 -19
  20. package/dist/cjs/ultramodern-workspace/policy.cjs +2 -2
  21. package/dist/cjs/ultramodern-workspace/public-api.cjs +4 -0
  22. package/dist/cjs/ultramodern-workspace/versions.cjs +5 -0
  23. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +80 -43
  24. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +63 -20
  25. package/dist/esm/index.js +37 -4
  26. package/dist/esm/locale/en.js +12 -2
  27. package/dist/esm/locale/zh.js +12 -2
  28. package/dist/esm/ultramodern-tooling/commands.js +132 -0
  29. package/dist/esm/ultramodern-tooling/config.js +120 -0
  30. package/dist/esm/ultramodern-workspace/add-vertical.js +81 -66
  31. package/dist/esm/ultramodern-workspace/bridge-config.js +270 -0
  32. package/dist/esm/ultramodern-workspace/codesmith.js +1 -0
  33. package/dist/esm/ultramodern-workspace/contracts.js +124 -603
  34. package/dist/esm/ultramodern-workspace/demo-components.js +26 -33
  35. package/dist/esm/ultramodern-workspace/descriptors.js +2 -2
  36. package/dist/esm/ultramodern-workspace/effect-api.js +230 -100
  37. package/dist/esm/ultramodern-workspace/fs-io.js +116 -2
  38. package/dist/esm/ultramodern-workspace/generation-result.js +2 -2
  39. package/dist/esm/ultramodern-workspace/index.js +1 -0
  40. package/dist/esm/ultramodern-workspace/mf-validation.js +516 -0
  41. package/dist/esm/ultramodern-workspace/module-federation.js +49 -21
  42. package/dist/esm/ultramodern-workspace/package-json.js +60 -20
  43. package/dist/esm/ultramodern-workspace/policy.js +2 -2
  44. package/dist/esm/ultramodern-workspace/public-api.js +1 -0
  45. package/dist/esm/ultramodern-workspace/versions.js +3 -1
  46. package/dist/esm/ultramodern-workspace/workspace-scripts.js +78 -26
  47. package/dist/esm/ultramodern-workspace/write-workspace.js +67 -27
  48. package/dist/esm-node/index.js +37 -4
  49. package/dist/esm-node/locale/en.js +12 -2
  50. package/dist/esm-node/locale/zh.js +12 -2
  51. package/dist/esm-node/ultramodern-tooling/commands.js +133 -0
  52. package/dist/esm-node/ultramodern-tooling/config.js +121 -0
  53. package/dist/esm-node/ultramodern-workspace/add-vertical.js +81 -66
  54. package/dist/esm-node/ultramodern-workspace/bridge-config.js +271 -0
  55. package/dist/esm-node/ultramodern-workspace/codesmith.js +1 -0
  56. package/dist/esm-node/ultramodern-workspace/contracts.js +124 -603
  57. package/dist/esm-node/ultramodern-workspace/demo-components.js +26 -33
  58. package/dist/esm-node/ultramodern-workspace/descriptors.js +2 -2
  59. package/dist/esm-node/ultramodern-workspace/effect-api.js +230 -100
  60. package/dist/esm-node/ultramodern-workspace/fs-io.js +116 -2
  61. package/dist/esm-node/ultramodern-workspace/generation-result.js +2 -2
  62. package/dist/esm-node/ultramodern-workspace/index.js +1 -0
  63. package/dist/esm-node/ultramodern-workspace/mf-validation.js +517 -0
  64. package/dist/esm-node/ultramodern-workspace/module-federation.js +49 -21
  65. package/dist/esm-node/ultramodern-workspace/package-json.js +60 -20
  66. package/dist/esm-node/ultramodern-workspace/policy.js +2 -2
  67. package/dist/esm-node/ultramodern-workspace/public-api.js +1 -0
  68. package/dist/esm-node/ultramodern-workspace/versions.js +3 -1
  69. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +78 -26
  70. package/dist/esm-node/ultramodern-workspace/write-workspace.js +67 -27
  71. package/dist/types/locale/en.d.ts +10 -0
  72. package/dist/types/locale/index.d.ts +20 -0
  73. package/dist/types/locale/zh.d.ts +10 -0
  74. package/dist/types/ultramodern-tooling/commands.d.ts +1 -0
  75. package/dist/types/ultramodern-tooling/config.d.ts +43 -0
  76. package/dist/types/ultramodern-workspace/add-vertical.d.ts +5 -2
  77. package/dist/types/ultramodern-workspace/bridge-config.d.ts +64 -0
  78. package/dist/types/ultramodern-workspace/codesmith.d.ts +1 -0
  79. package/dist/types/ultramodern-workspace/contracts.d.ts +2 -17
  80. package/dist/types/ultramodern-workspace/descriptors.d.ts +1 -1
  81. package/dist/types/ultramodern-workspace/fs-io.d.ts +1 -0
  82. package/dist/types/ultramodern-workspace/index.d.ts +2 -0
  83. package/dist/types/ultramodern-workspace/mf-validation.d.ts +27 -0
  84. package/dist/types/ultramodern-workspace/module-federation.d.ts +1 -0
  85. package/dist/types/ultramodern-workspace/package-json.d.ts +5 -3
  86. package/dist/types/ultramodern-workspace/public-api.d.ts +2 -0
  87. package/dist/types/ultramodern-workspace/types.d.ts +2 -0
  88. package/dist/types/ultramodern-workspace/versions.d.ts +2 -0
  89. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +2 -7
  90. package/dist/types/ultramodern-workspace/write-workspace.d.ts +4 -2
  91. package/package.json +5 -3
  92. package/template-workspace/{.agents → .codex}/skills-lock.json +12 -12
  93. package/template-workspace/.gitignore.handlebars +3 -0
  94. package/template-workspace/AGENTS.md.handlebars +11 -5
  95. package/template-workspace/README.md.handlebars +15 -12
  96. package/template-workspace/oxfmt.config.ts +1 -0
  97. package/template-workspace/oxlint.config.ts +1 -0
  98. package/template-workspace/patches/@tanstack__router-core@1.171.13.patch +51 -0
  99. package/template-workspace/pnpm-workspace.yaml.handlebars +12 -0
  100. package/template-workspace/scripts/bootstrap-agent-skills.mjs +96 -69
  101. package/templates/app/shell-frame.tsx +1 -2
  102. package/templates/app/ultramodern-route-head.tsx.handlebars +5 -6
  103. package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +59 -7
  104. package/templates/workspace-scripts/check-ultramodern-api-boundaries.mjs +339 -0
  105. package/templates/workspace-scripts/generate-public-surface-assets.mjs +245 -8
  106. package/templates/workspace-scripts/proof-cloudflare-version.mjs +276 -8
  107. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +35 -10
  108. package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +164 -1
  109. package/templates/workspace-scripts/ultramodern-typecheck.mjs +4 -2
  110. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +949 -63
  111. /package/template-workspace/{.agents → .codex}/rstackjs-agent-skills-LICENSE +0 -0
  112. /package/template-workspace/{.agents → .codex}/skills/rsbuild-best-practices/SKILL.md +0 -0
  113. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/SKILL.md +0 -0
  114. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/command-map.md +0 -0
  115. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/common-analysis-patterns.md +0 -0
  116. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor-common.md +0 -0
  117. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor-rspack.md +0 -0
  118. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor-webpack.md +0 -0
  119. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/install-rsdoctor.md +0 -0
  120. /package/template-workspace/{.agents → .codex}/skills/rsdoctor-analysis/references/rsdoctor-data-types.md +0 -0
  121. /package/template-workspace/{.agents → .codex}/skills/rslib-best-practices/SKILL.md +0 -0
  122. /package/template-workspace/{.agents → .codex}/skills/rslib-modern-package/SKILL.md +0 -0
  123. /package/template-workspace/{.agents → .codex}/skills/rspack-best-practices/SKILL.md +0 -0
  124. /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/SKILL.md +0 -0
  125. /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/references/bottlenecks.md +0 -0
  126. /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/references/tracing-guide.md +0 -0
  127. /package/template-workspace/{.agents → .codex}/skills/rspack-tracing/scripts/analyze_trace.js +0 -0
  128. /package/template-workspace/{.agents → .codex}/skills/rstest-best-practices/SKILL.md +0 -0
@@ -2,29 +2,28 @@ import node_fs from "node:fs";
2
2
  import node_os from "node:os";
3
3
  import node_path from "node:path";
4
4
  import { WORKSPACE_PACKAGE_VERSION } from "../ultramodern-package-source.js";
5
+ import { readUltramodernConfig } from "../ultramodern-tooling/config.js";
5
6
  import { createAppEnvDts, createAppRuntimeConfig, createShellFrameComponent } from "./app-files.js";
6
- import { createGeneratedContract } from "./contracts.js";
7
7
  import { createShellPage, createShellRemoteComponents } from "./demo-components.js";
8
- import { GENERATED_CONTRACT_PATH, appHasEffectApi, appI18nNamespace, createModuleFederationRemoteContracts, createNeutralOwnership, createRemoteManifestEnv, createShellHost, createVerticalDescriptor, effectApiPrefix, remoteDependencyAlias, shellApp, zephyrRemoteDependency } from "./descriptors.js";
8
+ import { ULTRAMODERN_CONFIG_PATH, appHasEffectApi, appI18nNamespace, createModuleFederationRemoteContracts, createNeutralOwnership, createRemoteManifestEnv, createShellHost, createVerticalDescriptor, effectApiPrefix, remoteDependencyAlias, shellApp, zephyrRemoteDependency } from "./descriptors.js";
9
9
  import { createShellEffectClient, effectApiTopologyMetadata } from "./effect-api.js";
10
- import { readJsonFile, writeFileReplacing, writeJsonFile } from "./fs-io.js";
10
+ import { formatGeneratedWorkspaceFiles, readJsonFile, writeFileReplacing, writeJsonFile } from "./fs-io.js";
11
11
  import { createFileSnapshot, createGenerationResult, diffFileSnapshots } from "./generation-result.js";
12
12
  import { createAppPublicLocaleMessages } from "./locales.js";
13
13
  import { createShellModuleFederationConfig } from "./module-federation.js";
14
14
  import { assertUniqueTailwindPrefixes, normalizePath, packageName, toEnvSegment, toKebabCase, toPackageScope, toPascalCase } from "./naming.js";
15
15
  import { runCodeSmithOverlays } from "./overlays.js";
16
- import { createAppPackage, createAppTsConfig, createRootPackageJson, createRootTsConfig } from "./package-json.js";
16
+ import { createAppMfTypesTsConfig, createAppPackage, createAppTsConfig, createRootPackageJson, createRootTsConfig } from "./package-json.js";
17
17
  import { resolvePackageSource } from "./package-source.js";
18
18
  import { createCloudflareDeployContract } from "./policy.js";
19
19
  import { createPublicWebAppArtifacts, rewriteWorkspaceAssetsForApp } from "./public-surface.js";
20
20
  import { isRecord } from "./types.js";
21
21
  import { writeGeneratedWorkspaceScripts } from "./workspace-scripts.js";
22
- import { writeApp } from "./write-workspace.js";
22
+ import { createCompactUltramodernConfig, writeApp } from "./write-workspace.js";
23
23
  const FIRST_VERTICAL_PORT = 4101;
24
24
  const TOPOLOGY_PATH = 'topology/reference-topology.json';
25
25
  const OWNERSHIP_PATH = 'topology/ownership.json';
26
26
  const DEVELOPMENT_OVERLAY_PATH = 'topology/local-overlays/development.json';
27
- const PACKAGE_SOURCE_METADATA_PATH = '.modernjs/ultramodern-package-source.json';
28
27
  function existingPackageSource(workspaceRoot, modernVersion, packageSource) {
29
28
  if (packageSource) return resolvePackageSource({
30
29
  targetDir: workspaceRoot,
@@ -32,35 +31,21 @@ function existingPackageSource(workspaceRoot, modernVersion, packageSource) {
32
31
  modernVersion,
33
32
  packageSource
34
33
  });
35
- const metadataPath = node_path.join(workspaceRoot, '.modernjs/ultramodern-package-source.json');
36
- if (!node_fs.existsSync(metadataPath)) return resolvePackageSource({
37
- targetDir: workspaceRoot,
38
- packageName: node_path.basename(workspaceRoot),
39
- modernVersion
40
- });
41
- const metadata = readJsonFile(metadataPath);
42
- const aliases = metadata.modernPackages?.aliases ?? {};
43
- const firstAlias = Object.values(aliases).find((value)=>'string' == typeof value);
44
- const firstPackage = Object.keys(aliases)[0];
45
- const aliasScope = firstAlias?.match(/^@([^/]+)\//)?.[1];
46
- const unscopedName = firstPackage?.split('/').at(-1) ?? '';
47
- const aliasUnscopedName = firstAlias?.split('/').at(-1) ?? '';
48
- const aliasPackageNamePrefix = aliasUnscopedName && unscopedName && aliasUnscopedName.endsWith(unscopedName) ? aliasUnscopedName.slice(0, -unscopedName.length) : void 0;
49
- return {
50
- strategy: 'install' === metadata.strategy ? 'install' : 'workspace',
51
- modernPackageVersion: 'string' == typeof metadata.modernPackages?.specifier ? metadata.modernPackages.specifier : modernVersion,
52
- registry: metadata.modernPackages?.registry,
53
- aliasScope,
54
- aliasPackageNamePrefix
55
- };
34
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
35
+ if (node_fs.existsSync(compactPath)) {
36
+ const compactConfig = readUltramodernConfig(workspaceRoot);
37
+ if (compactConfig.packageSource) return compactConfig.packageSource;
38
+ }
39
+ throw new Error(`Missing UltraModern workspace file: ${compactPath}`);
56
40
  }
57
41
  function existingTailwindEnabled(workspaceRoot) {
58
- const contractPath = node_path.join(workspaceRoot, GENERATED_CONTRACT_PATH);
59
- if (!node_fs.existsSync(contractPath)) return true;
60
- const contract = readJsonFile(contractPath);
61
- const apps = isRecord(contract) && Array.isArray(contract.apps) ? contract.apps : [];
62
- const shell = apps.find((app)=>isRecord(app) && app.id === shellApp.id);
63
- return shell?.styling && isRecord(shell.styling) ? false !== shell.styling.tailwind : true;
42
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
43
+ if (node_fs.existsSync(compactPath)) return readUltramodernConfig(workspaceRoot).features.tailwind;
44
+ throw new Error(`Missing UltraModern workspace file: ${compactPath}`);
45
+ }
46
+ function existingBridgeConfig(workspaceRoot) {
47
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
48
+ return node_fs.existsSync(compactPath) ? readUltramodernConfig(workspaceRoot).bridge : void 0;
64
49
  }
65
50
  function assertValidVerticalName(name) {
66
51
  const normalized = toKebabCase(name);
@@ -74,18 +59,19 @@ function nextAvailablePort(ports) {
74
59
  function assertCanCreate(workspaceRoot, relativePath) {
75
60
  if (node_fs.existsSync(node_path.join(workspaceRoot, relativePath))) throw new Error(`Refusing to overwrite existing path: ${relativePath}`);
76
61
  }
77
- function updateRootWorkspaceScripts(workspaceRoot, scope, packageSource, remotes) {
62
+ function updateRootWorkspaceScripts(workspaceRoot, scope, packageSource, remotes, bridge) {
78
63
  const packagePath = node_path.join(workspaceRoot, 'package.json');
79
64
  const rootPackage = readJsonFile(packagePath);
80
- const generatedRootPackage = createRootPackageJson(scope, packageSource, remotes);
65
+ const generatedRootPackage = createRootPackageJson(scope, packageSource, remotes, bridge);
81
66
  rootPackage.scripts = generatedRootPackage.scripts;
82
67
  writeJsonFile(packagePath, rootPackage);
83
68
  }
84
- function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwind, remotes) {
69
+ function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwind, remotes, bridge) {
85
70
  const shellHost = createShellHost(remotes);
86
71
  const publicWeb = createPublicWebAppArtifacts(shellHost);
87
- writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/package.json`), createAppPackage(scope, shellHost, packageSource, enableTailwind, remotes));
72
+ writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/package.json`), createAppPackage(scope, shellHost, packageSource, enableTailwind, remotes, bridge));
88
73
  writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/tsconfig.json`), createAppTsConfig(shellHost, remotes));
74
+ writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/tsconfig.mf-types.json`), createAppMfTypesTsConfig(shellHost));
89
75
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern-app-env.d.ts`, createAppEnvDts(shellHost, remotes));
90
76
  writeFileReplacing(workspaceRoot, publicWeb.jsonLdHelperFile.path, publicWeb.jsonLdHelperFile.content);
91
77
  writeFileReplacing(workspaceRoot, publicWeb.routeMetadataFile.path, publicWeb.routeMetadataFile.content);
@@ -101,7 +87,7 @@ function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwin
101
87
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/[lang]/page.tsx`, createShellPage(remotes));
102
88
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/vertical-components.tsx`, createShellRemoteComponents(scope, remotes));
103
89
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/shell-frame.tsx`, createShellFrameComponent());
104
- writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/effect/vertical-clients.ts`, createShellEffectClient(scope, remotes));
90
+ writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/api/vertical-clients.ts`, createShellEffectClient(scope, remotes));
105
91
  }
106
92
  function addShellZephyrDependency(workspaceRoot, scope, remote) {
107
93
  const packagePath = node_path.join(workspaceRoot, shellApp.directory, 'package.json');
@@ -157,10 +143,11 @@ function verticalsFromTopology(topology, ports) {
157
143
  return (topology.verticals ?? []).map((vertical)=>{
158
144
  const domain = vertical.domain ?? String(vertical.id);
159
145
  const packageSuffix = vertical.package?.split('/').at(-1) ?? domain;
160
- const effectApi = vertical.api?.effect ? {
161
- stem: 'string' == typeof vertical.api.effect.basePath ? vertical.api.effect.basePath.split('/').filter(Boolean).at(-1) ?? domain : domain,
162
- prefix: vertical.api.effect.bff?.prefix ?? `/${domain}-api`,
163
- consumedBy: Array.isArray(vertical.api.effect.consumedBy) ? vertical.api.effect.consumedBy : [
146
+ const apiTopology = vertical.api;
147
+ const effectApi = apiTopology?.runtime === 'effect' ? {
148
+ stem: 'string' == typeof apiTopology.basePath ? apiTopology.basePath.split('/').filter(Boolean).at(-1) ?? domain : domain,
149
+ prefix: apiTopology.bff?.prefix ?? `/${domain}-api`,
150
+ consumedBy: Array.isArray(apiTopology.consumedBy) ? apiTopology.consumedBy : [
164
151
  shellApp.id,
165
152
  vertical.id
166
153
  ]
@@ -202,8 +189,7 @@ function prepareAddUltramodernVertical(options) {
202
189
  const topology = readRequiredJsonObject(topologyPath);
203
190
  const ownership = readRequiredJsonObject(ownershipPath);
204
191
  const overlay = readRequiredJsonObject(overlayPath);
205
- readRequiredJsonObject(node_path.join(options.workspaceRoot, GENERATED_CONTRACT_PATH));
206
- readRequiredJsonObject(node_path.join(options.workspaceRoot, PACKAGE_SOURCE_METADATA_PATH));
192
+ readRequiredWorkspaceConfig(options.workspaceRoot);
207
193
  assertOptionalJsonObject(topology.shell, 'topology.shell', topologyPath);
208
194
  assertOptionalJsonArray(topology.verticals, 'topology.verticals', topologyPath);
209
195
  assertOptionalJsonArray(ownership.owners, 'ownership.owners', ownershipPath);
@@ -214,6 +200,7 @@ function prepareAddUltramodernVertical(options) {
214
200
  const scope = toPackageScope(String(rootPackage.name ?? node_path.basename(options.workspaceRoot)));
215
201
  const packageSource = existingPackageSource(options.workspaceRoot, options.modernVersion, options.packageSource);
216
202
  const enableTailwind = options.enableTailwind ?? existingTailwindEnabled(options.workspaceRoot);
203
+ const bridge = existingBridgeConfig(options.workspaceRoot);
217
204
  const existingVerticals = verticalsFromTopology(topology, overlay.ports);
218
205
  const port = nextAvailablePort(overlay.ports);
219
206
  const vertical = createVerticalDescriptor(name, port);
@@ -241,6 +228,7 @@ function prepareAddUltramodernVertical(options) {
241
228
  overlay,
242
229
  packageSource,
243
230
  enableTailwind,
231
+ bridge,
244
232
  vertical,
245
233
  updatedVerticals
246
234
  };
@@ -251,6 +239,10 @@ function readRequiredJsonObject(filePath) {
251
239
  if (!isRecord(value)) throw new Error(`UltraModern workspace file must contain a JSON object: ${filePath}`);
252
240
  return value;
253
241
  }
242
+ function readRequiredWorkspaceConfig(workspaceRoot) {
243
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
244
+ readRequiredJsonObject(compactPath);
245
+ }
254
246
  function assertOptionalJsonObject(value, label, filePath) {
255
247
  if (void 0 !== value && !isRecord(value)) throw new Error(`${label} in ${filePath} must be a JSON object`);
256
248
  }
@@ -270,8 +262,8 @@ function validateWorkspaceAppDescriptors(apps) {
270
262
  if ('number' != typeof app.port || !Number.isFinite(app.port) || app.port <= 0) throw new Error(`Invalid development port for ${appLabel}`);
271
263
  assertNonEmptyString(app.mfName, `Module Federation name for ${appLabel}`);
272
264
  if (app.effectApi) {
273
- assertNonEmptyString(app.effectApi.prefix, `Effect API prefix for ${appLabel}`);
274
- if (!app.effectApi.prefix.startsWith('/')) throw new Error(`Effect API prefix for ${appLabel} must start with "/"`);
265
+ assertNonEmptyString(app.effectApi.prefix, `API prefix for ${appLabel}`);
266
+ if (!app.effectApi.prefix.startsWith('/')) throw new Error(`API prefix for ${appLabel} must start with "/"`);
275
267
  }
276
268
  }
277
269
  }
@@ -281,7 +273,7 @@ function validateUniqueWorkspaceAppDescriptors(apps) {
281
273
  assertUniqueAppField(apps, 'output path', (app)=>normalizePath(app.directory));
282
274
  assertUniqueAppField(apps, 'Module Federation name', (app)=>app.mfName);
283
275
  assertUniqueAppField(apps, 'development port', (app)=>String(app.port));
284
- assertUniqueAppField(apps, 'Effect API prefix', (app)=>app.effectApi?.prefix);
276
+ assertUniqueAppField(apps, 'API prefix', (app)=>app.effectApi?.prefix);
285
277
  assertUniqueAppField(apps, 'manifest environment name', (app)=>'vertical' === app.kind ? createRemoteManifestEnv(app) : void 0);
286
278
  }
287
279
  function assertUniqueAppField(apps, label, readValue) {
@@ -355,7 +347,7 @@ function createVerticalPlan(preflight, result) {
355
347
  shellDependencyChanges: createShellDependencyChanges(scope, vertical),
356
348
  generatedContractChanges: [
357
349
  {
358
- path: GENERATED_CONTRACT_PATH,
350
+ path: ULTRAMODERN_CONFIG_PATH,
359
351
  addedAppIds: [
360
352
  vertical.id
361
353
  ],
@@ -370,7 +362,7 @@ function createDryRunJsonMutations(preflight, manifestUrl) {
370
362
  {
371
363
  path: DEVELOPMENT_OVERLAY_PATH,
372
364
  pointer: `/apis/${vertical.id}`,
373
- description: `Add local Effect API URL for ${vertical.id}`,
365
+ description: `Add local API URL for ${vertical.id}`,
374
366
  value: `http://localhost:${vertical.port}${effectApiPrefix(vertical)}`
375
367
  }
376
368
  ] : [];
@@ -437,9 +429,14 @@ function createDryRunJsonMutations(preflight, manifestUrl) {
437
429
  description: `Add ${vertical.id} to the shell TS-Go project references`
438
430
  },
439
431
  {
440
- path: GENERATED_CONTRACT_PATH,
441
- pointer: '/apps',
442
- description: `Regenerate contract with ${vertical.id}`
432
+ path: `${shellApp.directory}/tsconfig.mf-types.json`,
433
+ pointer: '/include',
434
+ description: 'Keep shell Module Federation DTS compilation scoped'
435
+ },
436
+ {
437
+ path: ULTRAMODERN_CONFIG_PATH,
438
+ pointer: '/topology/apps',
439
+ description: `Regenerate compact config with ${vertical.id}`
443
440
  }
444
441
  ];
445
442
  }
@@ -463,8 +460,8 @@ function createShellDependencyChanges(scope, vertical) {
463
460
  }
464
461
  function addUltramodernVertical(options) {
465
462
  const beforeFiles = createFileSnapshot(options.workspaceRoot);
466
- const { scope, topologyPath, ownershipPath, overlayPath, topology, ownership, overlay, packageSource, enableTailwind, vertical, updatedVerticals } = prepareAddUltramodernVertical(options);
467
- writeApp(options.workspaceRoot, scope, vertical, packageSource, enableTailwind);
463
+ const { scope, topologyPath, ownershipPath, overlayPath, topology, ownership, overlay, packageSource, enableTailwind, bridge, vertical, updatedVerticals } = prepareAddUltramodernVertical(options);
464
+ writeApp(options.workspaceRoot, scope, vertical, packageSource, enableTailwind, updatedVerticals, bridge);
468
465
  topology.shell ??= {};
469
466
  topology.shell.verticalRefs ??= [];
470
467
  topology.shell.verticalRefs.push(vertical.id);
@@ -487,18 +484,18 @@ function addUltramodernVertical(options) {
487
484
  writeJsonFile(topologyPath, topology);
488
485
  writeJsonFile(ownershipPath, ownership);
489
486
  writeJsonFile(overlayPath, overlay);
490
- writeJsonFile(node_path.join(options.workspaceRoot, GENERATED_CONTRACT_PATH), createGeneratedContract(scope, [
487
+ writeJsonFile(node_path.join(options.workspaceRoot, ULTRAMODERN_CONFIG_PATH), createCompactUltramodernConfig(scope, options.modernVersion, packageSource, [
491
488
  {
492
489
  ...shellApp,
493
490
  verticalRefs: updatedVerticals.map((vertical)=>vertical.id)
494
491
  },
495
492
  ...updatedVerticals
496
- ], enableTailwind));
497
- rewriteShellAppFiles(options.workspaceRoot, scope, packageSource, enableTailwind, updatedVerticals);
493
+ ], enableTailwind, bridge));
494
+ rewriteShellAppFiles(options.workspaceRoot, scope, packageSource, enableTailwind, updatedVerticals, bridge);
498
495
  writeGeneratedWorkspaceScripts(options.workspaceRoot, scope, enableTailwind, updatedVerticals);
499
496
  addShellZephyrDependency(options.workspaceRoot, scope, vertical);
500
497
  addShellWorkspaceDependency(options.workspaceRoot, scope, vertical);
501
- updateRootWorkspaceScripts(options.workspaceRoot, scope, packageSource, updatedVerticals);
498
+ updateRootWorkspaceScripts(options.workspaceRoot, scope, packageSource, updatedVerticals, bridge);
502
499
  writeJsonFile(node_path.join(options.workspaceRoot, 'tsconfig.json'), createRootTsConfig([
503
500
  {
504
501
  ...shellApp,
@@ -506,9 +503,9 @@ function addUltramodernVertical(options) {
506
503
  },
507
504
  ...updatedVerticals
508
505
  ]));
509
- const afterFiles = createFileSnapshot(options.workspaceRoot);
510
- const { createdPaths, rewrittenPaths } = diffFileSnapshots(beforeFiles, afterFiles);
511
- const result = createGenerationResult({
506
+ const preliminaryAfterFiles = createFileSnapshot(options.workspaceRoot);
507
+ const preliminaryDiff = diffFileSnapshots(beforeFiles, preliminaryAfterFiles);
508
+ const preliminaryResult = createGenerationResult({
512
509
  operation: 'vertical',
513
510
  workspaceRoot: options.workspaceRoot,
514
511
  packageScope: scope,
@@ -516,14 +513,32 @@ function addUltramodernVertical(options) {
516
513
  createdApps: [
517
514
  vertical
518
515
  ],
519
- createdPaths,
520
- rewrittenPaths
516
+ createdPaths: preliminaryDiff.createdPaths,
517
+ rewrittenPaths: preliminaryDiff.rewrittenPaths
521
518
  });
522
519
  runCodeSmithOverlays({
523
520
  workspaceRoot: options.workspaceRoot,
524
521
  overlays: options.overlays,
525
- result
522
+ result: preliminaryResult
523
+ });
524
+ const afterOverlaysFiles = createFileSnapshot(options.workspaceRoot);
525
+ const changedPaths = diffFileSnapshots(beforeFiles, afterOverlaysFiles);
526
+ formatGeneratedWorkspaceFiles(options.workspaceRoot, [
527
+ ...changedPaths.createdPaths,
528
+ ...changedPaths.rewrittenPaths
529
+ ]);
530
+ const afterFiles = createFileSnapshot(options.workspaceRoot);
531
+ const { createdPaths, rewrittenPaths } = diffFileSnapshots(beforeFiles, afterFiles);
532
+ return createGenerationResult({
533
+ operation: 'vertical',
534
+ workspaceRoot: options.workspaceRoot,
535
+ packageScope: scope,
536
+ packageSource,
537
+ createdApps: [
538
+ vertical
539
+ ],
540
+ createdPaths,
541
+ rewrittenPaths
526
542
  });
527
- return result;
528
543
  }
529
- export { addShellWorkspaceDependency, addShellZephyrDependency, addUltramodernVertical, assertCanCreate, assertValidVerticalName, existingPackageSource, existingTailwindEnabled, nextAvailablePort, ownershipEntry, planUltramodernVertical, prepareAddUltramodernVertical, rewriteShellAppFiles, updateRootWorkspaceScripts, verticalTopologyEntry, verticalsFromTopology };
544
+ export { addShellWorkspaceDependency, addShellZephyrDependency, addUltramodernVertical, assertCanCreate, assertValidVerticalName, existingBridgeConfig, existingPackageSource, existingTailwindEnabled, nextAvailablePort, ownershipEntry, planUltramodernVertical, prepareAddUltramodernVertical, rewriteShellAppFiles, updateRootWorkspaceScripts, verticalTopologyEntry, verticalsFromTopology };
@@ -0,0 +1,270 @@
1
+ const ultramodernBridgeCliFlags = {
2
+ enabled: '--bridge',
3
+ parentRoot: '--bridge-parent-root',
4
+ workspacePackage: '--bridge-workspace-package',
5
+ workspacePackageName: '--bridge-workspace-package-name',
6
+ testAlias: '--bridge-test-alias',
7
+ dependency: '--bridge-dependency',
8
+ lockfilePolicy: '--bridge-lockfile-policy',
9
+ gate: '--bridge-gate',
10
+ gateCwd: '--bridge-gate-cwd',
11
+ reactSingleton: '--bridge-react-singleton'
12
+ };
13
+ const ultramodernBridgeCliValueFlags = [
14
+ ultramodernBridgeCliFlags.parentRoot,
15
+ ultramodernBridgeCliFlags.workspacePackage,
16
+ ultramodernBridgeCliFlags.workspacePackageName,
17
+ ultramodernBridgeCliFlags.testAlias,
18
+ ultramodernBridgeCliFlags.dependency,
19
+ ultramodernBridgeCliFlags.lockfilePolicy,
20
+ ultramodernBridgeCliFlags.gate,
21
+ ultramodernBridgeCliFlags.gateCwd,
22
+ ultramodernBridgeCliFlags.reactSingleton
23
+ ];
24
+ const ultramodernBridgeCliBooleanFlags = [
25
+ ultramodernBridgeCliFlags.enabled
26
+ ];
27
+ const ultramodernBridgeLockfilePolicies = [
28
+ 'nested',
29
+ 'parent'
30
+ ];
31
+ const defaultReactSingletons = [
32
+ 'react',
33
+ 'react-dom'
34
+ ];
35
+ function hasUltramodernBridgeCliOptions(args) {
36
+ return args.some((arg)=>[
37
+ ...ultramodernBridgeCliBooleanFlags,
38
+ ...ultramodernBridgeCliValueFlags
39
+ ].some((flag)=>arg === flag || arg.startsWith(`${flag}=`)));
40
+ }
41
+ function parseUltramodernBridgeCliOptions(args) {
42
+ rejectBooleanFlagValues(args);
43
+ if (!hasUltramodernBridgeCliOptions(args)) return;
44
+ const parentRoot = readSingleValue(args, ultramodernBridgeCliFlags.parentRoot);
45
+ const lockfilePolicy = readSingleValue(args, ultramodernBridgeCliFlags.lockfilePolicy) ?? 'nested';
46
+ const workspacePackages = readWorkspacePackages(args);
47
+ const gates = readBridgeGates(args);
48
+ const reactSingletons = readCsvOptionValues(args, ultramodernBridgeCliFlags.reactSingleton);
49
+ return normalizeUltramodernBridgeConfig({
50
+ enabled: true,
51
+ parentRoot: requireNonEmptyValue(parentRoot, ultramodernBridgeCliFlags.parentRoot),
52
+ workspacePackages,
53
+ dependencies: readCsvOptionValues(args, ultramodernBridgeCliFlags.dependency),
54
+ lockfilePolicy: parseLockfilePolicy(lockfilePolicy),
55
+ gates,
56
+ reactSingletons: reactSingletons.length > 0 ? reactSingletons : void 0
57
+ });
58
+ }
59
+ function normalizeUltramodernBridgeConfig(bridge) {
60
+ if (!bridge || false === bridge.enabled) return;
61
+ const parentRoot = requireNonEmptyValue(bridge.parentRoot, 'bridge.parentRoot');
62
+ const workspacePackages = normalizeWorkspacePackages(bridge.workspacePackages);
63
+ const dependencies = uniqueNonEmptyValues(bridge.dependencies, 'bridge.dependencies');
64
+ const gates = normalizeBridgeGates(bridge.gates);
65
+ const reactSingletons = bridge.reactSingletons && bridge.reactSingletons.length > 0 ? uniqueNonEmptyValues(bridge.reactSingletons, 'bridge.reactSingletons') : [
66
+ ...defaultReactSingletons
67
+ ];
68
+ if (0 === workspacePackages.length) throw new Error('Bridge mode requires at least one bridge.workspacePackages entry.');
69
+ if (0 === dependencies.length) throw new Error('Bridge mode requires at least one explicit bridge.dependencies package name.');
70
+ if (0 === gates.length) throw new Error('Bridge mode requires at least one bridge.gates delegated parent command.');
71
+ validateParentPackageCoverage(workspacePackages, dependencies);
72
+ validateReactSingletons(reactSingletons);
73
+ return {
74
+ enabled: true,
75
+ parentRoot,
76
+ workspacePackages,
77
+ dependencies,
78
+ lockfilePolicy: parseLockfilePolicy(bridge.lockfilePolicy ?? 'nested'),
79
+ gates,
80
+ reactSingletons
81
+ };
82
+ }
83
+ function rejectBooleanFlagValues(args) {
84
+ for (const flag of ultramodernBridgeCliBooleanFlags)if (args.some((arg)=>arg.startsWith(`${flag}=`))) throw new Error(`${flag} does not accept a value.`);
85
+ }
86
+ function readRepeatedOptionValues(args, flag) {
87
+ const values = [];
88
+ for(let index = 0; index < args.length; index++){
89
+ const arg = args[index];
90
+ if (arg === flag) {
91
+ const value = args[index + 1];
92
+ if (!value || value.startsWith('-')) throw new Error(`${flag} requires a value.`);
93
+ values.push(value);
94
+ index += 1;
95
+ continue;
96
+ }
97
+ if (arg.startsWith(`${flag}=`)) values.push(arg.slice(flag.length + 1));
98
+ }
99
+ return values;
100
+ }
101
+ function readSingleValue(args, flag) {
102
+ const values = readRepeatedOptionValues(args, flag);
103
+ if (values.length > 1) throw new Error(`${flag} can be provided only once.`);
104
+ return values[0];
105
+ }
106
+ function readCsvOptionValues(args, flag) {
107
+ return readRepeatedOptionValues(args, flag).flatMap((value)=>value.split(',').map((entry)=>entry.trim()).filter(Boolean));
108
+ }
109
+ function requireNonEmptyValue(value, label) {
110
+ if ('string' != typeof value || 0 === value.trim().length) throw new Error(`${label} must be a non-empty string.`);
111
+ return value.trim();
112
+ }
113
+ function parseLockfilePolicy(value) {
114
+ if (!ultramodernBridgeLockfilePolicies.includes(value)) throw new Error(`${ultramodernBridgeCliFlags.lockfilePolicy} must be "nested" or "parent".`);
115
+ return value;
116
+ }
117
+ function readWorkspacePackages(args) {
118
+ const drafts = new Map();
119
+ const getDraft = (pattern)=>{
120
+ const normalizedPattern = requireNonEmptyValue(pattern, ultramodernBridgeCliFlags.workspacePackage);
121
+ const existing = drafts.get(normalizedPattern);
122
+ if (existing) return existing;
123
+ const draft = {
124
+ pattern: normalizedPattern,
125
+ packageNames: [],
126
+ testAliases: []
127
+ };
128
+ drafts.set(normalizedPattern, draft);
129
+ return draft;
130
+ };
131
+ for (const pattern of readRepeatedOptionValues(args, ultramodernBridgeCliFlags.workspacePackage))getDraft(pattern);
132
+ for (const value of readRepeatedOptionValues(args, ultramodernBridgeCliFlags.workspacePackageName)){
133
+ const { left: pattern, right: packageNames } = splitAssignment(value, ultramodernBridgeCliFlags.workspacePackageName, '<pattern>=<package-name>[,<package-name>]');
134
+ getDraft(pattern).packageNames.push(...packageNames.split(',').map((packageName)=>packageName.trim()).filter(Boolean));
135
+ }
136
+ for (const value of readRepeatedOptionValues(args, ultramodernBridgeCliFlags.testAlias)){
137
+ const separator = value.indexOf(':');
138
+ if (-1 === separator) throw new Error(`${ultramodernBridgeCliFlags.testAlias} must use <pattern>:<alias>=<target>.`);
139
+ const pattern = value.slice(0, separator);
140
+ const aliasDefinition = value.slice(separator + 1);
141
+ const { left: alias, right: target } = splitAssignment(aliasDefinition, ultramodernBridgeCliFlags.testAlias, '<pattern>:<alias>=<target>');
142
+ getDraft(pattern).testAliases.push({
143
+ alias: requireNonEmptyValue(alias, `${ultramodernBridgeCliFlags.testAlias} alias`),
144
+ target: requireNonEmptyValue(target, `${ultramodernBridgeCliFlags.testAlias} target`)
145
+ });
146
+ }
147
+ return normalizeWorkspacePackages([
148
+ ...drafts.values()
149
+ ]);
150
+ }
151
+ function readBridgeGates(args) {
152
+ const gates = new Map();
153
+ const getGate = (name)=>{
154
+ const normalizedName = requireNonEmptyValue(name, `${ultramodernBridgeCliFlags.gate} name`);
155
+ const existing = gates.get(normalizedName);
156
+ if (existing) return existing;
157
+ const draft = {
158
+ name: normalizedName
159
+ };
160
+ gates.set(normalizedName, draft);
161
+ return draft;
162
+ };
163
+ for (const value of readRepeatedOptionValues(args, ultramodernBridgeCliFlags.gate)){
164
+ const { left: name, right: command } = splitAssignment(value, ultramodernBridgeCliFlags.gate, '<name>=<command>');
165
+ getGate(name).command = requireNonEmptyValue(command, `${ultramodernBridgeCliFlags.gate} command`);
166
+ }
167
+ for (const value of readRepeatedOptionValues(args, ultramodernBridgeCliFlags.gateCwd)){
168
+ const { left: name, right: cwd } = splitAssignment(value, ultramodernBridgeCliFlags.gateCwd, '<name>=<cwd>');
169
+ getGate(name).cwd = requireNonEmptyValue(cwd, `${ultramodernBridgeCliFlags.gateCwd} cwd`);
170
+ }
171
+ const missingCommand = [
172
+ ...gates.values()
173
+ ].find((gate)=>!gate.command);
174
+ if (missingCommand) throw new Error(`${ultramodernBridgeCliFlags.gateCwd} references "${missingCommand.name}" without a matching ${ultramodernBridgeCliFlags.gate}.`);
175
+ return normalizeBridgeGates([
176
+ ...gates.values()
177
+ ].map((gate)=>({
178
+ name: gate.name,
179
+ command: gate.command,
180
+ cwd: gate.cwd
181
+ })));
182
+ }
183
+ function splitAssignment(value, flag, usage) {
184
+ const separator = value.indexOf('=');
185
+ if (-1 === separator) throw new Error(`${flag} must use ${usage}.`);
186
+ return {
187
+ left: value.slice(0, separator),
188
+ right: value.slice(separator + 1)
189
+ };
190
+ }
191
+ function normalizeWorkspacePackages(workspacePackages) {
192
+ return workspacePackages.map((entry, index)=>{
193
+ const label = `bridge.workspacePackages[${index}]`;
194
+ const packageNames = entry.packageNames && entry.packageNames.length > 0 ? uniqueNonEmptyValues(entry.packageNames, `${label}.packageNames`) : void 0;
195
+ const testAliases = entry.testAliases && entry.testAliases.length > 0 ? normalizeTestAliases(entry.testAliases, label) : void 0;
196
+ return {
197
+ pattern: requireNonEmptyValue(entry.pattern, `${label}.pattern`),
198
+ ...packageNames ? {
199
+ packageNames
200
+ } : {},
201
+ ...testAliases ? {
202
+ testAliases
203
+ } : {}
204
+ };
205
+ });
206
+ }
207
+ function validateParentPackageCoverage(workspacePackages, dependencies) {
208
+ const declaredPackages = new Set();
209
+ for (const [index, entry] of workspacePackages.entries()){
210
+ const label = `bridge.workspacePackages[${index}]`;
211
+ const packageNames = entry.packageNames ?? [];
212
+ 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.`);
213
+ for (const packageName of packageNames)declaredPackages.add(packageName);
214
+ for (const alias of entry.testAliases ?? []){
215
+ if (!packageNames.some((packageName)=>aliasMatchesPackage(alias.alias, packageName))) throw new Error(`${label}.testAliases entry "${alias.alias}" must match one of ${label}.packageNames.`);
216
+ if (pointsAtBuildOutput(alias.target)) throw new Error(`${label}.testAliases target "${alias.target}" must point at source files, not dist output.`);
217
+ }
218
+ }
219
+ const missingDependencies = dependencies.filter((dependency)=>!declaredPackages.has(dependency));
220
+ if (declaredPackages.size > 0 && missingDependencies.length > 0) throw new Error(`Bridge mode dependencies must be declared by bridge.workspacePackages[].packageNames: ${missingDependencies.join(', ')}.`);
221
+ }
222
+ function aliasMatchesPackage(alias, packageName) {
223
+ return alias === packageName || alias.startsWith(`${packageName}/`);
224
+ }
225
+ function pointsAtBuildOutput(target) {
226
+ return target.split(/[\\/]/u).includes('dist');
227
+ }
228
+ function validateReactSingletons(reactSingletons) {
229
+ const missing = defaultReactSingletons.filter((packageName)=>!reactSingletons.includes(packageName));
230
+ if (missing.length > 0) throw new Error(`Bridge mode React singleton/dedupe declarations must include ${defaultReactSingletons.join(' and ')}. Missing: ${missing.join(', ')}.`);
231
+ }
232
+ function normalizeTestAliases(testAliases, parentLabel) {
233
+ const aliases = new Map();
234
+ for (const [index, alias] of testAliases.entries()){
235
+ const label = `${parentLabel}.testAliases[${index}]`;
236
+ const normalizedAlias = requireNonEmptyValue(alias.alias, `${label}.alias`);
237
+ aliases.set(normalizedAlias, {
238
+ alias: normalizedAlias,
239
+ target: requireNonEmptyValue(alias.target, `${label}.target`)
240
+ });
241
+ }
242
+ return [
243
+ ...aliases.values()
244
+ ];
245
+ }
246
+ function normalizeBridgeGates(gates) {
247
+ const normalized = new Map();
248
+ for (const [index, gate] of gates.entries()){
249
+ const label = `bridge.gates[${index}]`;
250
+ const name = requireNonEmptyValue(gate.name, `${label}.name`);
251
+ normalized.set(name, {
252
+ name,
253
+ command: requireNonEmptyValue(gate.command, `${label}.command`),
254
+ ...gate.cwd ? {
255
+ cwd: requireNonEmptyValue(gate.cwd, `${label}.cwd`)
256
+ } : {}
257
+ });
258
+ }
259
+ return [
260
+ ...normalized.values()
261
+ ];
262
+ }
263
+ function uniqueNonEmptyValues(values, label) {
264
+ const unique = new Set();
265
+ for (const [index, value] of values.entries())unique.add(requireNonEmptyValue(value, `${label}[${index}]`));
266
+ return [
267
+ ...unique
268
+ ];
269
+ }
270
+ export { hasUltramodernBridgeCliOptions, normalizeUltramodernBridgeConfig, parseUltramodernBridgeCliOptions, ultramodernBridgeCliBooleanFlags, ultramodernBridgeCliFlags, ultramodernBridgeCliValueFlags, ultramodernBridgeLockfilePolicies };
@@ -14,6 +14,7 @@ async function ultramodernCodeSmithAdapter(context, runtime) {
14
14
  packageName: name,
15
15
  modernVersion,
16
16
  enableTailwind,
17
+ bridge: config.bridge,
17
18
  overlays: config.overlays,
18
19
  packageSource
19
20
  }) : runVerticalMode({