mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 14:50:02 +00:00
## What
Foundations of the `@hyperframes/core/figma` module — the pure, transport-agnostic layer every later phase builds on:
- **`types.ts`** — `FigmaRef`, `FigmaProvenance`, `FigmaManifestRecord`, and the Motion model (`MotionDoc`/`MotionTrack`/`TimelineSpec`/`GsapTween`) shared across the stack.
- **`parseFigmaRef`** — normalizes any user input (full `/design|/file|/proto` URLs with `?node-id=1-2`, `fileKey:nodeId` shorthand, bare `fileKey`) into `{ fileKey, nodeId }`, including the URL-dash → API-colon node-id conversion.
- **`freeze.ts`** — `freezeBytes`/`freezeUrl`/`freezeLocalFile` with a 256 MB cap; every Figma asset is frozen to a local file before it can reach a composition (determinism: no render-time network).
- **`manifest.ts`** — the `.media/manifest.jsonl` ledger (same layout `media-use` writes, so a project has one shared media inventory without either skill depending on the other): append/read/find-by-node/next-id, with a pure type-guard (`isFigmaManifestRecord`) instead of `as`-casts.
- **`assetSnippet.ts`** — manifest record → composition `<img>` snippet with escaped attrs + `data-figma-id`.
- **publishConfig fix** — `./figma` added to `packages/core` `publishConfig.exports` (the packed-manifest CI gate requires every source export to have a dist mapping).
## Why
Design spec: `docs/superpowers/specs/2026-06-30-figma-asset-integration-design.md`. These functions are deliberately transport-agnostic — when the project reversed from MCP-first to a REST/MCP split (spec §2), nothing in this layer changed. That was the point.
## Tests
Unit tests per module (URL variants, freeze cap edges, manifest round-trip/malformed-line tolerance, snippet escaping). All colocated `*.test.ts`, vitest, no network.
---
Stack (1/6): this PR → #1869 → #1870 → #1871 → #1872 → #1873
🤖 Generated with [Claude Code](https://claude.com/claude-code)
29 lines
994 B
TypeScript
29 lines
994 B
TypeScript
// @vitest-environment node
|
|
import { describe, expect, it } from "vitest";
|
|
import { buildAssetSnippet } from "./assetSnippet";
|
|
import type { FigmaManifestRecord } from "./types";
|
|
|
|
const record: FigmaManifestRecord = {
|
|
id: "image_001",
|
|
type: "image",
|
|
path: ".media/images/image_001.png",
|
|
source: "figma",
|
|
description: 'Hero "banner"',
|
|
width: 240,
|
|
height: 57,
|
|
provenance: { source: "figma", fileKey: "FK", nodeId: "92:573", format: "png" },
|
|
};
|
|
|
|
describe("buildAssetSnippet", () => {
|
|
it("emits an img tag with src, dims, escaped alt, and data-figma-id", () => {
|
|
const { path, html } = buildAssetSnippet(record);
|
|
expect(path).toBe(".media/images/image_001.png");
|
|
expect(html).toContain('src=".media/images/image_001.png"');
|
|
expect(html).toContain('width="240"');
|
|
expect(html).toContain('height="57"');
|
|
expect(html).toContain('data-figma-id="92:573"');
|
|
expect(html).toContain(""banner"");
|
|
expect(html).not.toContain('"banner"');
|
|
});
|
|
});
|