@hyperframes/lint 0.8.167 → 0.8.169

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/browser.js CHANGED
@@ -3388,37 +3388,6 @@ var compositionRules = [
3388
3388
  // defaults aren't applied.
3389
3389
  // Checked on every element that declares: <html>, or the composition root.
3390
3390
  ({ tags }) => tags.filter((tag) => readJsonAttr(tag.raw, "data-composition-variables")).flatMap((tag) => variablesDeclarationFindings(tag, tags)),
3391
- // html_dir_attribute_breaks_render — valid non-LTR dir values on
3392
- // <html> renders correctly in preview/snapshot but produces a fully
3393
- // blank/black video from render, with no other lint/validate/inspect
3394
- // check catching it (output file size, far smaller than expected, is the
3395
- // only tell). Confirmed independently by two separate reports, both
3396
- // diagnosing the same exact trigger and the same fix: drop dir from
3397
- // <html>, keep lang, and scope `direction: rtl` to individual
3398
- // text-containing elements via CSS instead (text still bidi-shapes
3399
- // correctly). Advisory-only — this does not attempt to fix the render
3400
- // pipeline's own root cause (suspected to be a capture step that clips a
3401
- // fixed top-left-origin screenshot region, which RTL layout can shift the
3402
- // actual content away from), only surfaces the already-confirmed footgun
3403
- // before someone hits it blind.
3404
- ({ tags }) => {
3405
- const htmlTag = findHtmlTag(tags);
3406
- if (!htmlTag) return [];
3407
- const dir = readAttr(htmlTag.raw, "dir");
3408
- if (!dir) return [];
3409
- const normalizedDir = dir.toLowerCase();
3410
- if (normalizedDir !== "rtl" && normalizedDir !== "auto") return [];
3411
- const scopedDirection = normalizedDir === "auto" ? 'dir="auto"' : `direction: ${normalizedDir}`;
3412
- return [
3413
- {
3414
- code: "html_dir_attribute_breaks_render",
3415
- severity: "error",
3416
- message: `<html dir="${dir}"> renders correctly in preview/snapshot but produces a fully blank/black video from render. This is a confirmed, silent failure.`,
3417
- fixHint: `Remove dir="${dir}" from <html>. Keep lang, and scope ${scopedDirection} to individual text-containing elements instead. Text still shapes correctly via the browser's own bidi algorithm.`,
3418
- snippet: truncateSnippet(htmlTag.raw)
3419
- }
3420
- ];
3421
- },
3422
3391
  // subcomposition_blanks_before_host
3423
3392
  // Warns when a full-bleed sub-composition slot ends before the host composition
3424
3393
  // does, leaving the slot blank for the remainder (issue #1540). Scoped narrowly to
@@ -6244,7 +6213,8 @@ var NODE_ATTRS = [
6244
6213
  "data-track-index",
6245
6214
  "data-track-kind",
6246
6215
  "data-composition-id",
6247
- "data-composition-src"
6216
+ "data-composition-src",
6217
+ "data-hf-media-start-basis"
6248
6218
  ];
6249
6219
  function toNode(open) {
6250
6220
  const attrs = {};
@@ -6352,6 +6322,33 @@ function clipsPastRootFindings(root, rows) {
6352
6322
  ];
6353
6323
  });
6354
6324
  }
6325
+ function followsParentVisibility(clip) {
6326
+ return clip.tag !== "audio" && clip.attrs["data-hf-media-start-basis"] !== "global";
6327
+ }
6328
+ function isTimed(timing) {
6329
+ return timing.start !== null && timing.end !== null;
6330
+ }
6331
+ function neverVisibleNestedFindings(parent, severity) {
6332
+ const window = readClipTiming2({ getAttribute: (name) => parent.attrs[name] ?? null });
6333
+ return topLevelElements(parent).flatMap((clip) => {
6334
+ const inner = isSubCompositionHost(clip) ? [] : neverVisibleNestedFindings(clip, severity);
6335
+ const own = readClipTiming2({ getAttribute: (name) => clip.attrs[name] ?? null });
6336
+ if (!followsParentVisibility(clip) || !isTimed(window) || !isTimed(own)) return inner;
6337
+ if (own.end > window.start && own.start < window.end) return inner;
6338
+ const shows = `${hundredths(window.start)}s to ${hundredths(window.end)}s`;
6339
+ return [
6340
+ {
6341
+ code: "nested_clip_never_visible",
6342
+ severity,
6343
+ message: `${describe(clip)} plays from ${hundredths(own.start)}s to ${hundredths(own.end)}s, but it sits inside ${describe(parent)}, which only shows from ${shows}, so it never appears. A nested data-start counts from the composition's start, not from ${describe(parent)}.`,
6344
+ elementId: clip.attrs.id,
6345
+ fixHint: `Give ${describe(clip)} a data-start between ${shows} in composition time, or move it out of ${describe(parent)}.`,
6346
+ snippet: truncateSnippet(clip.open.raw)
6347
+ },
6348
+ ...inner
6349
+ ];
6350
+ });
6351
+ }
6355
6352
  var structureRules = [
6356
6353
  (ctx) => {
6357
6354
  if (ctx.options.isSubComposition) return [];
@@ -6365,6 +6362,12 @@ var structureRules = [
6365
6362
  ...captionFindings(rows, severity),
6366
6363
  ...clipsPastRootFindings(root, rows)
6367
6364
  ];
6365
+ },
6366
+ (ctx) => {
6367
+ const root = buildTree(ctx);
6368
+ if (!root) return [];
6369
+ const severity = ctx.options.host === "studio" ? "error" : "warning";
6370
+ return topLevelElements(root).filter((row) => !isSubCompositionHost(row)).flatMap((row) => neverVisibleNestedFindings(row, severity));
6368
6371
  }
6369
6372
  ];
6370
6373
 
package/dist/index.js CHANGED
@@ -3394,37 +3394,6 @@ var compositionRules = [
3394
3394
  // defaults aren't applied.
3395
3395
  // Checked on every element that declares: <html>, or the composition root.
3396
3396
  ({ tags }) => tags.filter((tag) => readJsonAttr(tag.raw, "data-composition-variables")).flatMap((tag) => variablesDeclarationFindings(tag, tags)),
3397
- // html_dir_attribute_breaks_render — valid non-LTR dir values on
3398
- // <html> renders correctly in preview/snapshot but produces a fully
3399
- // blank/black video from render, with no other lint/validate/inspect
3400
- // check catching it (output file size, far smaller than expected, is the
3401
- // only tell). Confirmed independently by two separate reports, both
3402
- // diagnosing the same exact trigger and the same fix: drop dir from
3403
- // <html>, keep lang, and scope `direction: rtl` to individual
3404
- // text-containing elements via CSS instead (text still bidi-shapes
3405
- // correctly). Advisory-only — this does not attempt to fix the render
3406
- // pipeline's own root cause (suspected to be a capture step that clips a
3407
- // fixed top-left-origin screenshot region, which RTL layout can shift the
3408
- // actual content away from), only surfaces the already-confirmed footgun
3409
- // before someone hits it blind.
3410
- ({ tags }) => {
3411
- const htmlTag = findHtmlTag(tags);
3412
- if (!htmlTag) return [];
3413
- const dir = readAttr(htmlTag.raw, "dir");
3414
- if (!dir) return [];
3415
- const normalizedDir = dir.toLowerCase();
3416
- if (normalizedDir !== "rtl" && normalizedDir !== "auto") return [];
3417
- const scopedDirection = normalizedDir === "auto" ? 'dir="auto"' : `direction: ${normalizedDir}`;
3418
- return [
3419
- {
3420
- code: "html_dir_attribute_breaks_render",
3421
- severity: "error",
3422
- message: `<html dir="${dir}"> renders correctly in preview/snapshot but produces a fully blank/black video from render. This is a confirmed, silent failure.`,
3423
- fixHint: `Remove dir="${dir}" from <html>. Keep lang, and scope ${scopedDirection} to individual text-containing elements instead. Text still shapes correctly via the browser's own bidi algorithm.`,
3424
- snippet: truncateSnippet(htmlTag.raw)
3425
- }
3426
- ];
3427
- },
3428
3397
  // subcomposition_blanks_before_host
3429
3398
  // Warns when a full-bleed sub-composition slot ends before the host composition
3430
3399
  // does, leaving the slot blank for the remainder (issue #1540). Scoped narrowly to
@@ -6250,7 +6219,8 @@ var NODE_ATTRS = [
6250
6219
  "data-track-index",
6251
6220
  "data-track-kind",
6252
6221
  "data-composition-id",
6253
- "data-composition-src"
6222
+ "data-composition-src",
6223
+ "data-hf-media-start-basis"
6254
6224
  ];
6255
6225
  function toNode(open) {
6256
6226
  const attrs = {};
@@ -6358,6 +6328,33 @@ function clipsPastRootFindings(root, rows) {
6358
6328
  ];
6359
6329
  });
6360
6330
  }
6331
+ function followsParentVisibility(clip) {
6332
+ return clip.tag !== "audio" && clip.attrs["data-hf-media-start-basis"] !== "global";
6333
+ }
6334
+ function isTimed(timing) {
6335
+ return timing.start !== null && timing.end !== null;
6336
+ }
6337
+ function neverVisibleNestedFindings(parent, severity) {
6338
+ const window = readClipTiming2({ getAttribute: (name) => parent.attrs[name] ?? null });
6339
+ return topLevelElements(parent).flatMap((clip) => {
6340
+ const inner = isSubCompositionHost(clip) ? [] : neverVisibleNestedFindings(clip, severity);
6341
+ const own = readClipTiming2({ getAttribute: (name) => clip.attrs[name] ?? null });
6342
+ if (!followsParentVisibility(clip) || !isTimed(window) || !isTimed(own)) return inner;
6343
+ if (own.end > window.start && own.start < window.end) return inner;
6344
+ const shows = `${hundredths(window.start)}s to ${hundredths(window.end)}s`;
6345
+ return [
6346
+ {
6347
+ code: "nested_clip_never_visible",
6348
+ severity,
6349
+ message: `${describe(clip)} plays from ${hundredths(own.start)}s to ${hundredths(own.end)}s, but it sits inside ${describe(parent)}, which only shows from ${shows}, so it never appears. A nested data-start counts from the composition's start, not from ${describe(parent)}.`,
6350
+ elementId: clip.attrs.id,
6351
+ fixHint: `Give ${describe(clip)} a data-start between ${shows} in composition time, or move it out of ${describe(parent)}.`,
6352
+ snippet: truncateSnippet(clip.open.raw)
6353
+ },
6354
+ ...inner
6355
+ ];
6356
+ });
6357
+ }
6361
6358
  var structureRules = [
6362
6359
  (ctx) => {
6363
6360
  if (ctx.options.isSubComposition) return [];
@@ -6371,6 +6368,12 @@ var structureRules = [
6371
6368
  ...captionFindings(rows, severity),
6372
6369
  ...clipsPastRootFindings(root, rows)
6373
6370
  ];
6371
+ },
6372
+ (ctx) => {
6373
+ const root = buildTree(ctx);
6374
+ if (!root) return [];
6375
+ const severity = ctx.options.host === "studio" ? "error" : "warning";
6376
+ return topLevelElements(root).filter((row) => !isSubCompositionHost(row)).flatMap((row) => neverVisibleNestedFindings(row, severity));
6374
6377
  }
6375
6378
  ];
6376
6379
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/lint",
3
- "version": "0.8.167",
3
+ "version": "0.8.169",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/heygen-com/hyperframes",
@@ -35,7 +35,7 @@
35
35
  "linkedom": "^0.18.12",
36
36
  "postcss": "^8.5.8",
37
37
  "postcss-selector-parser": "^7.1.4",
38
- "@hyperframes/parsers": "0.8.167"
38
+ "@hyperframes/parsers": "0.8.169"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^25.0.10",