@hyperframes/parsers 0.7.60 → 0.7.61

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.
package/dist/index.js CHANGED
@@ -22,6 +22,12 @@ var RESOLUTION_ALIASES = {
22
22
  "square-1080p": "square",
23
23
  "4k-square": "square-4k"
24
24
  };
25
+ var ASPECT_AGNOSTIC_RESOLUTION_ALIASES = /* @__PURE__ */ new Set([
26
+ "1080p",
27
+ "hd",
28
+ "4k",
29
+ "uhd"
30
+ ]);
25
31
  function normalizeResolutionFlag(input) {
26
32
  if (!input) return void 0;
27
33
  const lowered = input.toLowerCase();
@@ -30,6 +36,16 @@ function normalizeResolutionFlag(input) {
30
36
  }
31
37
  return RESOLUTION_ALIASES[lowered];
32
38
  }
39
+ function isAspectAgnosticResolutionAlias(input) {
40
+ if (!input) return false;
41
+ return ASPECT_AGNOSTIC_RESOLUTION_ALIASES.has(input.toLowerCase());
42
+ }
43
+ function resolveResolutionFlagPair(input) {
44
+ return {
45
+ outputResolution: normalizeResolutionFlag(input),
46
+ outputResolutionAspectAgnostic: isAspectAgnosticResolutionAlias(input)
47
+ };
48
+ }
33
49
  var COMPOSITION_VARIABLE_TYPES = [
34
50
  "string",
35
51
  "number",
@@ -2232,6 +2248,276 @@ function removeAnimationFromScript(script, animationId) {
2232
2248
  return ms.toString();
2233
2249
  }
2234
2250
 
2251
+ // src/compositionContract.ts
2252
+ var COMPOSITION_CONTRACT_VERSION = 1;
2253
+ var COMPOSITION_ATTRIBUTES = Object.freeze({
2254
+ start: "data-start",
2255
+ duration: "data-duration",
2256
+ trackIndex: "data-track-index",
2257
+ derivedEnd: "data-end",
2258
+ legacyTrack: "data-layer"
2259
+ });
2260
+ var CANONICAL_AUTHORED_TIMING_ATTRIBUTES = Object.freeze([
2261
+ COMPOSITION_ATTRIBUTES.start,
2262
+ COMPOSITION_ATTRIBUTES.duration,
2263
+ COMPOSITION_ATTRIBUTES.trackIndex
2264
+ ]);
2265
+ var DERIVED_TIMING_ATTRIBUTES = Object.freeze([
2266
+ COMPOSITION_ATTRIBUTES.derivedEnd
2267
+ ]);
2268
+ var LEGACY_TIMING_ATTRIBUTES = Object.freeze([
2269
+ COMPOSITION_ATTRIBUTES.derivedEnd,
2270
+ COMPOSITION_ATTRIBUTES.legacyTrack
2271
+ ]);
2272
+ var ClipTimingWriteError = class extends Error {
2273
+ code;
2274
+ constructor(code, message) {
2275
+ super(message);
2276
+ this.name = "ClipTimingWriteError";
2277
+ this.code = code;
2278
+ }
2279
+ };
2280
+ function parseNumeric(value) {
2281
+ if (value == null || value.trim() === "") return null;
2282
+ const parsed = Number(value);
2283
+ return Number.isFinite(parsed) ? parsed : null;
2284
+ }
2285
+ var REFERENCE_ID_PATTERN = /^[A-Za-z0-9_.:-]+$/;
2286
+ function isAsciiDigitAt(value, index) {
2287
+ const code = value.charCodeAt(index);
2288
+ return code >= 48 && code <= 57;
2289
+ }
2290
+ function skipDigitsLeft(value, start) {
2291
+ let cursor = start;
2292
+ while (cursor >= 0 && isAsciiDigitAt(value, cursor)) cursor--;
2293
+ return cursor;
2294
+ }
2295
+ function skipWhitespaceLeft(value, start) {
2296
+ let cursor = start;
2297
+ while (cursor >= 0 && (value[cursor] ?? "").trim() === "") cursor--;
2298
+ return cursor;
2299
+ }
2300
+ function findMagnitudeStart(value) {
2301
+ const last = value.length - 1;
2302
+ if (!isAsciiDigitAt(value, last)) return null;
2303
+ let cursor = skipDigitsLeft(value, last);
2304
+ if (value[cursor] === ".") cursor = skipDigitsLeft(value, cursor - 1);
2305
+ return cursor + 1;
2306
+ }
2307
+ function parseReferenceOffset(value) {
2308
+ const magnitudeStart = findMagnitudeStart(value);
2309
+ if (magnitudeStart == null) return null;
2310
+ const operatorIndex = skipWhitespaceLeft(value, magnitudeStart - 1);
2311
+ const operator = value[operatorIndex];
2312
+ if (operator !== "+" && operator !== "-") return null;
2313
+ const refId = value.slice(0, operatorIndex).trim();
2314
+ if (!REFERENCE_ID_PATTERN.test(refId)) return null;
2315
+ const magnitude = Number(value.slice(magnitudeStart));
2316
+ if (!Number.isFinite(magnitude)) return null;
2317
+ return { refId, operator, magnitude };
2318
+ }
2319
+ function parseStartExpression(raw) {
2320
+ const normalized = (raw ?? "").trim();
2321
+ if (!normalized) return null;
2322
+ const absolute = parseNumeric(normalized);
2323
+ if (absolute != null) return { kind: "absolute", value: absolute };
2324
+ if (REFERENCE_ID_PATTERN.test(normalized)) {
2325
+ return { kind: "reference", refId: normalized, offset: 0 };
2326
+ }
2327
+ const reference = parseReferenceOffset(normalized);
2328
+ if (!reference) return null;
2329
+ return {
2330
+ kind: "reference",
2331
+ refId: reference.refId,
2332
+ offset: reference.operator === "-" ? -reference.magnitude : reference.magnitude
2333
+ };
2334
+ }
2335
+ function pushDiagnostic(diagnostics, code, attribute, value) {
2336
+ diagnostics.push({ code, attribute, value });
2337
+ }
2338
+ function resolveStart(expression, rawStart, options, diagnostics) {
2339
+ if (rawStart == null || rawStart.trim() === "") {
2340
+ return options.defaultStart === void 0 ? 0 : options.defaultStart;
2341
+ }
2342
+ if (!expression) {
2343
+ pushDiagnostic(diagnostics, "invalid-start", COMPOSITION_ATTRIBUTES.start, rawStart);
2344
+ return null;
2345
+ }
2346
+ if (expression.kind === "absolute") return Math.max(0, expression.value);
2347
+ const referencedEnd = options.resolveReferenceEnd?.(expression.refId);
2348
+ if (referencedEnd == null || !Number.isFinite(referencedEnd)) {
2349
+ pushDiagnostic(
2350
+ diagnostics,
2351
+ "unresolved-start-reference",
2352
+ COMPOSITION_ATTRIBUTES.start,
2353
+ rawStart
2354
+ );
2355
+ return null;
2356
+ }
2357
+ return Math.max(0, referencedEnd + expression.offset);
2358
+ }
2359
+ function diagnoseDerivedEnd(rawEnd, canonicalEnd, diagnostics) {
2360
+ pushDiagnostic(diagnostics, "deprecated-end", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);
2361
+ const parsedEnd = parseNumeric(rawEnd);
2362
+ if (parsedEnd == null) {
2363
+ pushDiagnostic(diagnostics, "invalid-end", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);
2364
+ } else if (canonicalEnd != null && parsedEnd !== canonicalEnd) {
2365
+ pushDiagnostic(diagnostics, "conflicting-end", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);
2366
+ }
2367
+ }
2368
+ function readCanonicalDuration(rawDuration, start, diagnostics) {
2369
+ const duration = parseNumeric(rawDuration);
2370
+ if (duration == null || duration < 0) {
2371
+ pushDiagnostic(diagnostics, "invalid-duration", COMPOSITION_ATTRIBUTES.duration, rawDuration);
2372
+ return { duration: null, end: null, durationSource: "invalid" };
2373
+ }
2374
+ return {
2375
+ duration,
2376
+ end: start == null ? null : start + duration,
2377
+ durationSource: "duration"
2378
+ };
2379
+ }
2380
+ function readLegacyEnd(rawEnd, start, diagnostics) {
2381
+ pushDiagnostic(diagnostics, "deprecated-end", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);
2382
+ const end = parseNumeric(rawEnd);
2383
+ if (end == null) {
2384
+ pushDiagnostic(diagnostics, "invalid-end", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);
2385
+ return { duration: null, end: null, durationSource: "invalid" };
2386
+ }
2387
+ if (start == null) return { duration: null, end, durationSource: "legacy-end" };
2388
+ if (end < start) {
2389
+ pushDiagnostic(diagnostics, "end-before-start", COMPOSITION_ATTRIBUTES.derivedEnd, rawEnd);
2390
+ return { duration: null, end: null, durationSource: "invalid" };
2391
+ }
2392
+ return { duration: end - start, end, durationSource: "legacy-end" };
2393
+ }
2394
+ function readDuration(attributes, start, diagnostics) {
2395
+ const rawDuration = attributes.getAttribute(COMPOSITION_ATTRIBUTES.duration);
2396
+ const rawEnd = attributes.getAttribute(COMPOSITION_ATTRIBUTES.derivedEnd);
2397
+ if (rawDuration == null) {
2398
+ return rawEnd == null ? { duration: null, end: null, durationSource: "missing" } : readLegacyEnd(rawEnd, start, diagnostics);
2399
+ }
2400
+ const canonical = readCanonicalDuration(rawDuration, start, diagnostics);
2401
+ if (rawEnd != null) diagnoseDerivedEnd(rawEnd, canonical.end, diagnostics);
2402
+ return canonical;
2403
+ }
2404
+ function readTrackValue(rawValue, attribute, source, diagnostics) {
2405
+ const trackIndex = parseNumeric(rawValue);
2406
+ if (trackIndex == null || !Number.isInteger(trackIndex)) {
2407
+ pushDiagnostic(diagnostics, "invalid-track-index", attribute, rawValue);
2408
+ return { trackIndex: 0, trackSource: "invalid" };
2409
+ }
2410
+ return { trackIndex, trackSource: source };
2411
+ }
2412
+ function readTrack(attributes, diagnostics) {
2413
+ const rawTrack = attributes.getAttribute(COMPOSITION_ATTRIBUTES.trackIndex);
2414
+ const rawLayer = attributes.getAttribute(COMPOSITION_ATTRIBUTES.legacyTrack);
2415
+ if (rawTrack == null && rawLayer == null) return { trackIndex: 0, trackSource: "default" };
2416
+ if (rawTrack == null && rawLayer != null) {
2417
+ pushDiagnostic(diagnostics, "deprecated-layer", COMPOSITION_ATTRIBUTES.legacyTrack, rawLayer);
2418
+ return readTrackValue(
2419
+ rawLayer,
2420
+ COMPOSITION_ATTRIBUTES.legacyTrack,
2421
+ "legacy-layer",
2422
+ diagnostics
2423
+ );
2424
+ }
2425
+ const canonical = readTrackValue(
2426
+ rawTrack ?? "",
2427
+ COMPOSITION_ATTRIBUTES.trackIndex,
2428
+ "track-index",
2429
+ diagnostics
2430
+ );
2431
+ if (rawLayer == null) return canonical;
2432
+ pushDiagnostic(diagnostics, "deprecated-layer", COMPOSITION_ATTRIBUTES.legacyTrack, rawLayer);
2433
+ const parsedLayer = parseNumeric(rawLayer);
2434
+ if (parsedLayer != null && parsedLayer !== canonical.trackIndex) {
2435
+ pushDiagnostic(diagnostics, "conflicting-layer", COMPOSITION_ATTRIBUTES.legacyTrack, rawLayer);
2436
+ }
2437
+ return canonical;
2438
+ }
2439
+ function readClipTiming(attributes, options = {}) {
2440
+ const diagnostics = [];
2441
+ const rawStart = attributes.getAttribute(COMPOSITION_ATTRIBUTES.start);
2442
+ const startExpression = parseStartExpression(rawStart);
2443
+ const start = resolveStart(startExpression, rawStart, options, diagnostics);
2444
+ const duration = readDuration(attributes, start, diagnostics);
2445
+ const track = readTrack(attributes, diagnostics);
2446
+ return { startExpression, start, ...duration, ...track, diagnostics };
2447
+ }
2448
+ function serializeStartNumber(start) {
2449
+ if (!Number.isFinite(start)) {
2450
+ throw new ClipTimingWriteError("invalid-start", "start must be finite");
2451
+ }
2452
+ return String(start);
2453
+ }
2454
+ function serializeStartReference(start) {
2455
+ if (start.kind === "absolute") return serializeStartNumber(start.value);
2456
+ if (!start.refId || !Number.isFinite(start.offset)) {
2457
+ throw new ClipTimingWriteError(
2458
+ "invalid-start",
2459
+ "reference start must have an id and finite offset"
2460
+ );
2461
+ }
2462
+ if (start.offset === 0) return start.refId;
2463
+ return `${start.refId} ${start.offset < 0 ? "-" : "+"} ${Math.abs(start.offset)}`;
2464
+ }
2465
+ function serializeStart(start) {
2466
+ if (typeof start === "number") return serializeStartNumber(start);
2467
+ if (typeof start === "object" && start != null) return serializeStartReference(start);
2468
+ if (typeof start === "string" && parseStartExpression(start)) return start.trim();
2469
+ throw new ClipTimingWriteError("invalid-start", `invalid start expression: ${start ?? ""}`);
2470
+ }
2471
+ function writeDuration(attributes, duration, current) {
2472
+ if (duration === null) {
2473
+ attributes.removeAttribute(COMPOSITION_ATTRIBUTES.duration);
2474
+ return;
2475
+ }
2476
+ if (duration === void 0) {
2477
+ if (attributes.getAttribute(COMPOSITION_ATTRIBUTES.duration) == null && current.duration != null) {
2478
+ attributes.setAttribute(COMPOSITION_ATTRIBUTES.duration, String(current.duration));
2479
+ }
2480
+ return;
2481
+ }
2482
+ if (!Number.isFinite(duration) || duration < 0) {
2483
+ throw new ClipTimingWriteError(
2484
+ "invalid-duration",
2485
+ "duration must be a finite, non-negative number"
2486
+ );
2487
+ }
2488
+ attributes.setAttribute(COMPOSITION_ATTRIBUTES.duration, String(duration));
2489
+ }
2490
+ function writeTrack(attributes, trackIndex, current) {
2491
+ if (trackIndex === null) {
2492
+ attributes.removeAttribute(COMPOSITION_ATTRIBUTES.trackIndex);
2493
+ return;
2494
+ }
2495
+ if (trackIndex === void 0) {
2496
+ if (attributes.getAttribute(COMPOSITION_ATTRIBUTES.trackIndex) == null && current.trackSource === "legacy-layer") {
2497
+ attributes.setAttribute(COMPOSITION_ATTRIBUTES.trackIndex, String(current.trackIndex));
2498
+ }
2499
+ return;
2500
+ }
2501
+ if (!Number.isFinite(trackIndex) || !Number.isInteger(trackIndex)) {
2502
+ throw new ClipTimingWriteError("invalid-track-index", "trackIndex must be a finite integer");
2503
+ }
2504
+ attributes.setAttribute(COMPOSITION_ATTRIBUTES.trackIndex, String(trackIndex));
2505
+ }
2506
+ function writeClipTiming(attributes, update) {
2507
+ const current = readClipTiming(attributes);
2508
+ if (update.start !== void 0) {
2509
+ attributes.setAttribute(COMPOSITION_ATTRIBUTES.start, serializeStart(update.start));
2510
+ }
2511
+ writeDuration(attributes, update.duration, current);
2512
+ writeTrack(attributes, update.trackIndex, current);
2513
+ const preserveUnresolvedLegacyEnd = update.duration === void 0 && attributes.getAttribute(COMPOSITION_ATTRIBUTES.duration) == null && attributes.getAttribute(COMPOSITION_ATTRIBUTES.derivedEnd) != null && current.duration == null;
2514
+ if (!preserveUnresolvedLegacyEnd) {
2515
+ attributes.removeAttribute(COMPOSITION_ATTRIBUTES.derivedEnd);
2516
+ }
2517
+ attributes.removeAttribute(COMPOSITION_ATTRIBUTES.legacyTrack);
2518
+ return readClipTiming(attributes);
2519
+ }
2520
+
2235
2521
  // src/htmlParser.ts
2236
2522
  var MEDIA_TYPES = /* @__PURE__ */ new Set(["video", "image", "audio"]);
2237
2523
  var CompositionHtmlParseError = class extends Error {
@@ -2269,8 +2555,10 @@ function getElementName(el) {
2269
2555
  return el.id || el.className?.toString().split(" ")[0] || "Element";
2270
2556
  }
2271
2557
  function getZIndex(el) {
2272
- const dataLayer = el.getAttribute("data-layer");
2273
- if (dataLayer) return parseInt(dataLayer, 10) || 0;
2558
+ const timing = readClipTiming(el);
2559
+ if (timing.trackSource !== "default" && timing.trackSource !== "invalid") {
2560
+ return timing.trackIndex;
2561
+ }
2274
2562
  const style = el.style?.zIndex;
2275
2563
  if (style) return parseInt(style, 10) || 0;
2276
2564
  return 0;
@@ -2357,18 +2645,32 @@ function parseHtml(html) {
2357
2645
  customStyles = customStylesAttr;
2358
2646
  }
2359
2647
  }
2360
- const timedElements = doc.querySelectorAll("[data-start]");
2648
+ const timedElements = Array.from(doc.querySelectorAll("[data-start]"));
2649
+ const timedById = /* @__PURE__ */ new Map();
2650
+ for (const element of timedElements) {
2651
+ for (const id of [element.id, element.getAttribute("data-hf-id")]) {
2652
+ if (id) timedById.set(id, element);
2653
+ }
2654
+ }
2655
+ const resolveEnd = (refId, visiting) => {
2656
+ if (visiting.has(refId)) return null;
2657
+ const referenced = timedById.get(refId);
2658
+ if (!referenced) return null;
2659
+ const next = new Set(visiting);
2660
+ next.add(refId);
2661
+ return readClipTiming(referenced, {
2662
+ resolveReferenceEnd: (nestedId) => resolveEnd(nestedId, next)
2663
+ }).end;
2664
+ };
2361
2665
  timedElements.forEach((el) => {
2362
2666
  const type = getElementType(el);
2363
2667
  if (!type) return;
2364
- const start = parseFloat(el.getAttribute("data-start") || "0");
2365
- const dataEnd = el.getAttribute("data-end");
2366
- let duration;
2367
- if (dataEnd) {
2368
- duration = Math.max(0, parseFloat(dataEnd) - start);
2369
- } else {
2370
- duration = 5;
2371
- }
2668
+ const ownId = el.id || el.getAttribute("data-hf-id");
2669
+ const timing = readClipTiming(el, {
2670
+ resolveReferenceEnd: (refId) => resolveEnd(refId, new Set(ownId ? [ownId] : []))
2671
+ });
2672
+ const start = timing.start ?? 0;
2673
+ const duration = timing.duration ?? 5;
2372
2674
  const id = el.getAttribute("data-hf-id") || el.id || `element-${++idCounter}`;
2373
2675
  const name = getElementName(el);
2374
2676
  const zIndex = getZIndex(el);
@@ -2605,23 +2907,16 @@ function updateElementInHtml(html, elementId, updates) {
2605
2907
  const doc = parser.parseFromString(html, "text/html");
2606
2908
  const el = doc.getElementById(elementId) || queryByAttr(doc, "data-name", elementId);
2607
2909
  if (!el) return html;
2608
- if (updates.startTime !== void 0) {
2609
- el.setAttribute("data-start", String(updates.startTime));
2610
- if (el.hasAttribute("data-end") && updates.duration !== void 0) {
2611
- el.setAttribute("data-end", String(updates.startTime + updates.duration));
2612
- }
2613
- }
2614
- if (updates.duration !== void 0) {
2615
- const start = parseFloat(el.getAttribute("data-start") || "0");
2616
- el.setAttribute("data-end", String(start + updates.duration));
2617
- el.removeAttribute("data-duration");
2910
+ if (updates.startTime !== void 0 || updates.duration !== void 0 || updates.zIndex !== void 0) {
2911
+ writeClipTiming(el, {
2912
+ start: updates.startTime,
2913
+ duration: updates.duration,
2914
+ trackIndex: updates.zIndex
2915
+ });
2618
2916
  }
2619
2917
  if (updates.name !== void 0) {
2620
2918
  el.setAttribute("data-name", updates.name);
2621
2919
  }
2622
- if (updates.zIndex !== void 0) {
2623
- el.setAttribute("data-layer", String(updates.zIndex));
2624
- }
2625
2920
  if ("src" in updates && updates.src !== void 0) {
2626
2921
  el.setAttribute("src", updates.src);
2627
2922
  }
@@ -2727,9 +3022,11 @@ function addElementToHtml(html, element) {
2727
3022
  }
2728
3023
  }
2729
3024
  newEl.id = id;
2730
- newEl.setAttribute("data-start", String(element.startTime));
2731
- newEl.setAttribute("data-end", String(element.startTime + element.duration));
2732
- newEl.setAttribute("data-layer", String(element.zIndex));
3025
+ writeClipTiming(newEl, {
3026
+ start: element.startTime,
3027
+ duration: element.duration,
3028
+ trackIndex: element.zIndex
3029
+ });
2733
3030
  newEl.setAttribute("data-name", element.name);
2734
3031
  container.appendChild(newEl);
2735
3032
  return {
@@ -3293,14 +3590,20 @@ function resolveAliasDisplayName(alias) {
3293
3590
  return CANONICAL_FONT_DISPLAY_NAMES[slug];
3294
3591
  }
3295
3592
  export {
3593
+ CANONICAL_AUTHORED_TIMING_ATTRIBUTES,
3296
3594
  CANONICAL_FONT_DISPLAY_NAMES,
3297
3595
  CANVAS_DIMENSIONS,
3596
+ COMPOSITION_ATTRIBUTES,
3597
+ COMPOSITION_CONTRACT_VERSION,
3298
3598
  COMPOSITION_VARIABLE_TYPES,
3599
+ ClipTimingWriteError,
3299
3600
  CompositionHtmlParseError,
3300
3601
  DEFAULT_DURATIONS,
3602
+ DERIVED_TIMING_ATTRIBUTES,
3301
3603
  EXCLUDED_TAGS,
3302
3604
  FONT_ALIAS_KEYS,
3303
3605
  FONT_ALIAS_MAP,
3606
+ LEGACY_TIMING_ATTRIBUTES,
3304
3607
  PROPERTY_GROUPS,
3305
3608
  SPRING_PRESETS,
3306
3609
  SUPPORTED_EASES,
@@ -3320,6 +3623,7 @@ export {
3320
3623
  getAnimationsForElementId,
3321
3624
  getDefaultStageZoom,
3322
3625
  gsapAnimationsToKeyframes,
3626
+ isAspectAgnosticResolutionAlias,
3323
3627
  isCompositionElement,
3324
3628
  isCompositionTemplate,
3325
3629
  isMediaElement,
@@ -3331,15 +3635,21 @@ export {
3331
3635
  parseCompositionVariables,
3332
3636
  parseGsapScriptAcorn as parseGsapScript,
3333
3637
  parseHtml,
3638
+ parseNumeric,
3639
+ parseStartExpression,
3334
3640
  queryByAttr,
3641
+ readClipTiming,
3335
3642
  removeElementFromHtml,
3336
3643
  resolveAliasDisplayName,
3644
+ resolveResolutionFlagPair,
3337
3645
  scanVariableUsage,
3338
3646
  serializeGsapAnimations,
3647
+ suggestMatchingPreset,
3339
3648
  unrollComputedTimeline,
3340
3649
  updateElementInHtml,
3341
3650
  validateCompositionGsap,
3342
3651
  validateCompositionHtml,
3343
- walkCompositionDescendants
3652
+ walkCompositionDescendants,
3653
+ writeClipTiming
3344
3654
  };
3345
3655
  //# sourceMappingURL=index.js.map