mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 15:20:13 +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
@@ -5,7 +5,9 @@
|
||||
|
||||
import { existsSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { installItem, listRegistryItems, loadAllItems, resolveItem } from "../registry/index.js";
|
||||
import { installItem, listRegistryItems, loadAllItems } from "../registry/index.js";
|
||||
import { resolveItemWithDependencies } from "../registry/resolver.js";
|
||||
import { gateRegistryItemsCompatibility } from "../registry/compatibility.js";
|
||||
|
||||
// Re-exported for the existing remote.test.ts regression guard. These paths
|
||||
// describe the repo layout under the default registry URL; updating them in
|
||||
@@ -38,10 +40,24 @@ export async function listRemoteTemplates(): Promise<RemoteTemplateInfo[]> {
|
||||
|
||||
/**
|
||||
* Download a template into destDir. Delegates to the registry installer.
|
||||
*
|
||||
* Resolves the template's transitive `registryDependencies` and installs them
|
||||
* before the template itself, so a template that depends on other registry
|
||||
* items gets a complete install rather than silently dropping its deps.
|
||||
*
|
||||
* Every resolved item is compatibility-gated up front (same gate as
|
||||
* `hyperframes add`), so an incompatible template — or any of its deps —
|
||||
* aborts before a single file is written.
|
||||
*/
|
||||
export async function fetchRemoteTemplate(templateId: string, destDir: string): Promise<void> {
|
||||
const item = await resolveItem(templateId);
|
||||
await installItem(item, { destDir });
|
||||
const items = await resolveItemWithDependencies(templateId);
|
||||
const warnings = gateRegistryItemsCompatibility(items);
|
||||
for (const warning of warnings) {
|
||||
process.stderr.write(`hyperframes:registry ${warning}\n`);
|
||||
}
|
||||
for (const item of items) {
|
||||
await installItem(item, { destDir });
|
||||
}
|
||||
|
||||
// Safety check — an item with no index.html isn't a valid example.
|
||||
if (!existsSync(join(destDir, "index.html"))) {
|
||||
|
||||
Reference in New Issue
Block a user