Skip to content

Commit 27f42f2

Browse files
committed
feat(cli): add canary publish routing
1 parent e40d393 commit 27f42f2

4 files changed

Lines changed: 55 additions & 2 deletions

File tree

‎docs/packages/cli.mdx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,11 +401,13 @@ This is suppressed in CI environments, non-TTY shells, and when `HYPERFRAMES_NO_
401401
```bash
402402
npx hyperframes publish [dir]
403403
npx hyperframes publish --yes
404+
npx hyperframes publish --canary
404405
```
405406

406407
| Flag | Description |
407408
|------|-------------|
408409
| `--yes` | Skip the confirmation prompt |
410+
| `--canary` | Route the publish request to canary heygen-server |
409411

410412
`publish` zips the current project, uploads it to the HyperFrames publish backend, and prints a stable `hyperframes.dev` URL for that stored project.
411413

‎packages/cli/src/commands/publish.ts‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { publishProjectArchive } from "../utils/publishProject.js";
1212

1313
export const examples: Example[] = [
1414
["Publish the current project with a public URL", "hyperframes publish"],
15+
["Publish against canary heygen-server", "hyperframes publish --canary"],
1516
["Publish a specific directory", "hyperframes publish ./my-video"],
1617
["Skip the consent prompt (scripts)", "hyperframes publish --yes"],
1718
];
@@ -29,6 +30,11 @@ export default defineCommand({
2930
description: "Skip the publish confirmation prompt",
3031
default: false,
3132
},
33+
canary: {
34+
type: "boolean",
35+
description: "Route the publish request to canary heygen-server",
36+
default: false,
37+
},
3238
},
3339
async run({ args }) {
3440
const rawArg = args.dir;
@@ -54,6 +60,11 @@ export default defineCommand({
5460
console.log(
5561
` ${c.dim("Anyone with the URL can open the published project and claim it after authenticating.")}`,
5662
);
63+
if (args.canary === true) {
64+
console.log(
65+
` ${c.dim("This run will route the publish request to canary heygen-server.")}`,
66+
);
67+
}
5768
console.log();
5869
const approved = await clack.confirm({ message: "Publish this project?" });
5970
if (clack.isCancel(approved) || approved !== true) {
@@ -69,14 +80,17 @@ export default defineCommand({
6980
publishSpinner.start("Uploading project...");
7081

7182
try {
72-
const published = await publishProjectArchive(dir);
83+
const published = await publishProjectArchive(dir, { canary: args.canary === true });
7384
const claimUrl = new URL(published.url);
7485
claimUrl.searchParams.set("claim_token", published.claimToken);
7586
publishSpinner.stop(c.success("Project published"));
7687

7788
console.log();
7889
console.log(` ${c.dim("Project")} ${c.accent(published.title)}`);
7990
console.log(` ${c.dim("Files")} ${String(published.fileCount)}`);
91+
if (args.canary === true) {
92+
console.log(` ${c.dim("Route")} ${c.accent("canary")}`);
93+
}
8094
console.log(` ${c.dim("Public")} ${c.accent(claimUrl.toString())}`);
8195
console.log();
8296
console.log(

‎packages/cli/src/utils/publishProject.test.ts‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,31 @@ describe("publishProjectArchive", () => {
7474
url: "https://hyperframes.dev/p/hfp_123",
7575
});
7676
expect(fetch).toHaveBeenCalledTimes(1);
77+
expect(fetch).toHaveBeenCalledWith(
78+
"https://api2.heygen.com/v1/hyperframes/projects/publish",
79+
expect.objectContaining({
80+
method: "POST",
81+
signal: expect.any(AbortSignal),
82+
}),
83+
);
84+
} finally {
85+
rmSync(dir, { recursive: true, force: true });
86+
}
87+
});
88+
89+
it("routes publish to canary when requested", async () => {
90+
const dir = makeProjectDir();
91+
try {
92+
writeFileSync(join(dir, "index.html"), "<html></html>", "utf-8");
93+
94+
await publishProjectArchive(dir, { canary: true });
95+
96+
expect(fetch).toHaveBeenCalledWith(
97+
"https://api2.heygen.com/v1/hyperframes/projects/publish",
98+
expect.objectContaining({
99+
headers: { heygen_route: "canary" },
100+
}),
101+
);
77102
} finally {
78103
rmSync(dir, { recursive: true, force: true });
79104
}

‎packages/cli/src/utils/publishProject.ts‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ export interface PublishedProjectResponse {
1818
claimToken: string;
1919
}
2020

21+
export interface PublishProjectOptions {
22+
canary?: boolean;
23+
}
24+
2125
function shouldIgnoreSegment(segment: string): boolean {
2226
return segment.startsWith(".") || IGNORED_DIRS.has(segment) || IGNORED_FILES.has(segment);
2327
}
@@ -65,18 +69,26 @@ export function getPublishApiBaseUrl(): string {
6569
).replace(/\/$/, "");
6670
}
6771

68-
export async function publishProjectArchive(projectDir: string): Promise<PublishedProjectResponse> {
72+
export async function publishProjectArchive(
73+
projectDir: string,
74+
options: PublishProjectOptions = {},
75+
): Promise<PublishedProjectResponse> {
6976
const title = basename(projectDir);
7077
const archive = createPublishArchive(projectDir);
7178
const archiveBytes = new Uint8Array(archive.buffer.byteLength);
7279
archiveBytes.set(archive.buffer);
7380
const body = new FormData();
7481
body.set("title", title);
7582
body.set("file", new File([archiveBytes], `${title}.zip`, { type: "application/zip" }));
83+
const headers: Record<string, string> = {};
84+
if (options.canary) {
85+
headers["heygen_route"] = "canary";
86+
}
7687

7788
const response = await fetch(`${getPublishApiBaseUrl()}/v1/hyperframes/projects/publish`, {
7889
method: "POST",
7990
body,
91+
headers,
8092
signal: AbortSignal.timeout(30_000),
8193
});
8294

0 commit comments

Comments
 (0)