mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-14 18:01:20 +08:00
8eac7e1cda
* fix(cli): resolve and install transitive registry dependencies `hyperframes add`, `hyperframes new` (fetchRemoteTemplate), and the studio "add block" path each resolved a single registry item and silently dropped any `registryDependencies` it declared. Add `resolveItemWithDependencies` (DFS topological sort, cycle detection, missing-dependency errors, and dedup of shared/diamond deps) and route all three install paths through it so dependencies are installed before the item that needs them. `resolveItem` becomes a thin guard that throws on dep-bearing items, so no future caller can silently reintroduce the drop. `runAdd` now returns the ordered `installed` list and compatibility-gates every dependency before any write. Reworks the stale PR #414 onto current main and addresses its review feedback: fetchRemoteTemplate installs deps, no out-of-scope files, dead null-checks dropped, diamond test added, and the deliberate serial-fetch tradeoff is noted. Co-authored-by: Rakibul Islam <40rakib70@gmail.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(cli): make getItem async so missing-dep surfaces as rejection Addresses review nit on #1396: getItem was typed Promise<RegistryItem> but threw synchronously on a missing dependency. Marking it async keeps the control flow consistent with the return type — the throw now becomes a rejection. The body has no await, so the item cache is still populated synchronously on first request and dedup is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): compatibility-gate transitive deps in all install paths Addresses Via's review on #1396: `assertCompatibleOrThrow` only ran inside `runAdd`, so `fetchRemoteTemplate` (hyperframes new) and the Studio "add block" action installed resolved items — now including transitive dependencies — with no minCliVersion enforcement or deprecation warnings. A pre-existing single-item asymmetry that this PR's dep loops amplify across N items. - Add shared `gateRegistryItemsCompatibility` + `RegistryCompatibilityError` to compatibility.ts; all three install paths now gate the full resolved set before any write. `runAdd` keeps its AddError mapping by wrapping the shared gate. - Surface deprecation warnings from the template/studio paths to stderr. - Extract the studio viewport rewrite into `rewriteWrittenToHostViewport` (also drops redundant dynamic node:fs imports) and document that it intentionally rewrites dep-shipped .html too (Via item 3). - Unit-test the shared gate directly (no fetch/cache flakiness): compatible set, accumulated deprecation warnings, and throw-on-incompatible. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Rakibul Islam <40rakib70@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
import type { RegistryItem } from "@hyperframes/core";
|
|
import { compareVersions } from "compare-versions";
|
|
import { VERSION } from "../version.js";
|
|
|
|
export interface RegistryCompatibilityResult {
|
|
warnings: string[];
|
|
error?: string;
|
|
}
|
|
|
|
const DEV_VERSION = "0.0.0-dev";
|
|
|
|
export function checkRegistryItemCompatibility(
|
|
item: RegistryItem,
|
|
currentCliVersion = VERSION,
|
|
): RegistryCompatibilityResult {
|
|
const warnings: string[] = [];
|
|
if (item.deprecated) {
|
|
warnings.push(`Registry item "${item.name}" is deprecated: ${item.deprecated}`);
|
|
}
|
|
|
|
const minCliVersion = item.minCliVersion?.trim();
|
|
if (!minCliVersion || currentCliVersion === DEV_VERSION) {
|
|
return { warnings };
|
|
}
|
|
|
|
try {
|
|
if (compareVersions(currentCliVersion, minCliVersion) >= 0) {
|
|
return { warnings };
|
|
}
|
|
} catch {
|
|
return {
|
|
warnings,
|
|
error: `Registry item "${item.name}" declares invalid minCliVersion "${minCliVersion}".`,
|
|
};
|
|
}
|
|
|
|
return {
|
|
warnings,
|
|
error:
|
|
`Registry item "${item.name}" requires hyperframes >= ${minCliVersion} ` +
|
|
`(current: ${currentCliVersion}). Run \`npx hyperframes@latest add ${item.name}\` ` +
|
|
"or upgrade your installed hyperframes CLI.",
|
|
};
|
|
}
|
|
|
|
/** Thrown by `gateRegistryItemsCompatibility` when an item requires a newer CLI. */
|
|
export class RegistryCompatibilityError extends Error {
|
|
constructor(message: string) {
|
|
super(message);
|
|
this.name = "RegistryCompatibilityError";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compatibility-gate a set of resolved items (e.g. an item plus its transitive
|
|
* `registryDependencies`) before any of them are installed. Throws a
|
|
* `RegistryCompatibilityError` on the first item that requires a newer CLI, so
|
|
* a partial install never happens; returns the accumulated (non-fatal)
|
|
* deprecation warnings from every item.
|
|
*
|
|
* Every install path — `add`, template fetch, and the Studio "add block"
|
|
* action — funnels through this so a dependency that ships `minCliVersion` is
|
|
* rejected uniformly, not just by `hyperframes add`.
|
|
*/
|
|
export function gateRegistryItemsCompatibility(
|
|
items: RegistryItem[],
|
|
currentCliVersion = VERSION,
|
|
): string[] {
|
|
const warnings: string[] = [];
|
|
for (const item of items) {
|
|
const result = checkRegistryItemCompatibility(item, currentCliVersion);
|
|
if (result.error) {
|
|
throw new RegistryCompatibilityError(result.error);
|
|
}
|
|
warnings.push(...result.warnings);
|
|
}
|
|
return warnings;
|
|
}
|