mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 07:09:59 +00:00
fix(cli): resolve and install transitive registry dependencies (#1396)
* 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>
This commit is contained in:
co-authored by
Claude Opus 4.8
Rakibul Islam
parent
583b47b039
commit
8eac7e1cda
@@ -1,6 +1,10 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { RegistryItem } from "@hyperframes/core";
|
||||
import { checkRegistryItemCompatibility } from "./compatibility.js";
|
||||
import {
|
||||
checkRegistryItemCompatibility,
|
||||
gateRegistryItemsCompatibility,
|
||||
RegistryCompatibilityError,
|
||||
} from "./compatibility.js";
|
||||
|
||||
const BASE_ITEM: RegistryItem = {
|
||||
name: "demo-block",
|
||||
@@ -55,3 +59,38 @@ describe("checkRegistryItemCompatibility", () => {
|
||||
expect(result.error).toContain('declares invalid minCliVersion "next"');
|
||||
});
|
||||
});
|
||||
|
||||
describe("gateRegistryItemsCompatibility", () => {
|
||||
const dep = (
|
||||
name: string,
|
||||
extra: { deprecated?: string; minCliVersion?: string } = {},
|
||||
): RegistryItem => ({
|
||||
...BASE_ITEM,
|
||||
name,
|
||||
...extra,
|
||||
});
|
||||
|
||||
it("returns no warnings when every item is compatible", () => {
|
||||
expect(gateRegistryItemsCompatibility([dep("a"), dep("b")], "0.6.79")).toEqual([]);
|
||||
});
|
||||
|
||||
it("accumulates deprecation warnings across all items", () => {
|
||||
const warnings = gateRegistryItemsCompatibility(
|
||||
[dep("a", { deprecated: "gone" }), dep("b"), dep("c", { deprecated: "also gone" })],
|
||||
"0.6.79",
|
||||
);
|
||||
expect(warnings).toEqual([
|
||||
'Registry item "a" is deprecated: gone',
|
||||
'Registry item "c" is deprecated: also gone',
|
||||
]);
|
||||
});
|
||||
|
||||
it("throws RegistryCompatibilityError on the first item requiring a newer CLI", () => {
|
||||
expect(() =>
|
||||
gateRegistryItemsCompatibility([dep("a"), dep("b", { minCliVersion: "999.0.0" })], "0.6.79"),
|
||||
).toThrow(RegistryCompatibilityError);
|
||||
expect(() =>
|
||||
gateRegistryItemsCompatibility([dep("a"), dep("b", { minCliVersion: "999.0.0" })], "0.6.79"),
|
||||
).toThrow(/requires hyperframes >= 999\.0\.0/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user