mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 00:56:23 +00:00
* 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>
97 lines
3.1 KiB
TypeScript
97 lines
3.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { RegistryItem } from "@hyperframes/core";
|
|
import {
|
|
checkRegistryItemCompatibility,
|
|
gateRegistryItemsCompatibility,
|
|
RegistryCompatibilityError,
|
|
} from "./compatibility.js";
|
|
|
|
const BASE_ITEM: RegistryItem = {
|
|
name: "demo-block",
|
|
type: "hyperframes:block",
|
|
title: "Demo Block",
|
|
description: "Block for tests",
|
|
dimensions: { width: 1080, height: 1350 },
|
|
duration: 6,
|
|
files: [
|
|
{
|
|
path: "demo-block.html",
|
|
target: "compositions/demo-block.html",
|
|
type: "hyperframes:composition",
|
|
},
|
|
],
|
|
};
|
|
|
|
describe("checkRegistryItemCompatibility", () => {
|
|
it("returns no warning or error for compatible items", () => {
|
|
expect(checkRegistryItemCompatibility(BASE_ITEM, "0.6.79")).toEqual({ warnings: [] });
|
|
});
|
|
|
|
it("returns a warning for deprecated items", () => {
|
|
const result = checkRegistryItemCompatibility(
|
|
{ ...BASE_ITEM, deprecated: "Use `demo-block-v2` instead." },
|
|
"0.6.79",
|
|
);
|
|
expect(result).toEqual({
|
|
warnings: ['Registry item "demo-block" is deprecated: Use `demo-block-v2` instead.'],
|
|
});
|
|
});
|
|
|
|
it("returns an error when the current CLI is below minCliVersion", () => {
|
|
const result = checkRegistryItemCompatibility(
|
|
{ ...BASE_ITEM, minCliVersion: "0.6.80" },
|
|
"0.6.79",
|
|
);
|
|
expect(result.error).toContain('Registry item "demo-block" requires hyperframes >= 0.6.80');
|
|
});
|
|
|
|
it("allows source/dev CLI builds to install future-gated registry items", () => {
|
|
expect(
|
|
checkRegistryItemCompatibility({ ...BASE_ITEM, minCliVersion: "999.0.0" }, "0.0.0-dev"),
|
|
).toEqual({ warnings: [] });
|
|
});
|
|
|
|
it("returns a clear error for malformed minCliVersion metadata", () => {
|
|
const result = checkRegistryItemCompatibility(
|
|
{ ...BASE_ITEM, minCliVersion: "next" },
|
|
"0.6.79",
|
|
);
|
|
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/);
|
|
});
|
|
});
|