create-next-hydra 0.3.0 → 0.4.1
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.
- package/README.md +126 -27
- package/dist/add-command.d.ts +11 -0
- package/dist/add-command.d.ts.map +1 -0
- package/dist/add-command.js +75 -0
- package/dist/addition.d.ts +67 -0
- package/dist/addition.d.ts.map +1 -0
- package/dist/addition.js +422 -0
- package/dist/application-git.d.ts +20 -0
- package/dist/application-git.d.ts.map +1 -0
- package/dist/application-git.js +57 -0
- package/dist/baseline.d.ts +22 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +90 -0
- package/dist/cli.js +5 -18
- package/dist/commands.d.ts +22 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +197 -0
- package/dist/composition-lint.d.ts +20 -0
- package/dist/composition-lint.d.ts.map +1 -0
- package/dist/composition-lint.js +288 -0
- package/dist/composition.d.ts +24 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js +420 -0
- package/dist/create-command.d.ts +21 -0
- package/dist/create-command.d.ts.map +1 -0
- package/dist/create-command.js +128 -0
- package/dist/demo-instrumentation.d.ts +5 -0
- package/dist/demo-instrumentation.d.ts.map +1 -0
- package/dist/demo-instrumentation.js +300 -0
- package/dist/environment.d.ts +7 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +81 -0
- package/dist/errors.d.ts +156 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +140 -0
- package/dist/file-policy.d.ts +6 -0
- package/dist/file-policy.d.ts.map +1 -0
- package/dist/file-policy.js +38 -0
- package/dist/files.d.ts +7 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +66 -0
- package/dist/hosting.d.ts +5 -0
- package/dist/hosting.d.ts.map +1 -0
- package/dist/hosting.js +107 -0
- package/dist/index.d.ts +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -89
- package/dist/initialization.d.ts +14 -0
- package/dist/initialization.d.ts.map +1 -0
- package/dist/initialization.js +266 -0
- package/dist/lint-workspaces.d.ts +2 -0
- package/dist/lint-workspaces.d.ts.map +1 -0
- package/dist/lint-workspaces.js +62 -0
- package/dist/model.d.ts +143 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +61 -0
- package/dist/package-catalog.d.ts +8 -0
- package/dist/package-catalog.d.ts.map +1 -0
- package/dist/package-catalog.js +61 -0
- package/dist/packages.d.ts +57 -0
- package/dist/packages.d.ts.map +1 -0
- package/dist/packages.js +237 -0
- package/dist/process-group.d.ts +5 -0
- package/dist/process-group.d.ts.map +1 -0
- package/dist/process-group.js +23 -0
- package/dist/reference-workspace-bindings.d.ts +38 -0
- package/dist/reference-workspace-bindings.d.ts.map +1 -0
- package/dist/reference-workspace-bindings.js +41 -0
- package/dist/reference-workspace.d.ts +6 -0
- package/dist/reference-workspace.d.ts.map +1 -0
- package/dist/reference-workspace.js +62 -0
- package/dist/registry.d.ts +187 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +107 -0
- package/dist/runtime.d.ts +9 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +22 -0
- package/dist/selection.d.ts +704 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +424 -0
- package/dist/shadcn-protocol.d.ts +22 -0
- package/dist/shadcn-protocol.d.ts.map +1 -0
- package/dist/shadcn-protocol.js +13 -0
- package/dist/shadcn-worker.d.ts +2 -0
- package/dist/shadcn-worker.d.ts.map +1 -0
- package/dist/shadcn-worker.js +43 -0
- package/dist/shadcn.d.ts +22 -0
- package/dist/shadcn.d.ts.map +1 -0
- package/dist/shadcn.js +101 -0
- package/dist/source-changes.d.ts +55 -0
- package/dist/source-changes.d.ts.map +1 -0
- package/dist/source-changes.js +225 -0
- package/dist/source-inventory.d.ts +25 -0
- package/dist/source-inventory.d.ts.map +1 -0
- package/dist/source-inventory.js +212 -0
- package/dist/source-root.d.ts +4 -0
- package/dist/source-root.d.ts.map +1 -0
- package/dist/source-root.js +20 -0
- package/dist/staging.d.ts +19 -0
- package/dist/staging.d.ts.map +1 -0
- package/dist/staging.js +42 -0
- package/dist/tasks.d.ts +44 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +137 -0
- package/dist/templates.d.ts +217 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +161 -0
- package/dist/test-reference-workspace.d.ts +2 -0
- package/dist/test-reference-workspace.d.ts.map +1 -0
- package/dist/test-reference-workspace.js +6 -0
- package/dist/types.d.ts +2 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/typescript-paths.d.ts +10 -0
- package/dist/typescript-paths.d.ts.map +1 -0
- package/dist/typescript-paths.js +52 -0
- package/dist/workspace-dependencies.d.ts +26 -0
- package/dist/workspace-dependencies.d.ts.map +1 -0
- package/dist/workspace-dependencies.js +478 -0
- package/dist/workspace-files.d.ts +24 -0
- package/dist/workspace-files.d.ts.map +1 -0
- package/dist/workspace-files.js +395 -0
- package/dist/workspace-lock.d.ts +12 -0
- package/dist/workspace-lock.d.ts.map +1 -0
- package/dist/workspace-lock.js +104 -0
- package/dist/workspace-snapshots.d.ts +24 -0
- package/dist/workspace-snapshots.d.ts.map +1 -0
- package/dist/workspace-snapshots.js +218 -0
- package/dist/workspace-sources.d.ts +27 -0
- package/dist/workspace-sources.d.ts.map +1 -0
- package/dist/workspace-sources.js +88 -0
- package/dist/workspace-state.d.ts +325 -0
- package/dist/workspace-state.d.ts.map +1 -0
- package/dist/workspace-state.js +571 -0
- package/dist/workspaces.d.ts +73 -0
- package/dist/workspaces.d.ts.map +1 -0
- package/dist/workspaces.js +396 -0
- package/package.json +25 -20
- package/schema/selection-definition.json +143 -4
- package/dist/clone.d.ts +0 -9
- package/dist/clone.d.ts.map +0 -1
- package/dist/clone.js +0 -19
- package/dist/composition/add.d.ts +0 -14
- package/dist/composition/add.d.ts.map +0 -1
- package/dist/composition/add.js +0 -484
- package/dist/composition/catalog.d.ts +0 -26
- package/dist/composition/catalog.d.ts.map +0 -1
- package/dist/composition/catalog.js +0 -317
- package/dist/composition/errors.d.ts +0 -5
- package/dist/composition/errors.d.ts.map +0 -1
- package/dist/composition/errors.js +0 -8
- package/dist/composition/format.d.ts +0 -6
- package/dist/composition/format.d.ts.map +0 -1
- package/dist/composition/format.js +0 -65
- package/dist/composition/install.d.ts +0 -16
- package/dist/composition/install.d.ts.map +0 -1
- package/dist/composition/install.js +0 -175
- package/dist/composition/packages.d.ts +0 -6
- package/dist/composition/packages.d.ts.map +0 -1
- package/dist/composition/packages.js +0 -55
- package/dist/composition/paths.d.ts +0 -4
- package/dist/composition/paths.d.ts.map +0 -1
- package/dist/composition/paths.js +0 -47
- package/dist/composition/planner.d.ts +0 -4
- package/dist/composition/planner.d.ts.map +0 -1
- package/dist/composition/planner.js +0 -366
- package/dist/composition/schema.d.ts +0 -294
- package/dist/composition/schema.d.ts.map +0 -1
- package/dist/composition/schema.js +0 -166
- package/dist/composition/types.d.ts +0 -111
- package/dist/composition/types.d.ts.map +0 -1
- package/dist/composition/types.js +0 -6
- package/dist/composition/typescript-paths.d.ts +0 -6
- package/dist/composition/typescript-paths.d.ts.map +0 -1
- package/dist/composition/typescript-paths.js +0 -157
- package/dist/composition/use.d.ts +0 -23
- package/dist/composition/use.d.ts.map +0 -1
- package/dist/composition/use.js +0 -187
- package/dist/composition/workspace.d.ts +0 -10
- package/dist/composition/workspace.d.ts.map +0 -1
- package/dist/composition/workspace.js +0 -210
- package/dist/constants.d.ts +0 -11
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js +0 -39
- package/dist/fs-utils.d.ts +0 -9
- package/dist/fs-utils.d.ts.map +0 -1
- package/dist/fs-utils.js +0 -64
- package/dist/git.d.ts +0 -28
- package/dist/git.d.ts.map +0 -1
- package/dist/git.js +0 -107
- package/dist/instruction-renderer.d.ts +0 -17
- package/dist/instruction-renderer.d.ts.map +0 -1
- package/dist/instruction-renderer.js +0 -132
- package/dist/logger.d.ts +0 -13
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -26
- package/dist/prompts.d.ts +0 -7
- package/dist/prompts.d.ts.map +0 -1
- package/dist/prompts.js +0 -54
- package/dist/sanitize.d.ts +0 -10
- package/dist/sanitize.d.ts.map +0 -1
- package/dist/sanitize.js +0 -110
- package/dist/scaffold.d.ts +0 -7
- package/dist/scaffold.d.ts.map +0 -1
- package/dist/scaffold.js +0 -330
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js +0 -2
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { CompositionValidationError } from "./errors.js";
|
|
4
|
-
import { formatZodError } from "./schema.js";
|
|
5
|
-
const dependencyEntriesSchema = z.record(z.string());
|
|
6
|
-
const packageJsonSchema = z
|
|
7
|
-
.object({
|
|
8
|
-
dependencies: dependencyEntriesSchema.optional(),
|
|
9
|
-
devDependencies: dependencyEntriesSchema.optional(),
|
|
10
|
-
optionalDependencies: dependencyEntriesSchema.optional(),
|
|
11
|
-
})
|
|
12
|
-
.passthrough();
|
|
13
|
-
function decodePackageJson(value, label) {
|
|
14
|
-
const result = packageJsonSchema.safeParse(value);
|
|
15
|
-
if (!result.success) {
|
|
16
|
-
throw new CompositionValidationError(`${label} is not a valid package.json file.`, formatZodError(result.error));
|
|
17
|
-
}
|
|
18
|
-
// Zod reconstructs objects in schema order, so return the validated input
|
|
19
|
-
// to preserve the manifest's existing key positions.
|
|
20
|
-
return value;
|
|
21
|
-
}
|
|
22
|
-
export function parsePackageJson(source, label) {
|
|
23
|
-
let value;
|
|
24
|
-
try {
|
|
25
|
-
value = JSON.parse(source);
|
|
26
|
-
}
|
|
27
|
-
catch (error) {
|
|
28
|
-
throw new CompositionValidationError(`${label} is not a valid package.json file.`, [error instanceof Error ? error.message : "JSON parsing failed"], { cause: error });
|
|
29
|
-
}
|
|
30
|
-
return decodePackageJson(value, label);
|
|
31
|
-
}
|
|
32
|
-
export async function readPackageJson(filePath, label = filePath) {
|
|
33
|
-
return parsePackageJson(await readFile(filePath, "utf-8"), label);
|
|
34
|
-
}
|
|
35
|
-
export function mergePackageRequirements(entries) {
|
|
36
|
-
const requirements = new Map();
|
|
37
|
-
const issues = [];
|
|
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);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (issues.length > 0) {
|
|
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
|
-
}
|
|
53
|
-
// eslint-disable-next-line unicorn/no-array-sort -- The newly-created array is safe to sort in place.
|
|
54
|
-
return [...requirements.values()].sort((left, right) => `${left.cwd}/${left.section}/${left.name}`.localeCompare(`${right.cwd}/${right.section}/${right.name}`));
|
|
55
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export declare function resolveRegistryTarget(target: string): string;
|
|
2
|
-
export declare function resolveWorkspacePath(value: string, label: string): string;
|
|
3
|
-
export declare function isManagedApplicationSource(sourcePath: string, target: string | undefined): boolean;
|
|
4
|
-
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/composition/paths.ts"],"names":[],"mappings":"AA6BA,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAS5D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,OAAO,CAiBT"}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { CompositionValidationError } from "./errors.js";
|
|
3
|
-
const LEADING_CURRENT_DIRECTORY = /^\.\//;
|
|
4
|
-
const REGISTRY_SOURCE_DIRECTORY = "registry";
|
|
5
|
-
function normalizeRelativePath(value, label) {
|
|
6
|
-
if (value.includes("\\")) {
|
|
7
|
-
throw new CompositionValidationError("Unsafe composition path.", [
|
|
8
|
-
`${label} must use forward slashes: ${value}`,
|
|
9
|
-
]);
|
|
10
|
-
}
|
|
11
|
-
const normalized = path.posix.normalize(value);
|
|
12
|
-
if (normalized === "." ||
|
|
13
|
-
normalized === ".." ||
|
|
14
|
-
normalized.startsWith("../") ||
|
|
15
|
-
path.posix.isAbsolute(normalized)) {
|
|
16
|
-
throw new CompositionValidationError("Unsafe composition path.", [
|
|
17
|
-
`${label} escapes or names the workspace root: ${value}`,
|
|
18
|
-
]);
|
|
19
|
-
}
|
|
20
|
-
return normalized.replace(LEADING_CURRENT_DIRECTORY, "");
|
|
21
|
-
}
|
|
22
|
-
export function resolveRegistryTarget(target) {
|
|
23
|
-
if (!target.startsWith("~/")) {
|
|
24
|
-
throw new CompositionValidationError("Registry files require workspace-root targets.", [`files[].target must start with ~/: ${target}`]);
|
|
25
|
-
}
|
|
26
|
-
const relativeTarget = target.startsWith("~/") ? target.slice(2) : target;
|
|
27
|
-
return normalizeRelativePath(relativeTarget, "registry target");
|
|
28
|
-
}
|
|
29
|
-
export function resolveWorkspacePath(value, label) {
|
|
30
|
-
return normalizeRelativePath(value, label);
|
|
31
|
-
}
|
|
32
|
-
export function isManagedApplicationSource(sourcePath, target) {
|
|
33
|
-
if (!target?.startsWith("~/")) {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
const normalized = path.posix
|
|
37
|
-
.normalize(sourcePath)
|
|
38
|
-
.replace(LEADING_CURRENT_DIRECTORY, "");
|
|
39
|
-
const marker = `/${REGISTRY_SOURCE_DIRECTORY}/`;
|
|
40
|
-
const sourceWithLeadingSlash = `/${normalized}`;
|
|
41
|
-
const markerIndex = sourceWithLeadingSlash.lastIndexOf(marker);
|
|
42
|
-
if (markerIndex === -1) {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
return (sourceWithLeadingSlash.slice(markerIndex + marker.length) ===
|
|
46
|
-
target.slice(2));
|
|
47
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { CompositionPlan, SourceRegistryCatalog, WorkspaceSelection } from "./types.js";
|
|
2
|
-
export declare function planComposition(catalog: SourceRegistryCatalog, selection: WorkspaceSelection): CompositionPlan;
|
|
3
|
-
export declare function selectionFromPreset(catalog: SourceRegistryCatalog, reference: string): WorkspaceSelection;
|
|
4
|
-
//# sourceMappingURL=planner.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
import { resolveCatalogSelection, resolveRegistryItemGraph, } from "./catalog.js";
|
|
2
|
-
import { CompositionValidationError } from "./errors.js";
|
|
3
|
-
import { mergePackageRequirements } from "./packages.js";
|
|
4
|
-
import { isManagedApplicationSource, resolveRegistryTarget, resolveWorkspacePath, } from "./paths.js";
|
|
5
|
-
import { PROVIDER_ALIASES, PROVIDER_SLOTS } from "./types.js";
|
|
6
|
-
function uniqueSorted(values) {
|
|
7
|
-
// eslint-disable-next-line unicorn/no-array-sort -- The newly-created array is safe to sort in place.
|
|
8
|
-
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
9
|
-
}
|
|
10
|
-
function itemTargets(catalog, itemName) {
|
|
11
|
-
const item = catalog.items.get(itemName);
|
|
12
|
-
if (!item) {
|
|
13
|
-
throw new CompositionValidationError("Missing registry item.", [
|
|
14
|
-
`${itemName} is not in the source registry`,
|
|
15
|
-
]);
|
|
16
|
-
}
|
|
17
|
-
return uniqueSorted((item.files ?? []).map((file) => {
|
|
18
|
-
if (!file.target) {
|
|
19
|
-
throw new CompositionValidationError("Registry items require explicit targets.", [`${itemName}:${file.path} does not declare files[].target`]);
|
|
20
|
-
}
|
|
21
|
-
return resolveRegistryTarget(file.target);
|
|
22
|
-
}));
|
|
23
|
-
}
|
|
24
|
-
function itemManagedTargets(catalog, itemName) {
|
|
25
|
-
const item = catalog.items.get(itemName);
|
|
26
|
-
if (!item) {
|
|
27
|
-
throw new CompositionValidationError("Missing registry item.", [
|
|
28
|
-
`${itemName} is not in the source registry`,
|
|
29
|
-
]);
|
|
30
|
-
}
|
|
31
|
-
return uniqueSorted((item.files ?? [])
|
|
32
|
-
.filter((file) => isManagedApplicationSource(file.path, file.target))
|
|
33
|
-
.map((file) => {
|
|
34
|
-
if (!file.target) {
|
|
35
|
-
throw new CompositionValidationError("Managed application files require explicit targets.", [`${itemName}:${file.path} does not declare files[].target`]);
|
|
36
|
-
}
|
|
37
|
-
return resolveRegistryTarget(file.target);
|
|
38
|
-
}));
|
|
39
|
-
}
|
|
40
|
-
function resolveProviders(catalog, selection) {
|
|
41
|
-
const providers = new Map();
|
|
42
|
-
const issues = [];
|
|
43
|
-
for (const slot of PROVIDER_SLOTS) {
|
|
44
|
-
const candidate = resolveCatalogSelection(catalog, selection.providers[slot]);
|
|
45
|
-
if (candidate.kind !== "provider" || candidate.slot !== slot) {
|
|
46
|
-
issues.push(`${selection.providers[slot]} is ${candidate.kind}${candidate.slot ? ` for ${candidate.slot}` : ""}, not a ${slot} provider`);
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
providers.set(slot, candidate);
|
|
50
|
-
}
|
|
51
|
-
if (issues.length > 0) {
|
|
52
|
-
throw new CompositionValidationError("Provider Slot cardinality is invalid.", issues);
|
|
53
|
-
}
|
|
54
|
-
return providers;
|
|
55
|
-
}
|
|
56
|
-
function resolveAddOns(catalog, references, providers) {
|
|
57
|
-
const selected = new Map();
|
|
58
|
-
const queue = references.map((reference) => resolveCatalogSelection(catalog, reference));
|
|
59
|
-
const selectedProviderIds = new Set(providers.map((provider) => provider.id));
|
|
60
|
-
const enqueueRequiredAddOns = (selection) => {
|
|
61
|
-
for (const requiredId of selection.compatibility.requires) {
|
|
62
|
-
if (selectedProviderIds.has(requiredId) || selected.has(requiredId)) {
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
const required = catalog.byId.get(requiredId);
|
|
66
|
-
if (required?.kind === "add-on") {
|
|
67
|
-
queue.push(required);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
for (const provider of providers) {
|
|
72
|
-
enqueueRequiredAddOns(provider);
|
|
73
|
-
}
|
|
74
|
-
while (queue.length > 0) {
|
|
75
|
-
const candidate = queue.shift();
|
|
76
|
-
if (!candidate) {
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
if (candidate.kind !== "add-on") {
|
|
80
|
-
throw new CompositionValidationError("Invalid Add-on selection.", [
|
|
81
|
-
`${candidate.id} is ${candidate.kind}, not an add-on`,
|
|
82
|
-
]);
|
|
83
|
-
}
|
|
84
|
-
if (selected.has(candidate.id)) {
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
selected.set(candidate.id, candidate);
|
|
88
|
-
enqueueRequiredAddOns(candidate);
|
|
89
|
-
}
|
|
90
|
-
// eslint-disable-next-line unicorn/no-array-sort -- The newly-created array is safe to sort in place.
|
|
91
|
-
return [...selected.values()].sort((left, right) => left.id.localeCompare(right.id));
|
|
92
|
-
}
|
|
93
|
-
function validateCompatibility(selections) {
|
|
94
|
-
const selectedIds = new Set(selections.map((selection) => selection.id));
|
|
95
|
-
const issues = [];
|
|
96
|
-
for (const selection of selections) {
|
|
97
|
-
for (const requiredId of selection.compatibility.requires) {
|
|
98
|
-
if (!selectedIds.has(requiredId)) {
|
|
99
|
-
issues.push(`${selection.id} requires ${requiredId}`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
for (const conflictingId of selection.compatibility.conflicts) {
|
|
103
|
-
if (selectedIds.has(conflictingId)) {
|
|
104
|
-
issues.push(`${selection.id} conflicts with ${conflictingId}`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
if (issues.length > 0) {
|
|
109
|
-
throw new CompositionValidationError("The requested selections are incompatible.", uniqueSorted(issues));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
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
|
-
]);
|
|
147
|
-
}
|
|
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
|
-
});
|
|
161
|
-
}
|
|
162
|
-
}
|
|
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
|
-
});
|
|
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}`));
|
|
178
|
-
}
|
|
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
|
-
};
|
|
184
|
-
for (const selection of catalog.selections) {
|
|
185
|
-
for (const { cwd, name, section } of selection.packages) {
|
|
186
|
-
add({ cwd, name, section });
|
|
187
|
-
}
|
|
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
|
-
}
|
|
196
|
-
// eslint-disable-next-line unicorn/no-array-sort -- The newly-created array is safe to sort in place.
|
|
197
|
-
return [...targets.values()].sort((left, right) => `${left.cwd}/${left.section}/${left.name}`.localeCompare(`${right.cwd}/${right.section}/${right.name}`));
|
|
198
|
-
}
|
|
199
|
-
function resolvePnpmPatches(selections) {
|
|
200
|
-
const patches = new Map();
|
|
201
|
-
const issues = [];
|
|
202
|
-
for (const selection of selections) {
|
|
203
|
-
for (const patch of selection.pnpmPatches) {
|
|
204
|
-
const normalized = {
|
|
205
|
-
...patch,
|
|
206
|
-
path: resolveWorkspacePath(patch.path, `${selection.id} pnpm patch path`),
|
|
207
|
-
};
|
|
208
|
-
const existing = patches.get(normalized.dependency);
|
|
209
|
-
if (existing && existing.path !== normalized.path) {
|
|
210
|
-
issues.push(`${normalized.dependency} is patched by both ${existing.path} and ${normalized.path}`);
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
patches.set(normalized.dependency, normalized);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
if (issues.length > 0) {
|
|
218
|
-
throw new CompositionValidationError("pnpm patches conflict.", issues);
|
|
219
|
-
}
|
|
220
|
-
// eslint-disable-next-line unicorn/no-array-sort -- The newly-created array is safe to sort in place.
|
|
221
|
-
return [...patches.values()].sort((left, right) => left.dependency.localeCompare(right.dependency));
|
|
222
|
-
}
|
|
223
|
-
function catalogPnpmPatches(catalog) {
|
|
224
|
-
const patches = new Map();
|
|
225
|
-
for (const selection of catalog.selections) {
|
|
226
|
-
for (const patch of selection.pnpmPatches) {
|
|
227
|
-
const normalized = {
|
|
228
|
-
...patch,
|
|
229
|
-
path: resolveWorkspacePath(patch.path, `${selection.id} pnpm patch path`),
|
|
230
|
-
};
|
|
231
|
-
if (!patches.has(normalized.dependency)) {
|
|
232
|
-
patches.set(normalized.dependency, normalized);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
// eslint-disable-next-line unicorn/no-array-sort -- The newly-created array is safe to sort in place.
|
|
237
|
-
return [...patches.values()].sort((left, right) => left.dependency.localeCompare(right.dependency));
|
|
238
|
-
}
|
|
239
|
-
function catalogVariableTargets(catalog) {
|
|
240
|
-
return uniqueSorted([...catalog.items.values()].flatMap((item) => item.files?.map((file) => {
|
|
241
|
-
if (!file.target) {
|
|
242
|
-
throw new CompositionValidationError("Registry items require explicit targets.", [`${item.name}:${file.path} does not declare files[].target`]);
|
|
243
|
-
}
|
|
244
|
-
return resolveRegistryTarget(file.target);
|
|
245
|
-
}) ?? []));
|
|
246
|
-
}
|
|
247
|
-
function validateMaterializationTargets(options) {
|
|
248
|
-
const claims = new Map();
|
|
249
|
-
const issues = [];
|
|
250
|
-
const claim = (target, owner) => {
|
|
251
|
-
const existing = claims.get(target);
|
|
252
|
-
if (existing && existing !== owner) {
|
|
253
|
-
issues.push(`${target} is claimed by both ${existing} and ${owner}`);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
claims.set(target, owner);
|
|
257
|
-
};
|
|
258
|
-
for (const item of options.registryItems) {
|
|
259
|
-
for (const target of itemTargets(options.catalog, item)) {
|
|
260
|
-
claim(target, `registry item ${item}`);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
for (const asset of options.assets) {
|
|
264
|
-
claim(asset.target, `asset ${asset.source}`);
|
|
265
|
-
}
|
|
266
|
-
if (issues.length > 0) {
|
|
267
|
-
throw new CompositionValidationError("Materialization targets conflict.", uniqueSorted(issues));
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
export function planComposition(catalog, selection) {
|
|
271
|
-
const providers = resolveProviders(catalog, selection);
|
|
272
|
-
const providerSelections = PROVIDER_SLOTS.map((slot) => {
|
|
273
|
-
const provider = providers.get(slot);
|
|
274
|
-
if (!provider) {
|
|
275
|
-
throw new CompositionValidationError("Provider Slot cardinality is invalid.", [`${slot} requires exactly one provider`]);
|
|
276
|
-
}
|
|
277
|
-
return provider;
|
|
278
|
-
});
|
|
279
|
-
const addOns = resolveAddOns(catalog, selection.addOns, providerSelections);
|
|
280
|
-
const selections = [...providerSelections, ...addOns];
|
|
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
|
-
]);
|
|
289
|
-
const entryItems = uniqueSorted(selections.map((selected) => selected.itemName));
|
|
290
|
-
const registryItems = resolveRegistryItemGraph(catalog, entryItems);
|
|
291
|
-
const assets = selections
|
|
292
|
-
.flatMap((selected) => selected.assets.map((asset) => ({
|
|
293
|
-
source: resolveWorkspacePath(asset.source, `${selected.id} asset source`),
|
|
294
|
-
target: resolveWorkspacePath(asset.target, `${selected.id} asset target`),
|
|
295
|
-
})))
|
|
296
|
-
// eslint-disable-next-line unicorn/no-array-sort -- flatMap creates a fresh array.
|
|
297
|
-
.sort((left, right) => left.target.localeCompare(right.target));
|
|
298
|
-
const pnpmPatches = resolvePnpmPatches(selections);
|
|
299
|
-
const assetTargets = new Set(assets.map((asset) => asset.target));
|
|
300
|
-
const missingPatchAssets = pnpmPatches
|
|
301
|
-
.filter((patch) => !assetTargets.has(patch.path))
|
|
302
|
-
.map((patch) => `${patch.dependency} references ${patch.path}, which is not a selected asset target`);
|
|
303
|
-
if (missingPatchAssets.length > 0) {
|
|
304
|
-
throw new CompositionValidationError("pnpm patch files are missing.", missingPatchAssets);
|
|
305
|
-
}
|
|
306
|
-
validateMaterializationTargets({
|
|
307
|
-
assets,
|
|
308
|
-
catalog,
|
|
309
|
-
registryItems,
|
|
310
|
-
});
|
|
311
|
-
const managedTargets = uniqueSorted(registryItems.flatMap((item) => itemManagedTargets(catalog, item)));
|
|
312
|
-
const directlySelectedAddOnIds = new Set(selection.addOns.map((reference) => resolveCatalogSelection(catalog, reference).id));
|
|
313
|
-
const requiredAddOnIds = addOns
|
|
314
|
-
.filter((addOn) => !directlySelectedAddOnIds.has(addOn.id))
|
|
315
|
-
.map((addOn) => addOn.id);
|
|
316
|
-
return {
|
|
317
|
-
assets,
|
|
318
|
-
catalogManagedTargets: catalog.managedTargets,
|
|
319
|
-
catalogPackageRequirementTargets: catalogPackageRequirementTargets(catalog, catalogDependencies),
|
|
320
|
-
catalogPnpmPatches: catalogPnpmPatches(catalog),
|
|
321
|
-
catalogTypeScriptPathAliases: catalogTypeScriptPathAliases(catalogDependencies),
|
|
322
|
-
entryItems,
|
|
323
|
-
instructions: [
|
|
324
|
-
...new Set(selections
|
|
325
|
-
.map((selected) => catalog.items.get(selected.itemName)?.docs)
|
|
326
|
-
.filter((value) => Boolean(value))),
|
|
327
|
-
],
|
|
328
|
-
managedTargets,
|
|
329
|
-
packageRequirements,
|
|
330
|
-
pnpmPatches,
|
|
331
|
-
registryItems,
|
|
332
|
-
selection: {
|
|
333
|
-
...selection,
|
|
334
|
-
addOns: uniqueSorted([...selection.addOns, ...requiredAddOnIds]),
|
|
335
|
-
providers: { ...selection.providers },
|
|
336
|
-
},
|
|
337
|
-
selections,
|
|
338
|
-
typeScriptPathAliases: providerRequirements.typeScriptPathAliases,
|
|
339
|
-
variableTargets: uniqueSorted([
|
|
340
|
-
...catalogVariableTargets(catalog),
|
|
341
|
-
...catalog.selections.flatMap((selected) => selected.assets.map((asset) => resolveWorkspacePath(asset.target, `${selected.id} asset target`))),
|
|
342
|
-
]),
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
export function selectionFromPreset(catalog, reference) {
|
|
346
|
-
const preset = resolveCatalogSelection(catalog, reference);
|
|
347
|
-
if (preset.kind !== "preset" || !preset.selections?.providers) {
|
|
348
|
-
throw new CompositionValidationError("Invalid Preset selection.", [
|
|
349
|
-
`${reference} is not a complete Next Hydra preset`,
|
|
350
|
-
]);
|
|
351
|
-
}
|
|
352
|
-
const { addOns, providers } = preset.selections;
|
|
353
|
-
if (!(providers.auth && providers.cms && providers.commerce)) {
|
|
354
|
-
throw new CompositionValidationError("Preset is incomplete.", [
|
|
355
|
-
`${preset.id} must select auth, cms, and commerce providers`,
|
|
356
|
-
]);
|
|
357
|
-
}
|
|
358
|
-
return {
|
|
359
|
-
addOns,
|
|
360
|
-
providers: {
|
|
361
|
-
auth: providers.auth,
|
|
362
|
-
cms: providers.cms,
|
|
363
|
-
commerce: providers.commerce,
|
|
364
|
-
},
|
|
365
|
-
};
|
|
366
|
-
}
|