mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-07 18:26:17 +00:00
* feat(sdk): can() returns CanResult; T4 dispatch-boundary tests
* fix(sdk): 8 code-review correctness fixes
- setGsapScript: remove element when newScript="" (fixes undo/redo duplicate-script bug)
- parseDeclarations: track quotes so ; inside CSS values (data URIs) doesn't split
- handleRemoveGsapKeyframe: guard against duplicate-percentage ambiguity (return EMPTY)
- resolveKeyframe: return kfs so callers can check uniqueness
- handleSetClassStyle: emit op:"add" (not "replace") when no prior <style> element
- FsAdapter listVersions: Number(f.split("_")[0]) — was NaN due to underscore in key
- FsAdapter doWrite: split try/catch so appendVersion failure doesn't fire error handlers
- FileAdapter playground: add content:"" field to satisfy PersistVersionEntry contract
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(sdk): export CanResult from package root so callers can switch on result.code
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
60 lines
2.1 KiB
TypeScript
60 lines
2.1 KiB
TypeScript
import type { PersistAdapter, PersistVersionEntry } from "@hyperframes/sdk/adapters/types";
|
|
import type { PersistErrorEvent } from "@hyperframes/sdk";
|
|
|
|
const API = "/api/composition";
|
|
|
|
class FileAdapter implements PersistAdapter {
|
|
private errorHandlers = new Set<(e: PersistErrorEvent) => void>();
|
|
|
|
async read(_path: string): Promise<string | undefined> {
|
|
const res = await fetch(API);
|
|
if (res.status === 404) return undefined;
|
|
if (!res.ok) throw new Error(`read failed: ${res.status}`);
|
|
return res.text();
|
|
}
|
|
|
|
async write(_path: string, content: string): Promise<void> {
|
|
try {
|
|
const res = await fetch(API, {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
body: content,
|
|
});
|
|
if (!res.ok) throw new Error(`write failed: ${res.status}`);
|
|
} catch (err) {
|
|
for (const h of this.errorHandlers) h({ error: { message: String(err), cause: err } });
|
|
}
|
|
}
|
|
|
|
async flush(): Promise<void> {}
|
|
|
|
async listVersions(_path: string): Promise<PersistVersionEntry[]> {
|
|
const res = await fetch("/api/composition/versions");
|
|
if (!res.ok) return [];
|
|
const rows = (await res.json()) as Array<{ key: string; timestamp?: number }>;
|
|
return rows.map((r) => ({ key: r.key, content: "", timestamp: r.timestamp }));
|
|
}
|
|
|
|
async loadFrom(_path: string, versionKey: string): Promise<string | undefined> {
|
|
const res = await fetch(`/api/composition?version=${encodeURIComponent(versionKey)}`);
|
|
if (res.status === 404) return undefined;
|
|
if (!res.ok) throw new Error(`loadFrom failed: ${res.status}`);
|
|
return res.text();
|
|
}
|
|
|
|
on(event: "persist:error", handler: (e: PersistErrorEvent) => void): () => void {
|
|
if (event !== "persist:error") return () => {};
|
|
this.errorHandlers.add(handler);
|
|
return () => this.errorHandlers.delete(handler);
|
|
}
|
|
}
|
|
|
|
export async function createFileAdapter(): Promise<{
|
|
adapter: PersistAdapter;
|
|
initialHtml: string | undefined;
|
|
}> {
|
|
const adapter = new FileAdapter();
|
|
const initialHtml = await adapter.read("composition.html");
|
|
return { adapter, initialHtml };
|
|
}
|