Skip to content

Commit 049cff6

Browse files
committed
feat(sdk): scaffold @hyperframes/sdk — engine layer (model, RFC 6902 patches, mutate, apply-patches)
1 parent 0c5614f commit 049cff6

15 files changed

Lines changed: 1692 additions & 9 deletions

.fallowrc.jsonc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"packages/**/__goldens__/**",
4444
"registry/**",
4545
"examples/**",
46+
"packages/sdk/examples/**",
4647
".github/workflows/fixtures/**",
4748
// Auto-generated TS client for the HeyGen cloud API. Regenerated by
4849
// experiment-framework/scripts/generate_hyperframes_cli_client.py via

bun.lock

Lines changed: 24 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/sdk/package.json

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{
2+
"name": "@hyperframes/sdk",
3+
"version": "0.6.86",
4+
"description": "Headless, framework-neutral HyperFrames composition editing engine",
5+
"repository": {
6+
"type": "git",
7+
"url": "https://github.com/heygen-com/hyperframes",
8+
"directory": "packages/sdk"
9+
},
10+
"files": [
11+
"dist",
12+
"README.md"
13+
],
14+
"type": "module",
15+
"sideEffects": false,
16+
"exports": {
17+
".": {
18+
"import": "./src/index.ts",
19+
"types": "./src/index.ts"
20+
},
21+
"./adapters/memory": {
22+
"import": "./src/adapters/memory.ts",
23+
"types": "./src/adapters/memory.ts"
24+
},
25+
"./adapters/fs": {
26+
"import": "./src/adapters/fs.ts",
27+
"types": "./src/adapters/fs.ts"
28+
},
29+
"./adapters/headless": {
30+
"import": "./src/adapters/headless.ts",
31+
"types": "./src/adapters/headless.ts"
32+
}
33+
},
34+
"publishConfig": {
35+
"access": "public",
36+
"exports": {
37+
".": {
38+
"import": "./dist/index.js",
39+
"types": "./dist/index.d.ts"
40+
},
41+
"./adapters/memory": {
42+
"import": "./dist/adapters/memory.js",
43+
"types": "./dist/adapters/memory.d.ts"
44+
},
45+
"./adapters/fs": {
46+
"import": "./dist/adapters/fs.js",
47+
"types": "./dist/adapters/fs.d.ts"
48+
},
49+
"./adapters/headless": {
50+
"import": "./dist/adapters/headless.js",
51+
"types": "./dist/adapters/headless.d.ts"
52+
}
53+
},
54+
"main": "./dist/index.js",
55+
"types": "./dist/index.d.ts"
56+
},
57+
"scripts": {
58+
"build": "tsc",
59+
"test": "vitest run",
60+
"test:watch": "vitest",
61+
"typecheck": "tsc --noEmit",
62+
"typecheck:examples": "tsc --noEmit -p tsconfig.check.json"
63+
},
64+
"dependencies": {
65+
"@hyperframes/core": "workspace:*",
66+
"linkedom": "^0.18.12"
67+
},
68+
"devDependencies": {
69+
"@types/node": "^25.0.10",
70+
"typescript": "^5.0.0",
71+
"vitest": "^3.2.4"
72+
}
73+
}

packages/sdk/src/adapters/types.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import type { PersistErrorEvent } from "../types.js";
2+
3+
// ─── PersistAdapter ───────────────────────────────────────────────────────────
4+
5+
export interface PersistVersionEntry {
6+
/** Opaque key identifying this version (adapter-defined format) */
7+
key: string;
8+
content: string;
9+
timestamp?: number;
10+
}
11+
12+
/**
13+
* Injectable storage adapter — decouples the SDK from the underlying persistence mechanism.
14+
* Implementations: memory (tests/demos), fs (local dev), S3 (cloud), HTTP (Pacific).
15+
*
16+
* Contract:
17+
* - read() returns undefined for a path never written
18+
* - write() is idempotent (second write overwrites)
19+
* - flush() resolves when any queued writes are committed
20+
* - listVersions() returns entries newest-first
21+
* - loadFrom() returns content for the given version key (undefined if not found)
22+
* - on('persist:error') fires when a write fails; the error must not propagate as a thrown exception
23+
*/
24+
export interface PersistAdapter {
25+
read(path: string): Promise<string | undefined>;
26+
write(path: string, content: string): Promise<void>;
27+
/** Force all pending writes to commit before returning */
28+
flush(): Promise<void>;
29+
listVersions(path: string): Promise<PersistVersionEntry[]>;
30+
loadFrom(path: string, versionKey: string): Promise<string | undefined>;
31+
on(event: "persist:error", handler: (event: PersistErrorEvent) => void): () => void;
32+
}
33+
34+
// ─── PreviewAdapter ───────────────────────────────────────────────────────────
35+
36+
export interface ElementAtPointResult {
37+
id: string;
38+
tag: string;
39+
}
40+
41+
export interface DraftProps {
42+
dx?: number;
43+
dy?: number;
44+
width?: number;
45+
height?: number;
46+
}
47+
48+
/**
49+
* Injectable preview adapter — decouples the SDK from the host preview surface.
50+
* The null/headless adapter stubs all methods (no browser needed).
51+
*
52+
* The SDK is NOT in the 60fps draft loop — consumers call applyDraft() directly on
53+
* the preview at 60fps; commitPreview() fires once on pointer-up to derive and
54+
* dispatch the resulting op.
55+
*/
56+
export interface PreviewAdapter {
57+
/** Sync hit-test at composition coordinates. Requires same-origin iframe. */
58+
elementAtPoint(x: number, y: number, opts?: { atTime?: number }): ElementAtPointResult | null;
59+
60+
/** Apply draft CSS markers to the preview element (60fps, SDK not involved) */
61+
applyDraft(id: string, props: DraftProps): void;
62+
63+
/** Derive op from draft markers, dispatch it, emit patch event, clear markers */
64+
commitPreview(): void;
65+
66+
/** Revert draft markers without committing. Model never changed. */
67+
cancelPreview(): void;
68+
69+
/** Set preview selection; fires selectionchange on the session */
70+
select(ids: string[], opts?: { additive?: boolean }): void;
71+
72+
on(event: "selection", handler: (ids: string[]) => void): () => void;
73+
}

0 commit comments

Comments
 (0)