Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .fallowrc.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"packages/**/__goldens__/**",
"registry/**",
"examples/**",
"packages/sdk/examples/**",
".github/workflows/fixtures/**",
// Auto-generated TS client for the HeyGen cloud API. Regenerated by
// experiment-framework/scripts/generate_hyperframes_cli_client.py via
Expand Down
35 changes: 25 additions & 10 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"type": "module",
"scripts": {
"dev": "bun run studio",
"build": "bun run --filter @hyperframes/core build && bun run --filter '@hyperframes/{core,engine,producer,player,studio,shader-transitions,aws-lambda,gcp-cloud-run}' build && bun run --filter @hyperframes/cli build",
"build": "bun run --filter @hyperframes/core build && bun run --filter '@hyperframes/{core,engine,producer,player,studio,shader-transitions,aws-lambda,gcp-cloud-run,sdk}' build && bun run --filter @hyperframes/cli build",
"build:producer": "bun run --filter @hyperframes/producer build",
"studio": "bun run --filter @hyperframes/studio dev",
"build:hyperframes-runtime": "bun run --filter @hyperframes/core build:hyperframes-runtime",
Expand Down
73 changes: 73 additions & 0 deletions packages/sdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"name": "@hyperframes/sdk",
"version": "0.6.86",
"description": "Headless, framework-neutral HyperFrames composition editing engine",
"repository": {
"type": "git",
"url": "https://github.com/heygen-com/hyperframes",
"directory": "packages/sdk"
},
"files": [
"dist",
"README.md"
],
"type": "module",
"sideEffects": false,
"exports": {
".": {
"import": "./src/index.ts",
"types": "./src/index.ts"
},
"./adapters/memory": {
"import": "./src/adapters/memory.ts",
"types": "./src/adapters/memory.ts"
},
"./adapters/fs": {
"import": "./src/adapters/fs.ts",
"types": "./src/adapters/fs.ts"
},
"./adapters/headless": {
"import": "./src/adapters/headless.ts",
"types": "./src/adapters/headless.ts"
}
},
"publishConfig": {
"access": "public",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./adapters/memory": {
"import": "./dist/adapters/memory.js",
"types": "./dist/adapters/memory.d.ts"
},
"./adapters/fs": {
"import": "./dist/adapters/fs.js",
"types": "./dist/adapters/fs.d.ts"
},
"./adapters/headless": {
"import": "./dist/adapters/headless.js",
"types": "./dist/adapters/headless.d.ts"
}
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"scripts": {
"build": "tsc",
"test": "vitest run",
"test:watch": "vitest",
"typecheck": "tsc --noEmit",
"typecheck:examples": "tsc --noEmit -p tsconfig.check.json"
},
"dependencies": {
"@hyperframes/core": "workspace:*",
"linkedom": "^0.18.12"
},
"devDependencies": {
"@types/node": "^25.0.10",
"typescript": "^5.0.0",
"vitest": "^3.2.4"
}
}
75 changes: 75 additions & 0 deletions packages/sdk/src/adapters/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Consumed by session.ts + adapter implementations in the next stacked PR (#1325).
// fallow-ignore-file unused-file
import type { PersistErrorEvent } from "../types.js";

// ─── PersistAdapter ───────────────────────────────────────────────────────────

export interface PersistVersionEntry {
/** Opaque key identifying this version (adapter-defined format) */
key: string;
content: string;
timestamp?: number;
}

/**
* Injectable storage adapter — decouples the SDK from the underlying persistence mechanism.
* Implementations: memory (tests/demos), fs (local dev), S3 (cloud), HTTP (Pacific).
*
* Contract:
* - read() returns undefined for a path never written
* - write() is idempotent (second write overwrites)
* - flush() resolves when any queued writes are committed
* - listVersions() returns entries newest-first
* - loadFrom() returns content for the given version key (undefined if not found)
* - on('persist:error') fires when a write fails; the error must not propagate as a thrown exception
*/
export interface PersistAdapter {
read(path: string): Promise<string | undefined>;
write(path: string, content: string): Promise<void>;
/** Force all pending writes to commit before returning */
flush(): Promise<void>;
listVersions(path: string): Promise<PersistVersionEntry[]>;
loadFrom(path: string, versionKey: string): Promise<string | undefined>;
on(event: "persist:error", handler: (event: PersistErrorEvent) => void): () => void;
}

// ─── PreviewAdapter ───────────────────────────────────────────────────────────

export interface ElementAtPointResult {
id: string;
tag: string;
}

export interface DraftProps {
dx?: number;
dy?: number;
width?: number;
height?: number;
}

/**
* Injectable preview adapter — decouples the SDK from the host preview surface.
* The null/headless adapter stubs all methods (no browser needed).
*
* The SDK is NOT in the 60fps draft loop — consumers call applyDraft() directly on
* the preview at 60fps; commitPreview() fires once on pointer-up to derive and
* dispatch the resulting op.
*/
export interface PreviewAdapter {
/** Sync hit-test at composition coordinates. Requires same-origin iframe. */
elementAtPoint(x: number, y: number, opts?: { atTime?: number }): ElementAtPointResult | null;

/** Apply draft CSS markers to the preview element (60fps, SDK not involved) */
applyDraft(id: string, props: DraftProps): void;

/** Derive op from draft markers, dispatch it, emit patch event, clear markers */
commitPreview(): void;

/** Revert draft markers without committing. Model never changed. */
cancelPreview(): void;

/** Set preview selection; fires selectionchange on the session */
select(ids: string[], opts?: { additive?: boolean }): void;

on(event: "selection", handler: (ids: string[]) => void): () => void;
}
Loading
Loading