From 7f2906ae52e6c57bbe5ab2350e214999510046f9 Mon Sep 17 00:00:00 2001 From: Harvey Zhao Date: Sun, 13 Sep 2026 09:03:20 +0800 Subject: [PATCH] refactor(vtt-thumbnail): [PKG-VTT-THUMB-04] type runtime data and resource boundaries --- .../artplayer-plugin-vtt-thumbnail.js | 2 +- .../artplayer-plugin-vtt-thumbnail.legacy.js | 2 +- .../artplayer-plugin-vtt-thumbnail.mjs | 11 +- .../ARCHITECTURE.md | 34 +- .../dist/artplayer-plugin-vtt-thumbnail.js | 2 +- .../artplayer-plugin-vtt-thumbnail.legacy.js | 2 +- .../dist/artplayer-plugin-vtt-thumbnail.mjs | 11 +- .../src/{getVttArray.js => getVttArray.ts} | 3 +- .../src/{index.js => index.ts} | 11 +- .../src/{lifetime.js => lifetime.ts} | 19 +- .../src/{parseVtt.js => parseVtt.ts} | 46 +- .../src/{preview.js => preview.ts} | 22 +- .../src/{request.js => request.ts} | 10 +- .../src/types.ts | 45 + .../tsconfig.json | 5 + .../vtt-thumbnail-runtime-types.json | 2692 +++++++++++++++++ ...26-09-13-PKG-VTT-THUMB-04-runtime-types.md | 22 + refactor/plan.md | 5 +- refactor/progress.md | 12 + refactor/risks.json | 4 +- refactor/tasks.json | 7 +- test/types/vtt-thumbnail-runtime.ts | 48 + 22 files changed, 2940 insertions(+), 75 deletions(-) rename packages/artplayer-plugin-vtt-thumbnail/src/{getVttArray.js => getVttArray.ts} (59%) rename packages/artplayer-plugin-vtt-thumbnail/src/{index.js => index.ts} (75%) rename packages/artplayer-plugin-vtt-thumbnail/src/{lifetime.js => lifetime.ts} (60%) rename packages/artplayer-plugin-vtt-thumbnail/src/{parseVtt.js => parseVtt.ts} (61%) rename packages/artplayer-plugin-vtt-thumbnail/src/{preview.js => preview.ts} (67%) rename packages/artplayer-plugin-vtt-thumbnail/src/{request.js => request.ts} (68%) create mode 100644 packages/artplayer-plugin-vtt-thumbnail/src/types.ts create mode 100644 packages/artplayer-plugin-vtt-thumbnail/tsconfig.json create mode 100644 refactor/baselines/vtt-thumbnail-runtime-types.json create mode 100644 refactor/changes/2026-09-13-PKG-VTT-THUMB-04-runtime-types.md create mode 100644 test/types/vtt-thumbnail-runtime.ts diff --git a/docs/compiled/artplayer-plugin-vtt-thumbnail.js b/docs/compiled/artplayer-plugin-vtt-thumbnail.js index 6433dcb4d..123ef4a7c 100644 --- a/docs/compiled/artplayer-plugin-vtt-thumbnail.js +++ b/docs/compiled/artplayer-plugin-vtt-thumbnail.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";function t(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function e(e,n){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(e)||t(n,"invalid timestamp");const[i,o="0"]=e.split("."),r=i.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||t(n,"timestamp exceeds the supported numeric range"),r}function n(e,n,i){const o=e.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||t(i,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&t(i,"expected four rectangle coordinates");const s={};for(let c=0;c<4;c++){const e=o[2][c],n=r[c];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(n)&&Number.isFinite(Number(n))||t(i,"rectangle coordinates must be finite non-negative decimals"),"w"!==e&&"h"!==e||0!==Number(n)||t(i,"rectangle width and height must be positive"),s[e]=n}let l=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(l)){const t=n.split("/");t.pop(),t.push(l),l=t.join("/")}return{url:l,...s}}function i({lifetime:t,thumbnails:e,progress:n,duration:i,setStyle:o,isMobile:r}){let s=null,l=0;function c(e,n,i){t.closed||o(e,n,i)}return t.own(()=>{if(l++,null!==s){const t=s;s=null,clearTimeout(t)}}),o=>async(u,a,d)=>{if(t.closed)return;const f="played"===u&&d&&r;if("hover"!==u&&!f)return;const m=n.clientWidth*a,h=a*i();if(c(o,"display","flex"),t.closed)return;const p=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,h);if(!p)return c(o,"display","none");if(m>0&&mn.clientWidth-e.w/2?c(t,"left",n.clientWidth-e.w+"px"):c(t,"left",i-e.w/2+"px")}(o,p,m):r||c(o,"display","none"),f&&!t.closed){const e=++l;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==l||t.closed||(s=null,c(o,"display","none"))},500);t.closed||e!==l?clearTimeout(n):s=n}}}return function(o){return async r=>{const{constructor:{utils:{setStyle:s,isMobile:l,addClass:c}},template:{$progress:u}}=r,a=function(t){let e=Boolean(t.isDestroy);const n=new Set;let i;const o=new Promise(t=>{i=t}),r={get closed(){return e},own:t=>(e?s(t):n.add(t),()=>n.delete(t)),listen(n,i){e||(r.own(()=>t.off(n,i)),t.on(n,i))},wait:t=>Promise.race([t,o]),dispose(){if(e)return;e=!0,i();const t=[...n].reverse();n.clear();for(const e of t)s(e)}};function s(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{r.listen("destroy",r.dispose)}catch(l){throw r.dispose(),l}return r}(r),d={name:"artplayerPluginVttThumbnail"};try{if(a.closed)return d;const f=o.vtt,m=await async function(t="",e){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,i=e.own(()=>n?.abort());try{if(e.closed)return;const i=await e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===i.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${i.status}`);if(e.closed)return;return await e.wait(i.text())}finally{i()}}(f,a);if(a.closed)return d;const h=function(i,o=""){"string"!==typeof i&&t(1,"expected text");const r=i.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let s=0;function l(){for(;s[\t ]*([\d:.]+)(?:[\t ].*)?$/);d||t(a,"invalid cue timing");const f=e(d[1],a),m=e(d[2],a);mr.duration,setStyle:s,isMobile:l}),g=o.style||{};return a.closed||r.controls.add({name:"vtt-thumbnail",position:"top",index:20,style:g,mounted(t){a.own(()=>{r.controls["vtt-thumbnail"]===t&&r.controls.remove("vtt-thumbnail")}),a.closed||(c(t,"art-control-thumbnails"),a.listen("setBar",p(t)))}}),d}catch(f){throw a.dispose(),f}}}}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";function t(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function e(e,n){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(e)||t(n,"invalid timestamp");const[i,o="0"]=e.split("."),r=i.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||t(n,"timestamp exceeds the supported numeric range"),r}function n(e,n,i){const o=e.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||t(i,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&t(i,"expected four rectangle coordinates");const s={};for(let c=0;c<4;c++){const e=o[2][c],n=r[c];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(n)&&Number.isFinite(Number(n))||t(i,"rectangle coordinates must be finite non-negative decimals"),"w"!==e&&"h"!==e||0!==Number(n)||t(i,"rectangle width and height must be positive"),s[e]=n}let l=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(l)){const t=n.split("/");t.pop(),t.push(l),l=t.join("/")}return{url:l,...s}}function i({lifetime:t,thumbnails:e,progress:n,duration:i,setStyle:o,isMobile:r}){let s=null,l=0;function c(e,n,i){t.closed||o(e,n,i)}return t.own(()=>{if(l++,null!==s){const t=s;s=null,clearTimeout(t)}}),o=>async(u,a,d)=>{if(t.closed)return;const f="played"===u&&d&&r;if("hover"!==u&&!f)return;const m=n.clientWidth*a,h=a*i();if(c(o,"display","flex"),t.closed)return;const p=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,h);if(!p)return c(o,"display","none");if(m>0&&mn.clientWidth-Number(e.w)/2?c(t,"left",n.clientWidth-Number(e.w)+"px"):c(t,"left",i-Number(e.w)/2+"px")}(o,p,m):r||c(o,"display","none"),f&&!t.closed){const e=++l;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==l||t.closed||(s=null,c(o,"display","none"))},500);t.closed||e!==l?clearTimeout(n):s=n}}}return function(o){return async r=>{const{setStyle:s,isMobile:l,addClass:c}=r.constructor.utils,{$progress:u}=r.template,a=function(t){let e=Boolean(t.isDestroy);const n=new Set;let i;const o=new Promise(t=>{i=t}),r={get closed(){return e},own:t=>(e?s(t):n.add(t),()=>n.delete(t)),listen(n,i){e||(r.own(()=>t.off(n,i)),t.on(n,i))},wait:t=>Promise.race([t,o]),dispose(){if(e)return;e=!0,i();const t=[...n].reverse();n.clear();for(const e of t)s(e)}};function s(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{r.listen("destroy",r.dispose)}catch(l){throw r.dispose(),l}return r}(r),d={name:"artplayerPluginVttThumbnail"};try{if(a.closed)return d;const f=o.vtt,m=await async function(t="",e){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,i=e.own(()=>n?.abort());try{if(e.closed)return;const i=await e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===i.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${i.status}`);if(e.closed)return;return await e.wait(i.text())}finally{i()}}(f,a);if(a.closed)return d;const h=function(i,o=""){"string"!==typeof i&&t(1,"expected text");const r=i.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let s=0;function l(){for(;s[\t ]*([\d:.]+)(?:[\t ].*)?$/);d||t(a,"invalid cue timing");const f=e(d[1],a),m=e(d[2],a);mr.duration,setStyle:s,isMobile:l}),b=o.style||{};return a.closed||r.controls.add({name:"vtt-thumbnail",position:"top",index:20,style:b,mounted(t){a.own(()=>{r.controls["vtt-thumbnail"]===t&&r.controls.remove("vtt-thumbnail")}),a.closed||(c(t,"art-control-thumbnails"),a.listen("setBar",p(t)))}}),d}catch(f){throw a.dispose(),f}}}}); diff --git a/docs/compiled/artplayer-plugin-vtt-thumbnail.legacy.js b/docs/compiled/artplayer-plugin-vtt-thumbnail.legacy.js index e2631f933..3454e65cb 100644 --- a/docs/compiled/artplayer-plugin-vtt-thumbnail.legacy.js +++ b/docs/compiled/artplayer-plugin-vtt-thumbnail.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,i=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,r=(t,r)=>{for(var l in r||(r={}))n.call(r,l)&&i(t,l,r[l]);if(e)for(var l of e(r))o.call(r,l)&&i(t,l,r[l]);return t},l=(t,e,n)=>new Promise((o,i)=>{var r=t=>{try{s(n.next(t))}catch(e){i(e)}},l=t=>{try{s(n.throw(t))}catch(e){i(e)}},s=t=>t.done?o(t.value):Promise.resolve(t.value).then(r,l);s((n=n.apply(t,e)).next())});function s(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function c(t,e){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(t)||s(e,"invalid timestamp");const[n,o="0"]=t.split("."),i=n.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(i))||s(e,"timestamp exceeds the supported numeric range"),i}function u(t,e,n){const o=t.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||s(n,"expected an image URL and four distinct xywh keys");const i=o[3].split(",").map(t=>t.trim());4!==i.length&&s(n,"expected four rectangle coordinates");const l={};for(let r=0;r<4;r++){const t=o[2][r],e=i[r];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(e)&&Number.isFinite(Number(e))||s(n,"rectangle coordinates must be finite non-negative decimals"),"w"!==t&&"h"!==t||0!==Number(e)||s(n,"rectangle width and height must be positive"),l[t]=e}let c=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(c)){const t=e.split("/");t.pop(),t.push(c),c=t.join("/")}return r({url:c},l)}function a({lifetime:t,thumbnails:e,progress:n,duration:o,setStyle:i,isMobile:r}){let s=null,c=0;function u(e,n,o){t.closed||i(e,n,o)}return t.own(()=>{if(c++,null!==s){const t=s;s=null,clearTimeout(t)}}),i=>(a,d,f)=>l(null,null,function*(){if(t.closed)return;const l="played"===a&&f&&r;if("hover"!==a&&!l)return;const p=n.clientWidth*d,m=d*o();if(u(i,"display","flex"),t.closed)return;const h=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,m);if(!h)return u(i,"display","none");if(p>0&&pn.clientWidth-e.w/2?u(t,"left",n.clientWidth-e.w+"px"):u(t,"left",o-e.w/2+"px")}(i,h,p):r||u(i,"display","none"),l&&!t.closed){const e=++c;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==c||t.closed||(s=null,u(i,"display","none"))},500);t.closed||e!==c?clearTimeout(n):s=n}})}return function(t){return e=>l(null,null,function*(){const{constructor:{utils:{setStyle:n,isMobile:o,addClass:i}},template:{$progress:d}}=e,f=function(t){let e=Boolean(t.isDestroy);const n=new Set;let o;const i=new Promise(t=>{o=t}),r={get closed(){return e},own:t=>(e?l(t):n.add(t),()=>n.delete(t)),listen(n,o){e||(r.own(()=>t.off(n,o)),t.on(n,o))},wait:t=>Promise.race([t,i]),dispose(){if(e)return;e=!0,o();const t=[...n].reverse();n.clear();for(const e of t)l(e)}};function l(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{r.listen("destroy",r.dispose)}catch(s){throw r.dispose(),s}return r}(e),p={name:"artplayerPluginVttThumbnail"};try{if(f.closed)return p;const m=t.vtt,h=yield function(t="",e){return l(this,null,function*(){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,o=e.own(()=>null==n?void 0:n.abort());try{if(e.closed)return;const o=yield e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${o.status}`);if(e.closed)return;return yield e.wait(o.text())}finally{o()}})}(m,f);if(f.closed)return p;const y=function(t,e=""){"string"!==typeof t&&s(1,"expected text");const n=t.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let o=0;function i(){for(;o[\t ]*([\d:.]+)(?:[\t ].*)?$/);f||s(d,"invalid cue timing");const p=c(f[1],d),m=c(f[2],d);me.duration,setStyle:n,isMobile:o}),g=t.style||{};return f.closed||e.controls.add({name:"vtt-thumbnail",position:"top",index:20,style:g,mounted(t){f.own(()=>{e.controls["vtt-thumbnail"]===t&&e.controls.remove("vtt-thumbnail")}),f.closed||(i(t,"art-control-thumbnails"),f.listen("setBar",b(t)))}}),p}catch(m){throw f.dispose(),m}})}}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,i=(t,i)=>{for(var l in i||(i={}))n.call(i,l)&&r(t,l,i[l]);if(e)for(var l of e(i))o.call(i,l)&&r(t,l,i[l]);return t},l=(t,e,n)=>new Promise((o,r)=>{var i=t=>{try{s(n.next(t))}catch(e){r(e)}},l=t=>{try{s(n.throw(t))}catch(e){r(e)}},s=t=>t.done?o(t.value):Promise.resolve(t.value).then(i,l);s((n=n.apply(t,e)).next())});function s(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function c(t,e){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(t)||s(e,"invalid timestamp");const[n,o="0"]=t.split("."),r=n.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||s(e,"timestamp exceeds the supported numeric range"),r}function u(t,e,n){const o=t.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||s(n,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&s(n,"expected four rectangle coordinates");const l={};for(let i=0;i<4;i++){const t=o[2][i],e=r[i];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(e)&&Number.isFinite(Number(e))||s(n,"rectangle coordinates must be finite non-negative decimals"),"w"!==t&&"h"!==t||0!==Number(e)||s(n,"rectangle width and height must be positive"),l[t]=e}let c=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(c)){const t=e.split("/");t.pop(),t.push(c),c=t.join("/")}return i({url:c},l)}function a({lifetime:t,thumbnails:e,progress:n,duration:o,setStyle:r,isMobile:i}){let s=null,c=0;function u(e,n,o){t.closed||r(e,n,o)}return t.own(()=>{if(c++,null!==s){const t=s;s=null,clearTimeout(t)}}),r=>(a,d,f)=>l(null,null,function*(){if(t.closed)return;const l="played"===a&&f&&i;if("hover"!==a&&!l)return;const m=n.clientWidth*d,p=d*o();if(u(r,"display","flex"),t.closed)return;const h=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,p);if(!h)return u(r,"display","none");if(m>0&&mn.clientWidth-Number(e.w)/2?u(t,"left",n.clientWidth-Number(e.w)+"px"):u(t,"left",o-Number(e.w)/2+"px")}(r,h,m):i||u(r,"display","none"),l&&!t.closed){const e=++c;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==c||t.closed||(s=null,u(r,"display","none"))},500);t.closed||e!==c?clearTimeout(n):s=n}})}return function(t){return e=>l(null,null,function*(){const{setStyle:n,isMobile:o,addClass:r}=e.constructor.utils,{$progress:d}=e.template,f=function(t){let e=Boolean(t.isDestroy);const n=new Set;let o;const r=new Promise(t=>{o=t}),i={get closed(){return e},own:t=>(e?l(t):n.add(t),()=>n.delete(t)),listen(n,o){e||(i.own(()=>t.off(n,o)),t.on(n,o))},wait:t=>Promise.race([t,r]),dispose(){if(e)return;e=!0,o();const t=[...n].reverse();n.clear();for(const e of t)l(e)}};function l(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{i.listen("destroy",i.dispose)}catch(s){throw i.dispose(),s}return i}(e),m={name:"artplayerPluginVttThumbnail"};try{if(f.closed)return m;const p=t.vtt,h=yield function(t="",e){return l(this,null,function*(){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,o=e.own(()=>null==n?void 0:n.abort());try{if(e.closed)return;const o=yield e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${o.status}`);if(e.closed)return;return yield e.wait(o.text())}finally{o()}})}(p,f);if(f.closed)return m;const b=function(t,e=""){"string"!==typeof t&&s(1,"expected text");const n=t.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let o=0;function r(){for(;o[\t ]*([\d:.]+)(?:[\t ].*)?$/);f||s(d,"invalid cue timing");const m=c(f[1],d),p=c(f[2],d);pe.duration,setStyle:n,isMobile:o}),g=t.style||{};return f.closed||e.controls.add({name:"vtt-thumbnail",position:"top",index:20,style:g,mounted(t){f.own(()=>{e.controls["vtt-thumbnail"]===t&&e.controls.remove("vtt-thumbnail")}),f.closed||(r(t,"art-control-thumbnails"),f.listen("setBar",y(t)))}}),m}catch(p){throw f.dispose(),p}})}}); diff --git a/docs/compiled/artplayer-plugin-vtt-thumbnail.mjs b/docs/compiled/artplayer-plugin-vtt-thumbnail.mjs index c42550625..9e410b152 100644 --- a/docs/compiled/artplayer-plugin-vtt-thumbnail.mjs +++ b/docs/compiled/artplayer-plugin-vtt-thumbnail.mjs @@ -168,12 +168,12 @@ function createPreview({ lifetime, thumbnails, progress, duration, setStyle, isM style(control, "height", `${cue.h}px`); style(control, "width", `${cue.w}px`); style(control, "backgroundPosition", `-${cue.x}px -${cue.y}px`); - if (width <= cue.w / 2) + if (width <= Number(cue.w) / 2) style(control, "left", 0); - else if (width > progress.clientWidth - cue.w / 2) - style(control, "left", `${progress.clientWidth - cue.w}px`); + else if (width > progress.clientWidth - Number(cue.w) / 2) + style(control, "left", `${progress.clientWidth - Number(cue.w)}px`); else - style(control, "left", `${width - cue.w / 2}px`); + style(control, "left", `${width - Number(cue.w) / 2}px`); } return (control) => async (type, percentage, event) => { if (lifetime.closed) @@ -234,7 +234,8 @@ async function requestVtt(url = "", lifetime) { } function artplayerPluginVttThumbnail(option) { return async (art) => { - const { constructor: { utils: { setStyle, isMobile, addClass } }, template: { $progress } } = art; + const { setStyle, isMobile, addClass } = art.constructor.utils; + const { $progress } = art.template; const lifetime = createLifetime(art); const result = { name: "artplayerPluginVttThumbnail" }; try { diff --git a/packages/artplayer-plugin-vtt-thumbnail/ARCHITECTURE.md b/packages/artplayer-plugin-vtt-thumbnail/ARCHITECTURE.md index f83e770fa..6595c429c 100644 --- a/packages/artplayer-plugin-vtt-thumbnail/ARCHITECTURE.md +++ b/packages/artplayer-plugin-vtt-thumbnail/ARCHITECTURE.md @@ -3,27 +3,27 @@ The factory remains `artplayerPluginVttThumbnail(option)`. Its registration function is asynchronous and resolves to `{ name: 'artplayerPluginVttThumbnail' }`. Existing public declarations still describe a synchronous result; task PKG-VTT-THUMB-04 owns that conflict -and the strict TypeScript migration. Do not infer the return shape from those declarations. +and the public declaration/consumer migration. The seven internal TS files are strict. Do not infer the return shape from those declarations. ## Modules and ownership -- `src/index.js` composes registration, captures the VTT URL before awaiting, reads style +- `src/index.ts` composes registration, captures the VTT URL before awaiting, reads style afterwards, and installs the `vtt-thumbnail` control with its existing CSS class/index. -- `src/lifetime.js` owns one registration's destroy listener, event listeners and cleanup +- `src/lifetime.ts` owns one registration's destroy listener, event listeners and cleanup callbacks. Close is idempotent. Cleanup continues after a callback fails. An independent cancellation Promise settles pending work even if fetch does not honor AbortSignal. -- `src/request.js` owns a native AbortController when available and reads the response body. +- `src/request.ts` owns a native AbortController when available and reads the response body. Destroy cancels both phases; late rejections are observed. Failed HTTP responses reject before parsing. The controller cleanup is released when the request ends. -- `src/parseVtt.js` parses thumbnail metadata and exports the pure `findThumbnail` lookup. +- `src/parseVtt.ts` parses thumbnail metadata and exports the pure `findThumbnail` lookup. It preserves timestamp flooring, first inclusive matches, decimal xywh strings and lexical URL joining. It handles cue IDs, NOTE/STYLE/REGION blocks, BOM/line endings and timing settings; invalid input raises TypeError with the original line before any UI is installed. -- `src/preview.js` owns the mobile 500ms timer and creates the setBar callback. Timer +- `src/preview.ts` owns the mobile 500ms timer and creates the setBar callback. Timer generations reject old callbacks. Every style write checks lifetime state, including callbacks captured before destroy. Geometry, mobile/desktop edges and cue selection retain the latest published behavior. -- `src/getVttArray.js` retains the old source helper signature as a fetch/parse wrapper. +- `src/getVttArray.ts` retains the old source helper signature as a fetch/parse wrapper. The player registration uses the owned request module instead. Distribution/deep-import review belongs to PKG-VTT-THUMB-06. @@ -32,6 +32,26 @@ removing it, cleanup compares the live `art.controls['vtt-thumbnail']` alias wit element. It must not remove a later replacement. Partial listener/control installation is rolled back if initialization throws, preserving the original rejection. +## Runtime types + +`src/types.ts` defines options, result, rectangle/cue data, owned events, cleanup and +preview inputs. Request cancellation is typed as a void branch; actual registration is +Promise. The lifetime accepts only destroy/setBar and uses the core event tuples. +The preview receives only its required DOM/style/duration inputs. + +Assertions are limited to the real Artplayer constructor boundary, validated regex groups +and loop indexes, the completed four-key rectangle, and native responses after the closed +check. The latter is valid because native fetch returns Response and cancellation only +resolves void after closing; malformed custom responses still throw at runtime. Numeric +rectangle strings are explicitly converted for arithmetic while the zero style write stays +numeric. No any, unchecked JS or skipLibCheck exception is introduced. + +The package tsconfig also checks test/types/vtt-thumbnail-runtime.ts. It tests the async +result, required options, CSS/URL types, event tuples, cancellation, readonly state and raw +rectangle strings, including expected errors. It separately proves the old public result +is synchronous in declarations; those declarations are not silently corrected in this +runtime checkpoint. New/old declaration consumers and export aliases remain task 04. + ## Compatibility and intentional fixes Option/style remain live at their historical read points. The return stays a Promise; diff --git a/packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.js b/packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.js index 6433dcb4d..123ef4a7c 100644 --- a/packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.js +++ b/packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";function t(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function e(e,n){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(e)||t(n,"invalid timestamp");const[i,o="0"]=e.split("."),r=i.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||t(n,"timestamp exceeds the supported numeric range"),r}function n(e,n,i){const o=e.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||t(i,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&t(i,"expected four rectangle coordinates");const s={};for(let c=0;c<4;c++){const e=o[2][c],n=r[c];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(n)&&Number.isFinite(Number(n))||t(i,"rectangle coordinates must be finite non-negative decimals"),"w"!==e&&"h"!==e||0!==Number(n)||t(i,"rectangle width and height must be positive"),s[e]=n}let l=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(l)){const t=n.split("/");t.pop(),t.push(l),l=t.join("/")}return{url:l,...s}}function i({lifetime:t,thumbnails:e,progress:n,duration:i,setStyle:o,isMobile:r}){let s=null,l=0;function c(e,n,i){t.closed||o(e,n,i)}return t.own(()=>{if(l++,null!==s){const t=s;s=null,clearTimeout(t)}}),o=>async(u,a,d)=>{if(t.closed)return;const f="played"===u&&d&&r;if("hover"!==u&&!f)return;const m=n.clientWidth*a,h=a*i();if(c(o,"display","flex"),t.closed)return;const p=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,h);if(!p)return c(o,"display","none");if(m>0&&mn.clientWidth-e.w/2?c(t,"left",n.clientWidth-e.w+"px"):c(t,"left",i-e.w/2+"px")}(o,p,m):r||c(o,"display","none"),f&&!t.closed){const e=++l;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==l||t.closed||(s=null,c(o,"display","none"))},500);t.closed||e!==l?clearTimeout(n):s=n}}}return function(o){return async r=>{const{constructor:{utils:{setStyle:s,isMobile:l,addClass:c}},template:{$progress:u}}=r,a=function(t){let e=Boolean(t.isDestroy);const n=new Set;let i;const o=new Promise(t=>{i=t}),r={get closed(){return e},own:t=>(e?s(t):n.add(t),()=>n.delete(t)),listen(n,i){e||(r.own(()=>t.off(n,i)),t.on(n,i))},wait:t=>Promise.race([t,o]),dispose(){if(e)return;e=!0,i();const t=[...n].reverse();n.clear();for(const e of t)s(e)}};function s(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{r.listen("destroy",r.dispose)}catch(l){throw r.dispose(),l}return r}(r),d={name:"artplayerPluginVttThumbnail"};try{if(a.closed)return d;const f=o.vtt,m=await async function(t="",e){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,i=e.own(()=>n?.abort());try{if(e.closed)return;const i=await e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===i.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${i.status}`);if(e.closed)return;return await e.wait(i.text())}finally{i()}}(f,a);if(a.closed)return d;const h=function(i,o=""){"string"!==typeof i&&t(1,"expected text");const r=i.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let s=0;function l(){for(;s[\t ]*([\d:.]+)(?:[\t ].*)?$/);d||t(a,"invalid cue timing");const f=e(d[1],a),m=e(d[2],a);mr.duration,setStyle:s,isMobile:l}),g=o.style||{};return a.closed||r.controls.add({name:"vtt-thumbnail",position:"top",index:20,style:g,mounted(t){a.own(()=>{r.controls["vtt-thumbnail"]===t&&r.controls.remove("vtt-thumbnail")}),a.closed||(c(t,"art-control-thumbnails"),a.listen("setBar",p(t)))}}),d}catch(f){throw a.dispose(),f}}}}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";function t(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function e(e,n){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(e)||t(n,"invalid timestamp");const[i,o="0"]=e.split("."),r=i.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||t(n,"timestamp exceeds the supported numeric range"),r}function n(e,n,i){const o=e.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||t(i,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&t(i,"expected four rectangle coordinates");const s={};for(let c=0;c<4;c++){const e=o[2][c],n=r[c];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(n)&&Number.isFinite(Number(n))||t(i,"rectangle coordinates must be finite non-negative decimals"),"w"!==e&&"h"!==e||0!==Number(n)||t(i,"rectangle width and height must be positive"),s[e]=n}let l=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(l)){const t=n.split("/");t.pop(),t.push(l),l=t.join("/")}return{url:l,...s}}function i({lifetime:t,thumbnails:e,progress:n,duration:i,setStyle:o,isMobile:r}){let s=null,l=0;function c(e,n,i){t.closed||o(e,n,i)}return t.own(()=>{if(l++,null!==s){const t=s;s=null,clearTimeout(t)}}),o=>async(u,a,d)=>{if(t.closed)return;const f="played"===u&&d&&r;if("hover"!==u&&!f)return;const m=n.clientWidth*a,h=a*i();if(c(o,"display","flex"),t.closed)return;const p=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,h);if(!p)return c(o,"display","none");if(m>0&&mn.clientWidth-Number(e.w)/2?c(t,"left",n.clientWidth-Number(e.w)+"px"):c(t,"left",i-Number(e.w)/2+"px")}(o,p,m):r||c(o,"display","none"),f&&!t.closed){const e=++l;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==l||t.closed||(s=null,c(o,"display","none"))},500);t.closed||e!==l?clearTimeout(n):s=n}}}return function(o){return async r=>{const{setStyle:s,isMobile:l,addClass:c}=r.constructor.utils,{$progress:u}=r.template,a=function(t){let e=Boolean(t.isDestroy);const n=new Set;let i;const o=new Promise(t=>{i=t}),r={get closed(){return e},own:t=>(e?s(t):n.add(t),()=>n.delete(t)),listen(n,i){e||(r.own(()=>t.off(n,i)),t.on(n,i))},wait:t=>Promise.race([t,o]),dispose(){if(e)return;e=!0,i();const t=[...n].reverse();n.clear();for(const e of t)s(e)}};function s(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{r.listen("destroy",r.dispose)}catch(l){throw r.dispose(),l}return r}(r),d={name:"artplayerPluginVttThumbnail"};try{if(a.closed)return d;const f=o.vtt,m=await async function(t="",e){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,i=e.own(()=>n?.abort());try{if(e.closed)return;const i=await e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===i.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${i.status}`);if(e.closed)return;return await e.wait(i.text())}finally{i()}}(f,a);if(a.closed)return d;const h=function(i,o=""){"string"!==typeof i&&t(1,"expected text");const r=i.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let s=0;function l(){for(;s[\t ]*([\d:.]+)(?:[\t ].*)?$/);d||t(a,"invalid cue timing");const f=e(d[1],a),m=e(d[2],a);mr.duration,setStyle:s,isMobile:l}),b=o.style||{};return a.closed||r.controls.add({name:"vtt-thumbnail",position:"top",index:20,style:b,mounted(t){a.own(()=>{r.controls["vtt-thumbnail"]===t&&r.controls.remove("vtt-thumbnail")}),a.closed||(c(t,"art-control-thumbnails"),a.listen("setBar",p(t)))}}),d}catch(f){throw a.dispose(),f}}}}); diff --git a/packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.legacy.js b/packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.legacy.js index e2631f933..3454e65cb 100644 --- a/packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.legacy.js +++ b/packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,i=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,r=(t,r)=>{for(var l in r||(r={}))n.call(r,l)&&i(t,l,r[l]);if(e)for(var l of e(r))o.call(r,l)&&i(t,l,r[l]);return t},l=(t,e,n)=>new Promise((o,i)=>{var r=t=>{try{s(n.next(t))}catch(e){i(e)}},l=t=>{try{s(n.throw(t))}catch(e){i(e)}},s=t=>t.done?o(t.value):Promise.resolve(t.value).then(r,l);s((n=n.apply(t,e)).next())});function s(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function c(t,e){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(t)||s(e,"invalid timestamp");const[n,o="0"]=t.split("."),i=n.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(i))||s(e,"timestamp exceeds the supported numeric range"),i}function u(t,e,n){const o=t.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||s(n,"expected an image URL and four distinct xywh keys");const i=o[3].split(",").map(t=>t.trim());4!==i.length&&s(n,"expected four rectangle coordinates");const l={};for(let r=0;r<4;r++){const t=o[2][r],e=i[r];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(e)&&Number.isFinite(Number(e))||s(n,"rectangle coordinates must be finite non-negative decimals"),"w"!==t&&"h"!==t||0!==Number(e)||s(n,"rectangle width and height must be positive"),l[t]=e}let c=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(c)){const t=e.split("/");t.pop(),t.push(c),c=t.join("/")}return r({url:c},l)}function a({lifetime:t,thumbnails:e,progress:n,duration:o,setStyle:i,isMobile:r}){let s=null,c=0;function u(e,n,o){t.closed||i(e,n,o)}return t.own(()=>{if(c++,null!==s){const t=s;s=null,clearTimeout(t)}}),i=>(a,d,f)=>l(null,null,function*(){if(t.closed)return;const l="played"===a&&f&&r;if("hover"!==a&&!l)return;const p=n.clientWidth*d,m=d*o();if(u(i,"display","flex"),t.closed)return;const h=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,m);if(!h)return u(i,"display","none");if(p>0&&pn.clientWidth-e.w/2?u(t,"left",n.clientWidth-e.w+"px"):u(t,"left",o-e.w/2+"px")}(i,h,p):r||u(i,"display","none"),l&&!t.closed){const e=++c;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==c||t.closed||(s=null,u(i,"display","none"))},500);t.closed||e!==c?clearTimeout(n):s=n}})}return function(t){return e=>l(null,null,function*(){const{constructor:{utils:{setStyle:n,isMobile:o,addClass:i}},template:{$progress:d}}=e,f=function(t){let e=Boolean(t.isDestroy);const n=new Set;let o;const i=new Promise(t=>{o=t}),r={get closed(){return e},own:t=>(e?l(t):n.add(t),()=>n.delete(t)),listen(n,o){e||(r.own(()=>t.off(n,o)),t.on(n,o))},wait:t=>Promise.race([t,i]),dispose(){if(e)return;e=!0,o();const t=[...n].reverse();n.clear();for(const e of t)l(e)}};function l(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{r.listen("destroy",r.dispose)}catch(s){throw r.dispose(),s}return r}(e),p={name:"artplayerPluginVttThumbnail"};try{if(f.closed)return p;const m=t.vtt,h=yield function(t="",e){return l(this,null,function*(){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,o=e.own(()=>null==n?void 0:n.abort());try{if(e.closed)return;const o=yield e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${o.status}`);if(e.closed)return;return yield e.wait(o.text())}finally{o()}})}(m,f);if(f.closed)return p;const y=function(t,e=""){"string"!==typeof t&&s(1,"expected text");const n=t.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let o=0;function i(){for(;o[\t ]*([\d:.]+)(?:[\t ].*)?$/);f||s(d,"invalid cue timing");const p=c(f[1],d),m=c(f[2],d);me.duration,setStyle:n,isMobile:o}),g=t.style||{};return f.closed||e.controls.add({name:"vtt-thumbnail",position:"top",index:20,style:g,mounted(t){f.own(()=>{e.controls["vtt-thumbnail"]===t&&e.controls.remove("vtt-thumbnail")}),f.closed||(i(t,"art-control-thumbnails"),f.listen("setBar",b(t)))}}),p}catch(m){throw f.dispose(),m}})}}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginVttThumbnail=e(),define(function(){return t.artplayerPluginVttThumbnail})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginVttThumbnail=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o,i=(t,i)=>{for(var l in i||(i={}))n.call(i,l)&&r(t,l,i[l]);if(e)for(var l of e(i))o.call(i,l)&&r(t,l,i[l]);return t},l=(t,e,n)=>new Promise((o,r)=>{var i=t=>{try{s(n.next(t))}catch(e){r(e)}},l=t=>{try{s(n.throw(t))}catch(e){r(e)}},s=t=>t.done?o(t.value):Promise.resolve(t.value).then(i,l);s((n=n.apply(t,e)).next())});function s(t,e){throw new TypeError(`Invalid VTT thumbnail at line ${t}: ${e}`)}function c(t,e){/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(t)||s(e,"invalid timestamp");const[n,o="0"]=t.split("."),r=n.split(":").reduce((t,e)=>60*t+Number(e),0)+Number(o)/1e3;return Number.isSafeInteger(Math.floor(r))||s(e,"timestamp exceeds the supported numeric range"),r}function u(t,e,n){const o=t.match(/^(.+)#([xywh]{4})=(.*)$/);o&&4===new Set(o[2]).size||s(n,"expected an image URL and four distinct xywh keys");const r=o[3].split(",").map(t=>t.trim());4!==r.length&&s(n,"expected four rectangle coordinates");const l={};for(let i=0;i<4;i++){const t=o[2][i],e=r[i];/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(e)&&Number.isFinite(Number(e))||s(n,"rectangle coordinates must be finite non-negative decimals"),"w"!==t&&"h"!==t||0!==Number(e)||s(n,"rectangle width and height must be positive"),l[t]=e}let c=o[1];if(!/^\/|(?:https?|ftp|file):\/\//i.test(c)){const t=e.split("/");t.pop(),t.push(c),c=t.join("/")}return i({url:c},l)}function a({lifetime:t,thumbnails:e,progress:n,duration:o,setStyle:r,isMobile:i}){let s=null,c=0;function u(e,n,o){t.closed||r(e,n,o)}return t.own(()=>{if(c++,null!==s){const t=s;s=null,clearTimeout(t)}}),r=>(a,d,f)=>l(null,null,function*(){if(t.closed)return;const l="played"===a&&f&&i;if("hover"!==a&&!l)return;const m=n.clientWidth*d,p=d*o();if(u(r,"display","flex"),t.closed)return;const h=function(t,e){return t.find(t=>e>=t.start&&e<=t.end)}(e,p);if(!h)return u(r,"display","none");if(m>0&&mn.clientWidth-Number(e.w)/2?u(t,"left",n.clientWidth-Number(e.w)+"px"):u(t,"left",o-Number(e.w)/2+"px")}(r,h,m):i||u(r,"display","none"),l&&!t.closed){const e=++c;if(null!==s&&clearTimeout(s),t.closed)return;const n=setTimeout(()=>{e!==c||t.closed||(s=null,u(r,"display","none"))},500);t.closed||e!==c?clearTimeout(n):s=n}})}return function(t){return e=>l(null,null,function*(){const{setStyle:n,isMobile:o,addClass:r}=e.constructor.utils,{$progress:d}=e.template,f=function(t){let e=Boolean(t.isDestroy);const n=new Set;let o;const r=new Promise(t=>{o=t}),i={get closed(){return e},own:t=>(e?l(t):n.add(t),()=>n.delete(t)),listen(n,o){e||(i.own(()=>t.off(n,o)),t.on(n,o))},wait:t=>Promise.race([t,r]),dispose(){if(e)return;e=!0,o();const t=[...n].reverse();n.clear();for(const e of t)l(e)}};function l(t){try{t()}catch(e){console.warn("Failed to clean up VTT thumbnails:",e)}}try{i.listen("destroy",i.dispose)}catch(s){throw i.dispose(),s}return i}(e),m={name:"artplayerPluginVttThumbnail"};try{if(f.closed)return m;const p=t.vtt,h=yield function(t="",e){return l(this,null,function*(){if(e.closed)return;const n="function"===typeof AbortController?new AbortController:null,o=e.own(()=>null==n?void 0:n.abort());try{if(e.closed)return;const o=yield e.wait(n?fetch(t,{signal:n.signal}):fetch(t));if(e.closed)return;if(!1===o.ok)throw new Error(`Failed to fetch VTT thumbnails: HTTP ${o.status}`);if(e.closed)return;return yield e.wait(o.text())}finally{o()}})}(p,f);if(f.closed)return m;const b=function(t,e=""){"string"!==typeof t&&s(1,"expected text");const n=t.replace(/^\uFEFF/,"").split(/\r\n|\r|\n/);let o=0;function r(){for(;o[\t ]*([\d:.]+)(?:[\t ].*)?$/);f||s(d,"invalid cue timing");const m=c(f[1],d),p=c(f[2],d);pe.duration,setStyle:n,isMobile:o}),g=t.style||{};return f.closed||e.controls.add({name:"vtt-thumbnail",position:"top",index:20,style:g,mounted(t){f.own(()=>{e.controls["vtt-thumbnail"]===t&&e.controls.remove("vtt-thumbnail")}),f.closed||(r(t,"art-control-thumbnails"),f.listen("setBar",y(t)))}}),m}catch(p){throw f.dispose(),p}})}}); diff --git a/packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.mjs b/packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.mjs index c42550625..9e410b152 100644 --- a/packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.mjs +++ b/packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.mjs @@ -168,12 +168,12 @@ function createPreview({ lifetime, thumbnails, progress, duration, setStyle, isM style(control, "height", `${cue.h}px`); style(control, "width", `${cue.w}px`); style(control, "backgroundPosition", `-${cue.x}px -${cue.y}px`); - if (width <= cue.w / 2) + if (width <= Number(cue.w) / 2) style(control, "left", 0); - else if (width > progress.clientWidth - cue.w / 2) - style(control, "left", `${progress.clientWidth - cue.w}px`); + else if (width > progress.clientWidth - Number(cue.w) / 2) + style(control, "left", `${progress.clientWidth - Number(cue.w)}px`); else - style(control, "left", `${width - cue.w / 2}px`); + style(control, "left", `${width - Number(cue.w) / 2}px`); } return (control) => async (type, percentage, event) => { if (lifetime.closed) @@ -234,7 +234,8 @@ async function requestVtt(url = "", lifetime) { } function artplayerPluginVttThumbnail(option) { return async (art) => { - const { constructor: { utils: { setStyle, isMobile, addClass } }, template: { $progress } } = art; + const { setStyle, isMobile, addClass } = art.constructor.utils; + const { $progress } = art.template; const lifetime = createLifetime(art); const result = { name: "artplayerPluginVttThumbnail" }; try { diff --git a/packages/artplayer-plugin-vtt-thumbnail/src/getVttArray.js b/packages/artplayer-plugin-vtt-thumbnail/src/getVttArray.ts similarity index 59% rename from packages/artplayer-plugin-vtt-thumbnail/src/getVttArray.js rename to packages/artplayer-plugin-vtt-thumbnail/src/getVttArray.ts index f452c60eb..73f88cf83 100644 --- a/packages/artplayer-plugin-vtt-thumbnail/src/getVttArray.js +++ b/packages/artplayer-plugin-vtt-thumbnail/src/getVttArray.ts @@ -1,6 +1,7 @@ +import type { Thumbnail } from './types' import parseVtt from './parseVtt' // Preserve the historical source helper while registration owns its requests. -export default async function getVttArray(vttUrl = '') { +export default async function getVttArray(vttUrl = ''): Promise { return parseVtt(await (await fetch(vttUrl)).text(), vttUrl) } diff --git a/packages/artplayer-plugin-vtt-thumbnail/src/index.js b/packages/artplayer-plugin-vtt-thumbnail/src/index.ts similarity index 75% rename from packages/artplayer-plugin-vtt-thumbnail/src/index.js rename to packages/artplayer-plugin-vtt-thumbnail/src/index.ts index 862434102..61a41aa2c 100644 --- a/packages/artplayer-plugin-vtt-thumbnail/src/index.js +++ b/packages/artplayer-plugin-vtt-thumbnail/src/index.ts @@ -1,13 +1,16 @@ +import type Artplayer from 'artplayer' +import type { Option, Result } from './types' import createLifetime from './lifetime' import parseVtt from './parseVtt' import createPreview from './preview' import requestVtt from './request' -export default function artplayerPluginVttThumbnail(option) { - return async (art) => { - const { constructor: { utils: { setStyle, isMobile, addClass } }, template: { $progress } } = art +export default function artplayerPluginVttThumbnail(option: Option) { + return async (art: Artplayer): Promise => { + const { setStyle, isMobile, addClass } = (art.constructor as typeof Artplayer).utils + const { $progress } = art.template const lifetime = createLifetime(art) - const result = { name: 'artplayerPluginVttThumbnail' } + const result: Result = { name: 'artplayerPluginVttThumbnail' } try { if (lifetime.closed) return result diff --git a/packages/artplayer-plugin-vtt-thumbnail/src/lifetime.js b/packages/artplayer-plugin-vtt-thumbnail/src/lifetime.ts similarity index 60% rename from packages/artplayer-plugin-vtt-thumbnail/src/lifetime.js rename to packages/artplayer-plugin-vtt-thumbnail/src/lifetime.ts index 7f96869d0..041d35026 100644 --- a/packages/artplayer-plugin-vtt-thumbnail/src/lifetime.js +++ b/packages/artplayer-plugin-vtt-thumbnail/src/lifetime.ts @@ -1,11 +1,14 @@ -export default function createLifetime(art) { +import type { Events } from 'artplayer' +import type { Cleanup, Lifetime, LifetimeHost, OwnedEvent } from './types' + +export default function createLifetime(art: LifetimeHost): Lifetime { let closed = Boolean(art.isDestroy) - const cleanups = new Set() - let cancel - const cancelled = new Promise((resolve) => { + const cleanups = new Set() + let cancel!: Cleanup + const cancelled = new Promise((resolve) => { cancel = resolve }) - const lifetime = { + const lifetime: Lifetime = { get closed() { return closed }, own(cleanup) { if (closed) @@ -14,13 +17,13 @@ export default function createLifetime(art) { cleanups.add(cleanup) return () => cleanups.delete(cleanup) }, - listen(name, callback) { + listen(name: Name, callback: (...args: Events[Name]) => unknown) { if (closed) return lifetime.own(() => art.off(name, callback)) art.on(name, callback) }, - wait(value) { return Promise.race([value, cancelled]) }, + wait(value: Value | PromiseLike) { return Promise.race([value, cancelled]) }, dispose() { if (closed) return @@ -32,7 +35,7 @@ export default function createLifetime(art) { run(cleanup) }, } - function run(cleanup) { + function run(cleanup: Cleanup) { try { cleanup() } diff --git a/packages/artplayer-plugin-vtt-thumbnail/src/parseVtt.js b/packages/artplayer-plugin-vtt-thumbnail/src/parseVtt.ts similarity index 61% rename from packages/artplayer-plugin-vtt-thumbnail/src/parseVtt.js rename to packages/artplayer-plugin-vtt-thumbnail/src/parseVtt.ts index 9a16c3269..f64555a94 100644 --- a/packages/artplayer-plugin-vtt-thumbnail/src/parseVtt.js +++ b/packages/artplayer-plugin-vtt-thumbnail/src/parseVtt.ts @@ -1,35 +1,37 @@ -function invalid(line, message) { +import type { Rectangle, Thumbnail } from './types' + +function invalid(line: number, message: string): never { throw new TypeError(`Invalid VTT thumbnail at line ${line}: ${message}`) } -function timestamp(value, line) { +function timestamp(value: string, line: number): number { if (!/^\d{2,}(?::\d{2}){0,2}(?:\.\d{3})?$/.test(value)) invalid(line, 'invalid timestamp') const [whole, fraction = '0'] = value.split('.') - const seconds = whole.split(':').reduce((total, part) => total * 60 + Number(part), 0) + Number(fraction) / 1000 + const seconds = whole!.split(':').reduce((total, part) => total * 60 + Number(part), 0) + Number(fraction) / 1000 if (!Number.isSafeInteger(Math.floor(seconds))) invalid(line, 'timestamp exceeds the supported numeric range') return seconds } -function rectangle(text, vttUrl, line) { +function rectangle(text: string, vttUrl: string, line: number): Rectangle & { url: string } { const match = text.match(/^(.+)#([xywh]{4})=(.*)$/) - if (!match || new Set(match[2]).size !== 4) + if (!match || new Set(match[2]!).size !== 4) invalid(line, 'expected an image URL and four distinct xywh keys') - const values = match[3].split(',').map(value => value.trim()) + const values = match[3]!.split(',').map(value => value.trim()) if (values.length !== 4) invalid(line, 'expected four rectangle coordinates') - const rect = {} + const rect = {} as Rectangle for (let index = 0; index < 4; index++) { - const key = match[2][index] - const value = values[index] + const key = match[2]![index] as keyof Rectangle + const value = values[index]! if (!/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(value) || !Number.isFinite(Number(value))) invalid(line, 'rectangle coordinates must be finite non-negative decimals') if ((key === 'w' || key === 'h') && Number(value) === 0) invalid(line, 'rectangle width and height must be positive') rect[key] = value } - let url = match[1] + let url = match[1]! if (!/^\/|(?:https?|ftp|file):\/\//i.test(url)) { const segments = vttUrl.split('/') segments.pop() @@ -39,55 +41,55 @@ function rectangle(text, vttUrl, line) { return { url, ...rect } } -export function findThumbnail(thumbnails, second) { +export function findThumbnail(thumbnails: readonly Thumbnail[], second: number): Thumbnail | undefined { return thumbnails.find(item => second >= item.start && second <= item.end) } -export default function parseVtt(text, vttUrl = '') { +export default function parseVtt(text: unknown, vttUrl = ''): Thumbnail[] { if (typeof text !== 'string') invalid(1, 'expected text') const lines = text.replace(/^\uFEFF/, '').split(/\r\n|\r|\n/) let index = 0 function skipEmpty() { - while (index < lines.length && !lines[index].trim()) + while (index < lines.length && !lines[index]!.trim()) index++ } skipEmpty() if (index === lines.length) return [] - if (!/^WEBVTT(?:[\t ].*)?$/.test(lines[index].trim()) || lines[index].includes('-->')) + if (!/^WEBVTT(?:[\t ].*)?$/.test(lines[index]!.trim()) || lines[index]!.includes('-->')) invalid(index + 1, 'expected WEBVTT header') index++ - const thumbnails = [] + const thumbnails: Thumbnail[] = [] while (index < lines.length) { skipEmpty() if (index === lines.length) break - const first = lines[index].trim() + const first = lines[index]!.trim() if (/^NOTE(?:[\t ]|$)/.test(first) || first === 'STYLE' || first === 'REGION') { - while (index < lines.length && lines[index].trim()) + while (index < lines.length && lines[index]!.trim()) index++ continue } const identifierLine = index + 1 if (!first.includes('-->')) { index++ - if (index === lines.length || !lines[index].trim()) + if (index === lines.length || !lines[index]!.trim()) invalid(identifierLine, 'cue identifier must be followed by timing') } const timingLine = index + 1 - const timing = lines[index].trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/) + const timing = lines[index]!.trim().match(/^([\d:.]+)[\t ]*-->[\t ]*([\d:.]+)(?:[\t ].*)?$/) if (!timing) invalid(timingLine, 'invalid cue timing') - const start = timestamp(timing[1], timingLine) - const end = timestamp(timing[2], timingLine) + const start = timestamp(timing[1]!, timingLine) + const end = timestamp(timing[2]!, timingLine) if (end < start) invalid(timingLine, 'cue end precedes its start') index++ skipEmpty() if (index === lines.length) invalid(timingLine, 'missing sprite image') - const image = rectangle(lines[index].trim(), vttUrl, index + 1) + const image = rectangle(lines[index]!.trim(), vttUrl, index + 1) thumbnails.push({ start: Math.floor(start), end: Math.floor(end), ...image }) index++ } diff --git a/packages/artplayer-plugin-vtt-thumbnail/src/preview.js b/packages/artplayer-plugin-vtt-thumbnail/src/preview.ts similarity index 67% rename from packages/artplayer-plugin-vtt-thumbnail/src/preview.js rename to packages/artplayer-plugin-vtt-thumbnail/src/preview.ts index b23940c85..66414e896 100644 --- a/packages/artplayer-plugin-vtt-thumbnail/src/preview.js +++ b/packages/artplayer-plugin-vtt-thumbnail/src/preview.ts @@ -1,7 +1,9 @@ +import type { Events } from 'artplayer' +import type { PreviewOptions, StyleKey, Thumbnail } from './types' import { findThumbnail } from './parseVtt' -export default function createPreview({ lifetime, thumbnails, progress, duration, setStyle, isMobile }) { - let timer = null +export default function createPreview({ lifetime, thumbnails, progress, duration, setStyle, isMobile }: PreviewOptions) { + let timer: ReturnType | null = null let generation = 0 lifetime.own(() => { generation++ @@ -11,23 +13,23 @@ export default function createPreview({ lifetime, thumbnails, progress, duration clearTimeout(previous) } }) - function style(control, key, value) { + function style(control: HTMLElement, key: StyleKey, value: string | number) { if (!lifetime.closed) - setStyle(control, key, value) + setStyle(control, key, value) } - function show(control, cue, width) { + function show(control: HTMLElement, cue: Thumbnail, width: number) { style(control, 'backgroundImage', `url(${cue.url})`) style(control, 'height', `${cue.h}px`) style(control, 'width', `${cue.w}px`) style(control, 'backgroundPosition', `-${cue.x}px -${cue.y}px`) - if (width <= cue.w / 2) + if (width <= Number(cue.w) / 2) style(control, 'left', 0) - else if (width > progress.clientWidth - cue.w / 2) - style(control, 'left', `${progress.clientWidth - cue.w}px`) + else if (width > progress.clientWidth - Number(cue.w) / 2) + style(control, 'left', `${progress.clientWidth - Number(cue.w)}px`) else - style(control, 'left', `${width - cue.w / 2}px`) + style(control, 'left', `${width - Number(cue.w) / 2}px`) } - return control => async (type, percentage, event) => { + return (control: HTMLElement) => async (type: Events['setBar'][0], percentage: number, event?: Event) => { if (lifetime.closed) return const dragging = type === 'played' && event && isMobile diff --git a/packages/artplayer-plugin-vtt-thumbnail/src/request.js b/packages/artplayer-plugin-vtt-thumbnail/src/request.ts similarity index 68% rename from packages/artplayer-plugin-vtt-thumbnail/src/request.js rename to packages/artplayer-plugin-vtt-thumbnail/src/request.ts index e52f67ac6..4432b17d0 100644 --- a/packages/artplayer-plugin-vtt-thumbnail/src/request.js +++ b/packages/artplayer-plugin-vtt-thumbnail/src/request.ts @@ -1,4 +1,6 @@ -export default async function requestVtt(url = '', lifetime) { +import type { Lifetime } from './types' + +export default async function requestVtt(url = '', lifetime: Lifetime): Promise { if (lifetime.closed) return const controller = typeof AbortController === 'function' ? new AbortController() : null @@ -9,11 +11,11 @@ export default async function requestVtt(url = '', lifetime) { const response = await lifetime.wait(controller ? fetch(url, { signal: controller.signal }) : fetch(url)) if (lifetime.closed) return - if (response.ok === false) - throw new Error(`Failed to fetch VTT thumbnails: HTTP ${response.status}`) + if (response!.ok === false) + throw new Error(`Failed to fetch VTT thumbnails: HTTP ${response!.status}`) if (lifetime.closed) return - return await lifetime.wait(response.text()) + return await lifetime.wait(response!.text()) } finally { release() diff --git a/packages/artplayer-plugin-vtt-thumbnail/src/types.ts b/packages/artplayer-plugin-vtt-thumbnail/src/types.ts new file mode 100644 index 000000000..056ad87dd --- /dev/null +++ b/packages/artplayer-plugin-vtt-thumbnail/src/types.ts @@ -0,0 +1,45 @@ +import type Artplayer from 'artplayer' +import type { Events, Utils } from 'artplayer' + +export interface Option { + vtt?: string + style?: Partial +} + +export interface Result { + name: 'artplayerPluginVttThumbnail' +} + +export interface Rectangle { + x: string + y: string + w: string + h: string +} + +export interface Thumbnail extends Rectangle { + start: number + end: number + url: string +} + +export type Cleanup = () => void +export type OwnedEvent = 'destroy' | 'setBar' +export interface Lifetime { + readonly closed: boolean + own: (cleanup: Cleanup) => () => boolean + listen: (name: Name, callback: (...args: Events[Name]) => unknown) => void + wait: (value: Value | PromiseLike) => Promise + dispose: Cleanup +} + +export type LifetimeHost = Pick +export type StyleKey = 'backgroundImage' | 'height' | 'width' | 'backgroundPosition' | 'left' | 'display' +export interface PreviewOptions { + lifetime: Lifetime + thumbnails: readonly Thumbnail[] + progress: Pick + duration: () => number + setStyle: Utils['setStyle'] + isMobile: boolean +} diff --git a/packages/artplayer-plugin-vtt-thumbnail/tsconfig.json b/packages/artplayer-plugin-vtt-thumbnail/tsconfig.json new file mode 100644 index 000000000..bee977b3f --- /dev/null +++ b/packages/artplayer-plugin-vtt-thumbnail/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { "allowJs": false }, + "include": ["src/**/*.ts", "types/**/*.d.ts", "../../types/assets.d.ts", "../../test/types/vtt-thumbnail-runtime.ts"] +} diff --git a/refactor/baselines/vtt-thumbnail-runtime-types.json b/refactor/baselines/vtt-thumbnail-runtime-types.json new file mode 100644 index 000000000..4899abe0b --- /dev/null +++ b/refactor/baselines/vtt-thumbnail-runtime-types.json @@ -0,0 +1,2692 @@ +{ + "schemaVersion": 1, + "task": "PKG-VTT-THUMB-04", + "status": "doing", + "parentCommit": "80588a3046c97223ed77656dcd9ecc2a059bcefd", + "checks": [ + { + "command": "strict runtime plus positive and negative consumer", + "results": [ + { + "version": "5.9.3", + "strict": true, + "allowJs": false, + "skipLibCheck": false, + "fixture": "D:\\github\\ArtPlayer\\test\\types\\vtt-thumbnail-runtime.ts", + "rootFiles": [ + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/getVttArray.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/index.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/lifetime.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/parseVtt.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/preview.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/request.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/types.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/types/artplayer-plugin-vtt-thumbnail.d.ts", + "D:/github/ArtPlayer/types/assets.d.ts", + "D:/github/ArtPlayer/test/types/vtt-thumbnail-runtime.ts" + ], + "negativeErrors": [ + { + "code": 2339, + "line": 15, + "message": "Property 'name' does not exist on type 'Promise'." + }, + { + "code": 2554, + "line": 17, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2322, + "line": 19, + "message": "Type 'number' is not assignable to type 'string'." + }, + { + "code": 2322, + "line": 21, + "message": "Type 'number' is not assignable to type 'string'." + }, + { + "code": 2345, + "line": 29, + "message": "Argument of type '\"not-an-event\"' is not assignable to parameter of type 'OwnedEvent'." + }, + { + "code": 2540, + "line": 35, + "message": "Cannot assign to 'closed' because it is a read-only property." + }, + { + "code": 2322, + "line": 41, + "message": "Type 'string' is not assignable to type 'number'." + }, + { + "code": 2322, + "line": 47, + "message": "Type '(art: Artplayer) => Promise' is not assignable to type '(art: Artplayer) => { name: \"artplayerPluginVttThumbnail\"; }'.\n Property 'name' is missing in type 'Promise' but required in type '{ name: \"artplayerPluginVttThumbnail\"; }'." + } + ] + }, + { + "version": "5.1.6", + "strict": true, + "allowJs": false, + "skipLibCheck": false, + "fixture": "D:\\github\\ArtPlayer\\test\\types\\vtt-thumbnail-runtime.ts", + "rootFiles": [ + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/getVttArray.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/index.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/lifetime.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/parseVtt.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/preview.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/request.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/src/types.ts", + "D:/github/ArtPlayer/packages/artplayer-plugin-vtt-thumbnail/types/artplayer-plugin-vtt-thumbnail.d.ts", + "D:/github/ArtPlayer/types/assets.d.ts", + "D:/github/ArtPlayer/test/types/vtt-thumbnail-runtime.ts" + ], + "negativeErrors": [ + { + "code": 2339, + "line": 15, + "message": "Property 'name' does not exist on type 'Promise'." + }, + { + "code": 2554, + "line": 17, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2322, + "line": 19, + "message": "Type 'number' is not assignable to type 'string'." + }, + { + "code": 2322, + "line": 21, + "message": "Type 'number' is not assignable to type 'string'." + }, + { + "code": 2345, + "line": 29, + "message": "Argument of type '\"not-an-event\"' is not assignable to parameter of type 'OwnedEvent'." + }, + { + "code": 2540, + "line": 35, + "message": "Cannot assign to 'closed' because it is a read-only property." + }, + { + "code": 2322, + "line": 41, + "message": "Type 'string' is not assignable to type 'number'." + }, + { + "code": 2322, + "line": 47, + "message": "Type '(art: Artplayer) => Promise' is not assignable to type '(art: Artplayer) => { name: \"artplayerPluginVttThumbnail\"; }'.\n Property 'name' is missing in type 'Promise' but required in type '{ name: \"artplayerPluginVttThumbnail\"; }'." + } + ] + } + ], + "file": "refactor/.cache/vtt-thumbnail04-types-verify-final.log", + "sha256": "0ba45fc43253f5c04c00cd5d3995cbddd2cee810179c6561ef79cc3423323968" + }, + { + "command": "all package regressions after TS migration", + "tests": 246, + "pass": 246, + "file": "refactor/.cache/vtt-thumbnail04-unit.log", + "sha256": "1310c345186e1b6cf6cb1799a4b6f91e4951d68a309a0f63dc561eba32573b64" + }, + { + "command": "parser and lifecycle source", + "tests": 47, + "pass": 47, + "file": "refactor/.cache/vtt-thumbnail04-unit-source.log", + "sha256": "cf1cf73d1a6a2dc7462529c2a44f27da10710520840caa84f540272702174ece", + "scope": "Pure parser assertions use source; malformed payloads, extended registration and lifecycle cases also execute the selected factory." + }, + { + "command": "native browser source", + "file": "refactor/.cache/vtt-thumbnail04-browser-source.json", + "sha256": "45e8955cfe7e81375faca71500f2f969470d136514349aa4b0c41a973cb7e906", + "resultsDirectory": "refactor/.cache/vtt-thumbnail04-browser-source-results", + "stats": { + "startTime": "2026-09-13T00:58:16.245Z", + "duration": 13587.298999999999, + "expected": 18, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "VTT native published core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native published core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native published core: destroy aborts a pending native fetch and settles registration", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: destroy aborts a pending native fetch and settles registration", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native published core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native published core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native published core: destroy aborts a pending native fetch and settles registration", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: destroy aborts a pending native fetch and settles registration", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native published core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native published core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native published core: destroy aborts a pending native fetch and settles registration", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "Load request cancelled" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: destroy aborts a pending native fetch and settles registration", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "e86aec1ff330ff0f3a09c20a6de1cfb4841b32895fd1426498fff412e6d15565", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "Load request cancelled" + } + } + ], + "consoleErrors": [] + } + ] + }, + { + "command": "parser and lifecycle main", + "tests": 47, + "pass": 47, + "file": "refactor/.cache/vtt-thumbnail04-unit-main.log", + "sha256": "68395030a56427a1dea562f49038e0cfa9086361d5ad282cab618e26651c8aca", + "scope": "Pure parser assertions use source; malformed payloads, extended registration and lifecycle cases also execute the selected factory." + }, + { + "command": "native browser main", + "file": "refactor/.cache/vtt-thumbnail04-browser-main.json", + "sha256": "4c95d429b24f0a2e1d7c9ce2c71eb6db12be4fd41c0bd6329fada6378a977148", + "resultsDirectory": "refactor/.cache/vtt-thumbnail04-browser-main-results", + "stats": { + "startTime": "2026-09-13T00:58:31.015Z", + "duration": 12938.773000000001, + "expected": 18, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "VTT native published core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native published core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native published core: destroy aborts a pending native fetch and settles registration", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: destroy aborts a pending native fetch and settles registration", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native published core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native published core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native published core: destroy aborts a pending native fetch and settles registration", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: destroy aborts a pending native fetch and settles registration", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native published core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native published core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native published core: destroy aborts a pending native fetch and settles registration", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "Load request cancelled" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: destroy aborts a pending native fetch and settles registration", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "Load request cancelled" + } + } + ], + "consoleErrors": [] + } + ] + }, + { + "command": "parser and lifecycle legacy", + "tests": 47, + "pass": 47, + "file": "refactor/.cache/vtt-thumbnail04-unit-legacy.log", + "sha256": "094da6cd67dd4fe130c6264abdf3d67d90956ebaad01f6491341b4b0c84dd3d1", + "scope": "Pure parser assertions use source; malformed payloads, extended registration and lifecycle cases also execute the selected factory." + }, + { + "command": "native browser legacy", + "file": "refactor/.cache/vtt-thumbnail04-browser-legacy.json", + "sha256": "d87af715fb5564e4a54d03691266317d020560e58e67f33dc33d9ed7d9816914", + "resultsDirectory": "refactor/.cache/vtt-thumbnail04-browser-legacy-results", + "stats": { + "startTime": "2026-09-13T00:58:45.126Z", + "duration": 13309.631000000001, + "expected": 18, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "cases": [ + { + "title": "VTT native published core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native published core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native published core: destroy aborts a pending native fetch and settles registration", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: destroy aborts a pending native fetch and settles registration", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "net::ERR_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "net::ERR_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native published core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native published core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native published core: destroy aborts a pending native fetch and settles registration", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + }, + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/pattern.mp4", + "resourceType": "media", + "failure": { + "errorText": "NS_ERROR_PARSED_DATA_CACHED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: destroy aborts a pending native fetch and settles registration", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "NS_BINDING_ABORTED" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native published core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native published core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native published core: destroy aborts a pending native fetch and settles registration", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "published", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "Load request cancelled" + } + } + ], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: real progress hover selects sprite regions and destroy removes owned UI", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "pixels": [ + [ + 255, + 0, + 0, + 255 + ], + [ + 0, + 0, + 255, + 255 + ] + ] + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: malformed sprite rejects before mounting and a corrected registration recovers", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "recoveredAfter": { + "name": "TypeError", + "message": "Invalid VTT thumbnail at line 4: rectangle coordinates must be finite non-negative decimals" + } + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [], + "consoleErrors": [] + }, + { + "title": "VTT native candidate core: destroy aborts a pending native fetch and settles registration", + "project": "webkit", + "status": "passed", + "browser": "26.6", + "platform": "win32", + "candidate": { + "core": "candidate", + "implementation": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "nativeAbort": true + }, + "core": { + "published": { + "source": { + "kind": "npm-release", + "version": "5.4.0", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "member": "package/dist/artplayer.js" + }, + "sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "bytes": 134037 + }, + "candidate": { + "source": { + "kind": "workspace-build", + "entry": "packages\\artplayer\\src\\index.ts" + }, + "sha256": "e8945bc9d3ff0a6e247772a662f874df13875f7dbeb949968943b9589f6e2946", + "bytes": 378073 + } + }, + "failedRequests": [ + { + "url": "http://127.0.0.1:8084/test/vtt-held.vtt", + "resourceType": "fetch", + "failure": { + "errorText": "Load request cancelled" + } + } + ], + "consoleErrors": [] + } + ] + }, + { + "command": "actual ESM import and controlled registration/hover/destroy", + "file": "packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.mjs", + "sha256": "02c0f18fcac6a690df10df4a9ca90e6e8c9c05134156a412a0691e35234982b1", + "status": "pass", + "scope": "Native import of actual .mjs; controlled host registration/hover/destroy, not installed tarball" + }, + { + "command": "yarn ci:check", + "tests": { + "unit": 1727, + "engineering": 14, + "baseline": 323, + "total": 2064 + }, + "strictProductionTypeScriptFiles": 346, + "file": "refactor/.cache/vtt-thumbnail04-ci.log", + "sha256": "9e6a9a16479ed6824b1045d3a00b51821ab8b7983c5ca5418d7d677de68de2a7" + } + ], + "limitations": [ + "Public declarations/old CommonJS shape and isolated consumers remain pending in 04", + "Installed-package, full core/device and demo/editor evidence remain 05/06" + ], + "scope": "Seven internal strict TypeScript modules and a package-checked positive/negative runtime consumer; public declarations unchanged", + "source": { + "packages/artplayer-plugin-vtt-thumbnail/src/getVttArray.ts": "b7576efb3bae92f973acdf82d0fd4e3a6bfe63c2354deb02567ba1adda25415a", + "packages/artplayer-plugin-vtt-thumbnail/src/index.ts": "24bf090c4975aa295de009a5ad4bc198debaabcd8fe3865e28135391f6c30c1f", + "packages/artplayer-plugin-vtt-thumbnail/src/lifetime.ts": "073d8f38ec7b37c58cf4a3e4b853068645c67d8a834510f5dc68601ace3f9176", + "packages/artplayer-plugin-vtt-thumbnail/src/parseVtt.ts": "66e1d8fc58029366cb78a541e75f8c0c29fa346a6de4f34bf7d262b969a84190", + "packages/artplayer-plugin-vtt-thumbnail/src/preview.ts": "841a16dd9d9b7ea482226cb400f48bf5998705b4a3321807e6c38ff6f7a3046e", + "packages/artplayer-plugin-vtt-thumbnail/src/request.ts": "afae0e57e9c61c034a1494f09d0b9ee4ab43755c61607a94f1d21292a2fee8ef", + "packages/artplayer-plugin-vtt-thumbnail/src/types.ts": "96d56a134c1a14b6ac8fde4381bbdef3875e41365d95ee892e83b968286661a3" + }, + "artifacts": { + "packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.js": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "docs/compiled/artplayer-plugin-vtt-thumbnail.js": "2a16fdbc7a2b52a8d916e2816c8d4eb0457b574fa48e71a4c8190424e84ee7f3", + "packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.legacy.js": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "docs/compiled/artplayer-plugin-vtt-thumbnail.legacy.js": "0ebe0b3ef760cf11253b59e239a753b57949b60d9cbd4a5d6380b354588bc0f4", + "packages/artplayer-plugin-vtt-thumbnail/dist/artplayer-plugin-vtt-thumbnail.mjs": "02c0f18fcac6a690df10df4a9ca90e6e8c9c05134156a412a0691e35234982b1", + "docs/compiled/artplayer-plugin-vtt-thumbnail.mjs": "02c0f18fcac6a690df10df4a9ca90e6e8c9c05134156a412a0691e35234982b1" + } +} diff --git a/refactor/changes/2026-09-13-PKG-VTT-THUMB-04-runtime-types.md b/refactor/changes/2026-09-13-PKG-VTT-THUMB-04-runtime-types.md new file mode 100644 index 000000000..94b663919 --- /dev/null +++ b/refactor/changes/2026-09-13-PKG-VTT-THUMB-04-runtime-types.md @@ -0,0 +1,22 @@ +# PKG-VTT-THUMB-04 严格运行时类型检查点 + +六个 JS 实现迁为 TS,并增加 types.ts,明确 Option/Result、字符串矩形和 Thumbnail、 +资源生命周期、事件元组与最小预览输入。分包 allowJs=false,继承 strict、 +noUncheckedIndexedAccess、skipLibCheck=false、types=[],不引入 any 或忽略检查。 +运行时注册准确返回 Promise,取消分支为 void;parser 输入用 unknown 并先验证。 + +新增由分包 tsconfig 检查的正反例,覆盖 URL/CSS、必传参数、异步结果、关闭状态、 +事件元组和矩形字段。非空/类型断言仅用于已校验正则/数组边界、四键对象构造、 +真实 Artplayer constructor 和关闭检查后的原生 Response;包内架构说明具体不变量。 +数值计算显式 Number 转换,保留正常坐标/进度样式及零值写入。 + +公开声明、package exports、版本、依赖保持原样。类型用例明确复现旧声明返回同步 +对象与实际 Promise 的冲突,没有靠给 Promise 增加 name 属性或类型断言伪造同步结果。 +04 继续 doing,下一步验证旧编译器/消费形式、异步类型视图、CJS default 兼容、 +模块声明和编辑器;不能将严格内部类型通过等同于公开声明已完成。 + +本次沿用 246 项本包契约/错误/候选测试,并验证正式构建和真实浏览器。 +准确入口、类型和 CI 结果见 [验证记录](../baselines/vtt-thumbnail-runtime-types.json)。 +正常构建生成 dist 与 docs 副本;无新依赖。内部源码路径从 .js 迁至 .ts,历史 +源码深导入仍归 06 分发审查,当前不能声称所有深路径已验收。 +独立本地检查点提交;回退可恢复前次 JS 实现,不影响历史冻结测试。不推送或发布。 diff --git a/refactor/plan.md b/refactor/plan.md index 71e52a926..134263ae4 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 222 项,范围 22 个包及工作区/示例。 -状态:todo 93 / doing 12 / blocked 0 / done 117 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 92 / doing 13 / blocked 0 / done 117 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -185,7 +185,7 @@ | PKG-VTT-THUMB-01 | artplayer-plugin-vtt-thumbnail
核对包契约与历史用法 | BASE-05 | vtt/style、异步返回、预览控件名与样式契约 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | M | done | | PKG-VTT-THUMB-02 | artplayer-plugin-vtt-thumbnail
建立特有行为与错误测试 | PKG-VTT-THUMB-01, ENG-03, ENG-05 | VTT 解析、xywh、相对 URL、时间边界、请求失败和定位 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | M | done | | PKG-VTT-THUMB-03 | artplayer-plugin-vtt-thumbnail
整理内部职责与资源 | PKG-VTT-THUMB-02, CORE-09, CORE-19 | 纯解析/区间查找与 DOM、请求/定时器生命周期分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | M | done | -| PKG-VTT-THUMB-04 | artplayer-plugin-vtt-thumbnail
迁移自有源码和公开类型 | PKG-VTT-THUMB-03, ENG-04, ENG-06, CORE-07 | cue/rect/result 和参数可选性保持兼容 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | M | todo | +| PKG-VTT-THUMB-04 | artplayer-plugin-vtt-thumbnail
迁移自有源码和公开类型 | PKG-VTT-THUMB-03, ENG-04, ENG-06, CORE-07 | cue/rect/result 和参数可选性保持兼容 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | M | doing | | PKG-VTT-THUMB-05 | artplayer-plugin-vtt-thumbnail
验证新旧核心和组合 | PKG-VTT-THUMB-04, CORE-22 | 鼠标和移动进度、全屏、切源、chapter 组合 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | M | todo | | PKG-VTT-THUMB-06 | artplayer-plugin-vtt-thumbnail
验证分发并同步文档 | PKG-VTT-THUMB-05, ENG-07 | vtt.thumbnail.js、VTT/图片资源解析和产物验证 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | M | todo | @@ -500,6 +500,7 @@ - PKG-VTT-THUMB-01: [记录](baselines/vtt-thumbnail-release.json) [记录](baselines/vtt-thumbnail-contract.md) [记录](changes/2026-09-13-PKG-VTT-THUMB-01-contract.md) - PKG-VTT-THUMB-02: [记录](baselines/vtt-thumbnail-failures.json) [记录](changes/2026-09-13-PKG-VTT-THUMB-02-failures.md) - PKG-VTT-THUMB-03: [记录](changes/2026-09-13-PKG-VTT-THUMB-03-resources.md) [记录](baselines/vtt-thumbnail-resources.json) [记录](changes/2026-09-13-PKG-VTT-THUMB-03-parser.md) [记录](baselines/vtt-thumbnail-parser.json) +- PKG-VTT-THUMB-04: [记录](changes/2026-09-13-PKG-VTT-THUMB-04-runtime-types.md) [记录](baselines/vtt-thumbnail-runtime-types.json) - PKG-HLS-01: [记录](baselines/hls-control-contract.md) [记录](baselines/hls-control-release.json) [记录](changes/2026-09-11-PKG-HLS-01-contract.md) - PKG-HLS-02: [记录](hls-validation.md) [记录](baselines/hls-sdk.json) [记录](baselines/hls-validation.json) [记录](changes/2026-09-11-PKG-HLS-02-tests.md) - PKG-HLS-03: [记录](changes/2026-09-12-PKG-HLS-03-modules.md) [记录](baselines/hls-modules-validation.json) [记录](hls-validation.md) diff --git a/refactor/progress.md b/refactor/progress.md index 74edc91a1..c47a461e2 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,17 @@ # 进度与证据 +## PKG-VTT-THUMB-04 严格运行时 TS 检查点(doing) + +六个 JS 实现迁为 TS,另有明确数据/资源类型模块,分包 allowJs=false。TS 5.9.3/5.1.6 +严格检查及正反例通过,移除 expect-error 后各复现 8 个预期类型错误。内部注册准确为 +Promise,公开旧同步声明保持原样,旧消费者/类型视图和导出兼容仍待 04。 +源码/main/legacy 的解析与生命周期各 47 通过,原生三入口各 18 通过,共 54 项; +实际 ESM 和完整 CI 2064 项通过,全仓严格生产 TS 增至 346 文件。包内架构和构建同步。 +见[变更](changes/2026-09-13-PKG-VTT-THUMB-04-runtime-types.md)和 +[验证](baselines/vtt-thumbnail-runtime-types.json)。222 项:117 done、13 doing、92 todo。 +本次为独立本地检查点提交,不推送、不发布。 + + ## PKG-VTT-THUMB-03 内部模块与解析修复完成 在资源检查点上完成纯解析/首个区间查找,支持 cue ID、注释/元数据块和 timing settings, diff --git a/refactor/risks.json b/refactor/risks.json index f58ed4d8c..1751a1188 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -4200,7 +4200,9 @@ "evidence": [ "refactor/baselines/vtt-thumbnail-release.json", "refactor/baselines/vtt-thumbnail-contract.md", - "refactor/scripts/vtt-thumbnail-contract.test.mjs" + "refactor/scripts/vtt-thumbnail-contract.test.mjs", + "test/types/vtt-thumbnail-runtime.ts", + "refactor/baselines/vtt-thumbnail-runtime-types.json" ], "compatibleResolution": "Preserve valid consumers and actual async runtime, with explicit decisions for irreconcilable inference changes.", "closureCriteria": "Isolated old/new compiler and installed consumers verify options, styles, callback and result forms." diff --git a/refactor/tasks.json b/refactor/tasks.json index b441cd01c..9c922fe19 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -1921,11 +1921,14 @@ "ENG-06", "CORE-07" ], - "status": "todo", + "status": "doing", "risk": "M", "deliverable": "cue/rect/result 和参数可选性保持兼容", "acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容", - "evidence": [] + "evidence": [ + "changes/2026-09-13-PKG-VTT-THUMB-04-runtime-types.md", + "baselines/vtt-thumbnail-runtime-types.json" + ] }, { "id": "PKG-VTT-THUMB-05", diff --git a/test/types/vtt-thumbnail-runtime.ts b/test/types/vtt-thumbnail-runtime.ts new file mode 100644 index 000000000..c967b6d25 --- /dev/null +++ b/test/types/vtt-thumbnail-runtime.ts @@ -0,0 +1,48 @@ +import type Artplayer from 'artplayer' +import type { Option, Result, Thumbnail } from '../../packages/artplayer-plugin-vtt-thumbnail/src/types' +import factory from '../../packages/artplayer-plugin-vtt-thumbnail/src/index' +import createLifetime from '../../packages/artplayer-plugin-vtt-thumbnail/src/lifetime' +import parseVtt, { findThumbnail } from '../../packages/artplayer-plugin-vtt-thumbnail/src/parseVtt' +import requestVtt from '../../packages/artplayer-plugin-vtt-thumbnail/src/request' +import legacyFactory from '../../packages/artplayer-plugin-vtt-thumbnail/types/artplayer-plugin-vtt-thumbnail' + +declare const art: Artplayer +const option: Option = { vtt: '/cues.vtt', style: { opacity: '0.8', width: '80px' } } +const registration = factory(option) +const result: Promise = registration(art) +result.then(value => value.name satisfies 'artplayerPluginVttThumbnail') +// @ts-expect-error Registration is asynchronous, without a synchronous name property. +void result.name +// @ts-expect-error Omitted options remain invalid in typed calls. +factory() +// @ts-expect-error VTT URL is a string. +factory({ vtt: 12 }) +// @ts-expect-error CSS width retains the existing CSSStyleDeclaration input type. +factory({ style: { width: 12 } }) + +const lifetime = createLifetime(art) +lifetime.listen('setBar', (_type, percentage, event) => { + percentage.toFixed() + event?.preventDefault() +}) +// @ts-expect-error The lifetime only owns the events this plugin actually uses. +lifetime.listen('not-an-event', () => {}) +const value: Promise = lifetime.wait(Promise.resolve(3)) +const text: Promise = requestVtt('/cues.vtt', lifetime) +void value +void text +// @ts-expect-error Closed is a read-only state. +lifetime.closed = false + +const cues: Thumbnail[] = parseVtt('WEBVTT') +const selected: Thumbnail | undefined = findThumbnail(cues, 1) +selected?.x.toUpperCase() +// @ts-expect-error Rectangle strings preserve historical formatting. +const width: number = cues[0]!.w +void width + +const legacyRegistration = legacyFactory(option) +legacyRegistration(art).name satisfies 'artplayerPluginVttThumbnail' +// @ts-expect-error Published declarations are falsely synchronous; do not hide the conflict. +const legacyReplacement: typeof legacyRegistration = registration +void legacyReplacement