Skip to content

refactor(core)!: move media capability predicates to core layer#1705

Merged
luwes merged 4 commits into
mainfrom
refactor/media-predicates-to-core
Jun 18, 2026
Merged

refactor(core)!: move media capability predicates to core layer#1705
luwes merged 4 commits into
mainfrom
refactor/media-predicates-to-core

Conversation

@luwes

@luwes luwes commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

The media capability type guards lived in the DOM layer (dom/media/predicate.ts) but are fundamentally runtime-agnostic — they only check object shapes. This moves them (and the sentinel constants they compare against) into the core/media layer where they belong, keeping the DOM layer focused on genuinely DOM-bound code.

Changes

  • Capability guards (isMediaPauseCapable, isMediaSeekCapable, hasMetadata, etc.) and the EMPTY_* sentinel constants now live in core/media and are part of the @videojs/core surface.
  • isQuerySelectorAllCapable moved too; its DOM-lib return type (NodeListOf<HTMLElementTagNameMap[...]>) is replaced with a framework-agnostic NodeListLike<Element> look-alike, and the element type is now supplied at the call site.
  • DOM consumers (feature definitions, media-host, vimeo) updated to import from the new locations.
Why this needed more than a file move

The core/ project compiles with lib: ["ES2022", "WebWorker"] (no DOM lib) and cannot import from dom/. So the predicates couldn't simply be relocated:

  • isQuerySelectorAllCapable referenced NodeListOf / HTMLElementTagNameMap, which don't exist without the DOM lib — hence the NodeListLike look-alike.
  • The guards compare against EMPTY_TIME_RANGES / EMPTY_TEXT_TRACKS / EMPTY_REMOTE, so those constants moved alongside (they're framework-agnostic — EventTarget and Object.freeze only).

Breaking change

The guards and isQuerySelectorAllCapable now export from @videojs/core instead of @videojs/core/dom. No in-repo or cross-package code imports them by name, so nothing internal breaks.

Testing

Covered by existing tests (moved alongside the source). pnpm -F @videojs/core test passes (1614 tests); pnpm typecheck reports zero errors in packages/core.


Note

Medium Risk
Breaking public export paths for anyone importing predicates from @videojs/core/dom; behavior should be unchanged but widespread type-guard usage affects feature gating across the player store.

Overview
Breaking: Runtime-agnostic media capability type guards (isMediaPauseCapable, hasMetadata, etc.), the EMPTY_* sentinel constants they use, and isQuerySelectorAllCapable now live under core/media and ship from @videojs/core (main entry) and @videojs/core/media/predicate, not from @videojs/core/dom.

isQuerySelectorAllCapable no longer depends on DOM lib types (NodeListOf / HTMLElementTagNameMap); it uses a framework-agnostic NodeListLike<Element>, with the element type supplied at call sites (e.g. HTMLTrackElement in the text-track feature).

DOM store features, media-host, and Vimeo imports are retargeted to ../../../core/media/predicate and ../../../core/media/constants. @videojs/html and @videojs/react re-export predicates from the new subpath so consumers keep a single import surface.

Reviewed by Cursor Bugbot for commit 3724ac3. Bugbot is set up for automated code reviews on this repo. Configure here.

Move the media capability type guards and their sentinel constants from
the DOM layer (dom/media) into the runtime-agnostic core/media layer.
Replace the DOM-lib types in isQuerySelectorAllCapable with a
framework-agnostic NodeListLike look-alike so it compiles under the core
project's WebWorker lib.

BREAKING CHANGE: the media capability guards (isMediaPauseCapable,
isMediaSeekCapable, etc.) and isQuerySelectorAllCapable now export from
@videojs/core instead of @videojs/core/dom.
@netlify

netlify Bot commented Jun 18, 2026

Copy link
Copy Markdown

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit 3724ac3
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/6a3469bdf319940008d7b26e
😎 Deploy Preview https://deploy-preview-1705--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment Jun 18, 2026 9:57pm

Request Review

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

🎨 @videojs/html — no changes
Presets (7)
Entry Size
/video (default) 44.92 kB
/video (default + hls) 184.43 kB
/video (minimal) 44.58 kB
/video (minimal + hls) 184.27 kB
/audio (default) 38.38 kB
/audio (minimal) 37.21 kB
/background 4.20 kB
Media (10)
Entry Size
/media/background-video 1.14 kB
/media/container 1.72 kB
/media/dash-video 242.62 kB
/media/hls-video 141.27 kB
/media/mux-audio 163.78 kB
/media/mux-video 163.64 kB
/media/native-hls-video 9.07 kB
/media/simple-hls-audio-only 16.99 kB
/media/simple-hls-video 18.78 kB
/media/vimeo-video 12.32 kB
Players (5)
Entry Size
/video/player 8.06 kB
/audio/player 5.38 kB
/background/player 3.93 kB
/live-video/player 7.63 kB
/live-audio/player 5.40 kB
Skins (30)
Entry Type Size
/video/minimal-skin.css css 5.45 kB
/video/skin.css css 5.43 kB
/video/minimal-skin js 44.62 kB
/video/minimal-skin.tailwind js 45.16 kB
/video/skin js 44.91 kB
/video/skin.tailwind js 45.59 kB
/audio/minimal-skin.css css 3.60 kB
/audio/skin.css css 3.53 kB
/audio/minimal-skin js 37.21 kB
/audio/minimal-skin.tailwind js 37.59 kB
/audio/skin js 38.42 kB
/audio/skin.tailwind js 38.77 kB
/background/skin.css css 133 B
/background/skin js 1.14 kB
/live-video/minimal-skin.css css 5.45 kB
/live-video/skin.css css 5.43 kB
/live-video/minimal-skin js 43.70 kB
/live-video/minimal-skin.tailwind js 44.14 kB
/live-video/skin js 43.67 kB
/live-video/skin.tailwind js 44.21 kB
/live-audio/minimal-skin.css css 3.60 kB
/live-audio/skin.css css 3.53 kB
/live-audio/minimal-skin js 30.15 kB
/live-audio/minimal-skin.tailwind js 29.64 kB
/live-audio/skin js 31.50 kB
/live-audio/skin.tailwind js 31.12 kB
/global.css css 176 B
/shared.css css 88 B
/tailwind.css css 228 B
/skin-element js 1.44 kB
UI Components (38)
Entry Size
/ui/airplay-button 3.56 kB
/ui/alert-dialog 1.25 kB
/ui/alert-dialog-close 477 B
/ui/alert-dialog-description 426 B
/ui/alert-dialog-title 424 B
/ui/buffering-indicator 2.35 kB
/ui/captions-button 3.72 kB
/ui/captions-radio-group 3.38 kB
/ui/cast-button 3.60 kB
/ui/compounds 8.67 kB
/ui/controls 2.26 kB
/ui/error-dialog 3.48 kB
/ui/fullscreen-button 3.61 kB
/ui/hotkey 2.19 kB
/ui/menu 5.16 kB
/ui/mute-button 3.67 kB
/ui/pip-button 3.67 kB
/ui/play-button 3.64 kB
/ui/playback-rate-button 3.76 kB
/ui/playback-rate-radio-group 3.19 kB
/ui/popover 1.95 kB
/ui/poster 2.29 kB
/ui/quality-radio-group 2.30 kB
/ui/seek-button 3.69 kB
/ui/seek-indicator 3.89 kB
/ui/seek-indicator-value 260 B
/ui/slider 1.46 kB
/ui/status-announcer 3.56 kB
/ui/status-indicator 3.69 kB
/ui/status-indicator-value 218 B
/ui/thumbnail 3.32 kB
/ui/time 3.12 kB
/ui/time-slider 3.92 kB
/ui/tooltip 2.18 kB
/ui/volume-indicator 3.94 kB
/ui/volume-indicator-fill 230 B
/ui/volume-indicator-value 227 B
/ui/volume-slider 2.89 kB

Sizes are marginal over the root entry point.

⚛️ @videojs/react — no changes
Presets (7)
Entry Size
/video (default) 37.38 kB
/video (default + hls) 175.59 kB
/video (minimal) 37.50 kB
/video (minimal + hls) 175.83 kB
/audio (default) 30.57 kB
/audio (minimal) 30.62 kB
/background 754 B
Media (9)
Entry Size
/media/background-video 575 B
/media/dash-video 241.23 kB
/media/hls-video 139.89 kB
/media/mux-audio 162.20 kB
/media/mux-video 162.30 kB
/media/native-hls-video 7.41 kB
/media/simple-hls-audio-only 15.38 kB
/media/simple-hls-video 17.18 kB
/media/vimeo-video 10.58 kB
Skins (27)
Entry Type Size
/tailwind.css css 228 B
/video/minimal-skin.css css 5.37 kB
/video/skin.css css 5.34 kB
/video/minimal-skin js 37.42 kB
/video/minimal-skin.tailwind js 43.10 kB
/video/skin js 37.39 kB
/video/skin.tailwind js 43.09 kB
/audio/minimal-skin.css css 3.47 kB
/audio/skin.css css 3.39 kB
/audio/minimal-skin js 30.52 kB
/audio/minimal-skin.tailwind js 32.32 kB
/audio/skin js 30.48 kB
/audio/skin.tailwind js 34.36 kB
/background/skin.css css 90 B
/background/skin js 272 B
/live-video/minimal-skin.css css 5.37 kB
/live-video/skin.css css 5.34 kB
/live-video/minimal-skin js 32.91 kB
/live-video/minimal-skin.tailwind js 38.53 kB
/live-video/skin js 32.94 kB
/live-video/skin.tailwind js 38.61 kB
/live-audio/minimal-skin.css css 3.47 kB
/live-audio/skin.css css 3.39 kB
/live-audio/minimal-skin js 21.98 kB
/live-audio/minimal-skin.tailwind js 24.84 kB
/live-audio/skin js 22.02 kB
/live-audio/skin.tailwind js 25.01 kB
UI Components (32)
Entry Size
/ui/airplay-button 3.04 kB
/ui/alert-dialog 1.30 kB
/ui/buffering-indicator 2.48 kB
/ui/captions-button 3.12 kB
/ui/captions-radio-group 2.86 kB
/ui/cast-button 2.74 kB
/ui/controls 2.46 kB
/ui/error-dialog 2.70 kB
/ui/fullscreen-button 2.71 kB
/ui/gesture 2.21 kB
/ui/hotkey 2.17 kB
/ui/live-button 3.32 kB
/ui/menu 6.72 kB
/ui/mute-button 2.78 kB
/ui/pip-button 3.05 kB
/ui/play-button 2.76 kB
/ui/playback-rate 2.84 kB
/ui/playback-rate-button 2.73 kB
/ui/popover 2.42 kB
/ui/poster 2.39 kB
/ui/quality 2.99 kB
/ui/seek-button 2.76 kB
/ui/seek-indicator 2.28 kB
/ui/slider 3.59 kB
/ui/status-announcer 2.48 kB
/ui/status-indicator 2.25 kB
/ui/thumbnail 2.71 kB
/ui/time 3.18 kB
/ui/time-slider 4.24 kB
/ui/tooltip 2.42 kB
/ui/volume-indicator 2.19 kB
/ui/volume-slider 3.56 kB

Sizes are marginal over the root entry point.

🧩 @videojs/core

Path Base PR Diff %
. 10.05 kB 10.50 kB +463 B +4.5% 🔺
/media/predicate 573 B 🆕
Entries (68)
Entry Size
. 10.50 kB
/dom 17.01 kB
/dom/media/custom-media-element 2.09 kB
/dom/media/dash 236.88 kB
/dom/media/google-cast 4.03 kB
/dom/media/hls 135.73 kB
/dom/media/media-host 1.25 kB
/dom/media/media-played-ranges 576 B
/dom/media/mux 151.26 kB
/dom/media/native-hls 3.07 kB
/dom/media/simple-hls 16.54 kB
/dom/media/simple-hls-audio-only 14.75 kB
/dom/media/vimeo 9.86 kB
/media/predicate 573 B
/i18n 29.34 kB
/i18n/locales/all 26.96 kB
/i18n/locales/ar 1017 B
/i18n/locales/az 914 B
/i18n/locales/bg 1.02 kB
/i18n/locales/bn 1.04 kB
/i18n/locales/bs 837 B
/i18n/locales/ca 878 B
/i18n/locales/cs 865 B
/i18n/locales/cy 829 B
/i18n/locales/da 821 B
/i18n/locales/de 916 B
/i18n/locales/el 1.20 kB
/i18n/locales/en 645 B
/i18n/locales/es 829 B
/i18n/locales/et 882 B
/i18n/locales/eu 828 B
/i18n/locales/fa 1008 B
/i18n/locales/fi 856 B
/i18n/locales/fr 896 B
/i18n/locales/gd 917 B
/i18n/locales/gl 816 B
/i18n/locales/he 940 B
/i18n/locales/hi 1.06 kB
/i18n/locales/hr 846 B
/i18n/locales/hu 913 B
/i18n/locales/it 856 B
/i18n/locales/ja 997 B
/i18n/locales/ko 960 B
/i18n/locales/lv 886 B
/i18n/locales/mr 1.07 kB
/i18n/locales/nb 814 B
/i18n/locales/ne 1.06 kB
/i18n/locales/nl 834 B
/i18n/locales/nn 801 B
/i18n/locales/oc 904 B
/i18n/locales/pl 951 B
/i18n/locales/pt 836 B
/i18n/locales/pt-BR 836 B
/i18n/locales/pt-PT 807 B
/i18n/locales/ro 882 B
/i18n/locales/ru 1.10 kB
/i18n/locales/sk 929 B
/i18n/locales/sl 861 B
/i18n/locales/sr 827 B
/i18n/locales/sv 825 B
/i18n/locales/te 1.10 kB
/i18n/locales/th 1.08 kB
/i18n/locales/tr 906 B
/i18n/locales/uk 1.13 kB
/i18n/locales/vi 903 B
/i18n/locales/zh 810 B
/i18n/locales/zh-CN 810 B
/i18n/locales/zh-TW 821 B
🏷️ @videojs/element — no changes
Entries (2)
Entry Size
. 996 B
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Size
. 1.39 kB
/html 696 B
/react 360 B
🔧 @videojs/utils — no changes
Entries (10)
Entry Size
/array 104 B
/dom 2.67 kB
/events 319 B
/function 327 B
/object 275 B
/predicate 265 B
/string 231 B
/style 190 B
/time 930 B
/number 158 B
📦 @videojs/spf — no changes
Entries (4)
Entry Size
. 4.45 kB
/dom 6.33 kB
/hls 15.44 kB
/background-looping-video 12.97 kB

ℹ️ How to interpret

All sizes are standalone totals (minified + brotli).

Icon Meaning
No change
🔺 Increased ≤ 10%
🔴 Increased > 10%
🔽 Decreased
🆕 New (no baseline)

Run pnpm size locally to check current sizes.

…path

Expose the media capability predicates as a granular @videojs/core/media/predicate
subpath and re-export them from the html and react package entry points.
…ml and react

The predicates are already re-exported via the @videojs/core/media/predicate
subpath, so the explicit @videojs/core re-export block is redundant.
@luwes luwes merged commit f0f9eed into main Jun 18, 2026
26 checks passed
@luwes luwes deleted the refactor/media-predicates-to-core branch June 18, 2026 22:00
@luwes luwes mentioned this pull request Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants