hyperframes 0.4.25 → 0.4.27

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 (2) hide show
  1. package/dist/cli.js +175 -50
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -54,7 +54,7 @@ var VERSION;
54
54
  var init_version = __esm({
55
55
  "src/version.ts"() {
56
56
  "use strict";
57
- VERSION = true ? "0.4.25" : "0.0.0-dev";
57
+ VERSION = true ? "0.4.27" : "0.0.0-dev";
58
58
  }
59
59
  });
60
60
 
@@ -5723,24 +5723,6 @@ var init_composition = __esm({
5723
5723
  }
5724
5724
  return findings;
5725
5725
  },
5726
- // root_composition_missing_data_duration
5727
- ({ rootTag }) => {
5728
- const findings = [];
5729
- if (!rootTag) return findings;
5730
- const compId = readAttr(rootTag.raw, "data-composition-id");
5731
- if (!compId) return findings;
5732
- const hasDuration = readAttr(rootTag.raw, "data-duration") !== null;
5733
- if (!hasDuration) {
5734
- findings.push({
5735
- code: "root_composition_missing_data_duration",
5736
- severity: "warning",
5737
- message: `Root composition "${compId}" is missing data-duration. Without an explicit duration, the runtime may infer Infinity for compositions with repeating animations, causing playback issues.`,
5738
- fixHint: 'Add data-duration="X" to the root composition element, where X is the total duration in seconds.',
5739
- snippet: truncateSnippet(rootTag.raw)
5740
- });
5741
- }
5742
- return findings;
5743
- },
5744
5726
  // standalone_composition_wrapped_in_template
5745
5727
  ({ rawSource, options }) => {
5746
5728
  const findings = [];
@@ -37572,6 +37554,89 @@ var init_play = __esm({
37572
37554
  import { basename as basename7, join as join39, relative as relative4 } from "path";
37573
37555
  import { readdirSync as readdirSync15, readFileSync as readFileSync27, statSync as statSync14 } from "fs";
37574
37556
  import AdmZip from "adm-zip";
37557
+ function isRecord(value) {
37558
+ return typeof value === "object" && value !== null && !Array.isArray(value);
37559
+ }
37560
+ function dataRecord(payload) {
37561
+ if (!isRecord(payload) || !isRecord(payload["data"])) return null;
37562
+ return payload["data"];
37563
+ }
37564
+ function stringField(record, key2) {
37565
+ const value = record[key2];
37566
+ return typeof value === "string" ? value : null;
37567
+ }
37568
+ function parsePublishedProjectResponse(payload) {
37569
+ const data = dataRecord(payload);
37570
+ if (!data) return null;
37571
+ const projectId = stringField(data, "project_id");
37572
+ const title = stringField(data, "title");
37573
+ const url = stringField(data, "url");
37574
+ const claimToken = stringField(data, "claim_token");
37575
+ const fileCount = data["file_count"];
37576
+ if (!projectId || !title || !url || !claimToken || typeof fileCount !== "number") {
37577
+ return null;
37578
+ }
37579
+ return {
37580
+ projectId,
37581
+ title,
37582
+ fileCount,
37583
+ url,
37584
+ claimToken
37585
+ };
37586
+ }
37587
+ function parseStagedUploadResponse(payload, archiveByteLength) {
37588
+ const data = dataRecord(payload);
37589
+ if (!data) return null;
37590
+ const uploadUrl = stringField(data, "upload_url");
37591
+ const uploadKey = stringField(data, "upload_key");
37592
+ const contentType = stringField(data, "content_type") || PUBLISH_CONTENT_TYPE;
37593
+ if (!uploadUrl || !uploadKey) return null;
37594
+ return {
37595
+ uploadUrl,
37596
+ uploadKey,
37597
+ contentType,
37598
+ uploadHeaders: getUploadHeaders(data, uploadUrl, contentType, archiveByteLength)
37599
+ };
37600
+ }
37601
+ function getUploadHeaders(data, uploadUrl, contentType, archiveByteLength) {
37602
+ const headers = {};
37603
+ const uploadHeaders = data["upload_headers"];
37604
+ if (isRecord(uploadHeaders)) {
37605
+ for (const [key2, value] of Object.entries(uploadHeaders)) {
37606
+ if (typeof value === "string" && key2.trim()) {
37607
+ headers[key2] = value;
37608
+ }
37609
+ }
37610
+ }
37611
+ if (!Object.keys(headers).some((key2) => key2.toLowerCase() === "content-type")) {
37612
+ headers["content-type"] = contentType;
37613
+ }
37614
+ const signedHeaders = new URL(uploadUrl).searchParams.get("X-Amz-SignedHeaders");
37615
+ if (signedHeaders?.split(";").includes("x-amz-server-side-encryption") && !Object.keys(headers).some((key2) => key2.toLowerCase() === "x-amz-server-side-encryption")) {
37616
+ headers["x-amz-server-side-encryption"] = "AES256";
37617
+ }
37618
+ if (signedHeaders?.split(";").includes("content-length") && !Object.keys(headers).some((key2) => key2.toLowerCase() === "content-length")) {
37619
+ headers["content-length"] = String(archiveByteLength);
37620
+ }
37621
+ return headers;
37622
+ }
37623
+ async function readJson(response) {
37624
+ return response.clone().json().catch(() => null);
37625
+ }
37626
+ async function readErrorMessage(response, fallback) {
37627
+ const contentType = response.headers.get("content-type") || "";
37628
+ if (contentType.includes("application/json")) {
37629
+ const payload = await readJson(response);
37630
+ if (isRecord(payload) && typeof payload["message"] === "string") {
37631
+ return payload["message"];
37632
+ }
37633
+ }
37634
+ if (response.status === 403 && response.headers.get("cf-mitigated") === "challenge") {
37635
+ return "Publish upload was blocked before reaching HyperFrames. Please retry after staged uploads are available.";
37636
+ }
37637
+ const text = await response.text().catch(() => "");
37638
+ return text.trim() ? `${fallback}: ${text.trim().slice(0, 180)}` : fallback;
37639
+ }
37575
37640
  function shouldIgnoreSegment(segment) {
37576
37641
  return segment.startsWith(".") || IGNORED_DIRS.has(segment) || IGNORED_FILES.has(segment);
37577
37642
  }
@@ -37607,42 +37672,102 @@ function createPublishArchive(projectDir) {
37607
37672
  function getPublishApiBaseUrl() {
37608
37673
  return (process.env["HYPERFRAMES_PUBLISHED_PROJECTS_API_URL"] || process.env["HEYGEN_API_URL"] || "https://api2.heygen.com").replace(/\/$/, "");
37609
37674
  }
37610
- async function publishProjectArchive(projectDir) {
37611
- const title = basename7(projectDir);
37612
- const archive = createPublishArchive(projectDir);
37613
- const archiveBytes = new Uint8Array(archive.buffer.byteLength);
37614
- archiveBytes.set(archive.buffer);
37675
+ function archiveArrayBuffer(archive) {
37676
+ const arrayBuffer = new ArrayBuffer(archive.buffer.byteLength);
37677
+ new Uint8Array(arrayBuffer).set(archive.buffer);
37678
+ return arrayBuffer;
37679
+ }
37680
+ async function publishProjectArchiveDirect(apiBaseUrl, title, archive) {
37615
37681
  const body = new FormData();
37616
37682
  body.set("title", title);
37617
- body.set("file", new File([archiveBytes], `${title}.zip`, { type: "application/zip" }));
37683
+ body.set(
37684
+ "file",
37685
+ new File([archiveArrayBuffer(archive)], `${title}.zip`, { type: PUBLISH_CONTENT_TYPE })
37686
+ );
37618
37687
  const headers = {
37619
37688
  heygen_route: "canary"
37620
37689
  };
37621
- const response = await fetch(`${getPublishApiBaseUrl()}/v1/hyperframes/projects/publish`, {
37690
+ const response = await fetch(`${apiBaseUrl}/v1/hyperframes/projects/publish`, {
37622
37691
  method: "POST",
37623
37692
  body,
37624
37693
  headers,
37625
- signal: AbortSignal.timeout(3e4)
37694
+ signal: AbortSignal.timeout(PUBLISH_REQUEST_TIMEOUT_MS)
37626
37695
  });
37627
- const payload = await response.json().catch(() => null);
37628
- const message = typeof payload?.message === "string" ? payload.message : "Failed to publish project";
37629
- if (!response.ok || !payload?.data) {
37630
- throw new Error(message);
37696
+ const payload = await readJson(response);
37697
+ const publishedProject = parsePublishedProjectResponse(payload);
37698
+ if (!response.ok || !publishedProject) {
37699
+ throw new Error(await readErrorMessage(response, "Failed to publish project"));
37631
37700
  }
37632
- return {
37633
- projectId: String(payload.data.project_id),
37634
- title: String(payload.data.title),
37635
- fileCount: Number(payload.data.file_count),
37636
- url: String(payload.data.url),
37637
- claimToken: String(payload.data.claim_token)
37638
- };
37701
+ return publishedProject;
37702
+ }
37703
+ async function publishProjectArchiveStaged(apiBaseUrl, title, archive) {
37704
+ const fileName = `${title}.zip`;
37705
+ const uploadResponse = await fetch(`${apiBaseUrl}/v1/hyperframes/projects/publish/upload`, {
37706
+ method: "POST",
37707
+ body: JSON.stringify({
37708
+ file_name: fileName,
37709
+ content_type: PUBLISH_CONTENT_TYPE,
37710
+ content_length: archive.buffer.byteLength
37711
+ }),
37712
+ headers: {
37713
+ "content-type": "application/json",
37714
+ heygen_route: "canary"
37715
+ },
37716
+ signal: AbortSignal.timeout(PUBLISH_REQUEST_TIMEOUT_MS)
37717
+ });
37718
+ if (uploadResponse.status === 404 || uploadResponse.status === 405) {
37719
+ return null;
37720
+ }
37721
+ const uploadPayload = await readJson(uploadResponse);
37722
+ const stagedUpload = parseStagedUploadResponse(uploadPayload, archive.buffer.byteLength);
37723
+ if (!uploadResponse.ok || !stagedUpload) {
37724
+ throw new Error(await readErrorMessage(uploadResponse, "Failed to prepare project upload"));
37725
+ }
37726
+ const s3Response = await fetch(stagedUpload.uploadUrl, {
37727
+ method: "PUT",
37728
+ body: new Blob([archiveArrayBuffer(archive)], { type: stagedUpload.contentType }),
37729
+ headers: stagedUpload.uploadHeaders,
37730
+ signal: AbortSignal.timeout(PUBLISH_REQUEST_TIMEOUT_MS)
37731
+ });
37732
+ if (!s3Response.ok) {
37733
+ throw new Error(await readErrorMessage(s3Response, "Failed to upload project archive"));
37734
+ }
37735
+ const completeResponse = await fetch(`${apiBaseUrl}/v1/hyperframes/projects/publish/complete`, {
37736
+ method: "POST",
37737
+ body: JSON.stringify({
37738
+ upload_key: stagedUpload.uploadKey,
37739
+ file_name: fileName,
37740
+ title
37741
+ }),
37742
+ headers: {
37743
+ "content-type": "application/json",
37744
+ heygen_route: "canary"
37745
+ },
37746
+ signal: AbortSignal.timeout(PUBLISH_REQUEST_TIMEOUT_MS)
37747
+ });
37748
+ const completePayload = await readJson(completeResponse);
37749
+ const publishedProject = parsePublishedProjectResponse(completePayload);
37750
+ if (!completeResponse.ok || !publishedProject) {
37751
+ throw new Error(await readErrorMessage(completeResponse, "Failed to publish project"));
37752
+ }
37753
+ return publishedProject;
37754
+ }
37755
+ async function publishProjectArchive(projectDir) {
37756
+ const title = basename7(projectDir);
37757
+ const archive = createPublishArchive(projectDir);
37758
+ const apiBaseUrl = getPublishApiBaseUrl();
37759
+ const stagedResult = await publishProjectArchiveStaged(apiBaseUrl, title, archive);
37760
+ if (stagedResult) return stagedResult;
37761
+ return publishProjectArchiveDirect(apiBaseUrl, title, archive);
37639
37762
  }
37640
- var IGNORED_DIRS, IGNORED_FILES;
37763
+ var IGNORED_DIRS, IGNORED_FILES, PUBLISH_CONTENT_TYPE, PUBLISH_REQUEST_TIMEOUT_MS;
37641
37764
  var init_publishProject = __esm({
37642
37765
  "src/utils/publishProject.ts"() {
37643
37766
  "use strict";
37644
37767
  IGNORED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".next", "coverage"]);
37645
37768
  IGNORED_FILES = /* @__PURE__ */ new Set([".DS_Store", "Thumbs.db"]);
37769
+ PUBLISH_CONTENT_TYPE = "application/zip";
37770
+ PUBLISH_REQUEST_TIMEOUT_MS = 3e4;
37646
37771
  }
37647
37772
  });
37648
37773
 
@@ -65171,19 +65296,19 @@ function _moveValueRecursive(data, sourceKeys, destKeys, keyIdx, excludeKeys) {
65171
65296
  const key2 = sourceKeys[keyIdx];
65172
65297
  if (key2.endsWith("[]")) {
65173
65298
  const keyName = key2.slice(0, -2);
65174
- const dataRecord = data;
65175
- if (keyName in dataRecord && Array.isArray(dataRecord[keyName])) {
65176
- for (const item of dataRecord[keyName]) {
65299
+ const dataRecord2 = data;
65300
+ if (keyName in dataRecord2 && Array.isArray(dataRecord2[keyName])) {
65301
+ for (const item of dataRecord2[keyName]) {
65177
65302
  _moveValueRecursive(item, sourceKeys, destKeys, keyIdx + 1, excludeKeys);
65178
65303
  }
65179
65304
  }
65180
65305
  } else if (key2 === "*") {
65181
65306
  if (typeof data === "object" && data !== null && !Array.isArray(data)) {
65182
- const dataRecord = data;
65183
- const keysToMove = Object.keys(dataRecord).filter((k2) => !k2.startsWith("_") && !excludeKeys.has(k2));
65307
+ const dataRecord2 = data;
65308
+ const keysToMove = Object.keys(dataRecord2).filter((k2) => !k2.startsWith("_") && !excludeKeys.has(k2));
65184
65309
  const valuesToMove = {};
65185
65310
  for (const k2 of keysToMove) {
65186
- valuesToMove[k2] = dataRecord[k2];
65311
+ valuesToMove[k2] = dataRecord2[k2];
65187
65312
  }
65188
65313
  for (const [k2, v] of Object.entries(valuesToMove)) {
65189
65314
  const newDestKeys = [];
@@ -65194,16 +65319,16 @@ function _moveValueRecursive(data, sourceKeys, destKeys, keyIdx, excludeKeys) {
65194
65319
  newDestKeys.push(dk);
65195
65320
  }
65196
65321
  }
65197
- setValueByPath(dataRecord, newDestKeys, v);
65322
+ setValueByPath(dataRecord2, newDestKeys, v);
65198
65323
  }
65199
65324
  for (const k2 of keysToMove) {
65200
- delete dataRecord[k2];
65325
+ delete dataRecord2[k2];
65201
65326
  }
65202
65327
  }
65203
65328
  } else {
65204
- const dataRecord = data;
65205
- if (key2 in dataRecord) {
65206
- _moveValueRecursive(dataRecord[key2], sourceKeys, destKeys, keyIdx + 1, excludeKeys);
65329
+ const dataRecord2 = data;
65330
+ if (key2 in dataRecord2) {
65331
+ _moveValueRecursive(dataRecord2[key2], sourceKeys, destKeys, keyIdx + 1, excludeKeys);
65207
65332
  }
65208
65333
  }
65209
65334
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperframes",
3
- "version": "0.4.25",
3
+ "version": "0.4.27",
4
4
  "description": "HyperFrames CLI — create, preview, and render HTML video compositions",
5
5
  "repository": {
6
6
  "type": "git",