create-next-hydra 0.2.0 → 0.3.0

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 (36) hide show
  1. package/README.md +34 -3
  2. package/dist/composition/add.d.ts.map +1 -1
  3. package/dist/composition/add.js +64 -33
  4. package/dist/composition/catalog.d.ts +4 -1
  5. package/dist/composition/catalog.d.ts.map +1 -1
  6. package/dist/composition/catalog.js +22 -6
  7. package/dist/composition/format.d.ts.map +1 -1
  8. package/dist/composition/format.js +3 -1
  9. package/dist/composition/packages.d.ts +2 -2
  10. package/dist/composition/packages.d.ts.map +1 -1
  11. package/dist/composition/packages.js +13 -12
  12. package/dist/composition/planner.d.ts.map +1 -1
  13. package/dist/composition/planner.js +88 -42
  14. package/dist/composition/schema.d.ts +59 -0
  15. package/dist/composition/schema.d.ts.map +1 -1
  16. package/dist/composition/schema.js +52 -1
  17. package/dist/composition/types.d.ts +26 -2
  18. package/dist/composition/types.d.ts.map +1 -1
  19. package/dist/composition/typescript-paths.d.ts +6 -0
  20. package/dist/composition/typescript-paths.d.ts.map +1 -0
  21. package/dist/composition/typescript-paths.js +157 -0
  22. package/dist/composition/use.d.ts.map +1 -1
  23. package/dist/composition/use.js +5 -0
  24. package/dist/composition/workspace.d.ts.map +1 -1
  25. package/dist/composition/workspace.js +5 -3
  26. package/dist/constants.d.ts +1 -1
  27. package/dist/constants.d.ts.map +1 -1
  28. package/dist/constants.js +1 -0
  29. package/dist/prompts.d.ts.map +1 -1
  30. package/dist/prompts.js +8 -1
  31. package/dist/sanitize.d.ts.map +1 -1
  32. package/dist/sanitize.js +89 -2
  33. package/dist/scaffold.d.ts.map +1 -1
  34. package/dist/scaffold.js +5 -0
  35. package/package.json +11 -4
  36. package/schema/selection-definition.json +65 -3
package/README.md CHANGED
@@ -57,10 +57,12 @@ Keep source in its normal package or application directory and place a `registry
57
57
  A Selection Definition declares:
58
58
 
59
59
  - its stable ID and whether it is a Provider, Add-on, or Preset;
60
- - a Provider Slot when it is a Provider;
60
+ - a Provider Slot and one package `binding` when it is a Provider;
61
+ - an optional `binding.sourcePath` for resolving a maintained Provider directly to workspace source;
62
+ - `providerDependencies` for packages contributed by the selection that consume a Provider Slot;
61
63
  - standard ShadCN `registryDependencies` for any other registry items it needs;
62
64
  - required and conflicting selection IDs;
63
- - exact package entries and aliases;
65
+ - ordinary exact package entries;
64
66
  - exact pnpm patch entries and their patch-file assets; and
65
67
  - setup instructions through the registry item's standard `docs` field.
66
68
 
@@ -77,7 +79,36 @@ pnpm registry:check
77
79
 
78
80
  `registry:sync` regenerates only each registry item's `files` list and its final workspace-root targets. Provider-owned metadata stays in the colocated registry file. Next Hydra first walks the intact `registryDependencies` graph to retain metadata and detect target conflicts, then prepares those exact artifacts and asks ShadCN to install them once from the workspace root.
79
81
 
80
- Standard ShadCN `dependencies` and `devDependencies` apply to the workspace root. Use `meta.nextHydra.packages` only when a dependency must be added to a specific workspace package.
82
+ Standard ShadCN `dependencies` and `devDependencies` apply to the workspace root. Use `meta.nextHydra.packages` only when an ordinary dependency must be added to a specific workspace package. Stable Provider aliases are derived from the slot and cannot be declared in `packages`.
83
+
84
+ A Provider declares its installable package once:
85
+
86
+ ```json
87
+ {
88
+ "kind": "provider",
89
+ "slot": "cms",
90
+ "binding": {
91
+ "specifier": "npm:@vendor/cms-provider@^1.0.0"
92
+ }
93
+ }
94
+ ```
95
+
96
+ An Add-on that contributes a package which imports the selected CMS declares the consumer, not a concrete CMS package:
97
+
98
+ ```json
99
+ {
100
+ "kind": "add-on",
101
+ "providerDependencies": [
102
+ {
103
+ "cwd": "packages/vendor-search",
104
+ "section": "dependencies",
105
+ "slot": "cms"
106
+ }
107
+ ]
108
+ }
109
+ ```
110
+
111
+ Scaffold and maintainer `use` resolve that dependency to `@repo/cms` using the selected Provider's `binding.specifier`. If the Provider also declares `binding.sourcePath`, they write the exact alias and its `/*` wildcard into every selected consumer's `tsconfig.json` for direct-source development. Without `sourcePath`, pnpm's installed alias is the only resolution path and Next Hydra writes no TypeScript path override. Catalog-governed overrides are removed when they are no longer selected, and `use --check` reports incorrect or stale paths. Customer `add`, which has no retained selection receipt, copies the exact Provider alias already present in `apps/web/package.json` into each contributed consumer.
81
112
 
82
113
  ShadCN reads registry source files as text. For a binary file that must survive byte-for-byte, or a root patch file referenced by `pnpmPatches`, an official or locally included source-registry selection may declare a typed `assets` source and target instead. Separately fetched external selections cannot contribute assets in v1.
83
114
 
@@ -1 +1 @@
1
- {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/composition/add.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAY,MAAM,gBAAgB,CAAC;AA4BnD,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,KAAK,aAAa,GAAG,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,KACnC,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAEhC,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C,CAAC;AAufF,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,UAAe,EACxB,YAAY,GAAE,eAAoB,GACjC,OAAO,CAAC,IAAI,CAAC,CAuKf"}
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/composition/add.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAY,MAAM,gBAAgB,CAAC;AA6BnD,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,KAAK,aAAa,GAAG,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,KACnC,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAEhC,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C,CAAC;AAihBF,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,UAAe,EACxB,YAAY,GAAE,eAAoB,GACjC,OAAO,CAAC,IAAI,CAAC,CAiMf"}
@@ -13,9 +13,12 @@ import { resolveRegistryTarget } from "./paths.js";
13
13
  import { formatZodError, NEXT_HYDRA_SELECTION_SCHEMA_URL, selectionDefinitionSchema, } from "./schema.js";
14
14
  import { PROVIDER_ALIASES } from "./types.js";
15
15
  import { applyPackageEntries } from "./workspace.js";
16
- const PACKAGE_ALIAS_PROTOCOL = /^(?:workspace|npm):/;
17
16
  const EXACT_COPY_FILE_TYPES = new Set(["registry:file", "registry:item"]);
18
17
  const KNOWN_PROVIDERS = new Map([
18
+ [
19
+ "next-hydra/auth/clerk",
20
+ { dependency: "@repo/auth", packageName: "@repo/auth-clerk" },
21
+ ],
19
22
  [
20
23
  "next-hydra/auth/workos",
21
24
  { dependency: "@repo/auth", packageName: "@repo/auth-workos" },
@@ -171,36 +174,25 @@ function usesProviderAlias(actual, packageName) {
171
174
  actual.startsWith(`workspace:${packageName}@`) ||
172
175
  actual.startsWith(`npm:${packageName}@`));
173
176
  }
174
- function aliasedPackageName(specifier) {
175
- const request = specifier.replace(PACKAGE_ALIAS_PROTOCOL, "");
176
- const packageEnd = request.startsWith("@")
177
- ? request.indexOf("@", request.indexOf("/") + 1)
178
- : request.indexOf("@");
179
- return packageEnd < 1 ? request || undefined : request.slice(0, packageEnd);
180
- }
181
177
  function providerExpectation(selectionId, selectionsById) {
182
- const known = KNOWN_PROVIDERS.get(selectionId);
183
- if (known) {
184
- return known;
185
- }
186
178
  const selection = selectionsById.get(selectionId);
187
- if (selection?.kind !== "provider" || !selection.slot) {
188
- return;
179
+ if (selection?.kind === "provider" && selection.slot && selection.binding) {
180
+ return {
181
+ dependency: PROVIDER_ALIASES[selection.slot],
182
+ specifier: selection.binding.specifier,
183
+ };
189
184
  }
190
- const dependency = PROVIDER_ALIASES[selection.slot];
191
- const requirement = selection.packages.find((candidate) => candidate.cwd === "apps/web" &&
192
- candidate.section === "dependencies" &&
193
- candidate.name === dependency);
194
- const packageName = requirement
195
- ? aliasedPackageName(requirement.specifier)
196
- : undefined;
197
- return packageName ? { dependency, packageName } : undefined;
185
+ return KNOWN_PROVIDERS.get(selectionId);
198
186
  }
199
- async function validateKnownProviderCompatibility(workspaceRoot, selections) {
187
+ function matchesProviderExpectation(actual, expectation) {
188
+ return "specifier" in expectation
189
+ ? actual === expectation.specifier
190
+ : usesProviderAlias(actual, expectation.packageName);
191
+ }
192
+ function validateKnownProviderCompatibility(manifest, selections) {
200
193
  if (selections.length === 0) {
201
194
  return [];
202
195
  }
203
- const manifest = await readPackageJson(path.join(workspaceRoot, "apps/web/package.json"), "apps/web/package.json");
204
196
  const issues = [];
205
197
  const assumptions = [];
206
198
  const selectionsById = new Map(selections.map((item) => [item.id, item]));
@@ -210,11 +202,11 @@ async function validateKnownProviderCompatibility(workspaceRoot, selections) {
210
202
  }
211
203
  const provider = providerExpectation(selection.id, selectionsById);
212
204
  if (!provider) {
213
- issues.push(`${selection.id} is a Provider in the requested graph but does not declare its apps/web stable alias`);
205
+ issues.push(`${selection.id} is a Provider in the requested graph but does not declare a usable Provider binding`);
214
206
  continue;
215
207
  }
216
208
  const actual = manifest.dependencies?.[provider.dependency];
217
- if (!usesProviderAlias(actual, provider.packageName)) {
209
+ if (!matchesProviderExpectation(actual, provider)) {
218
210
  issues.push(`${selection.id} is a Provider in the requested graph, but the current provider alias ${provider.dependency} is ${actual ?? "missing"}`);
219
211
  }
220
212
  }
@@ -223,7 +215,7 @@ async function validateKnownProviderCompatibility(workspaceRoot, selections) {
223
215
  const provider = providerExpectation(required, selectionsById);
224
216
  const requiredSelection = selectionsById.get(required);
225
217
  if (provider &&
226
- !usesProviderAlias(manifest.dependencies?.[provider.dependency], provider.packageName)) {
218
+ !matchesProviderExpectation(manifest.dependencies?.[provider.dependency], provider)) {
227
219
  issues.push(`${selection.id} requires ${required}`);
228
220
  }
229
221
  else if (!(provider || requiredSelection?.kind === "add-on")) {
@@ -234,7 +226,7 @@ async function validateKnownProviderCompatibility(workspaceRoot, selections) {
234
226
  const provider = providerExpectation(conflict, selectionsById);
235
227
  const conflictingSelection = selectionsById.get(conflict);
236
228
  if ((provider &&
237
- usesProviderAlias(manifest.dependencies?.[provider.dependency], provider.packageName)) ||
229
+ matchesProviderExpectation(manifest.dependencies?.[provider.dependency], provider)) ||
238
230
  conflictingSelection?.kind === "add-on") {
239
231
  issues.push(`${selection.id} conflicts with ${conflict}`);
240
232
  }
@@ -248,6 +240,30 @@ async function validateKnownProviderCompatibility(workspaceRoot, selections) {
248
240
  }
249
241
  return [...new Set(assumptions)].sort((left, right) => left.localeCompare(right));
250
242
  }
243
+ function resolveCustomerProviderRequirements(manifest, selections) {
244
+ const requirements = [];
245
+ const issues = [];
246
+ for (const selection of selections) {
247
+ for (const dependency of selection.providerDependencies) {
248
+ const name = PROVIDER_ALIASES[dependency.slot];
249
+ const specifier = manifest.dependencies?.[name];
250
+ if (!specifier) {
251
+ issues.push(`${selection.id} needs the ${dependency.slot} Provider at ${dependency.cwd}, but apps/web/package.json does not declare ${name}`);
252
+ continue;
253
+ }
254
+ requirements.push({
255
+ cwd: dependency.cwd,
256
+ name,
257
+ section: dependency.section,
258
+ specifier,
259
+ });
260
+ }
261
+ }
262
+ if (issues.length > 0) {
263
+ throw new CompositionValidationError("The Add-on's Provider dependencies could not be resolved from the customer workspace.", issues);
264
+ }
265
+ return requirements;
266
+ }
251
267
  function validateRegistryTargetClaims(items) {
252
268
  const claims = new Map();
253
269
  const issues = [];
@@ -360,9 +376,7 @@ export async function addRegistryItem(reference, options = {}, dependencies = {}
360
376
  references: [resolvedReference],
361
377
  });
362
378
  const primaryName = graph.itemByReference.get(resolvedReference);
363
- const primary = primaryName
364
- ? graph.items.get(primaryName)
365
- : undefined;
379
+ const primary = primaryName ? graph.items.get(primaryName) : undefined;
366
380
  if (!primary) {
367
381
  throw new Error(`The registry returned no item for ${reference}.`);
368
382
  }
@@ -390,8 +404,25 @@ export async function addRegistryItem(reference, options = {}, dependencies = {}
390
404
  if (patchSelections.length > 0) {
391
405
  throw new CompositionValidationError("Customer Add-ons cannot change pnpm patches in v1.", patchSelections.map((candidate) => `${candidate.id} must avoid pnpmPatches or be selected during a new scaffold`));
392
406
  }
393
- const compatibilityAssumptions = await validateKnownProviderCompatibility(cwd, graphSelections);
394
407
  validateRegistryTargetClaims(graph.items.values());
408
+ const providerSelectionIds = new Set(graphSelections
409
+ .filter((candidate) => candidate.kind === "provider")
410
+ .map((candidate) => candidate.id));
411
+ const needsProviderManifest = graphSelections.some((candidate) => candidate.kind === "provider" ||
412
+ candidate.providerDependencies.length > 0 ||
413
+ [
414
+ ...candidate.compatibility.requires,
415
+ ...candidate.compatibility.conflicts,
416
+ ].some((selectionId) => KNOWN_PROVIDERS.has(selectionId) ||
417
+ providerSelectionIds.has(selectionId)));
418
+ const providerManifest = needsProviderManifest
419
+ ? await readPackageJson(path.join(cwd, "apps/web/package.json"), "apps/web/package.json")
420
+ : {};
421
+ const compatibilityAssumptions = validateKnownProviderCompatibility(providerManifest, graphSelections);
422
+ const packageRequirements = mergePackageRequirements([
423
+ ...graphSelections.flatMap((candidate) => candidate.packages),
424
+ ...resolveCustomerProviderRequirements(providerManifest, graphSelections),
425
+ ]);
395
426
  await withPreparedRegistryArtifacts({
396
427
  artifacts: [...graph.items.values()],
397
428
  entryItems: [primary.name],
@@ -402,7 +433,7 @@ export async function addRegistryItem(reference, options = {}, dependencies = {}
402
433
  throw new Error(`The registry dependency graph for ${reference} is empty.`);
403
434
  }
404
435
  const fileChanges = await inspectFiles(cwd, tree);
405
- const packageChanges = await inspectPackages(cwd, mergePackageRequirements(graphSelections), tree);
436
+ const packageChanges = await inspectPackages(cwd, packageRequirements, tree);
406
437
  const rootDependencyChanges = await inspectRootDependencies(cwd, tree);
407
438
  const disclosedPackageChanges = [
408
439
  ...packageChanges,
@@ -17,7 +17,10 @@ export declare function fetchRegistryItemGraph(options: {
17
17
  repository?: string;
18
18
  }): Promise<RegistryItemGraph>;
19
19
  export declare function loadSourceRegistryCatalog(cwd: string, registryFile?: string): Promise<SourceRegistryCatalog>;
20
- export declare function loadGitHubSourceRegistryCatalog(repository: string, ref?: string): Promise<SourceRegistryCatalog>;
20
+ export declare function loadGitHubSourceRegistryCatalog(repository: string, ref?: string, dependencies?: {
21
+ fetchItems?: (addresses: string[]) => RegistryItem[] | Promise<RegistryItem[]>;
22
+ loadRegistryConfig?: (cwd: string) => RegistriesConfig | Promise<RegistriesConfig>;
23
+ }): Promise<SourceRegistryCatalog>;
21
24
  export declare function addCatalogReferences(catalog: SourceRegistryCatalog, references: Iterable<string>, cwd?: string): Promise<SourceRegistryCatalog>;
22
25
  export declare function resolveCatalogSelection(catalog: SourceRegistryCatalog, reference: string): CatalogSelection;
23
26
  //# sourceMappingURL=catalog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/composition/catalog.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,gBAAgB,EAEjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAclD,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AA4JpB,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,qBAAqB,EAC9B,UAAU,EAAE,MAAM,EAAE,GACnB,MAAM,EAAE,CA+BV;AAaD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAClC,CAAC;AAEF,wBAAsB,sBAAsB,CAAC,OAAO,EAAE;IACpD,MAAM,EAAE,gBAAgB,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,OAAO,gBAAgB,CAAC;IACrC,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC/B,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAyF7B;AAED,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,MAAM,EACX,YAAY,SAAkB,GAC7B,OAAO,CAAC,qBAAqB,CAAC,CAqChC;AAED,wBAAsB,+BAA+B,CACnD,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,qBAAqB,CAAC,CAuBhC;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,qBAAqB,EAC9B,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC5B,GAAG,SAA+B,GACjC,OAAO,CAAC,qBAAqB,CAAC,CA8ChC;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,qBAAqB,EAC9B,SAAS,EAAE,MAAM,GAChB,gBAAgB,CASlB"}
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/composition/catalog.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,gBAAgB,EAEjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAclD,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AA8KpB,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,qBAAqB,EAC9B,UAAU,EAAE,MAAM,EAAE,GACnB,MAAM,EAAE,CA+BV;AAaD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAClC,CAAC;AAEF,wBAAsB,sBAAsB,CAAC,OAAO,EAAE;IACpD,MAAM,EAAE,gBAAgB,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,OAAO,gBAAgB,CAAC;IACrC,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC/B,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA0F7B;AAED,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,MAAM,EACX,YAAY,SAAkB,GAC7B,OAAO,CAAC,qBAAqB,CAAC,CAqChC;AAED,wBAAsB,+BAA+B,CACnD,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,MAAM,EACZ,YAAY,GAAE;IACZ,UAAU,CAAC,EAAE,CACX,SAAS,EAAE,MAAM,EAAE,KAChB,YAAY,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC9C,kBAAkB,CAAC,EAAE,CACnB,GAAG,EAAE,MAAM,KACR,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC9C,GACL,OAAO,CAAC,qBAAqB,CAAC,CA4BhC;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,qBAAqB,EAC9B,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC5B,GAAG,SAA+B,GACjC,OAAO,CAAC,qBAAqB,CAAC,CAiDhC;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,qBAAqB,EAC9B,SAAS,EAAE,MAAM,GAChB,gBAAgB,CASlB"}
@@ -7,7 +7,9 @@ import { CompositionValidationError } from "./errors.js";
7
7
  import { isManagedApplicationSource, resolveRegistryTarget, resolveWorkspacePath, } from "./paths.js";
8
8
  import { formatZodError, NEXT_HYDRA_SELECTION_SCHEMA_URL, selectionDefinitionSchema, } from "./schema.js";
9
9
  const GITHUB_HOMEPAGE_PATTERN = /^https:\/\/github\.com\/([^/]+\/[^/]+?)(?:\.git)?\/?$/;
10
+ const SHADCN_REGISTRY_ITEM_SCHEMA_URL = "https://ui.shadcn.com/schema/registry-item.json";
10
11
  const OFFICIAL_REFERENCES = {
12
+ clerk: "next-hydra/auth/clerk",
11
13
  commercetools: "next-hydra/commerce/commercetools",
12
14
  contentstack: "next-hydra/cms/contentstack",
13
15
  drupal: "next-hydra/cms/drupal",
@@ -15,6 +17,8 @@ const OFFICIAL_REFERENCES = {
15
17
  workos: "next-hydra/auth/workos",
16
18
  };
17
19
  const OFFICIAL_ITEM_NAMES = [
20
+ "auth-clerk",
21
+ "auth-contract",
18
22
  "auth-workos",
19
23
  "cms-contentstack",
20
24
  "cms-drupal",
@@ -22,6 +26,15 @@ const OFFICIAL_ITEM_NAMES = [
22
26
  "drupal",
23
27
  "next-hydra-standard",
24
28
  ];
29
+ function restoreFetchedSelectionSchema(item) {
30
+ // ShadCN assigns its registry-item schema to every resolved artifact,
31
+ // including artifacts authored with the Next Hydra Selection schema.
32
+ if (item.meta?.nextHydra !== undefined &&
33
+ item.$schema === SHADCN_REGISTRY_ITEM_SCHEMA_URL) {
34
+ return { ...item, $schema: NEXT_HYDRA_SELECTION_SCHEMA_URL };
35
+ }
36
+ return item;
37
+ }
25
38
  function createCatalog(options) {
26
39
  const items = new Map(options.registryItems.map((item) => [item.name, item]));
27
40
  const itemByReference = new Map(options.itemByReference);
@@ -162,12 +175,13 @@ export async function fetchRegistryItemGraph(options) {
162
175
  if (!itemName) {
163
176
  // oxlint-disable-next-line no-await-in-loop -- Breadth-first discovery reveals each next reference in order.
164
177
  const resolvedReference = await resolveRegistryReference(reference, options.cwd);
165
- const [artifact] = await fetchItems([resolvedReference], {
178
+ const [fetchedArtifact] = await fetchItems([resolvedReference], {
166
179
  config: options.config,
167
180
  });
168
- if (!artifact) {
181
+ if (!fetchedArtifact) {
169
182
  throw new CompositionValidationError("The registry dependency graph is incomplete.", [`${reference} returned no registry item`]);
170
183
  }
184
+ const artifact = restoreFetchedSelectionSchema(fetchedArtifact);
171
185
  const fetched = fetchedItems.get(artifact.name);
172
186
  const unpinnedReference = reference.split("#", 1)[0] ?? reference;
173
187
  const replacesPinnedRepositoryItem = reference.includes("#") &&
@@ -233,11 +247,12 @@ export async function loadSourceRegistryCatalog(cwd, registryFile = "registry.js
233
247
  repository,
234
248
  });
235
249
  }
236
- export async function loadGitHubSourceRegistryCatalog(repository, ref) {
250
+ export async function loadGitHubSourceRegistryCatalog(repository, ref, dependencies = {}) {
237
251
  const suffix = ref ? `#${ref}` : "";
238
252
  const addresses = OFFICIAL_ITEM_NAMES.map((item) => `${repository}/${item}${suffix}`);
239
- const registryItems = await getRegistryItems(addresses);
240
- const registryConfig = await getRegistriesConfig(process.cwd());
253
+ const fetchedItems = await (dependencies.fetchItems ?? getRegistryItems)(addresses);
254
+ const registryItems = fetchedItems.map(restoreFetchedSelectionSchema);
255
+ const registryConfig = await (dependencies.loadRegistryConfig ?? getRegistriesConfig)(process.cwd());
241
256
  const itemByReference = new Map(registryItems.flatMap((item, index) => {
242
257
  const address = addresses[index];
243
258
  return address ? [[address, item.name]] : [];
@@ -282,7 +297,8 @@ export async function addCatalogReferences(catalog, references, cwd = catalog.cw
282
297
  }
283
298
  for (const itemName of graph.fetchedItemNames) {
284
299
  const externalSelection = current.byReference.get(itemName);
285
- if (externalSelection !== undefined && externalSelection.assets.length > 0) {
300
+ if (externalSelection !== undefined &&
301
+ externalSelection.assets.length > 0) {
286
302
  throw new CompositionValidationError("External selections cannot declare separate binary assets in v1.", [
287
303
  `${externalSelection.id} must be included by the starter source registry; separately fetched binary assets are not supported`,
288
304
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/composition/format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAwCtE,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAET;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,kBAAkB,GAC3B,MAAM,CA4BR;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,kBAAkB,GAC3B,MAAM,CAKR;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAuBnE"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/composition/format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAwCtE,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAET;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,kBAAkB,GAC3B,MAAM,CA6BR;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,kBAAkB,GAC3B,MAAM,CAKR;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CA2BnE"}
@@ -35,7 +35,7 @@ export function formatCompositionPreview(current, proposed) {
35
35
  `Add-ons: ${currentAddOns === proposedAddOns ? `${currentAddOns} (unchanged)` : `${currentAddOns} -> ${proposedAddOns}`}`,
36
36
  ];
37
37
  if (hasCompositionChanges(current, proposed)) {
38
- lines.push("", "Planned actions:", " replace managed application files", " install selected source", " update package aliases", " update pnpm patches", " run pnpm install");
38
+ lines.push("", "Planned actions:", " replace managed application files", " install selected source", " update package aliases", " update TypeScript paths", " update pnpm patches", " run pnpm install");
39
39
  }
40
40
  return lines.join("\n");
41
41
  }
@@ -54,6 +54,8 @@ export function formatCompositionPlan(plan) {
54
54
  ...plan.registryItems.map((item) => ` ${item}`),
55
55
  "Package entries:",
56
56
  ...plan.packageRequirements.map((requirement) => ` ${requirement.cwd}: ${requirement.name} = ${requirement.specifier}`),
57
+ "TypeScript paths:",
58
+ ...plan.typeScriptPathAliases.map((entry) => ` ${entry.cwd}: ${entry.alias} -> ${entry.sourcePath}`),
57
59
  "pnpm patches:",
58
60
  ...plan.pnpmPatches.map((patch) => ` ${patch.dependency} -> ${patch.path}`),
59
61
  "Managed application files:",
@@ -1,6 +1,6 @@
1
- import type { DependencySection, PackageRequirement, SelectionDefinition } from "./types.js";
1
+ import type { DependencySection, PackageRequirement } from "./types.js";
2
2
  export type PackageJson = Partial<Record<DependencySection, Record<string, string>>> & Record<string, unknown>;
3
3
  export declare function parsePackageJson(source: string, label: string): PackageJson;
4
4
  export declare function readPackageJson(filePath: string, label?: string): Promise<PackageJson>;
5
- export declare function mergePackageRequirements(selections: Pick<SelectionDefinition, "packages">[]): PackageRequirement[];
5
+ export declare function mergePackageRequirements(entries: Iterable<PackageRequirement>): PackageRequirement[];
6
6
  //# sourceMappingURL=packages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"packages.d.ts","sourceRoot":"","sources":["../../src/composition/packages.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAWpB,MAAM,MAAM,WAAW,GAAG,OAAO,CAC/B,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAClD,GACC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAe1B,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAY3E;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,KAAK,SAAW,GACf,OAAO,CAAC,WAAW,CAAC,CAEtB;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAAE,GAClD,kBAAkB,EAAE,CA8BtB"}
1
+ {"version":3,"file":"packages.d.ts","sourceRoot":"","sources":["../../src/composition/packages.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAWxE,MAAM,MAAM,WAAW,GAAG,OAAO,CAC/B,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAClD,GACC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAe1B,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAY3E;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,KAAK,SAAW,GACf,OAAO,CAAC,WAAW,CAAC,CAEtB;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,QAAQ,CAAC,kBAAkB,CAAC,GACpC,kBAAkB,EAAE,CA8BtB"}
@@ -32,23 +32,24 @@ export function parsePackageJson(source, label) {
32
32
  export async function readPackageJson(filePath, label = filePath) {
33
33
  return parsePackageJson(await readFile(filePath, "utf-8"), label);
34
34
  }
35
- export function mergePackageRequirements(selections) {
35
+ export function mergePackageRequirements(entries) {
36
36
  const requirements = new Map();
37
37
  const issues = [];
38
- for (const selection of selections) {
39
- for (const requirement of selection.packages) {
40
- const key = `${requirement.cwd}\0${requirement.section}\0${requirement.name}`;
41
- const existing = requirements.get(key);
42
- if (existing && existing.specifier !== requirement.specifier) {
43
- issues.push(`${requirement.cwd}/${requirement.section}.${requirement.name} is requested as both ${existing.specifier} and ${requirement.specifier}`);
44
- }
45
- else {
46
- requirements.set(key, requirement);
47
- }
38
+ for (const requirement of entries) {
39
+ const key = `${requirement.cwd}\0${requirement.section}\0${requirement.name}`;
40
+ const existing = requirements.get(key);
41
+ if (existing && existing.specifier !== requirement.specifier) {
42
+ issues.push(`${requirement.cwd}/${requirement.section}.${requirement.name} is requested as both ${existing.specifier} and ${requirement.specifier}`);
43
+ }
44
+ else {
45
+ requirements.set(key, requirement);
48
46
  }
49
47
  }
50
48
  if (issues.length > 0) {
51
- throw new CompositionValidationError("Package requirements conflict.", [...new Set(issues)].sort((left, right) => left.localeCompare(right)));
49
+ throw new CompositionValidationError("Package requirements conflict.",
50
+ // eslint-disable-next-line unicorn/no-array-sort -- The newly-created array is safe to sort in place.
51
+ [...new Set(issues)].sort((left, right) => left.localeCompare(right)));
52
52
  }
53
+ // eslint-disable-next-line unicorn/no-array-sort -- The newly-created array is safe to sort in place.
53
54
  return [...requirements.values()].sort((left, right) => `${left.cwd}/${left.section}/${left.name}`.localeCompare(`${right.cwd}/${right.section}/${right.name}`));
54
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"planner.d.ts","sourceRoot":"","sources":["../../src/composition/planner.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAEV,eAAe,EAIf,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAiWpB,wBAAgB,eAAe,CAC7B,OAAO,EAAE,qBAAqB,EAC9B,SAAS,EAAE,kBAAkB,GAC5B,eAAe,CAoGjB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,qBAAqB,EAC9B,SAAS,EAAE,MAAM,GAChB,kBAAkB,CAuBpB"}
1
+ {"version":3,"file":"planner.d.ts","sourceRoot":"","sources":["../../src/composition/planner.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAEV,eAAe,EAMf,qBAAqB,EAGrB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AA2YpB,wBAAgB,eAAe,CAC7B,OAAO,EAAE,qBAAqB,EAC9B,SAAS,EAAE,kBAAkB,GAC5B,eAAe,CAmHjB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,qBAAqB,EAC9B,SAAS,EAAE,MAAM,GAChB,kBAAkB,CAuBpB"}
@@ -109,54 +109,92 @@ function validateCompatibility(selections) {
109
109
  throw new CompositionValidationError("The requested selections are incompatible.", uniqueSorted(issues));
110
110
  }
111
111
  }
112
- function validateProviderAliasRequirements(providers) {
113
- const requiredLocations = {
114
- auth: [
115
- { cwd: "apps/api", section: "dependencies" },
116
- { cwd: "apps/web", section: "dependencies" },
117
- { cwd: "packages/feature-flags", section: "dependencies" },
118
- ],
119
- cms: [
120
- { cwd: "apps/cli", section: "dependencies" },
121
- { cwd: "apps/web", section: "dependencies" },
122
- ],
123
- commerce: [
124
- { cwd: "apps/api", section: "dependencies" },
125
- { cwd: "apps/cli", section: "dependencies" },
126
- { cwd: "apps/web", section: "dependencies" },
127
- ],
128
- };
129
- const issues = [];
130
- for (const provider of providers) {
131
- if (!provider.slot) {
132
- continue;
112
+ const BASELINE_PROVIDER_DEPENDENCIES = [
113
+ { cwd: "apps/admin", section: "dependencies", slot: "auth" },
114
+ { cwd: "apps/api", section: "dependencies", slot: "auth" },
115
+ { cwd: "apps/cli", section: "dependencies", slot: "auth" },
116
+ { cwd: "apps/web", section: "dependencies", slot: "auth" },
117
+ { cwd: "packages/feature-flags", section: "dependencies", slot: "auth" },
118
+ { cwd: "tests/e2e", section: "devDependencies", slot: "auth" },
119
+ { cwd: "apps/cli", section: "dependencies", slot: "cms" },
120
+ { cwd: "apps/web", section: "dependencies", slot: "cms" },
121
+ { cwd: "apps/api", section: "dependencies", slot: "commerce" },
122
+ { cwd: "apps/cli", section: "dependencies", slot: "commerce" },
123
+ { cwd: "apps/web", section: "dependencies", slot: "commerce" },
124
+ { cwd: "tests/e2e", section: "devDependencies", slot: "commerce" },
125
+ ];
126
+ function selectedProviderDependencies(selections) {
127
+ return [
128
+ ...BASELINE_PROVIDER_DEPENDENCIES,
129
+ ...selections.flatMap((selection) => selection.providerDependencies),
130
+ ];
131
+ }
132
+ function catalogProviderDependencies(catalog) {
133
+ return [
134
+ ...BASELINE_PROVIDER_DEPENDENCIES,
135
+ ...catalog.selections.flatMap((selection) => selection.providerDependencies),
136
+ ];
137
+ }
138
+ function resolveProviderRequirements(providers, dependencies) {
139
+ const packageRequirements = [];
140
+ const typeScriptPathAliases = new Map();
141
+ for (const dependency of dependencies) {
142
+ const provider = providers.get(dependency.slot);
143
+ if (!provider?.binding) {
144
+ throw new CompositionValidationError("Provider binding is missing.", [
145
+ `${dependency.slot} does not supply a Provider binding`,
146
+ ]);
133
147
  }
134
- const alias = PROVIDER_ALIASES[provider.slot];
135
- const requiredClaims = new Set(requiredLocations[provider.slot].map((requirement) => `${requirement.cwd}\0${requirement.section}\0${alias}`));
136
- const actualClaims = new Set(provider.packages.map((requirement) => `${requirement.cwd}\0${requirement.section}\0${requirement.name}`));
137
- for (const claim of requiredClaims) {
138
- if (!actualClaims.has(claim)) {
139
- const [cwd, section, name] = claim.split("\0");
140
- issues.push(`${provider.id} must declare ${cwd}/${section}.${name} for the ${provider.slot} Provider Slot`);
141
- }
148
+ const alias = PROVIDER_ALIASES[dependency.slot];
149
+ packageRequirements.push({
150
+ cwd: dependency.cwd,
151
+ name: alias,
152
+ section: dependency.section,
153
+ specifier: provider.binding.specifier,
154
+ });
155
+ if (provider.binding.sourcePath) {
156
+ typeScriptPathAliases.set(`${dependency.cwd}\0${alias}`, {
157
+ alias,
158
+ cwd: dependency.cwd,
159
+ sourcePath: resolveWorkspacePath(provider.binding.sourcePath, `${provider.id} Provider binding source path`),
160
+ });
142
161
  }
143
162
  }
144
- if (issues.length > 0) {
145
- throw new CompositionValidationError("Providers do not supply all stable Provider aliases required by the Baseline.", uniqueSorted(issues));
163
+ // eslint-disable-next-line unicorn/no-array-sort -- The newly-created array is safe to sort in place.
164
+ const aliases = [...typeScriptPathAliases.values()].sort((left, right) => `${left.cwd}/${left.alias}`.localeCompare(`${right.cwd}/${right.alias}`));
165
+ return { packageRequirements, typeScriptPathAliases: aliases };
166
+ }
167
+ function catalogTypeScriptPathAliases(dependencies) {
168
+ const aliases = new Map();
169
+ for (const dependency of dependencies) {
170
+ const alias = PROVIDER_ALIASES[dependency.slot];
171
+ aliases.set(`${dependency.cwd}\0${alias}`, {
172
+ alias,
173
+ cwd: dependency.cwd,
174
+ });
146
175
  }
176
+ // eslint-disable-next-line unicorn/no-array-sort -- The newly-created array is safe to sort in place.
177
+ return [...aliases.values()].sort((left, right) => `${left.cwd}/${left.alias}`.localeCompare(`${right.cwd}/${right.alias}`));
147
178
  }
148
- function catalogPackageRequirements(catalog) {
149
- const requirements = new Map();
179
+ function catalogPackageRequirementTargets(catalog, providerDependencies) {
180
+ const targets = new Map();
181
+ const add = (target) => {
182
+ targets.set(`${target.cwd}\0${target.section}\0${target.name}`, target);
183
+ };
150
184
  for (const selection of catalog.selections) {
151
- for (const requirement of selection.packages) {
152
- const key = `${requirement.cwd}\0${requirement.section}\0${requirement.name}`;
153
- if (!requirements.has(key)) {
154
- requirements.set(key, requirement);
155
- }
185
+ for (const { cwd, name, section } of selection.packages) {
186
+ add({ cwd, name, section });
156
187
  }
157
188
  }
189
+ for (const dependency of providerDependencies) {
190
+ add({
191
+ cwd: dependency.cwd,
192
+ name: PROVIDER_ALIASES[dependency.slot],
193
+ section: dependency.section,
194
+ });
195
+ }
158
196
  // eslint-disable-next-line unicorn/no-array-sort -- The newly-created array is safe to sort in place.
159
- return [...requirements.values()].sort((left, right) => `${left.cwd}/${left.section}/${left.name}`.localeCompare(`${right.cwd}/${right.section}/${right.name}`));
197
+ return [...targets.values()].sort((left, right) => `${left.cwd}/${left.section}/${left.name}`.localeCompare(`${right.cwd}/${right.section}/${right.name}`));
160
198
  }
161
199
  function resolvePnpmPatches(selections) {
162
200
  const patches = new Map();
@@ -240,8 +278,14 @@ export function planComposition(catalog, selection) {
240
278
  });
241
279
  const addOns = resolveAddOns(catalog, selection.addOns, providerSelections);
242
280
  const selections = [...providerSelections, ...addOns];
243
- validateProviderAliasRequirements(providerSelections);
244
281
  validateCompatibility(selections);
282
+ const providerDependencies = selectedProviderDependencies(selections);
283
+ const catalogDependencies = catalogProviderDependencies(catalog);
284
+ const providerRequirements = resolveProviderRequirements(providers, providerDependencies);
285
+ const packageRequirements = mergePackageRequirements([
286
+ ...selections.flatMap((selected) => selected.packages),
287
+ ...providerRequirements.packageRequirements,
288
+ ]);
245
289
  const entryItems = uniqueSorted(selections.map((selected) => selected.itemName));
246
290
  const registryItems = resolveRegistryItemGraph(catalog, entryItems);
247
291
  const assets = selections
@@ -272,8 +316,9 @@ export function planComposition(catalog, selection) {
272
316
  return {
273
317
  assets,
274
318
  catalogManagedTargets: catalog.managedTargets,
275
- catalogPackageRequirements: catalogPackageRequirements(catalog),
319
+ catalogPackageRequirementTargets: catalogPackageRequirementTargets(catalog, catalogDependencies),
276
320
  catalogPnpmPatches: catalogPnpmPatches(catalog),
321
+ catalogTypeScriptPathAliases: catalogTypeScriptPathAliases(catalogDependencies),
277
322
  entryItems,
278
323
  instructions: [
279
324
  ...new Set(selections
@@ -281,7 +326,7 @@ export function planComposition(catalog, selection) {
281
326
  .filter((value) => Boolean(value))),
282
327
  ],
283
328
  managedTargets,
284
- packageRequirements: mergePackageRequirements(selections),
329
+ packageRequirements,
285
330
  pnpmPatches,
286
331
  registryItems,
287
332
  selection: {
@@ -290,6 +335,7 @@ export function planComposition(catalog, selection) {
290
335
  providers: { ...selection.providers },
291
336
  },
292
337
  selections,
338
+ typeScriptPathAliases: providerRequirements.typeScriptPathAliases,
293
339
  variableTargets: uniqueSorted([
294
340
  ...catalogVariableTargets(catalog),
295
341
  ...catalog.selections.flatMap((selected) => selected.assets.map((asset) => resolveWorkspacePath(asset.target, `${selected.id} asset target`))),