@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
@@ -3,29 +3,28 @@ 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 { 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
- import { readJsonFile, writeFileReplacing, writeJsonFile } from "./fs-io.js";
11
+ import { formatGeneratedWorkspaceFiles, readJsonFile, writeFileReplacing, writeJsonFile } from "./fs-io.js";
12
12
  import { createFileSnapshot, createGenerationResult, diffFileSnapshots } from "./generation-result.js";
13
13
  import { createAppPublicLocaleMessages } from "./locales.js";
14
14
  import { createShellModuleFederationConfig } from "./module-federation.js";
15
15
  import { assertUniqueTailwindPrefixes, normalizePath, packageName, toEnvSegment, toKebabCase, toPackageScope, toPascalCase } from "./naming.js";
16
16
  import { runCodeSmithOverlays } from "./overlays.js";
17
- import { createAppPackage, createAppTsConfig, createRootPackageJson, createRootTsConfig } from "./package-json.js";
17
+ import { createAppMfTypesTsConfig, createAppPackage, createAppTsConfig, createRootPackageJson, createRootTsConfig } from "./package-json.js";
18
18
  import { resolvePackageSource } from "./package-source.js";
19
19
  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';
27
27
  const DEVELOPMENT_OVERLAY_PATH = 'topology/local-overlays/development.json';
28
- const PACKAGE_SOURCE_METADATA_PATH = '.modernjs/ultramodern-package-source.json';
29
28
  function existingPackageSource(workspaceRoot, modernVersion, packageSource) {
30
29
  if (packageSource) return resolvePackageSource({
31
30
  targetDir: workspaceRoot,
@@ -33,35 +32,21 @@ function existingPackageSource(workspaceRoot, modernVersion, packageSource) {
33
32
  modernVersion,
34
33
  packageSource
35
34
  });
36
- const metadataPath = node_path.join(workspaceRoot, '.modernjs/ultramodern-package-source.json');
37
- if (!node_fs.existsSync(metadataPath)) return resolvePackageSource({
38
- targetDir: workspaceRoot,
39
- packageName: node_path.basename(workspaceRoot),
40
- modernVersion
41
- });
42
- const metadata = readJsonFile(metadataPath);
43
- const aliases = metadata.modernPackages?.aliases ?? {};
44
- const firstAlias = Object.values(aliases).find((value)=>'string' == typeof value);
45
- const firstPackage = Object.keys(aliases)[0];
46
- const aliasScope = firstAlias?.match(/^@([^/]+)\//)?.[1];
47
- const unscopedName = firstPackage?.split('/').at(-1) ?? '';
48
- const aliasUnscopedName = firstAlias?.split('/').at(-1) ?? '';
49
- const aliasPackageNamePrefix = aliasUnscopedName && unscopedName && aliasUnscopedName.endsWith(unscopedName) ? aliasUnscopedName.slice(0, -unscopedName.length) : void 0;
50
- return {
51
- strategy: 'install' === metadata.strategy ? 'install' : 'workspace',
52
- modernPackageVersion: 'string' == typeof metadata.modernPackages?.specifier ? metadata.modernPackages.specifier : modernVersion,
53
- registry: metadata.modernPackages?.registry,
54
- aliasScope,
55
- aliasPackageNamePrefix
56
- };
35
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
36
+ if (node_fs.existsSync(compactPath)) {
37
+ const compactConfig = readUltramodernConfig(workspaceRoot);
38
+ if (compactConfig.packageSource) return compactConfig.packageSource;
39
+ }
40
+ throw new Error(`Missing UltraModern workspace file: ${compactPath}`);
57
41
  }
58
42
  function existingTailwindEnabled(workspaceRoot) {
59
- const contractPath = node_path.join(workspaceRoot, GENERATED_CONTRACT_PATH);
60
- if (!node_fs.existsSync(contractPath)) return true;
61
- const contract = readJsonFile(contractPath);
62
- const apps = isRecord(contract) && Array.isArray(contract.apps) ? contract.apps : [];
63
- const shell = apps.find((app)=>isRecord(app) && app.id === shellApp.id);
64
- return shell?.styling && isRecord(shell.styling) ? false !== shell.styling.tailwind : true;
43
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
44
+ if (node_fs.existsSync(compactPath)) return readUltramodernConfig(workspaceRoot).features.tailwind;
45
+ throw new Error(`Missing UltraModern workspace file: ${compactPath}`);
46
+ }
47
+ function existingBridgeConfig(workspaceRoot) {
48
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
49
+ return node_fs.existsSync(compactPath) ? readUltramodernConfig(workspaceRoot).bridge : void 0;
65
50
  }
66
51
  function assertValidVerticalName(name) {
67
52
  const normalized = toKebabCase(name);
@@ -75,18 +60,19 @@ function nextAvailablePort(ports) {
75
60
  function assertCanCreate(workspaceRoot, relativePath) {
76
61
  if (node_fs.existsSync(node_path.join(workspaceRoot, relativePath))) throw new Error(`Refusing to overwrite existing path: ${relativePath}`);
77
62
  }
78
- function updateRootWorkspaceScripts(workspaceRoot, scope, packageSource, remotes) {
63
+ function updateRootWorkspaceScripts(workspaceRoot, scope, packageSource, remotes, bridge) {
79
64
  const packagePath = node_path.join(workspaceRoot, 'package.json');
80
65
  const rootPackage = readJsonFile(packagePath);
81
- const generatedRootPackage = createRootPackageJson(scope, packageSource, remotes);
66
+ const generatedRootPackage = createRootPackageJson(scope, packageSource, remotes, bridge);
82
67
  rootPackage.scripts = generatedRootPackage.scripts;
83
68
  writeJsonFile(packagePath, rootPackage);
84
69
  }
85
- function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwind, remotes) {
70
+ function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwind, remotes, bridge) {
86
71
  const shellHost = createShellHost(remotes);
87
72
  const publicWeb = createPublicWebAppArtifacts(shellHost);
88
- writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/package.json`), createAppPackage(scope, shellHost, packageSource, enableTailwind, remotes));
73
+ writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/package.json`), createAppPackage(scope, shellHost, packageSource, enableTailwind, remotes, bridge));
89
74
  writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/tsconfig.json`), createAppTsConfig(shellHost, remotes));
75
+ writeJsonFile(node_path.join(workspaceRoot, `${shellApp.directory}/tsconfig.mf-types.json`), createAppMfTypesTsConfig(shellHost));
90
76
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/modern-app-env.d.ts`, createAppEnvDts(shellHost, remotes));
91
77
  writeFileReplacing(workspaceRoot, publicWeb.jsonLdHelperFile.path, publicWeb.jsonLdHelperFile.content);
92
78
  writeFileReplacing(workspaceRoot, publicWeb.routeMetadataFile.path, publicWeb.routeMetadataFile.content);
@@ -102,7 +88,7 @@ function rewriteShellAppFiles(workspaceRoot, scope, packageSource, enableTailwin
102
88
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/[lang]/page.tsx`, createShellPage(remotes));
103
89
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/vertical-components.tsx`, createShellRemoteComponents(scope, remotes));
104
90
  writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/routes/shell-frame.tsx`, createShellFrameComponent());
105
- writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/effect/vertical-clients.ts`, createShellEffectClient(scope, remotes));
91
+ writeFileReplacing(workspaceRoot, `${shellApp.directory}/src/api/vertical-clients.ts`, createShellEffectClient(scope, remotes));
106
92
  }
107
93
  function addShellZephyrDependency(workspaceRoot, scope, remote) {
108
94
  const packagePath = node_path.join(workspaceRoot, shellApp.directory, 'package.json');
@@ -158,10 +144,11 @@ function verticalsFromTopology(topology, ports) {
158
144
  return (topology.verticals ?? []).map((vertical)=>{
159
145
  const domain = vertical.domain ?? String(vertical.id);
160
146
  const packageSuffix = vertical.package?.split('/').at(-1) ?? domain;
161
- const effectApi = vertical.api?.effect ? {
162
- stem: 'string' == typeof vertical.api.effect.basePath ? vertical.api.effect.basePath.split('/').filter(Boolean).at(-1) ?? domain : domain,
163
- prefix: vertical.api.effect.bff?.prefix ?? `/${domain}-api`,
164
- consumedBy: Array.isArray(vertical.api.effect.consumedBy) ? vertical.api.effect.consumedBy : [
147
+ const apiTopology = vertical.api;
148
+ const effectApi = apiTopology?.runtime === 'effect' ? {
149
+ stem: 'string' == typeof apiTopology.basePath ? apiTopology.basePath.split('/').filter(Boolean).at(-1) ?? domain : domain,
150
+ prefix: apiTopology.bff?.prefix ?? `/${domain}-api`,
151
+ consumedBy: Array.isArray(apiTopology.consumedBy) ? apiTopology.consumedBy : [
165
152
  shellApp.id,
166
153
  vertical.id
167
154
  ]
@@ -203,8 +190,7 @@ function prepareAddUltramodernVertical(options) {
203
190
  const topology = readRequiredJsonObject(topologyPath);
204
191
  const ownership = readRequiredJsonObject(ownershipPath);
205
192
  const overlay = readRequiredJsonObject(overlayPath);
206
- readRequiredJsonObject(node_path.join(options.workspaceRoot, GENERATED_CONTRACT_PATH));
207
- readRequiredJsonObject(node_path.join(options.workspaceRoot, PACKAGE_SOURCE_METADATA_PATH));
193
+ readRequiredWorkspaceConfig(options.workspaceRoot);
208
194
  assertOptionalJsonObject(topology.shell, 'topology.shell', topologyPath);
209
195
  assertOptionalJsonArray(topology.verticals, 'topology.verticals', topologyPath);
210
196
  assertOptionalJsonArray(ownership.owners, 'ownership.owners', ownershipPath);
@@ -215,6 +201,7 @@ function prepareAddUltramodernVertical(options) {
215
201
  const scope = toPackageScope(String(rootPackage.name ?? node_path.basename(options.workspaceRoot)));
216
202
  const packageSource = existingPackageSource(options.workspaceRoot, options.modernVersion, options.packageSource);
217
203
  const enableTailwind = options.enableTailwind ?? existingTailwindEnabled(options.workspaceRoot);
204
+ const bridge = existingBridgeConfig(options.workspaceRoot);
218
205
  const existingVerticals = verticalsFromTopology(topology, overlay.ports);
219
206
  const port = nextAvailablePort(overlay.ports);
220
207
  const vertical = createVerticalDescriptor(name, port);
@@ -242,6 +229,7 @@ function prepareAddUltramodernVertical(options) {
242
229
  overlay,
243
230
  packageSource,
244
231
  enableTailwind,
232
+ bridge,
245
233
  vertical,
246
234
  updatedVerticals
247
235
  };
@@ -252,6 +240,10 @@ function readRequiredJsonObject(filePath) {
252
240
  if (!isRecord(value)) throw new Error(`UltraModern workspace file must contain a JSON object: ${filePath}`);
253
241
  return value;
254
242
  }
243
+ function readRequiredWorkspaceConfig(workspaceRoot) {
244
+ const compactPath = node_path.join(workspaceRoot, ULTRAMODERN_CONFIG_PATH);
245
+ readRequiredJsonObject(compactPath);
246
+ }
255
247
  function assertOptionalJsonObject(value, label, filePath) {
256
248
  if (void 0 !== value && !isRecord(value)) throw new Error(`${label} in ${filePath} must be a JSON object`);
257
249
  }
@@ -271,8 +263,8 @@ function validateWorkspaceAppDescriptors(apps) {
271
263
  if ('number' != typeof app.port || !Number.isFinite(app.port) || app.port <= 0) throw new Error(`Invalid development port for ${appLabel}`);
272
264
  assertNonEmptyString(app.mfName, `Module Federation name for ${appLabel}`);
273
265
  if (app.effectApi) {
274
- assertNonEmptyString(app.effectApi.prefix, `Effect API prefix for ${appLabel}`);
275
- if (!app.effectApi.prefix.startsWith('/')) throw new Error(`Effect API prefix for ${appLabel} must start with "/"`);
266
+ assertNonEmptyString(app.effectApi.prefix, `API prefix for ${appLabel}`);
267
+ if (!app.effectApi.prefix.startsWith('/')) throw new Error(`API prefix for ${appLabel} must start with "/"`);
276
268
  }
277
269
  }
278
270
  }
@@ -282,7 +274,7 @@ function validateUniqueWorkspaceAppDescriptors(apps) {
282
274
  assertUniqueAppField(apps, 'output path', (app)=>normalizePath(app.directory));
283
275
  assertUniqueAppField(apps, 'Module Federation name', (app)=>app.mfName);
284
276
  assertUniqueAppField(apps, 'development port', (app)=>String(app.port));
285
- assertUniqueAppField(apps, 'Effect API prefix', (app)=>app.effectApi?.prefix);
277
+ assertUniqueAppField(apps, 'API prefix', (app)=>app.effectApi?.prefix);
286
278
  assertUniqueAppField(apps, 'manifest environment name', (app)=>'vertical' === app.kind ? createRemoteManifestEnv(app) : void 0);
287
279
  }
288
280
  function assertUniqueAppField(apps, label, readValue) {
@@ -356,7 +348,7 @@ function createVerticalPlan(preflight, result) {
356
348
  shellDependencyChanges: createShellDependencyChanges(scope, vertical),
357
349
  generatedContractChanges: [
358
350
  {
359
- path: GENERATED_CONTRACT_PATH,
351
+ path: ULTRAMODERN_CONFIG_PATH,
360
352
  addedAppIds: [
361
353
  vertical.id
362
354
  ],
@@ -371,7 +363,7 @@ function createDryRunJsonMutations(preflight, manifestUrl) {
371
363
  {
372
364
  path: DEVELOPMENT_OVERLAY_PATH,
373
365
  pointer: `/apis/${vertical.id}`,
374
- description: `Add local Effect API URL for ${vertical.id}`,
366
+ description: `Add local API URL for ${vertical.id}`,
375
367
  value: `http://localhost:${vertical.port}${effectApiPrefix(vertical)}`
376
368
  }
377
369
  ] : [];
@@ -438,9 +430,14 @@ function createDryRunJsonMutations(preflight, manifestUrl) {
438
430
  description: `Add ${vertical.id} to the shell TS-Go project references`
439
431
  },
440
432
  {
441
- path: GENERATED_CONTRACT_PATH,
442
- pointer: '/apps',
443
- description: `Regenerate contract with ${vertical.id}`
433
+ path: `${shellApp.directory}/tsconfig.mf-types.json`,
434
+ pointer: '/include',
435
+ description: 'Keep shell Module Federation DTS compilation scoped'
436
+ },
437
+ {
438
+ path: ULTRAMODERN_CONFIG_PATH,
439
+ pointer: '/topology/apps',
440
+ description: `Regenerate compact config with ${vertical.id}`
444
441
  }
445
442
  ];
446
443
  }
@@ -464,8 +461,8 @@ function createShellDependencyChanges(scope, vertical) {
464
461
  }
465
462
  function addUltramodernVertical(options) {
466
463
  const beforeFiles = createFileSnapshot(options.workspaceRoot);
467
- const { scope, topologyPath, ownershipPath, overlayPath, topology, ownership, overlay, packageSource, enableTailwind, vertical, updatedVerticals } = prepareAddUltramodernVertical(options);
468
- writeApp(options.workspaceRoot, scope, vertical, packageSource, enableTailwind);
464
+ const { scope, topologyPath, ownershipPath, overlayPath, topology, ownership, overlay, packageSource, enableTailwind, bridge, vertical, updatedVerticals } = prepareAddUltramodernVertical(options);
465
+ writeApp(options.workspaceRoot, scope, vertical, packageSource, enableTailwind, updatedVerticals, bridge);
469
466
  topology.shell ??= {};
470
467
  topology.shell.verticalRefs ??= [];
471
468
  topology.shell.verticalRefs.push(vertical.id);
@@ -488,18 +485,18 @@ function addUltramodernVertical(options) {
488
485
  writeJsonFile(topologyPath, topology);
489
486
  writeJsonFile(ownershipPath, ownership);
490
487
  writeJsonFile(overlayPath, overlay);
491
- writeJsonFile(node_path.join(options.workspaceRoot, GENERATED_CONTRACT_PATH), createGeneratedContract(scope, [
488
+ writeJsonFile(node_path.join(options.workspaceRoot, ULTRAMODERN_CONFIG_PATH), createCompactUltramodernConfig(scope, options.modernVersion, packageSource, [
492
489
  {
493
490
  ...shellApp,
494
491
  verticalRefs: updatedVerticals.map((vertical)=>vertical.id)
495
492
  },
496
493
  ...updatedVerticals
497
- ], enableTailwind));
498
- rewriteShellAppFiles(options.workspaceRoot, scope, packageSource, enableTailwind, updatedVerticals);
494
+ ], enableTailwind, bridge));
495
+ rewriteShellAppFiles(options.workspaceRoot, scope, packageSource, enableTailwind, updatedVerticals, bridge);
499
496
  writeGeneratedWorkspaceScripts(options.workspaceRoot, scope, enableTailwind, updatedVerticals);
500
497
  addShellZephyrDependency(options.workspaceRoot, scope, vertical);
501
498
  addShellWorkspaceDependency(options.workspaceRoot, scope, vertical);
502
- updateRootWorkspaceScripts(options.workspaceRoot, scope, packageSource, updatedVerticals);
499
+ updateRootWorkspaceScripts(options.workspaceRoot, scope, packageSource, updatedVerticals, bridge);
503
500
  writeJsonFile(node_path.join(options.workspaceRoot, 'tsconfig.json'), createRootTsConfig([
504
501
  {
505
502
  ...shellApp,
@@ -507,9 +504,9 @@ function addUltramodernVertical(options) {
507
504
  },
508
505
  ...updatedVerticals
509
506
  ]));
510
- const afterFiles = createFileSnapshot(options.workspaceRoot);
511
- const { createdPaths, rewrittenPaths } = diffFileSnapshots(beforeFiles, afterFiles);
512
- const result = createGenerationResult({
507
+ const preliminaryAfterFiles = createFileSnapshot(options.workspaceRoot);
508
+ const preliminaryDiff = diffFileSnapshots(beforeFiles, preliminaryAfterFiles);
509
+ const preliminaryResult = createGenerationResult({
513
510
  operation: 'vertical',
514
511
  workspaceRoot: options.workspaceRoot,
515
512
  packageScope: scope,
@@ -517,14 +514,32 @@ function addUltramodernVertical(options) {
517
514
  createdApps: [
518
515
  vertical
519
516
  ],
520
- createdPaths,
521
- rewrittenPaths
517
+ createdPaths: preliminaryDiff.createdPaths,
518
+ rewrittenPaths: preliminaryDiff.rewrittenPaths
522
519
  });
523
520
  runCodeSmithOverlays({
524
521
  workspaceRoot: options.workspaceRoot,
525
522
  overlays: options.overlays,
526
- result
523
+ result: preliminaryResult
524
+ });
525
+ const afterOverlaysFiles = createFileSnapshot(options.workspaceRoot);
526
+ const changedPaths = diffFileSnapshots(beforeFiles, afterOverlaysFiles);
527
+ formatGeneratedWorkspaceFiles(options.workspaceRoot, [
528
+ ...changedPaths.createdPaths,
529
+ ...changedPaths.rewrittenPaths
530
+ ]);
531
+ const afterFiles = createFileSnapshot(options.workspaceRoot);
532
+ const { createdPaths, rewrittenPaths } = diffFileSnapshots(beforeFiles, afterFiles);
533
+ return createGenerationResult({
534
+ operation: 'vertical',
535
+ workspaceRoot: options.workspaceRoot,
536
+ packageScope: scope,
537
+ packageSource,
538
+ createdApps: [
539
+ vertical
540
+ ],
541
+ createdPaths,
542
+ rewrittenPaths
527
543
  });
528
- return result;
529
544
  }
530
- export { addShellWorkspaceDependency, addShellZephyrDependency, addUltramodernVertical, assertCanCreate, assertValidVerticalName, existingPackageSource, existingTailwindEnabled, nextAvailablePort, ownershipEntry, planUltramodernVertical, prepareAddUltramodernVertical, rewriteShellAppFiles, updateRootWorkspaceScripts, verticalTopologyEntry, verticalsFromTopology };
545
+ 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({