BillingApi::plans() threw TypeError when the API returned an empty response body; now falls back to [].CinemaPlayer, LivePlayer, and PlaylistPlayer silently skipped broadcast events that VideoPlayer emitted: Cinema never broadcast player.ready/player.timeupdate, Live never broadcast player.ready/player.ended, and Playlist broadcast nothing at all. All players now broadcast the README-documented event set (LivePlayer intentionally never broadcasts player.timeupdate — live streams have no seekable timeline; playlist-specific transitions stay unbroadcast pending dedicated event classes).player_version defaults to 0.1.0 while the config ships 0.3.0: sync-player-version.yml never updated the README row. The row is now synced by the workflow and locked by a ConfigTest assertion against the shipped default.default_format, default_controls, default_muted, and primary_color (with VIZOR_PRIMARY_COLOR): documented as working options but never read by any rendering path. Player defaults come from the player itself and from component props; forcing format/primary-color attributes from config would override content-API metadata and player-side theming.brand_name and brand_logo (with VIZOR_BRAND_NAME/VIZOR_BRAND_LOGO): same problem — documented but read by nothing. Re-add when white-label theming is actually designed.Vizor::content() (ContentApi) and Vizor::analytics() (AnalyticsApi), plus the management methods ApiKeysApi::list()/create()/revoke(), LicenseKeysApi::list()/generate()/revoke(), and BillingApi::status(). A 2026-07-22 audit of the Vizor API proved every one of these routes requires a Clerk user session (requireAuth, most also admin/owner role) and the API has no dual-auth path — the package's x-api-key header can never authenticate them, so all 18 methods returned 401 in production regardless of input. The surviving surface is exactly what works server-side: ApiKeysApi::validate()/validateDetailed(), LicenseKeysApi::validate()/validateDetailed() (key posted in the body to the public license endpoints), and BillingApi::plans() (public route). Server-side content/analytics access may return if the API grows scoped x-api-key auth for those routes.tier parameter died with LicenseKeysApi::generate(): the API's Zod schema silently stripped it and derives tier from the organization's plan server-side, so it never had any effect.ApiKeysApi::validateDetailed() and LicenseKeysApi::validateDetailed(), returning the full {valid, tier} license result (the existing validate() methods now delegate to these internally).validate() methods gained an optional ?string $domain parameter (defaults to the host parsed from app.url).config('vizor.license_tier') now ships a default value of 'free'.ApiKeysApi::validate()) POSTed to /api/v1/api-keys/validate, a route that doesn't exist, so every request 404'd, was swallowed by the catch block, and every paying customer silently got the free tier + watermark. It now hits POST /api/v1/license/validate (with the required domain field).LicenseKeysApi::validate()) POSTed to /api/v1/license-keys/validate, which also doesn't exist. It now hits POST /api/v1/license/validate-standalone.AnalyticsApi::viewsOverTime(), contentSummary(), and gazeData() called made-up routes instead of the real API paths. They now call /api/v1/analytics/views-over-time, /api/v1/analytics/summary/{id}, and /api/v1/analytics/gaze/{id} respectively.ValidateVizorLicense middleware now exposes the validated tier via config('vizor.license_tier') on success as well as failure (previously the config was only ever set — to free — on the failure path, so a valid license never surfaced its real tier).VideoPlayer::onTimeUpdate() broadcast PlayerTimeUpdate with no payload, so every player.timeupdate event over the wire reported 0/0 regardless of actual playback position. It now broadcasts the real currentTime and duration.video-player view never rendered the content-id, api-key, license-key, or api-endpoint attributes onto the underlying <vz-video> element, breaking API-driven embeds (analytics, license gating) configured through the Livewire component. All four now render when set.Vizor\Laravel\Middleware\VizorCors, shipped since v0.1.0 but never registered as a route middleware alias, documented, or otherwise wired up. It set Access-Control-Allow-Origin: * with X-Api-Key in Access-Control-Allow-Headers — a wildcard-origin CORS footgun with no reason to exist unused. If you manually registered this middleware yourself, migrate to Laravel's built-in HandleCors via config/cors.php.vizor_license_valid (bool) to vizor_license_status (array shape, {valid, tier}), needed to carry the tier fix above. No action needed — old cache entries simply expire within their TTL and are never read by the new code.0.3.0 (player_version config default, player-dist-manifest.json, and PlayerScript's runtime fallback), applied manually with the exact sed rules from sync-player-version.yml — the workflow itself could not run automatically because the publishing repo has no LARAVEL_DISPATCH_TOKEN set, and this repo does not allow Actions to open PRs. Dist layout unchanged (still dist/vizor.js and dist/register.js).0.2.1 (player_version config default, player-dist-manifest.json, and the PlayerScript pin), and sync-player-version.yml extended to also update ConfigTest's shipped-default assertion and PlayerScript's runtime fallback — previously only config/vizor.php, the dist manifest, and PlayerScriptTest were patched, so those two lagged behind after a version bump.pint-fix workflow for contributors without a local PHP toolchain.[email protected]; added a Security section to the README linking the org-wide policy.vizor-vr/laravel-vizor.[@latest](https://github.com/latest)/dist/vizor-player.register.es.js — a file the npm package never shipped — so [@vizorScripts](https://github.com/vizorScripts) 404'd on every page. The script tag now pins [@vizor-vr](https://github.com/vizor-vr)/player@<player_version>/dist/register.js, single-sourced in Vizor\Laravel\Support\PlayerScript and locked by a test against the committed player-dist-manifest.json.sync-player-version.yml sed patterns matched a config format that no longer existed; they now update the config default, the dist manifest, and the pin test together.<x-vizor-caption> Blade component rendering an HTML5 <track kind="subtitles"> (the player consumes track children; there is no vz-caption custom element).InjectVizorAssets middleware (vizor.inject alias): opt-in auto-injection of the pinned player script before </head> (vizor.auto_inject, default off).SECURITY.md.How can I help you explore Laravel packages today?