devrel-toolkit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +63 -0
  2. package/dist/__tests__/cli.test.d.ts +2 -0
  3. package/dist/__tests__/cli.test.d.ts.map +1 -0
  4. package/dist/__tests__/cli.test.js +47 -0
  5. package/dist/__tests__/cli.test.js.map +1 -0
  6. package/dist/__tests__/components.test.d.ts +2 -0
  7. package/dist/__tests__/components.test.d.ts.map +1 -0
  8. package/dist/__tests__/components.test.js +73 -0
  9. package/dist/__tests__/components.test.js.map +1 -0
  10. package/dist/__tests__/d-id-client.test.d.ts +2 -0
  11. package/dist/__tests__/d-id-client.test.d.ts.map +1 -0
  12. package/dist/__tests__/d-id-client.test.js +116 -0
  13. package/dist/__tests__/d-id-client.test.js.map +1 -0
  14. package/dist/__tests__/render-integration.test.d.ts +2 -0
  15. package/dist/__tests__/render-integration.test.d.ts.map +1 -0
  16. package/dist/__tests__/render-integration.test.js +26 -0
  17. package/dist/__tests__/render-integration.test.js.map +1 -0
  18. package/dist/__tests__/schema.test.d.ts +2 -0
  19. package/dist/__tests__/schema.test.d.ts.map +1 -0
  20. package/dist/__tests__/schema.test.js +173 -0
  21. package/dist/__tests__/schema.test.js.map +1 -0
  22. package/dist/__tests__/timing.test.d.ts +2 -0
  23. package/dist/__tests__/timing.test.d.ts.map +1 -0
  24. package/dist/__tests__/timing.test.js +69 -0
  25. package/dist/__tests__/timing.test.js.map +1 -0
  26. package/dist/cli/d-id.d.ts +3 -0
  27. package/dist/cli/d-id.d.ts.map +1 -0
  28. package/dist/cli/d-id.js +53 -0
  29. package/dist/cli/d-id.js.map +1 -0
  30. package/dist/cli/doctor.d.ts +3 -0
  31. package/dist/cli/doctor.d.ts.map +1 -0
  32. package/dist/cli/doctor.js +134 -0
  33. package/dist/cli/doctor.js.map +1 -0
  34. package/dist/cli/index.d.ts +3 -0
  35. package/dist/cli/index.d.ts.map +1 -0
  36. package/dist/cli/index.js +21 -0
  37. package/dist/cli/index.js.map +1 -0
  38. package/dist/cli/install-skill.d.ts +3 -0
  39. package/dist/cli/install-skill.d.ts.map +1 -0
  40. package/dist/cli/install-skill.js +50 -0
  41. package/dist/cli/install-skill.js.map +1 -0
  42. package/dist/cli/render.d.ts +4 -0
  43. package/dist/cli/render.d.ts.map +1 -0
  44. package/dist/cli/render.js +134 -0
  45. package/dist/cli/render.js.map +1 -0
  46. package/dist/cli/setup.d.ts +3 -0
  47. package/dist/cli/setup.d.ts.map +1 -0
  48. package/dist/cli/setup.js +60 -0
  49. package/dist/cli/setup.js.map +1 -0
  50. package/dist/compositor/DemoVideo.d.ts +4 -0
  51. package/dist/compositor/DemoVideo.d.ts.map +1 -0
  52. package/dist/compositor/DemoVideo.js +64 -0
  53. package/dist/compositor/DemoVideo.js.map +1 -0
  54. package/dist/compositor/Root.d.ts +3 -0
  55. package/dist/compositor/Root.d.ts.map +1 -0
  56. package/dist/compositor/Root.js +30 -0
  57. package/dist/compositor/Root.js.map +1 -0
  58. package/dist/compositor/components/AvatarPiP.d.ts +10 -0
  59. package/dist/compositor/components/AvatarPiP.d.ts.map +1 -0
  60. package/dist/compositor/components/AvatarPiP.js +52 -0
  61. package/dist/compositor/components/AvatarPiP.js.map +1 -0
  62. package/dist/compositor/components/BrowserFrame.d.ts +18 -0
  63. package/dist/compositor/components/BrowserFrame.d.ts.map +1 -0
  64. package/dist/compositor/components/BrowserFrame.js +97 -0
  65. package/dist/compositor/components/BrowserFrame.js.map +1 -0
  66. package/dist/compositor/components/Cursor.d.ts +13 -0
  67. package/dist/compositor/components/Cursor.d.ts.map +1 -0
  68. package/dist/compositor/components/Cursor.js +65 -0
  69. package/dist/compositor/components/Cursor.js.map +1 -0
  70. package/dist/compositor/components/Highlight.d.ts +9 -0
  71. package/dist/compositor/components/Highlight.d.ts.map +1 -0
  72. package/dist/compositor/components/Highlight.js +83 -0
  73. package/dist/compositor/components/Highlight.js.map +1 -0
  74. package/dist/compositor/components/IntroOutro.d.ts +10 -0
  75. package/dist/compositor/components/IntroOutro.d.ts.map +1 -0
  76. package/dist/compositor/components/IntroOutro.js +48 -0
  77. package/dist/compositor/components/IntroOutro.js.map +1 -0
  78. package/dist/compositor/components/SceneTransition.d.ts +9 -0
  79. package/dist/compositor/components/SceneTransition.d.ts.map +1 -0
  80. package/dist/compositor/components/SceneTransition.js +35 -0
  81. package/dist/compositor/components/SceneTransition.js.map +1 -0
  82. package/dist/compositor/components/Subtitles.d.ts +9 -0
  83. package/dist/compositor/components/Subtitles.d.ts.map +1 -0
  84. package/dist/compositor/components/Subtitles.js +45 -0
  85. package/dist/compositor/components/Subtitles.js.map +1 -0
  86. package/dist/compositor/index.d.ts +2 -0
  87. package/dist/compositor/index.d.ts.map +1 -0
  88. package/dist/compositor/index.js +4 -0
  89. package/dist/compositor/index.js.map +1 -0
  90. package/dist/compositor/timing.d.ts +16 -0
  91. package/dist/compositor/timing.d.ts.map +1 -0
  92. package/dist/compositor/timing.js +39 -0
  93. package/dist/compositor/timing.js.map +1 -0
  94. package/dist/d-id/client.d.ts +50 -0
  95. package/dist/d-id/client.d.ts.map +1 -0
  96. package/dist/d-id/client.js +114 -0
  97. package/dist/d-id/client.js.map +1 -0
  98. package/dist/d-id/generate.d.ts +9 -0
  99. package/dist/d-id/generate.d.ts.map +1 -0
  100. package/dist/d-id/generate.js +81 -0
  101. package/dist/d-id/generate.js.map +1 -0
  102. package/dist/utils/config.d.ts +14 -0
  103. package/dist/utils/config.d.ts.map +1 -0
  104. package/dist/utils/config.js +34 -0
  105. package/dist/utils/config.js.map +1 -0
  106. package/dist/utils/schema.d.ts +273 -0
  107. package/dist/utils/schema.d.ts.map +1 -0
  108. package/dist/utils/schema.js +98 -0
  109. package/dist/utils/schema.js.map +1 -0
  110. package/package.json +54 -0
  111. package/remotion.config.ts +4 -0
  112. package/skills/make-demo/SKILL.md +282 -0
  113. package/skills/make-demo/references/demo-script-schema.md +47 -0
  114. package/skills/make-demo/references/render-props-schema.md +49 -0
  115. package/skills/make-demo/references/toolkit-commands.md +23 -0
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # devrel-toolkit
2
+
3
+ Toolkit for automated product demo video creation with AI avatars and Remotion compositing. Designed to be used with the `/make-demo` Claude Code skill.
4
+
5
+ ## Prerequisites
6
+
7
+ - **Node.js 18+**
8
+ - **FFmpeg** — `brew install ffmpeg` (macOS) or `apt install ffmpeg` (Linux)
9
+ - **Browser-Use CLI** — `curl -fsSL https://browser-use.com/cli/install.sh | bash`
10
+ - **D-ID API key** — Sign up at [d-id.com](https://d-id.com), add to `.env.local` as `DID_API_KEY`
11
+
12
+ ## Quick Start
13
+
14
+ ```bash
15
+ # Check dependencies
16
+ npx devrel-toolkit doctor
17
+
18
+ # Install the Claude Code skill
19
+ npx skills add <repo-url> --skill make-demo
20
+
21
+ # Then in Claude Code:
22
+ /make-demo "show the signup flow and dashboard"
23
+ ```
24
+
25
+ ## Install the Skill
26
+
27
+ ```bash
28
+ # Option A: Use the built-in command
29
+ npx devrel-toolkit install-skill
30
+
31
+ # Option B: Install from repo
32
+ npx skills add <repo-url> --skill make-demo
33
+ ```
34
+
35
+ ## CLI Commands
36
+
37
+ | Command | Description |
38
+ |---------|-------------|
39
+ | `npx devrel-toolkit doctor` | Check all dependencies |
40
+ | `npx devrel-toolkit d-id avatars` | List available D-ID avatars |
41
+ | `npx devrel-toolkit d-id generate --script <path> --output <dir>` | Generate avatar clips |
42
+ | `npx devrel-toolkit render --props <path> --output <path>` | Full quality video render |
43
+ | `npx devrel-toolkit preview --props <path> --output <path>` | Quick low-res preview |
44
+ | `npx devrel-toolkit render --props <path> --transparent` | ProRes 4444 with transparency |
45
+ | `npx devrel-toolkit setup` | Install missing dependencies |
46
+ | `npx devrel-toolkit install-skill` | Install make-demo skill to ~/.claude/skills/ |
47
+
48
+ ## Why a Custom Remotion Setup
49
+
50
+ This toolkit uses a custom Remotion configuration rather than `npx create-video` because:
51
+
52
+ - The compositor needs programmatic rendering via `bundle()` + `renderMedia()` (not Remotion Studio)
53
+ - Components are designed for the specific demo video pipeline (BrowserFrame, AvatarPiP, Highlights)
54
+ - The CLI wraps Remotion rendering with pre/post-processing steps (D-ID avatar generation, file validation)
55
+ - No Tailwind or extra styling dependencies needed — all styles are inline for Remotion compatibility
56
+
57
+ ## Environment Variables
58
+
59
+ ```bash
60
+ # In your project's .env.local
61
+ DID_API_KEY=... # D-ID API key for avatar video generation
62
+ BROWSER_USE_API_KEY=... # (Optional) Browser-Use Cloud API key
63
+ ```
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cli.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,47 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { execSync } from "node:child_process";
3
+ const CLI = "node dist/cli/index.js";
4
+ function run(args) {
5
+ try {
6
+ const stdout = execSync(`${CLI} ${args}`, {
7
+ encoding: "utf-8",
8
+ cwd: "/Users/ashnouruzi/siwx/devrel/devrel-toolkit",
9
+ stdio: ["pipe", "pipe", "pipe"],
10
+ });
11
+ return { stdout, exitCode: 0 };
12
+ }
13
+ catch (err) {
14
+ const e = err;
15
+ return { stdout: e.stdout ?? "", exitCode: e.status ?? 1 };
16
+ }
17
+ }
18
+ describe("CLI", () => {
19
+ it("shows help with --help", () => {
20
+ const { stdout, exitCode } = run("--help");
21
+ expect(exitCode).toBe(0);
22
+ expect(stdout).toContain("devrel-toolkit");
23
+ expect(stdout).toContain("d-id");
24
+ expect(stdout).toContain("render");
25
+ expect(stdout).toContain("preview");
26
+ expect(stdout).toContain("doctor");
27
+ expect(stdout).toContain("setup");
28
+ });
29
+ it("shows version with --version", () => {
30
+ const { stdout, exitCode } = run("--version");
31
+ expect(exitCode).toBe(0);
32
+ expect(stdout.trim()).toMatch(/^\d+\.\d+\.\d+$/);
33
+ });
34
+ it("render requires --props", () => {
35
+ const { exitCode } = run("render");
36
+ expect(exitCode).not.toBe(0);
37
+ });
38
+ it("d-id generate requires --script", () => {
39
+ const { exitCode } = run("d-id generate");
40
+ expect(exitCode).not.toBe(0);
41
+ });
42
+ it("preview requires --props", () => {
43
+ const { exitCode } = run("preview");
44
+ expect(exitCode).not.toBe(0);
45
+ });
46
+ });
47
+ //# sourceMappingURL=cli.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.test.js","sourceRoot":"","sources":["../../src/__tests__/cli.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,MAAM,GAAG,GAAG,wBAAwB,CAAC;AAErC,SAAS,GAAG,CAAC,IAAY;IACvB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,EAAE;YACxC,QAAQ,EAAE,OAAO;YACjB,GAAG,EAAE,8CAA8C;YACnD,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;QACH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACjC,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,CAAC,GAAG,GAA2C,CAAC;QACtD,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE;IACnB,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;QAC9C,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,eAAe,CAAC,CAAC;QAC1C,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC;QACpC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=components.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/components.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,73 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { calculateTotalFrames, estimateDuration } from "../compositor/timing.js";
3
+ describe("Component data validation", () => {
4
+ it("BrowserFrame accepts valid zoom config", () => {
5
+ const zoom = {
6
+ bbox: { x: 100, y: 100, width: 400, height: 300 },
7
+ level: 1.5,
8
+ };
9
+ expect(zoom.level).toBeGreaterThan(1);
10
+ expect(zoom.bbox.width).toBeGreaterThan(0);
11
+ });
12
+ it("Highlight accepts all four styles", () => {
13
+ const styles = ["box", "glow", "arrow", "underline"];
14
+ for (const style of styles) {
15
+ const highlight = {
16
+ bbox: { x: 0, y: 0, width: 100, height: 50 },
17
+ style,
18
+ };
19
+ expect(highlight.style).toBe(style);
20
+ }
21
+ });
22
+ it("AvatarPiP accepts all four positions", () => {
23
+ const positions = ["bottom-right", "bottom-left", "top-right", "top-left"];
24
+ for (const pos of positions) {
25
+ expect(pos).toMatch(/^(bottom|top)-(right|left)$/);
26
+ }
27
+ });
28
+ it("IntroOutro card data validates correctly", () => {
29
+ const intro = { title: "My Demo", subtitle: "A walkthrough", durationSeconds: 3 };
30
+ const outro = { title: "Thanks!", durationSeconds: 4 };
31
+ expect(intro.title).toBe("My Demo");
32
+ expect(outro.durationSeconds).toBe(4);
33
+ });
34
+ it("SceneTransition types are valid", () => {
35
+ const types = ["fade", "slide", "cut"];
36
+ for (const t of types) {
37
+ expect(["fade", "slide", "cut"]).toContain(t);
38
+ }
39
+ });
40
+ it("Cursor path timestamps are monotonically increasing", () => {
41
+ const path = [
42
+ { x: 100, y: 200, timestamp: 0 },
43
+ { x: 300, y: 400, timestamp: 500 },
44
+ { x: 500, y: 300, timestamp: 1000 },
45
+ ];
46
+ for (let i = 1; i < path.length; i++) {
47
+ expect(path[i].timestamp).toBeGreaterThan(path[i - 1].timestamp);
48
+ }
49
+ });
50
+ it("estimateDuration produces reasonable values for different narrations", () => {
51
+ expect(estimateDuration("Hello")).toBe(2); // minimum
52
+ expect(estimateDuration("This is a longer narration that should take about four seconds to read aloud")).toBeGreaterThan(3);
53
+ expect(estimateDuration("A".repeat(1000).split("").join(" "))).toBeGreaterThan(100);
54
+ });
55
+ it("total frames calculation is consistent with scene count", () => {
56
+ const props = {
57
+ resolution: { width: 1920, height: 1080 },
58
+ fps: 30,
59
+ scenes: Array.from({ length: 5 }, (_, i) => ({
60
+ id: `scene-${i}`,
61
+ screenshotPath: `/tmp/s${i}.png`,
62
+ avatarDuration: 4,
63
+ narration: "test",
64
+ highlights: [],
65
+ transition: "fade",
66
+ })),
67
+ };
68
+ const total = calculateTotalFrames(props);
69
+ // 5 scenes * 4s * 30fps = 600 frames - 4 transition overlaps * 15 frames = 540
70
+ expect(total).toBe(540);
71
+ });
72
+ });
73
+ //# sourceMappingURL=components.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.test.js","sourceRoot":"","sources":["../../src/__tests__/components.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=d-id-client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"d-id-client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/d-id-client.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,116 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { DIDClient } from "../d-id/client.js";
3
+ const mockFetch = vi.fn();
4
+ vi.stubGlobal("fetch", mockFetch);
5
+ describe("DIDClient", () => {
6
+ let client;
7
+ beforeEach(() => {
8
+ client = new DIDClient("test-api-key");
9
+ mockFetch.mockReset();
10
+ });
11
+ describe("listAvatars", () => {
12
+ it("returns avatars from API", async () => {
13
+ const avatars = [
14
+ { id: "avt_1", name: "Amber", sentiments: [{ id: "snt_1", name: "Professional" }] },
15
+ ];
16
+ mockFetch.mockResolvedValueOnce({
17
+ ok: true,
18
+ json: () => Promise.resolve({ avatars }),
19
+ });
20
+ const result = await client.listAvatars();
21
+ expect(result).toEqual(avatars);
22
+ expect(mockFetch).toHaveBeenCalledWith("https://api.d-id.com/expressives/avatars", expect.objectContaining({
23
+ method: "GET",
24
+ headers: expect.objectContaining({
25
+ Authorization: "Basic test-api-key",
26
+ }),
27
+ }));
28
+ });
29
+ });
30
+ describe("createVideo", () => {
31
+ it("sends correct payload", async () => {
32
+ mockFetch.mockResolvedValueOnce({
33
+ ok: true,
34
+ json: () => Promise.resolve({ id: "exp_123", status: "created" }),
35
+ });
36
+ const result = await client.createVideo({
37
+ avatarId: "avt_1",
38
+ sentimentId: "snt_1",
39
+ script: "Hello world",
40
+ config: { result_format: "mp4", output_resolution: 1080 },
41
+ background: { type: "color", value: "#1a1a1a" },
42
+ });
43
+ expect(result.id).toBe("exp_123");
44
+ const body = JSON.parse(mockFetch.mock.calls[0][1].body);
45
+ expect(body.avatar_id).toBe("avt_1");
46
+ expect(body.sentiment_id).toBe("snt_1");
47
+ expect(body.script.type).toBe("text");
48
+ expect(body.script.input).toBe("Hello world");
49
+ });
50
+ it("throws on 401", async () => {
51
+ mockFetch.mockResolvedValueOnce({
52
+ ok: false,
53
+ status: 401,
54
+ json: () => Promise.resolve({ kind: "AuthorizationError", description: "unauthenticated" }),
55
+ });
56
+ await expect(client.createVideo({ avatarId: "avt_1", script: "test" })).rejects.toThrow("authentication failed");
57
+ });
58
+ it("throws on 402", async () => {
59
+ mockFetch.mockResolvedValueOnce({
60
+ ok: false,
61
+ status: 402,
62
+ json: () => Promise.resolve({ kind: "InsufficientCreditsError", description: "no credits" }),
63
+ });
64
+ await expect(client.createVideo({ avatarId: "avt_1", script: "test" })).rejects.toThrow("insufficient credits");
65
+ });
66
+ });
67
+ describe("getVideo", () => {
68
+ it("returns video status", async () => {
69
+ mockFetch.mockResolvedValueOnce({
70
+ ok: true,
71
+ json: () => Promise.resolve({
72
+ id: "exp_123",
73
+ status: "done",
74
+ result_url: "https://result.d-id.com/video.mp4",
75
+ duration: 4.5,
76
+ }),
77
+ });
78
+ const result = await client.getVideo("exp_123");
79
+ expect(result.status).toBe("done");
80
+ expect(result.result_url).toBe("https://result.d-id.com/video.mp4");
81
+ });
82
+ });
83
+ describe("pollUntilDone", () => {
84
+ it("polls until status is done", async () => {
85
+ mockFetch
86
+ .mockResolvedValueOnce({
87
+ ok: true,
88
+ json: () => Promise.resolve({ id: "exp_123", status: "started" }),
89
+ })
90
+ .mockResolvedValueOnce({
91
+ ok: true,
92
+ json: () => Promise.resolve({
93
+ id: "exp_123",
94
+ status: "done",
95
+ result_url: "https://result.d-id.com/video.mp4",
96
+ duration: 3,
97
+ }),
98
+ });
99
+ const result = await client.pollUntilDone("exp_123", { interval: 10 });
100
+ expect(result.status).toBe("done");
101
+ expect(mockFetch).toHaveBeenCalledTimes(2);
102
+ });
103
+ it("throws on error status", async () => {
104
+ mockFetch.mockResolvedValueOnce({
105
+ ok: true,
106
+ json: () => Promise.resolve({
107
+ id: "exp_123",
108
+ status: "error",
109
+ error: { kind: "Error", description: "generation failed" },
110
+ }),
111
+ });
112
+ await expect(client.pollUntilDone("exp_123", { interval: 10 })).rejects.toThrow("generation failed");
113
+ });
114
+ });
115
+ });
116
+ //# sourceMappingURL=d-id-client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"d-id-client.test.js","sourceRoot":"","sources":["../../src/__tests__/d-id-client.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=render-integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/render-integration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,26 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { resolve, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { RenderPropsSchema } from "../utils/schema.js";
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ describe("Render Integration", () => {
8
+ it("validates render props with real file paths", () => {
9
+ const fixtureDir = resolve(__dirname, "fixtures");
10
+ const screenshotPath = resolve(fixtureDir, "test-screenshot.png");
11
+ const propsPath = resolve(fixtureDir, "test-render-props.json");
12
+ // Verify fixture files exist
13
+ expect(existsSync(screenshotPath)).toBe(true);
14
+ expect(existsSync(propsPath)).toBe(true);
15
+ // Load and patch the props with the real screenshot path
16
+ const props = JSON.parse(readFileSync(propsPath, "utf-8"));
17
+ props.scenes[0].screenshotPath = screenshotPath;
18
+ // Validate with schema
19
+ const result = RenderPropsSchema.safeParse(props);
20
+ expect(result.success).toBe(true);
21
+ });
22
+ it("file validation catches missing screenshots", () => {
23
+ expect(existsSync("/tmp/nonexistent-screenshot-xyz.png")).toBe(false);
24
+ });
25
+ });
26
+ //# sourceMappingURL=render-integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-integration.test.js","sourceRoot":"","sources":["../../src/__tests__/render-integration.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAClD,MAAM,cAAc,GAAG,OAAO,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAAC;QAClE,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAC;QAEhE,6BAA6B;QAC7B,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEzC,yDAAyD;QACzD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3D,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,cAAc,GAAG,cAAc,CAAC;QAEhD,uBAAuB;QACvB,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAClD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,UAAU,CAAC,qCAAqC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=schema.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/schema.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,173 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { DemoScriptSchema, RenderPropsSchema, NavigationStepSchema, HighlightSchema, RenderSceneSchema, } from "../utils/schema.js";
3
+ describe("NavigationStepSchema", () => {
4
+ it("accepts valid navigation steps", () => {
5
+ const steps = [
6
+ { action: "goto", target: "http://localhost:3000" },
7
+ { action: "click", target: "#btn" },
8
+ { action: "type", target: "#input", value: "hello" },
9
+ { action: "wait", value: "2000" },
10
+ { action: "scroll", value: "down" },
11
+ { action: "hover", target: ".menu" },
12
+ ];
13
+ for (const step of steps) {
14
+ expect(NavigationStepSchema.safeParse(step).success).toBe(true);
15
+ }
16
+ });
17
+ it("rejects invalid action", () => {
18
+ const result = NavigationStepSchema.safeParse({ action: "jump" });
19
+ expect(result.success).toBe(false);
20
+ });
21
+ });
22
+ describe("HighlightSchema", () => {
23
+ it("accepts valid highlights", () => {
24
+ expect(HighlightSchema.safeParse({ selector: ".btn", style: "box" }).success).toBe(true);
25
+ expect(HighlightSchema.safeParse({
26
+ selector: "#cta",
27
+ style: "glow",
28
+ color: "#ff0000",
29
+ }).success).toBe(true);
30
+ });
31
+ it("rejects invalid style", () => {
32
+ expect(HighlightSchema.safeParse({ selector: ".btn", style: "sparkle" }).success).toBe(false);
33
+ });
34
+ });
35
+ describe("DemoScriptSchema", () => {
36
+ const validScript = {
37
+ title: "Test Demo",
38
+ description: "A test demo",
39
+ url: "http://localhost:3000",
40
+ resolution: { width: 1920, height: 1080 },
41
+ fps: 30,
42
+ scenes: [
43
+ {
44
+ id: "scene-1",
45
+ title: "Intro",
46
+ narration: "Welcome",
47
+ navigation: [{ action: "goto", target: "http://localhost:3000" }],
48
+ highlights: [{ selector: ".btn", style: "box" }],
49
+ transition: "fade",
50
+ },
51
+ ],
52
+ };
53
+ it("accepts a valid demo script", () => {
54
+ expect(DemoScriptSchema.safeParse(validScript).success).toBe(true);
55
+ });
56
+ it("rejects missing required fields", () => {
57
+ expect(DemoScriptSchema.safeParse({}).success).toBe(false);
58
+ expect(DemoScriptSchema.safeParse({ title: "only title" }).success).toBe(false);
59
+ });
60
+ it("accepts script with zoom target", () => {
61
+ const withZoom = {
62
+ ...validScript,
63
+ scenes: [
64
+ {
65
+ ...validScript.scenes[0],
66
+ zoom: { selector: ".hero", level: 2.0 },
67
+ },
68
+ ],
69
+ };
70
+ expect(DemoScriptSchema.safeParse(withZoom).success).toBe(true);
71
+ });
72
+ it("rejects invalid transition type", () => {
73
+ const invalid = {
74
+ ...validScript,
75
+ scenes: [{ ...validScript.scenes[0], transition: "wipe" }],
76
+ };
77
+ expect(DemoScriptSchema.safeParse(invalid).success).toBe(false);
78
+ });
79
+ });
80
+ describe("RenderPropsSchema", () => {
81
+ const validProps = {
82
+ resolution: { width: 1920, height: 1080 },
83
+ fps: 30,
84
+ scenes: [
85
+ {
86
+ id: "scene-1",
87
+ screenshotPath: "/tmp/scene-1.png",
88
+ avatarDuration: 5.2,
89
+ narration: "Welcome",
90
+ highlights: [
91
+ {
92
+ bbox: { x: 100, y: 200, width: 300, height: 50 },
93
+ style: "glow",
94
+ },
95
+ ],
96
+ transition: "slide",
97
+ },
98
+ ],
99
+ avatarPosition: "bottom-right",
100
+ showSubtitles: true,
101
+ };
102
+ it("accepts valid render props", () => {
103
+ expect(RenderPropsSchema.safeParse(validProps).success).toBe(true);
104
+ });
105
+ it("accepts render props with intro/outro", () => {
106
+ const withIntroOutro = {
107
+ ...validProps,
108
+ intro: { title: "My Demo", subtitle: "A walkthrough" },
109
+ outro: { title: "Thanks!", durationSeconds: 4 },
110
+ };
111
+ expect(RenderPropsSchema.safeParse(withIntroOutro).success).toBe(true);
112
+ });
113
+ it("accepts render props with background music", () => {
114
+ const withMusic = { ...validProps, backgroundMusic: "/tmp/music.mp3" };
115
+ expect(RenderPropsSchema.safeParse(withMusic).success).toBe(true);
116
+ });
117
+ it("accepts render props with cursor path", () => {
118
+ const withCursor = {
119
+ ...validProps,
120
+ scenes: [
121
+ {
122
+ ...validProps.scenes[0],
123
+ cursorPath: [
124
+ { x: 100, y: 200, timestamp: 0 },
125
+ { x: 300, y: 400, timestamp: 1000 },
126
+ ],
127
+ },
128
+ ],
129
+ };
130
+ expect(RenderPropsSchema.safeParse(withCursor).success).toBe(true);
131
+ });
132
+ it("rejects invalid avatar position", () => {
133
+ const invalid = { ...validProps, avatarPosition: "center" };
134
+ expect(RenderPropsSchema.safeParse(invalid).success).toBe(false);
135
+ });
136
+ });
137
+ describe("RenderSceneSchema", () => {
138
+ it("accepts scene without optional fields", () => {
139
+ const minimal = {
140
+ id: "s1",
141
+ screenshotPath: "/tmp/s1.png",
142
+ avatarDuration: 3,
143
+ narration: "Hello",
144
+ highlights: [],
145
+ transition: "cut",
146
+ };
147
+ expect(RenderSceneSchema.safeParse(minimal).success).toBe(true);
148
+ });
149
+ it("accepts scene with all optional fields", () => {
150
+ const full = {
151
+ id: "s1",
152
+ screenshotPath: "/tmp/s1.png",
153
+ avatarClipPath: "/tmp/s1-avatar.mp4",
154
+ avatarDuration: 5,
155
+ narration: "Welcome to the demo",
156
+ highlights: [
157
+ {
158
+ bbox: { x: 0, y: 0, width: 100, height: 50 },
159
+ style: "box",
160
+ color: "#00ff00",
161
+ },
162
+ ],
163
+ zoom: {
164
+ bbox: { x: 100, y: 100, width: 400, height: 300 },
165
+ level: 1.8,
166
+ },
167
+ transition: "fade",
168
+ cursorPath: [{ x: 50, y: 50, timestamp: 0 }],
169
+ };
170
+ expect(RenderSceneSchema.safeParse(full).success).toBe(true);
171
+ });
172
+ });
173
+ //# sourceMappingURL=schema.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.test.js","sourceRoot":"","sources":["../../src/__tests__/schema.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=timing.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timing.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/timing.test.ts"],"names":[],"mappings":""}