@hyperframes/studio-server 0.9.14 → 0.9.16

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.
@@ -343,6 +343,12 @@ function probeElementsInSource(source, targets) {
343
343
  function probeElementInSource(source, target) {
344
344
  return probeElementsInSource(source, [target])[0] ?? false;
345
345
  }
346
+ var refused = (html, refusal) => ({
347
+ html,
348
+ matched: false,
349
+ newId: null,
350
+ refusal
351
+ });
346
352
  function resolveElementTiming(el) {
347
353
  const timing = readClipTiming(el);
348
354
  return { start: timing.start ?? 0, duration: timing.duration ?? 0 };
@@ -363,7 +369,7 @@ function isWindowOf(media, row) {
363
369
  function splitElementInHtml(source, target, splitTime, newId, fallbackTiming) {
364
370
  const { document, wrappedFragment } = parseSourceDocument(source);
365
371
  const el = findTargetElement(document, target);
366
- if (!el || !isHTMLElement(el)) return { html: source, matched: false, newId: null };
372
+ if (!el || !isHTMLElement(el)) return refused(source, "target-not-found");
367
373
  const timedMediaSelector = "video[data-start], audio[data-start]";
368
374
  const innerMedia = el.hasAttribute("data-start") || resolveElementTiming(el).duration > 0 || el.matches("[data-composition-src], [data-composition-id]") ? null : el.querySelector(timedMediaSelector);
369
375
  const timedMedia = innerMedia && isWindowOf(resolveElementTiming(innerMedia), fallbackTiming) ? innerMedia : null;
@@ -373,21 +379,22 @@ function splitElementInHtml(source, target, splitTime, newId, fallbackTiming) {
373
379
  const reference = readClipTiming(timed).startExpression;
374
380
  const startsAfter = reference?.kind === "reference" ? reference : null;
375
381
  if (startsAfter) {
376
- if (!fallbackTiming) return { html: source, matched: false, newId: null };
382
+ if (!fallbackTiming) return refused(source, "reference-without-range");
377
383
  start = fallbackTiming.start;
378
384
  }
379
385
  if (duration <= 0 && fallbackTiming && fallbackTiming.duration > 0) {
380
386
  start = fallbackTiming.start;
381
387
  duration = fallbackTiming.duration;
382
388
  }
383
- if (duration <= 0 || splitTime < start + SPLIT_BOUNDARY_EPSILON_S || splitTime > start + duration - SPLIT_BOUNDARY_EPSILON_S) {
384
- return { html: source, matched: false, newId: null };
389
+ if (duration <= 0) return refused(source, "no-duration");
390
+ if (splitTime < start + SPLIT_BOUNDARY_EPSILON_S || splitTime > start + duration - SPLIT_BOUNDARY_EPSILON_S) {
391
+ return refused(source, "outside-clip");
385
392
  }
386
393
  newId = nextUniqueId(document, newId);
387
394
  const firstDuration = splitTime - start;
388
395
  const secondDuration = duration - firstDuration;
389
396
  const clone = el.cloneNode(true);
390
- if (!isHTMLElement(clone)) return { html: source, matched: false, newId: null };
397
+ if (!isHTMLElement(clone)) return refused(source, "target-not-found");
391
398
  clone.setAttribute("id", newId);
392
399
  dedupeClonedCompositionId(document, clone);
393
400
  clone.removeAttribute("data-hf-id");
@@ -1,5 +1,5 @@
1
1
  import '@hyperframes/core';
2
- export { a as PreviewApiAdapter, P as PreviewDocumentStore, i as injectMediaCodecMap, f as injectMediaCodecMapIntoHtml, g as isAutoProxyEnabled, p as proxyEtagSalt, r as resolvePreviewMediaCodecProbeCache } from '../mediaProxyPreview-Ds5TElPq.js';
2
+ export { a as PreviewApiAdapter, P as PreviewDocumentStore, i as injectMediaCodecMap, f as injectMediaCodecMapIntoHtml, g as isAutoProxyEnabled, p as proxyEtagSalt, r as resolvePreviewMediaCodecProbeCache } from '../mediaProxyPreview-pNW7nfS0.js';
3
3
  import './mediaCodecMap.js';
4
4
  import 'sharp';
5
5
  import '@hyperframes/parsers';
@@ -34,10 +34,13 @@ declare function nextUniqueId(document: Document, base: string, takenIds?: Reado
34
34
  /** Whether each target exists in `source`; the document is parsed once however many targets ask. */
35
35
  declare function probeElementsInSource(source: string, targets: readonly SourceMutationTarget[]): boolean[];
36
36
  declare function probeElementInSource(source: string, target: SourceMutationTarget): boolean;
37
+ /** Why a split changed nothing: the cut route says it, so a report names the cause and not just "rejected". */
38
+ type SplitRefusal = "target-not-found" | "reference-without-range" | "no-duration" | "outside-clip";
37
39
  interface SplitElementResult {
38
40
  html: string;
39
41
  matched: boolean;
40
42
  newId: string | null;
43
+ refusal?: SplitRefusal;
41
44
  }
42
45
  declare function splitElementInHtml(source: string, target: SourceMutationTarget, splitTime: number, newId: string, fallbackTiming?: {
43
46
  start: number;
@@ -96,4 +99,4 @@ declare function unwrapElementsFromHtml(source: string, groupTarget: SourceMutat
96
99
  /** After a cut, give each linked group's right halves their own `data-link` and `data-sync-origin`. */
97
100
  declare function relinkSplitHalvesInHtml(source: string, rightHalfIds: readonly string[]): string;
98
101
 
99
- export { type ElementRebase, type PatchOperation, type SourceMutationTarget, type SplitElementResult, type UnwrapChildTrack, type UnwrapElementsResult, type WrapElementsResult, dedupeClonedCompositionId, findTargetElement, isHTMLElement, nextUniqueId, parseSourceDocument, patchElementInHtml, probeElementInSource, probeElementsInSource, relinkSplitHalvesInHtml, removeElementFromHtml, removeElementsFromHtml, splitElementInHtml, unwrapElementsFromHtml, wrapElementsInHtml };
102
+ export { type ElementRebase, type PatchOperation, type SourceMutationTarget, type SplitElementResult, type SplitRefusal, type UnwrapChildTrack, type UnwrapElementsResult, type WrapElementsResult, dedupeClonedCompositionId, findTargetElement, isHTMLElement, nextUniqueId, parseSourceDocument, patchElementInHtml, probeElementInSource, probeElementsInSource, relinkSplitHalvesInHtml, removeElementFromHtml, removeElementsFromHtml, splitElementInHtml, unwrapElementsFromHtml, wrapElementsInHtml };
@@ -13,7 +13,7 @@ import {
13
13
  splitElementInHtml,
14
14
  unwrapElementsFromHtml,
15
15
  wrapElementsInHtml
16
- } from "../chunk-LK3ZTZGN.js";
16
+ } from "../chunk-REQUL5L4.js";
17
17
  export {
18
18
  dedupeClonedCompositionId,
19
19
  findTargetElement,
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Hono } from 'hono';
2
- import { S as StudioApiAdapter, P as PreviewDocumentStore, M as MediaProcessingJobState } from './mediaProxyPreview-Ds5TElPq.js';
3
- export { L as LintResult, a as PreviewApiAdapter, R as RenderJobState, b as ResolvedProject, c as StudioSelectionResponse, d as StudioSelectionSnapshot, e as StudioSelectionTextField } from './mediaProxyPreview-Ds5TElPq.js';
2
+ import { S as StudioApiAdapter, P as PreviewDocumentStore, M as MediaProcessingJobState } from './mediaProxyPreview-pNW7nfS0.js';
3
+ export { L as LintResult, a as PreviewApiAdapter, R as RenderJobState, b as ResolvedProject, c as StudioSelectionResponse, d as StudioSelectionSnapshot, e as StudioSelectionTextField } from './mediaProxyPreview-pNW7nfS0.js';
4
4
  export { C as ClosedWindow, D as DEFAULT_HISTORY_ROOT, S as HISTORY_START, H as HistoryClosedError, a as HistoryEntry, b as HistoryEntrySide, c as HistoryFileChange, d as HistoryListItem, e as HistoryRefusedError, f as HistoryResult, g as HistoryTarget, h as HistoryWho, i as HistoryWindow, M as MAX_WINDOW_IDLE_MS, P as ProjectHistory, j as ProjectHistoryOptions, k as ProjectHistoryView, U as UNDO_MODES, l as UndoMode, o as openProjectHistory, r as readProjectHistory } from './projectHistory-C18HDvac.js';
5
5
  export { ArtifactCapture, ArtifactInput, ArtifactRecord, HistoryBusyError, HistoryIdError, InPlaceOutcome, ProjectHistoryRecord, PrunedHistory, ReclaimedHistory, RenderRecord, VideoState, artifactDirectory, beginArtifactCapture, finishSnapshotRun, forgetRecordedArtifacts, historyCache, isUncopiedMedia, listProjectHistories, onRestoredInPlace, pruneGoneProjectHistories, reclaimUncopiedBlobs, recordArtifact, recordArtifacts, recordRenderSuccess, registerRenderOutput, retainSnapshotOutput } from './history/index.js';
6
6
  export { CACHE_DIR_NAME as PROXY_CACHE_DIR_NAME } from './helpers/proxyTranscoder.js';
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  splitElementInHtml,
18
18
  unwrapElementsFromHtml,
19
19
  wrapElementsInHtml
20
- } from "./chunk-LK3ZTZGN.js";
20
+ } from "./chunk-REQUL5L4.js";
21
21
  import {
22
22
  isCompositionSource,
23
23
  stampProjectHfIds,
@@ -2930,6 +2930,12 @@ async function executeGsapMutationRecast(body, block, respond) {
2930
2930
  return respond({ error: `unknown mutation type: ${body.type}` }, 400);
2931
2931
  }
2932
2932
  }
2933
+ var CUT_REFUSALS = {
2934
+ "target-not-found": "Cut target was not found",
2935
+ "reference-without-range": "Cut target starts after another clip and the cut sent no range for it",
2936
+ "no-duration": "Cut target has no duration",
2937
+ "outside-clip": "Cut point was outside the clip's start and end"
2938
+ };
2933
2939
  async function foldAtomicCutFile(c, file, absPath, before, writer) {
2934
2940
  let after = before;
2935
2941
  let splitCount = 0;
@@ -2958,7 +2964,7 @@ async function foldAtomicCutFile(c, file, absPath, before, writer) {
2958
2964
  });
2959
2965
  if (!split.matched || !split.newId) {
2960
2966
  return c.json(
2961
- { error: `Cut target was not found or was outside its authored bounds in ${file.path}` },
2967
+ { error: `${CUT_REFUSALS[split.refusal ?? "target-not-found"]} in ${file.path}` },
2962
2968
  400
2963
2969
  );
2964
2970
  }
@@ -4848,6 +4854,7 @@ function contentDispositionHeader(disposition, filename) {
4848
4854
  }
4849
4855
  function registerRenderRoutes(api, adapter) {
4850
4856
  const renderJobs = /* @__PURE__ */ new Map();
4857
+ const CANCEL_SETTLE_MS = 45e3;
4851
4858
  const nextFreeRenderJobId = (baseId, ext, rendersDir) => {
4852
4859
  let jobId = baseId;
4853
4860
  for (let n = 2; renderJobs.has(jobId) || existsSync6(join10(rendersDir, `${jobId}${ext}`)); n++) {
@@ -4965,13 +4972,19 @@ function registerRenderRoutes(api, adapter) {
4965
4972
  }
4966
4973
  });
4967
4974
  });
4968
- api.post("/render/:jobId/cancel", (c) => {
4975
+ api.post("/render/:jobId/cancel", async (c) => {
4969
4976
  const { jobId } = c.req.param();
4970
4977
  const job = renderJobs.get(jobId);
4971
4978
  if (!job) return c.json({ error: "not found" }, 404);
4972
4979
  if (job.status === "rendering") {
4973
4980
  job.status = "cancelled";
4974
- job.cancel?.();
4981
+ let giveUp;
4982
+ const late = new Promise((done) => giveUp = setTimeout(done, CANCEL_SETTLE_MS));
4983
+ try {
4984
+ await Promise.race([job.cancel?.(), late]);
4985
+ } finally {
4986
+ clearTimeout(giveUp);
4987
+ }
4975
4988
  }
4976
4989
  return c.json({ status: job.status });
4977
4990
  });
@@ -27,7 +27,7 @@ interface RenderJobState {
27
27
  * stop an in-flight render; adapters that can't abort may omit it (the
28
28
  * route still marks the job cancelled so the SSE stream terminates).
29
29
  */
30
- cancel?: () => void;
30
+ cancel?: () => void | Promise<void>;
31
31
  }
32
32
  interface MediaProcessingJobState {
33
33
  id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/studio-server",
3
- "version": "0.9.14",
3
+ "version": "0.9.16",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/heygen-com/hyperframes",
@@ -88,8 +88,8 @@
88
88
  "linkedom": "^0.18.12",
89
89
  "postcss": "^8.5.8",
90
90
  "postcss-selector-parser": "^7.1.2",
91
- "@hyperframes/core": "0.9.14",
92
- "@hyperframes/parsers": "0.9.14"
91
+ "@hyperframes/core": "0.9.16",
92
+ "@hyperframes/parsers": "0.9.16"
93
93
  },
94
94
  "devDependencies": {
95
95
  "@types/node": "^25.0.10",