@hyperframes/studio-server 0.7.27 → 0.7.29

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
@@ -447,6 +447,58 @@ import { parseHTML } from "linkedom";
447
447
  import postcss from "postcss";
448
448
  import selectorParser from "postcss-selector-parser";
449
449
  import { isAllowedHtmlAttribute, isSafeAttributeValue } from "@hyperframes/core/html-attr-safety";
450
+
451
+ // src/helpers/sourceStyleMutation.ts
452
+ function parseStyleDecls(style) {
453
+ const props = /* @__PURE__ */ new Map();
454
+ const order = [];
455
+ let i = 0;
456
+ while (i < style.length) {
457
+ let depth = 0;
458
+ let inSingle = false;
459
+ let inDouble = false;
460
+ const start = i;
461
+ while (i < style.length) {
462
+ const ch = style[i];
463
+ if (ch === "'" && !inDouble) inSingle = !inSingle;
464
+ else if (ch === '"' && !inSingle) inDouble = !inDouble;
465
+ else if (!inSingle && !inDouble) {
466
+ if (ch === "(") depth++;
467
+ else if (ch === ")") depth = Math.max(0, depth - 1);
468
+ else if (ch === ";" && depth === 0) break;
469
+ }
470
+ i++;
471
+ }
472
+ const decl = style.slice(start, i).trim();
473
+ i++;
474
+ if (!decl) continue;
475
+ const colon = decl.indexOf(":");
476
+ if (colon < 0) continue;
477
+ const key = decl.slice(0, colon).trim();
478
+ const val = decl.slice(colon + 1).trim();
479
+ if (!key) continue;
480
+ if (!props.has(key)) order.push(key);
481
+ props.set(key, val);
482
+ }
483
+ return { props, order };
484
+ }
485
+ function serializeStyleDecls(props, order) {
486
+ return order.map((k) => `${k}: ${props.get(k) ?? ""}`).filter((d) => d.trim()).join("; ");
487
+ }
488
+ function patchStyleAttrString(style, property, value) {
489
+ const { props, order } = parseStyleDecls(style);
490
+ if (value === null) {
491
+ props.delete(property);
492
+ const idx = order.indexOf(property);
493
+ if (idx >= 0) order.splice(idx, 1);
494
+ } else {
495
+ if (!props.has(property)) order.push(property);
496
+ props.set(property, value);
497
+ }
498
+ return serializeStyleDecls(props, order);
499
+ }
500
+
501
+ // src/helpers/sourceMutation.ts
450
502
  function parseSourceDocument(source) {
451
503
  const hasDocumentShell = /<!doctype|<html[\s>]/i.test(source);
452
504
  if (hasDocumentShell) {
@@ -540,78 +592,45 @@ function removeElementFromHtml(source, target) {
540
592
  return wrappedFragment ? document2.body.innerHTML || "" : document2.toString();
541
593
  }
542
594
  function isHTMLElement(el) {
543
- const HTMLEl = el.ownerDocument.defaultView?.HTMLElement;
544
- return HTMLEl ? el instanceof HTMLEl : "style" in el;
595
+ const HTMLEl = el.ownerDocument?.defaultView?.HTMLElement;
596
+ return HTMLEl ? el instanceof HTMLEl : el.nodeType === 1 && "style" in el;
545
597
  }
546
- function parseStyleDecls(style) {
547
- const props = /* @__PURE__ */ new Map();
548
- const order = [];
549
- let i = 0;
550
- while (i < style.length) {
551
- let depth = 0;
552
- let inSingle = false;
553
- let inDouble = false;
554
- const start = i;
555
- while (i < style.length) {
556
- const ch = style[i];
557
- if (ch === "'" && !inDouble) inSingle = !inSingle;
558
- else if (ch === '"' && !inSingle) inDouble = !inDouble;
559
- else if (!inSingle && !inDouble) {
560
- if (ch === "(") depth++;
561
- else if (ch === ")") depth = Math.max(0, depth - 1);
562
- else if (ch === ";" && depth === 0) break;
563
- }
564
- i++;
565
- }
566
- const decl = style.slice(start, i).trim();
567
- i++;
568
- if (!decl) continue;
569
- const colon = decl.indexOf(":");
570
- if (colon < 0) continue;
571
- const key = decl.slice(0, colon).trim();
572
- const val = decl.slice(colon + 1).trim();
573
- if (!key) continue;
574
- if (!props.has(key)) order.push(key);
575
- props.set(key, val);
576
- }
577
- return { props, order };
578
- }
579
- function serializeStyleDecls(props, order) {
580
- return order.map((k) => `${k}: ${props.get(k) ?? ""}`).filter((d) => d.trim()).join("; ");
581
- }
582
- function patchStyleAttrString(style, property, value) {
583
- const { props, order } = parseStyleDecls(style);
584
- if (value === null) {
585
- props.delete(property);
586
- const idx = order.indexOf(property);
587
- if (idx >= 0) order.splice(idx, 1);
588
- } else {
589
- if (!props.has(property)) order.push(property);
590
- props.set(property, value);
598
+ function resolveOperationTarget(parent, op) {
599
+ if (op.childSelector === void 0) return parent;
600
+ try {
601
+ const child = parent.querySelectorAll(op.childSelector)[op.childIndex ?? 0] ?? null;
602
+ return child && isHTMLElement(child) ? child : null;
603
+ } catch {
604
+ return null;
591
605
  }
592
- return serializeStyleDecls(props, order);
593
606
  }
594
607
  function patchElementInHtml(source, target, operations) {
595
608
  const { document: document2, wrappedFragment } = parseSourceDocument(source);
596
609
  const el = findTargetElement(document2, target);
597
610
  if (!el || !isHTMLElement(el)) return { html: source, matched: false };
598
611
  const htmlEl = el;
612
+ const resolved = [];
599
613
  for (const op of operations) {
614
+ const opTarget = resolveOperationTarget(htmlEl, op);
615
+ if (!opTarget) return { html: source, matched: false };
616
+ resolved.push({ op, target: opTarget });
617
+ }
618
+ for (const { op, target: opTarget } of resolved) {
600
619
  switch (op.type) {
601
620
  case "inline-style":
602
621
  {
603
- const raw = htmlEl.getAttribute("style") ?? "";
622
+ const raw = opTarget.getAttribute("style") ?? "";
604
623
  const patched = patchStyleAttrString(raw, op.property, op.value);
605
- htmlEl.setAttribute("style", patched);
624
+ opTarget.setAttribute("style", patched);
606
625
  }
607
626
  break;
608
627
  case "attribute":
609
628
  {
610
629
  const fullAttr = op.property.startsWith("data-") ? op.property : `data-${op.property}`;
611
630
  if (op.value != null) {
612
- htmlEl.setAttribute(fullAttr, op.value);
631
+ opTarget.setAttribute(fullAttr, op.value);
613
632
  } else {
614
- htmlEl.removeAttribute(fullAttr);
633
+ opTarget.removeAttribute(fullAttr);
615
634
  }
616
635
  }
617
636
  break;
@@ -619,15 +638,15 @@ function patchElementInHtml(source, target, operations) {
619
638
  if (!isAllowedHtmlAttribute(op.property)) break;
620
639
  if (op.value != null) {
621
640
  if (!isSafeAttributeValue(op.property, op.value)) break;
622
- htmlEl.setAttribute(op.property, op.value);
641
+ opTarget.setAttribute(op.property, op.value);
623
642
  } else {
624
- htmlEl.removeAttribute(op.property);
643
+ opTarget.removeAttribute(op.property);
625
644
  }
626
645
  break;
627
646
  case "text-content":
628
647
  if (op.value != null) {
629
- const inner = htmlEl.children.length === 1 ? htmlEl.firstElementChild : null;
630
- const textTarget = inner && isHTMLElement(inner) ? inner : htmlEl;
648
+ const inner = opTarget.children.length === 1 ? opTarget.firstElementChild : null;
649
+ const textTarget = inner && isHTMLElement(inner) ? inner : opTarget;
631
650
  textTarget.textContent = op.value;
632
651
  }
633
652
  break;
@@ -684,6 +703,7 @@ function splitElementInHtml(source, target, splitTime, newId, fallbackTiming) {
684
703
  const firstDuration = splitTime - start;
685
704
  const secondDuration = duration - firstDuration;
686
705
  const clone = el.cloneNode(true);
706
+ if (!isHTMLElement(clone)) return { html: source, matched: false, newId: null };
687
707
  clone.setAttribute("id", newId);
688
708
  clone.removeAttribute("data-hf-id");
689
709
  for (const node of clone.querySelectorAll("[data-hf-id]")) node.removeAttribute("data-hf-id");