mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-12 23:36:15 -08:00
fix(site): [SITE-VCONSOLE-01] cancel stale console lifecycle work
This commit is contained in:
1 parent
838c3eb432
commit
e7f31b6fc8
22 files changed
+2318
-32
No files matched your search
@@ -2,13 +2,13 @@
|
||||
|
||||
Frozen source identities and verbatim upstream notice texts for Monaco/vConsole. This is not clearance of all bundled components, the console bundle, fonts, media or other site assets.
|
||||
|
||||
Generated by yarn build:site-notices. Runtime asset URLs and bytes are unchanged.
|
||||
Generated by yarn build:site-notices. Runtime asset URLs are retained; reviewed local patches are identified below.
|
||||
|
||||
## vconsole 3.15.0
|
||||
|
||||
Source: https://registry.npmjs.org/vconsole/-/vconsole-3.15.0.tgz
|
||||
|
||||
The 3.15.0 upstream LICENSE declares MIT but omits the promised full license text. The copied file preserves this upstream evidence; completeness and bundled dependency notices remain under review.
|
||||
The 3.15.0 upstream LICENSE declares MIT but omits the promised full license text. The copied file preserves this upstream evidence; completeness and bundled dependency notices remain under review. ArtPlayer applies a reproducible local lifecycle patch to log frame cancellation and delayed panel ownership; the upstream bundle is frozen in scripts/site-vendor/vconsole/upstream.js and generated by yarn build:vconsole. Public asset URL and UMD/CSS contracts are retained.
|
||||
|
||||
- licenses/vconsole/LICENSE
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -7,4 +7,4 @@
|
||||
* http://opensource.org/licenses/MIT
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define("VConsole",[],n):"object"==typeof exports?exports.VConsole=n():t.VConsole=n()}(this||self,(function(){return function(){var __webpack_modules__={4264:function(t,n,e){t.exports=e(7588)},5036:function(t,n,e){e(1719),e(5677),e(6394),e(5334),e(6969),e(2021),e(8328),e(2129);var o=e(1287);t.exports=o.Promise},2582:function(t,n,e){e(1646),e(6394),e(2004),e(462),e(8407),e(2429),e(1172),e(8288),e(1274),e(8201),e(6626),e(3211),e(9952),e(15),e(9831),e(7521),e(2972),e(6956),e(5222),e(2257);var o=e(1287);t.exports=o.Symbol},8257:function(t,n,e){var o=e(7583),r=e(9212),i=e(5637),a=o.TypeError;t.exports=function(t){if(r(t))return t;throw a(i(t)+" is not a function")}},1186:function(t,n,e){var o=e(7583),r=e(2097),i=e(5637),a=o.TypeError;t.exports=function(t){if(r(t))return t;throw a(i(t)+" is not a constructor")}},9882:function(t,n,e){var o=e(7583),r=e(9212),i=o.String,a=o.TypeError;t.exports=function(t){if("object"==typeof t||r(t))return t;throw a("Can't set "+i(t)+" as a prototype")}},6288:function(t,n,e){var o=e(3649),r=e(3590),i=e(4615),a=o("unscopables"),c=Array.prototype;null==c[a]&&i.f(c,a,{configurable:!0,value:r(null)}),t.exports=function(t){c[a][t]=!0}},4761:function(t,n,e){var o=e(7583),r=e(2447),i=o.TypeError;t.exports=function(t,n){if(r(n,t))return t;throw i("Incorrect invocation")}},2569:function(t,n,e){var o=e(7583),r=e(794),i=o.String,a=o.TypeError;t.exports=function(t){if(r(t))return t;throw a(i(t)+" is not an object")}},5766:function(t,n,e){var o=e(2977),r=e(6782),i=e(1825),a=function(t){return function(n,e,a){var c,u=o(n),s=i(u),l=r(a,s);if(t&&e!=e){for(;s>l;)if((c=u[l++])!=c)return!0}else for(;s>l;l++)if((t||l in u)&&u[l]===e)return t||l||0;return!t&&-1}};t.exports={includes:a(!0),indexOf:a(!1)}},4805:function(t,n,e){var o=e(2938),r=e(7386),i=e(5044),a=e(1324),c=e(1825),u=e(4822),s=r([].push),l=function(t){var n=1==t,e=2==t,r=3==t,l=4==t,f=6==t,d=7==t,v=5==t||f;return function(h,p,g,m){for(var _,b,y=a(h),w=i(y),E=o(p,g),L=c(w),T=0,x=m||u,C=n?x(h,L):e||d?x(h,0):void 0;L>T;T++)if((v||T in w)&&(b=E(_=w[T],T,y),t))if(n)C[T]=b;else if(b)switch(t){case 3:return!0;case 5:return _;case 6:return T;case 2:s(C,_)}else switch(t){case 4:return!1;case 7:s(C,_)}return f?-1:r||l?l:C}};t.exports={forEach:l(0),map:l(1),filter:l(2),some:l(3),every:l(4),find:l(5),findIndex:l(6),filterReject:l(7)}},9269:function(t,n,e){var o=e(6544),r=e(3649),i=e(4061),a=r("species");t.exports=function(t){return i>=51||!o((function(){var n=[];return(n.constructor={})[a]=function(){return{foo:1}},1!==n[t](Boolean).foo}))}},4546:function(t,n,e){var o=e(7583),r=e(6782),i=e(1825),a=e(5999),c=o.Array,u=Math.max;t.exports=function(t,n,e){for(var o=i(t),s=r(n,o),l=r(void 0===e?o:e,o),f=c(u(l-s,0)),d=0;s<l;s++,d++)a(f,d,t[s]);return f.length=d,f}},6917:function(t,n,e){var o=e(7386);t.exports=o([].slice)},5289:function(t,n,e){var o=e(7583),r=e(4521),i=e(2097),a=e(794),c=e(3649)("species"),u=o.Array;t.exports=function(t){var n;return r(t)&&(n=t.constructor,(i(n)&&(n===u||r(n.prototype))||a(n)&&null===(n=n[c]))&&(n=void 0)),void 0===n?u:n}},4822:function(t,n,e){var o=e(5289);t.exports=function(t,n){return new(o(t))(0===n?0:n)}},3616:function(t,n,e){var o=e(3649)("iterator"),r=!1;try{var i=0,a={next:function(){return{done:!!i++}},return:function(){r=!0}};a[o]=function(){return this},Array.from(a,(function(){throw 2}))}catch(t){}t.exports=function(t,n){if(!n&&!r)return!1;var e=!1;try{var i={};i[o]=function(){return{next:function(){return{done:e=!0}}}},t(i)}catch(t){}return e}},9624:function(t,n,e){var o=e(7386),r=o({}.toString),i=o("".slice);t.exports=function(t){return i(r(t),8,-1)}},3058:function(t,n,e){var o=e(7583),r=e(8191),i=e(9212),a=e(9624),c=e(3649)("toStringTag"),u=o.Object,s="Arguments"==a(function(){return arguments}());t.exports=r?a:function(t){var n,e,o;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(e=function(t,n){try{return t[n]}catch(t){}}(n=u(t),c))?e:s?a(n):"Object"==(o=a(n))&&i(n.callee)?"Arguments":o}},1509:function(t,n,e){var o=e(7386)("".replace),r=String(Error("zxcasd").stack),i=/\n\s*at [^:]*:[^\n]*/,a=i.test(r);t.exports=function(t,n){if(a&&"string"==typeof t)for(;n--;)t=o(t,i,"");return t}},3478:function(t,n,e){var o=e(2870),r=e(929),i=e(6683),a=e(4615);t.exports=function(t,n,e){for(var c=r(n),u=a.f,s=i.f,l=0;l<c.length;l++){var f=c[l];o(t,f)||e&&o(e,f)||u(t,f,s(n,f))}}},926:function(t,n,e){var o=e(6544);t.exports=!o((function(){function t(){}return t.prototype.constructor=null,Object.getPrototypeOf(new t)!==t.prototype}))},4683:function(t,n,e){"use strict";var o=e(2365).IteratorPrototype,r=e(3590),i=e(4677),a=e(8821),c=e(339),u=function(){return this};t.exports=function(t,n,e,s){var l=n+" Iterator";return t.prototype=r(o,{next:i(+!s,e)}),a(t,l,!1,!0),c[l]=u,t}},57:function(t,n,e){var o=e(8494),r=e(4615),i=e(4677);t.exports=o?function(t,n,e){return r.f(t,n,i(1,e))}:function(t,n,e){reLine truncated
|
||||
!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define("VConsole",[],n):"object"==typeof exports?exports.VConsole=n():t.VConsole=n()}(this||self,(function(){var __artplayerVConsoleLogLifecycle=(function(){var o=Object.defineProperty;var t=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var s=function(e,n){for(var u in n)o(e,u,{get:n[u],enumerable:!0})},L=function(e,n,u,i){if(n&&typeof n=="object"||typeof n=="function")for(var r=g(n),a=0,c=r.length,l;a<c;a++)l=r[a],!p.call(e,l)&&l!==u&&o(e,l,{get:function(d){return n[d]}.bind(null,l),enumerable:!(i=t(n,l))||i.enumerable});return e};var m=function(e){return L(o({},"__esModule",{value:!0}),e)};var D={};s(D,{enqueue:function(){return v},renderTab:function(){return h},unbind:function(){return A}});var f=[];function h(e,n,u){setTimeout(function(){e.isInited&&e.pluginList[n.id]===n&&u()},0)}function v(e,n,u){if(e.ADDED_LOG_PLUGIN_ID.length!==0){if(!e.flushLogScheduled){var i={host:e,id:0};f.push(i),e.flushLogScheduled=!0,i.id=u.requestAnimationFrame(function(){var r=f.indexOf(i);r!==-1&&(f.splice(r,1),e.flushLogScheduled=!1,e._flushLogs())})}e.logQueue.push(n)}}function A(e,n){for(var u,i=0;i<f.length;i++)if(f[i].host===e){u=f[i],f.splice(i,1);break}e.logQueue=[],e.flushLogScheduled=!1;try{u&&typeof n.cancelAnimationFrame=="function"&&n.cancelAnimationFrame(u.id)}finally{e.unmockConsole()}}return m(D);})();return function(){var __webpack_modules__={4264:function(t,n,e){t.exports=e(7588)},5036:function(t,n,e){e(1719),e(5677),e(6394),e(5334),e(6969),e(2021),e(8328),e(2129);var o=e(1287);t.exports=o.Promise},2582:function(t,n,e){e(1646),e(6394),e(2004),e(462),e(8407),e(2429),e(1172),e(8288),e(1274),e(8201),e(6626),e(3211),e(9952),e(15),e(9831),e(7521),e(2972),e(6956),e(5222),e(2257);var o=e(1287);t.exports=o.Symbol},8257:function(t,n,e){var o=e(7583),r=e(9212),i=e(5637),a=o.TypeError;t.exports=function(t){if(r(t))return t;throw a(i(t)+" is not a function")}},1186:function(t,n,e){var o=e(7583),r=e(2097),i=e(5637),a=o.TypeError;t.exports=function(t){if(r(t))return t;throw a(i(t)+" is not a constructor")}},9882:function(t,n,e){var o=e(7583),r=e(9212),i=o.String,a=o.TypeError;t.exports=function(t){if("object"==typeof t||r(t))return t;throw a("Can't set "+i(t)+" as a prototype")}},6288:function(t,n,e){var o=e(3649),r=e(3590),i=e(4615),a=o("unscopables"),c=Array.prototype;null==c[a]&&i.f(c,a,{configurable:!0,value:r(null)}),t.exports=function(t){c[a][t]=!0}},4761:function(t,n,e){var o=e(7583),r=e(2447),i=o.TypeError;t.exports=function(t,n){if(r(n,t))return t;throw i("Incorrect invocation")}},2569:function(t,n,e){var o=e(7583),r=e(794),i=o.String,a=o.TypeError;t.exports=function(t){if(r(t))return t;throw a(i(t)+" is not an object")}},5766:function(t,n,e){var o=e(2977),r=e(6782),i=e(1825),a=function(t){return function(n,e,a){var c,u=o(n),s=i(u),l=r(a,s);if(t&&e!=e){for(;s>l;)if((c=u[l++])!=c)return!0}else for(;s>l;l++)if((t||l in u)&&u[l]===e)return t||l||0;return!t&&-1}};t.exports={includes:a(!0),indexOf:a(!1)}},4805:function(t,n,e){var o=e(2938),r=e(7386),i=e(5044),a=e(1324),c=e(1825),u=e(4822),s=r([].push),l=function(t){var n=1==t,e=2==t,r=3==t,l=4==t,f=6==t,d=7==t,v=5==t||f;return function(h,p,g,m){for(var _,b,y=a(h),w=i(y),E=o(p,g),L=c(w),T=0,x=m||u,C=n?x(h,L):e||d?x(h,0):void 0;L>T;T++)if((v||T in w)&&(b=E(_=w[T],T,y),t))if(n)C[T]=b;else if(b)switch(t){case 3:return!0;case 5:return _;case 6:return T;case 2:s(C,_)}else switch(t){case 4:return!1;case 7:s(C,_)}return f?-1:r||l?l:C}};t.exports={forEach:l(0),map:l(1),filter:l(2),some:l(3),every:l(4),find:l(5),findIndex:l(6),filterReject:l(7)}},9269:function(t,n,e){var o=e(6544),r=e(3649),i=e(4061),a=r("species");t.exports=function(t){return i>=51||!o((function(){var n=[];return(n.constructor={})[a]=function(){return{foo:1}},1!==n[t](Boolean).foo}))}},4546:function(t,n,e){var o=e(7583),r=e(6782),i=e(1825),a=e(5999),c=o.Array,u=Math.max;t.exports=function(t,n,e){for(var o=i(t),s=r(n,o),l=r(void 0===e?o:e,o),f=c(u(l-s,0)),d=0;s<l;s++,d++)a(f,d,t[s]);return f.length=d,f}},6917:function(t,n,e){var o=e(7386);t.exports=o([].slice)},5289:function(t,n,e){var o=e(7583),r=e(4521),i=e(2097),a=e(794),c=e(3649)("species"),u=o.Array;t.exports=function(t){var n;return r(t)&&(n=t.constructor,(i(n)&&(n===u||r(n.prototype))||a(n)&&null===(n=n[c]))&&(n=void 0)),void 0===n?u:n}},4822:function(t,n,e){var o=e(5289);t.exports=function(t,n){return new(o(t))(0===n?0:n)}},3616:function(t,n,e){var o=e(3649)("iterator"),r=!1;try{var i=0,a={next:function(){return{done:!!i++}},return:function(){r=!0}};a[o]=function(){return this},Array.from(a,(function(){throw 2}))}catch(t){}t.exports=function(t,n){if(!n&&!r)return!1;var e=!1;try{var i={};i[o]=function(){return{next:function(){return{done:e=!0}}}},t(i)}catch(t){}return e}},9624:function(t,n,e){var o=e(7386),r=o({}.toString),i=o("".slice);t.exports=function(t){return i(r(t),8,-Line truncated
|
||||
+7
-4
@@ -45,10 +45,10 @@
|
||||
"check:toolchain": "node scripts/check-toolchain.mjs",
|
||||
"lint:fix": "eslint \"packages/*/{src,public,types,package.json}\" \"scripts/*.{js,mjs}\" \"packages/artplayer-vitepress/{browser,build}/**/*.ts\" \"scripts/{docs-smoke,editor-declarations,documentation,site-build,library,pages,site-vendor}/**/*.ts\" \"scripts/plugin/*.{js,ts}\" \"test/*\" \"docs/assets/ts/*\" \"types/*.d.ts\" \"playwright*.config.js\" --fix",
|
||||
"check:plan": "node refactor/scripts/plan.mjs --check",
|
||||
"test:node": "yarn test:unit && node --test test/toolchain.test.js test/build-docs.test.js test/package-check.test.js test/declarations.test.js test/editor-types.test.js test/coverage.test.js test/performance-report.test.js test/media-gate.test.js test/ci-summary.test.js test/package-runtime.test.js test/library-build.test.js test/site-loading.test.js test/documentation-pipeline.test.js test/site-build.test.js test/site-markdown.test.js test/site-editor.test.js test/plugin-scaffold.test.js test/dev-server.test.js test/pages-artifact.test.js test/site-notices.test.js",
|
||||
"test:node": "yarn test:unit && node --test test/toolchain.test.js test/build-docs.test.js test/package-check.test.js test/declarations.test.js test/editor-types.test.js test/coverage.test.js test/performance-report.test.js test/media-gate.test.js test/ci-summary.test.js test/package-runtime.test.js test/library-build.test.js test/site-loading.test.js test/documentation-pipeline.test.js test/site-build.test.js test/site-markdown.test.js test/site-editor.test.js test/plugin-scaffold.test.js test/dev-server.test.js test/pages-artifact.test.js test/site-notices.test.js test/vconsole-lifecycle.test.js",
|
||||
"test:baseline": "node --test refactor/scripts/*.test.mjs",
|
||||
"ci:check": "yarn check:toolchain --strict && yarn check:commits --report && yarn check:impact --report && yarn check:ci && yarn test:contracts && yarn check:contracts --report && yarn check:plan && yarn check:release-ledger && yarn lint && yarn typecheck:library && yarn typecheck:scaffold && yarn typecheck:docs-tools && yarn check:docs-smoke && yarn check:editor-types && yarn check:llm && yarn check:site-notices && yarn typecheck:site-assets && yarn check:site-assets && yarn check:types && yarn typecheck && yarn typecheck:react && yarn lint:react && yarn typecheck:vue && yarn lint:vue && yarn test",
|
||||
"ci:build": "yarn build:types && yarn build all && yarn build:i18n && yarn build:ts && yarn build:llm && yarn build:site-notices && yarn build:test && yarn build:docs && yarn test:imports",
|
||||
"ci:check": "yarn check:toolchain --strict && yarn check:commits --report && yarn check:impact --report && yarn check:ci && yarn test:contracts && yarn check:contracts --report && yarn check:plan && yarn check:release-ledger && yarn lint && yarn typecheck:library && yarn typecheck:scaffold && yarn typecheck:docs-tools && yarn check:docs-smoke && yarn check:editor-types && yarn check:llm && yarn check:vconsole && yarn check:site-notices && yarn typecheck:site-assets && yarn check:site-assets && yarn check:types && yarn typecheck && yarn typecheck:react && yarn lint:react && yarn typecheck:vue && yarn lint:vue && yarn test",
|
||||
"ci:build": "yarn build:types && yarn build all && yarn build:i18n && yarn build:ts && yarn build:llm && yarn build:vconsole && yarn build:site-notices && yarn build:test && yarn build:docs && yarn test:imports",
|
||||
"test:imports": "node --test test/esm.test.js test/i18n.test.js test/ssr.test.js test/asr-distribution.test.js",
|
||||
"typecheck": "node scripts/typecheck.mjs",
|
||||
"test:unit": "node --test test/danmuku-setting.test.js test/danmuku-heatmap.test.js test/danmuku-renderer.test.js test/danmuku-scheduler.test.js test/danmuku-worker-client.test.js test/danmuku-input.test.js test/danmuku-parser.test.js test/danmuku-failures.test.js test/danmuku-mask-failures.test.js test/danmuku-mask-lifecycle.test.js test/asr-encoding.test.js test/asr-lifecycle.test.js test/asr-audio.test.js test/asr.test.js test/jassub.test.js test/jassub-registration.test.js test/jassub-runtime.test.js test/jassub-vendor-lifecycle.test.js test/jassub-frame-polyfill.test.js test/jassub-offscreen.test.js test/multiple-subtitles-ass.test.js test/multiple-subtitles-caption.test.js test/multiple-subtitles-merge.test.js test/multiple-subtitles-entities.test.js test/multiple-subtitles-lifecycle.test.js test/multiple-subtitles-failures.test.js test/multiple-subtitles.test.js test/multiple-subtitles-vendor.test.js test/vtt-thumbnail-parser.test.js test/vtt-thumbnail-lifecycle.test.js test/vtt-thumbnail.test.js test/auto-thumbnail-exports.test.js test/auto-thumbnail-canvas.test.js test/auto-thumbnail-frames.test.js test/auto-thumbnail-encoding.test.js test/auto-thumbnail-lifecycle.test.js test/auto-thumbnail.test.js test/thumbnail-emitter.test.js test/thumbnail-runtime.test.js test/thumbnail-vendor.test.js test/thumbnail-lifecycle.test.js test/thumbnail-input.test.js test/thumbnail.test.js test/iframe-navigation.test.js test/iframe-boundaries.test.js test/iframe-lifecycle.test.js test/iframe.test.js test/mediabunny.test.js test/mediabunny-shim.test.js test/mediabunny-coordination.test.js test/mediabunny-video.test.js test/mediabunny-audio.test.js test/mediabunny-hls.test.js test/mediabunny-entry.test.js test/mediabunny-capability.test.js test/mediabunny-load.test.js test/mediabunny-input.test.js test/dpip.test.js test/dpip-lifecycle.test.js test/canvas.test.js test/canvas-lifecycle.test.js test/ambilight.test.js test/ambilight-lifecycle.test.js test/ambilight-proxy.test.js test/vast-exports.test.js test/vast.test.js test/vast-lifecycle.test.js test/ads.test.js test/ads-lifecycle.test.js test/playback.test.js test/dash-control.test.js test/dash-contract.test.js test/dash-lifecycle.test.js test/dash-events.test.js test/hls-control.test.js test/audio-track.test.js test/public-behavior.test.js test/helpers.test.js test/chapter.test.js test/utils.test.js test/resource-scope.test.js test/instance-lifecycle.test.js test/options.test.js test/media-hosts.test.js test/plugins.test.js test/source.test.js test/playback-properties.test.js test/media-events.test.js test/template-resources.test.js test/core-vendor.test.js test/component-resources.test.js test/setting-model.test.js test/setting-layout.test.js test/setting-resources.test.js test/subtitle.test.js test/display-native.test.js test/display-video-fullscreen.test.js test/display-pip.test.js test/display-mini.test.js test/display-sizing.test.js test/display-orientation.test.js test/hotkey.test.js test/listener-registry.test.js test/global-events.test.js test/pointer-events.test.js test/gesture.test.js test/event-scheduling.test.js test/notice.test.js test/fast-forward.test.js test/auto-playback.test.js test/builtin-layers.test.js test/prompt-components.test.js test/screenshot.test.js test/thumbnails.test.js test/progress.test.js test/environment.test.js test/storage.test.js test/facade-properties.test.js test/dom-boundaries.test.js test/initialization.test.js test/entry.test.js test/accessibility-button.test.js test/accessibility-focus.test.js test/accessibility-slider.test.js test/asr-fallback-routing.test.js test/asr-local-example.test.js test/asr-explicit-capture.test.js test/chromecast.test.js test/chromecast-failures.test.js test/chromecast-runtime.test.js",
|
||||
@@ -143,7 +143,10 @@
|
||||
"test:pages:browser": "node test/pages-browser.mjs",
|
||||
"build:site-notices": "node scripts/build-site-notices.mjs",
|
||||
"check:site-notices": "node scripts/build-site-notices.mjs --check",
|
||||
"test:site-notices": "node --test test/site-notices.test.js"
|
||||
"test:site-notices": "node --test test/site-notices.test.js",
|
||||
"build:vconsole": "node scripts/build-vconsole.mjs",
|
||||
"check:vconsole": "node scripts/build-vconsole.mjs --check",
|
||||
"test:vconsole": "node --test test/vconsole-lifecycle.test.js"
|
||||
},
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"devDependencies": {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,56 @@
|
||||
# SITE-VCONSOLE-01: vConsole 销毁后的异步工作
|
||||
|
||||
## 问题与修复
|
||||
|
||||
SITE-07 的真实 mobile 页面发现 vConsole 3.15.0 销毁后更新已删除日志 store。
|
||||
冻结 npm bundle 后,原生三引擎进一步确认三条独立路径:
|
||||
|
||||
- 最后一个日志插件移除时没有取消 RAF,也没有清空单例日志模型的旧队列。
|
||||
`lifecycle.ts` 管理调度票据;先失效再取消,清空队列/标志,最后恢复 console。
|
||||
被人为迟到交付的已取消回调也不能消费重建后的队列。
|
||||
- core 的延迟面板插入以全局同名 DOM 查找目标,销毁后会报错,同名插件替换后会
|
||||
把旧面板插入新插件。延迟回调现在核对原实例 isInited 和 plugin 对象身份。
|
||||
- recycleScroller 的高度和条目尺寸异步续段在 timer 后访问已销毁的 Svelte 绑定
|
||||
元素。两个续段现在在绑定元素为空时返回,保留正常布局路径。
|
||||
|
||||
## 结构、类型与兼容
|
||||
|
||||
`scripts/site-vendor/vconsole/upstream.js` 保留 npm 3.15.0 完整原字节,构建器校验
|
||||
整个 SHA-256 及六个唯一替换位置。自有生命周期逻辑使用严格 TS,既有 TypeScript
|
||||
5.9.3 和 esbuild 0.27.7 生成 ES5 私有辅助模块,不新增依赖或锁文件。
|
||||
其余 bundle 字节、UMD 分支、CSS 和原 URL 保留;命名 AMD、CommonJS 和 browser
|
||||
global 都有真实页面回归。没有改 ArtPlayer 核心/插件公开 API 或声明。
|
||||
模块维护与重跑命令见 `scripts/site-vendor/vconsole/README.md`。
|
||||
|
||||
新增 `build:vconsole`、只读 `check:vconsole`、`test:vconsole`;接入根 Node 测试、
|
||||
严格 docs-tools TS,以及 ci:check/ci:build 的 notices 前置顺序。生产 minified 文件
|
||||
由生成器写出;manifest 与第三方台账分别保存上游和候选指纹。
|
||||
|
||||
## 验证记录
|
||||
|
||||
最终结果与输入指纹见 [机器证据](../baselines/vconsole-lifecycle-validation.json)。
|
||||
原失败报告保留在 refactor/.cache,不能以最终通过覆盖:
|
||||
|
||||
- 最初原版 6 失败,首个仅日志补丁仍有面板异常;第二轮 9 项通过。
|
||||
- 扩展原版回归 13 失败/2 通过中,AMD harness 错把命名 define 当匿名 define;
|
||||
这是测试错误,已按冻结 UMD 原文改正,不计为产品缺陷。
|
||||
- 扩展候选 17 通过/1 失败暴露间歇 scroller style 异常;没有扩大等待或吞掉错误。
|
||||
- 原版 scroller 专项通过控制零延迟 timer 在三引擎稳定得到空节点 style 异常。
|
||||
该轮 `.vc-log` 选择器错误也留档;仅改成 `.vc-log-row` 时,因为挂起了初始化,
|
||||
仍没有日志行。正式测试先确认日志可见,再挂起 timer、改变 viewport 高度,
|
||||
销毁前均有 21 条真实日志行;原版出现空节点异常,候选无异常。
|
||||
- 最终同一测试:原版 12 失败/6 通过;候选含 mobile 播放 21 全通过。40 项单元/
|
||||
CI 测试、严格 docs-tools TS、工具链、冻结安装、生成校验和定向 lint 通过。
|
||||
根 lint 无错误,保留既有 docs/assets/ts/artplayer.d.ts 的 1 条 unused-disable warning。
|
||||
|
||||
日志及面板用例使用原生 RAF/DOM;迟到已取消 RAF 和挂起的零延迟 timer 是显式
|
||||
故障注入,未把它们称作未经控制的用户操作。mobile 用例仍有实际视频播放和可见
|
||||
日志。浏览器是本机 Playwright 三引擎,不是物理 iOS/Android 或远端 CI 结果。
|
||||
|
||||
## 剩余边界与回退
|
||||
|
||||
本任务只关闭已复现的日志/面板/布局迟到工作,不宣称覆盖 vConsole 所有生命周期。
|
||||
原 LICENSE 原文继续分发;缺失的 MIT 正文和依赖 notices 仍属于 VENDOR-07,
|
||||
SITE-07 仍 doing。Monaco/Codicons、console bundle、字体/媒体许可也未关闭。
|
||||
没有 npm 发布、部署或 push。回退本任务提交同时恢复生成器、候选、指纹和 CI
|
||||
命令;不要只替换 docs 中的压缩文件而留下失配的生成检查。
|
||||
+6
-4
@@ -2,9 +2,9 @@
|
||||
|
||||
> 由 tasks.json 生成。请修改数据后运行 `node refactor/scripts/plan.mjs --write`,不要手改本表。
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 255 项,范围 22 个包及工作区/示例。
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 256 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 51 / doing 18 / blocked 0 / done 186 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 51 / doing 18 / blocked 0 / done 187 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
| artplayer-proxy-mediabunny | 1.2.0 | PKG-MB-01, PKG-MB-02, PKG-MB-03, PKG-MB-04, PKG-MB-05, PKG-MB-06, PKG-MB-07, PKG-MB-08, PKG-MB-09, PKG-MB-10 |
|
||||
| artplayer-tool-iframe | 1.1.0 | PKG-IFRAME-01, PKG-IFRAME-02, PKG-IFRAME-03, PKG-IFRAME-04, PKG-IFRAME-05, PKG-IFRAME-06 |
|
||||
| artplayer-tool-thumbnail | 4.4.0 | PKG-TOOL-THUMB-01, PKG-TOOL-THUMB-02, PKG-TOOL-THUMB-03, PKG-TOOL-THUMB-04, PKG-TOOL-THUMB-05, PKG-TOOL-THUMB-06 |
|
||||
| artplayer-vitepress | 1.1.0 | SITE-01, SITE-SMOKE-01, SITE-02, SITE-LOAD-01, SITE-AI-DOCS-01, SITE-BUILD-01, SITE-03, SITE-04, SITE-05, SITE-06, SITE-07 |
|
||||
| artplayer-vitepress | 1.1.0 | SITE-01, SITE-SMOKE-01, SITE-02, SITE-LOAD-01, SITE-AI-DOCS-01, SITE-BUILD-01, SITE-03, SITE-04, SITE-05, SITE-06, SITE-07, SITE-VCONSOLE-01 |
|
||||
|
||||
## 0 规划
|
||||
|
||||
@@ -397,12 +397,13 @@
|
||||
| SITE-BUILD-01 | artplayer, artplayer-vitepress, workspace<br>重构 i18n 与文档构建的暂存、替换和 Yarn 编排 | SITE-AI-DOCS-01 | 严格 TS 构建模块、编译后替换产物与失败回退,固定 Yarn 子进程和旧构建入口 | 旧 i18n 删除后失败有复现;失败不改变既有产物;语言集合、UMD/ESM/历史别名兼容;文档子进程退出码准确并完成真实 VitePress 构建;不隐式翻译、不修改播放器 API | M | done |
|
||||
| SITE-03 | artplayer-vitepress<br>整理 i18n/文档/LLM 生成流程 | SITE-02, SITE-LOAD-01, SITE-AI-DOCS-01, SITE-BUILD-01 | build-i18n/build-docs/build-llm/trans-docs 的任务边界和错误处理;桌面 common.js 自有 UI 的 TS 迁移与模块拆分 | 原命令兼容、生成可复现,翻译步骤不隐式运行远程服务;覆盖移动 loader 失败恢复 define、脚本依赖顺序、localhost/127.0.0.1 Run Code 目标和语言重定向;桌面剩余 UI 完成 TS 职责拆分并回归运行、导入与持久设置 | M | done |
|
||||
| SITE-04 | artplayer-vitepress<br>交叉核对逐包持续维护的文档 | CORE-21, SITE-03, PKG-CHAPTER-04, PKG-AMBILIGHT-04, PKG-AUDIO-04, PKG-AUTO-THUMB-04, PKG-VTT-THUMB-04, PKG-HLS-04, PKG-DASH-04, PKG-MULTI-SUB-04, PKG-JASSUB-04, PKG-MASK-04, PKG-ASR-04, PKG-ADS-04, PKG-VAST-04, PKG-CAST-04, PKG-DPIP-04, PKG-CANVAS-04, PKG-IFRAME-04, PKG-TOOL-THUMB-04, PKG-DANMUKU-06, PKG-MB-08 | 已随实现更新的中文/英文 API、包内实现地图、旧 JS 示例及已知能力限制的全包核对 | 未把缺环境的能力写成已验证,静态核对不等待设备任务;最终 demo 仍由 EX-03 验收;按 SITE-01 声明成员/候选标题清单逐项语义核对,补齐缺失的双语插件说明和 Danmuku 英文入口 | M | todo |
|
||||
| SITE-05 | artplayer-vitepress<br>构建文档站和验证链接/示例 | SITE-04, EX-01, EX-02, SITE-07 | VitePress 构建、链接与嵌入 demo 检查 | 文档构建、链接、嵌入路径与声明注入通过;真实完整 demo 保留 EX-03 独立门槛;核对 ENG-PM-01 登记的搜索 peer 范围和真实搜索行为 | M | todo |
|
||||
| SITE-05 | artplayer-vitepress<br>构建文档站和验证链接/示例 | SITE-04, EX-01, EX-02, SITE-07, SITE-VCONSOLE-01 | VitePress 构建、链接与嵌入 demo 检查 | 文档构建、链接、嵌入路径与声明注入通过;真实完整 demo 保留 EX-03 独立门槛;核对 ENG-PM-01 登记的搜索 peer 范围和真实搜索行为 | M | todo |
|
||||
| SITE-06 | artplayer-vitepress<br>文档站交付验收 | SITE-05 | 维护指南和站点变更记录 | 未手改 generated 目录,旧 URL 可用、部署与检查分离 | M | todo |
|
||||
| SITE-07 | artplayer-vitepress, workspace<br>整理站点第三方资产与来源说明 | SITE-01 | Monaco/vConsole/console bundle/字体和样本的冻结来源、可复现构建或替代与站点 notices | 接续 SITE-01 的字节与换行差异证据;完整许可随实际分发,consoleLog/global/CSS/worker 与旧 URL 兼容;缺字体/媒体使用依据明确处置;资产变更后执行编辑器/移动/字幕/示例回归,不以来源指纹替代授权或运行证据 | H | doing |
|
||||
| EX-01 | example/react.js, workspace<br>验证 React 消费者与 TS | CORE-22, ENG-07 | React 挂载/卸载/重挂载、引用和插件样例 | 真实 tarball + TS 消费通过,保留既有 React 集成 API | M | done |
|
||||
| EX-02 | example/vue.js, workspace<br>验证 Vue 消费者与更新卸载 | CORE-22, ENG-07 | Vue 实例/ref、参数更新、卸载及插件样例 | 旧 JS 组件用法无需修改,重复挂载不泄漏 | M | done |
|
||||
| EX-03 | workspace<br>验证全部原生 demo 与外部播放集成 | EX-01, EX-02, PKG-CHAPTER-06, PKG-AMBILIGHT-06, PKG-AUDIO-06, PKG-AUTO-THUMB-06, PKG-VTT-THUMB-06, PKG-HLS-06, PKG-DASH-06, PKG-MULTI-SUB-06, PKG-JASSUB-06, PKG-MASK-06, PKG-ASR-06, PKG-ADS-06, PKG-VAST-06, PKG-CAST-06, PKG-DPIP-06, PKG-DANMUKU-09, PKG-CANVAS-06, PKG-MB-10, PKG-IFRAME-06, PKG-TOOL-THUMB-06 | 8082 全 demo、HLS/DASH/FLV/MPEGTS/WebTorrent 集成记录 | 旧 URL/参数/脚本加载保持;网络/SDK 限制明确,不静默跳过;覆盖编辑器重复 Run 和 ESM/i18n/mobile/iframe,记录实际候选脚本及状态隔离证据;依据 BASE-04 路径台账逐项消除 not-run,处理 BASE-DEMO-01 | H | todo |
|
||||
| SITE-VCONSOLE-01 | artplayer-vitepress, workspace<br>修复 vConsole 日志帧与延迟面板销毁竞态 | SITE-01 | 冻结上游 bundle 的可复现 TS 生命周期补丁、旧红新绿回归、来源及生成检查;销毁后虚拟列表布局续段停止 | 最后日志插件移除后取消 RAF 并隔离旧队列;延迟面板不得写入已销毁或替换插件;三引擎移动页与 UMD 入口通过,维护声明与原有许可缺口分别记录;原生 ResizeObserver 布局与挂起 timer 旧红新绿 | M | done |
|
||||
|
||||
## 7 工具链与性能
|
||||
|
||||
@@ -664,3 +665,4 @@
|
||||
- PKG-VAST-07: [记录](changes/2026-09-14-PKG-VAST-07-default-alias.md) [记录](baselines/vast-alias-validation.json)
|
||||
- PKG-JASSUB-10: [记录](changes/2026-09-14-PKG-JASSUB-10-render-resources.md) [记录](baselines/jassub-render-validation.json) [记录](baselines/jassub-render-patch.json)
|
||||
- ENG-13: [记录](changes/2026-09-14-ENG-13-observation-window.md) [记录](baselines/observation-window-validation.json)
|
||||
- SITE-VCONSOLE-01: [记录](changes/2026-09-14-SITE-VCONSOLE-01-lifecycle.md) [记录](baselines/vconsole-lifecycle-validation.json)
|
||||
@@ -1,5 +1,19 @@
|
||||
# 进度与证据
|
||||
|
||||
## SITE-VCONSOLE-01 站点控制台生命周期修复完成
|
||||
|
||||
冻结 vConsole 3.15.0 原始 bundle,以严格 TS 和六个唯一构建替换点修复日志 RAF、
|
||||
同名面板替换、销毁后虚拟列表布局续段。原版最终对照 12 失败/6 通过,候选三引擎
|
||||
21 项全通过,包含实际 mobile 页面播放、可见日志和 AMD/CommonJS 入口。早期
|
||||
测试前置条件/选择器错误与候选暴露的额外异常均留档,没有跳过销毁或吞页面错误。
|
||||
40 项单元/CI 回归、严格工具链、冻结安装、docs-tools TS、根 lint(1 条既有 warning)
|
||||
通过;生成与只读检查接入 CI 的 notices 前置步骤。新增依赖为零,仍使用 Yarn。
|
||||
见[变更](changes/2026-09-14-SITE-VCONSOLE-01-lifecycle.md)与
|
||||
[证据](baselines/vconsole-lifecycle-validation.json)。256项:187 done、18 doing、51 todo。
|
||||
VCONSOLE-LIFECYCLE-01 运行时风险已处理;VENDOR-07 的完整许可/依赖 notices、
|
||||
Monaco 字体和其他资产仍 open,SITE-07 仍 doing。没有远端运行、部署、push/publish。
|
||||
下一步继续站点来源/许可及剩余包验收;VAST/缩略图工具历史默认行为选择仍待回复。
|
||||
|
||||
## SITE-07 固定vendor来源与notice生成检查点(仍doing)
|
||||
|
||||
新增Monaco0.30.1/vConsole3.15.0的100文件固定指纹、TS生成器和只读检查,三份上游
|
||||
|
||||
@@ -270,4 +270,4 @@
|
||||
| MULTI-SUB-ASS-01 | resolved / 已复现 | Published core5.1.2 ASS converter loses required VTT line breaks | PKG-MULTI-SUB-11 |
|
||||
| JASSUB-RENDER-RESOURCES-01 | resolved / 已复现 | Main-thread async rendering leaves received ImageBitmaps open when native canvas operations throw | PKG-JASSUB-10 |
|
||||
| ENG-PERF-OBS-01 | resolved / 已复现 | A nominal 350 ms resource observation can record only 349 ms and fail the required minimum | ENG-13 |
|
||||
| VCONSOLE-LIFECYCLE-01 | open / 已复现 | vConsole 3.15.0 mobile destruction can leave a callback updating a removed log store | SITE-07, SITE-05 |
|
||||
| VCONSOLE-LIFECYCLE-01 | resolved / 已复现 | vConsole delayed log, panel and scroller work outlives its owner | SITE-07, SITE-05, SITE-VCONSOLE-01 |
|
||||
+12
-4
@@ -6169,12 +6169,13 @@
|
||||
},
|
||||
{
|
||||
"id": "VCONSOLE-LIFECYCLE-01",
|
||||
"title": "vConsole 3.15.0 mobile destruction can leave a callback updating a removed log store",
|
||||
"title": "vConsole delayed log, panel and scroller work outlives its owner",
|
||||
"confirmation": "reproduced",
|
||||
"status": "open",
|
||||
"status": "resolved",
|
||||
"owners": [
|
||||
"SITE-07",
|
||||
"SITE-05"
|
||||
"SITE-05",
|
||||
"SITE-VCONSOLE-01"
|
||||
],
|
||||
"evidence": [
|
||||
"refactor/changes/2026-09-14-SITE-07-vendor-notices.md",
|
||||
@@ -6184,7 +6185,14 @@
|
||||
],
|
||||
"compatibleResolution": "Reproduce the exact delayed callback from fixed upstream source and preserve logging/global/CSS behavior while preventing work on a removed store; do not remove destruction checks or catch page errors to pass.",
|
||||
"closureCriteria": "A deterministic old-build failure and fixed native three-engine logging/destruction checks with no late errors, plus maintainable source/build provenance and required notices.",
|
||||
"workspaceState": "First native check: desktop worker 3/3 and mobile Chromium/Firefox pass; WebKit mobile page fails with undefined is not an object (evaluating s.O.get(n).update) after destruction. Upstream log model schedules RAF and reads Store.get(id).update while unbindPlugin deletes the store; exact causal callback remains to isolate."
|
||||
"workspaceState": "Frozen original deterministically fails 12 lifecycle cases across three engines; candidate passes 21 including actual mobile logging/playback. The six pinned replacements fix RAF ownership, panel identity and two scroller resumptions.",
|
||||
"resolutionEvidence": [
|
||||
"refactor/changes/2026-09-14-SITE-VCONSOLE-01-lifecycle.md",
|
||||
"refactor/baselines/vconsole-lifecycle-validation.json",
|
||||
"scripts/site-vendor/vconsole/README.md",
|
||||
"test/browser/vconsole-lifecycle.spec.js"
|
||||
],
|
||||
"resolutionRationale": "Runtime lifecycle regression is fixed and old/native evidence retained. Original notices remain distributed; the distinct missing license body/dependency notices remain open under VENDOR-07 and SITE-07, without treating this runtime fix as clearance."
|
||||
}
|
||||
]
|
||||
}
|
||||
+22
-1
@@ -4329,7 +4329,8 @@
|
||||
"SITE-04",
|
||||
"EX-01",
|
||||
"EX-02",
|
||||
"SITE-07"
|
||||
"SITE-07",
|
||||
"SITE-VCONSOLE-01"
|
||||
],
|
||||
"status": "todo",
|
||||
"risk": "M",
|
||||
@@ -5324,6 +5325,26 @@
|
||||
"changes/2026-09-14-ENG-13-observation-window.md",
|
||||
"baselines/observation-window-validation.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "SITE-VCONSOLE-01",
|
||||
"phase": "6 文档与消费者",
|
||||
"title": "修复 vConsole 日志帧与延迟面板销毁竞态",
|
||||
"scope": [
|
||||
"artplayer-vitepress",
|
||||
"workspace"
|
||||
],
|
||||
"dependsOn": [
|
||||
"SITE-01"
|
||||
],
|
||||
"status": "done",
|
||||
"risk": "M",
|
||||
"deliverable": "冻结上游 bundle 的可复现 TS 生命周期补丁、旧红新绿回归、来源及生成检查;销毁后虚拟列表布局续段停止",
|
||||
"acceptance": "最后日志插件移除后取消 RAF 并隔离旧队列;延迟面板不得写入已销毁或替换插件;三引擎移动页与 UMD 入口通过,维护声明与原有许可缺口分别记录;原生 ResizeObserver 布局与挂起 timer 旧红新绿",
|
||||
"evidence": [
|
||||
"changes/2026-09-14-SITE-VCONSOLE-01-lifecycle.md",
|
||||
"baselines/vconsole-lifecycle-validation.json"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1018,20 +1018,31 @@
|
||||
"id": "vconsole",
|
||||
"riskId": "VENDOR-07",
|
||||
"inputs": [
|
||||
"docs/assets/js/vconsole.min.js"
|
||||
"docs/assets/js/vconsole.min.js",
|
||||
"scripts/site-vendor/vconsole/upstream.js"
|
||||
],
|
||||
"owners": [
|
||||
"SITE-01"
|
||||
"SITE-01",
|
||||
"SITE-07",
|
||||
"SITE-VCONSOLE-01"
|
||||
],
|
||||
"sourceStatus": "Local header identifies vConsole 3.15.0 and Tencent copyright.",
|
||||
"licenseStatus": "Local header identifies MIT; original tarball correspondence and deployment notices not yet verified.",
|
||||
"upstreamSources": [],
|
||||
"upstreamReviewedAt": null,
|
||||
"sourceStatus": "Frozen npm 3.15.0 bundle, with reproducible six-hook lifecycle patch; see scripts/site-vendor/vconsole/README.md.",
|
||||
"licenseStatus": "Original npm LICENSE is distributed verbatim but lacks its promised MIT body; bundled dependency notices remain open in VENDOR-07.",
|
||||
"upstreamSources": [
|
||||
"https://registry.npmjs.org/vconsole/-/vconsole-3.15.0.tgz",
|
||||
"https://github.com/Tencent/vConsole/tree/05d80398bae35e793774f74e3c052b4e530e293a"
|
||||
],
|
||||
"upstreamReviewedAt": "2026-09-14",
|
||||
"updatePolicy": "Keep separate from owned TS migration. Verify source/version/diff and license notices before replacement; preserve API/CSS/worker URLs and run owning package tests.",
|
||||
"fingerprints": [
|
||||
{
|
||||
"path": "docs/assets/js/vconsole.min.js",
|
||||
"algorithm": "sha256-lf",
|
||||
"sha256": "3d1b8c3aff5f415ccf3bdc3cdcec6e01cff5b4c64d4558c078ca33d003f616ac"
|
||||
},
|
||||
{
|
||||
"path": "scripts/site-vendor/vconsole/upstream.js",
|
||||
"algorithm": "sha256-lf",
|
||||
"sha256": "671f47427e1e3048919147c765e9fb71e4ea40d79a8c2829089f499d3e9b9bf4"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import process from 'node:process'
|
||||
import { generateVconsole } from './site-vendor/vconsole/build.ts'
|
||||
|
||||
assert(process.argv.slice(2).every(arg => arg === '--check'), 'Use yarn build:vconsole [--check]')
|
||||
const candidate = await generateVconsole(process.cwd())
|
||||
const output = 'docs/assets/js/vconsole.min.js'
|
||||
if (process.argv.includes('--check'))
|
||||
assert.equal(fs.readFileSync(output, 'utf8'), candidate, 'Generated vConsole lifecycle patch drift')
|
||||
else fs.writeFileSync(output, candidate)
|
||||
console.log('vConsole 3.15.0 lifecycle patch generated/checked; upstream code outside the six pinned insertion points is retained.')
|
||||
@@ -29,11 +29,11 @@ own fingerprinted site. `.gitattributes` preserves text bytes across Windows/Lin
|
||||
`yarn test:site-notices` tests missing/extra/modified assets, changed or incomplete
|
||||
inputs, path escape and read-only output drift. The browser fixture
|
||||
`test/browser/site-vendor.spec.js` checks actual mobile logging, native playback,
|
||||
HTTP notice contents and destruction. Its WebKit destruction error is a real
|
||||
open vendor issue; do not skip the test or catch the page error to turn it green.
|
||||
HTTP notice contents and destruction. The fixed-source lifecycle patch and its
|
||||
old-build reproduction are maintained in `vconsole/README.md`; generated asset
|
||||
checks run before the notice checks. Do not skip destruction or catch page errors.
|
||||
Existing desktop TypeScript editor tests cover the real Monaco worker and Run.
|
||||
|
||||
Follow-up: reproduce the pending vConsole log-store callback after destruction,
|
||||
restore complete licensing from fixed upstream evidence, then recover the
|
||||
Follow-up: restore complete licensing from fixed upstream evidence, then recover the
|
||||
console.js build and resolve remaining fonts/media. Do not upgrade these assets
|
||||
without verifying globals, AMD/worker paths, CSS, consoleLog and user interaction.
|
||||
@@ -16,8 +16,9 @@
|
||||
"path": "docs/assets/js/vconsole.min.js",
|
||||
"member": "package/dist/vconsole.min.js",
|
||||
"upstreamSha256": "671f47427e1e3048919147c765e9fb71e4ea40d79a8c2829089f499d3e9b9bf4",
|
||||
"sha256": "671f47427e1e3048919147c765e9fb71e4ea40d79a8c2829089f499d3e9b9bf4",
|
||||
"mode": "lf-text"
|
||||
"sha256": "3d1b8c3aff5f415ccf3bdc3cdcec6e01cff5b4c64d4558c078ca33d003f616ac",
|
||||
"mode": "lf-text",
|
||||
"patchSource": "scripts/site-vendor/vconsole/README.md"
|
||||
}
|
||||
],
|
||||
"notices": [
|
||||
@@ -27,7 +28,7 @@
|
||||
"sha256": "272588ba6d7c09d6c3bedf3601e37b2ede6d4539d28a9052fcc3038df99cf683"
|
||||
}
|
||||
],
|
||||
"review": "The 3.15.0 upstream LICENSE declares MIT but omits the promised full license text. The copied file preserves this upstream evidence; completeness and bundled dependency notices remain under review."
|
||||
"review": "The 3.15.0 upstream LICENSE declares MIT but omits the promised full license text. The copied file preserves this upstream evidence; completeness and bundled dependency notices remain under review. ArtPlayer applies a reproducible local lifecycle patch to log frame cancellation and delayed panel ownership; the upstream bundle is frozen in scripts/site-vendor/vconsole/upstream.js and generated by yarn build:vconsole. Public asset URL and UMD/CSS contracts are retained."
|
||||
},
|
||||
{
|
||||
"name": "monaco-editor",
|
||||
|
||||
@@ -30,7 +30,7 @@ function list(root: string, relative: string): string[] {
|
||||
export function generateNotices(root: string, manifest: VendorManifest): Map<string, Uint8Array> {
|
||||
assert.equal(manifest.schemaVersion, 1)
|
||||
const outputs = new Map<string, Uint8Array>()
|
||||
const summary = ['# Verified site component notices', '', manifest.scope, '', 'Generated by yarn build:site-notices. Runtime asset URLs and bytes are unchanged.', '']
|
||||
const summary = ['# Verified site component notices', '', manifest.scope, '', 'Generated by yarn build:site-notices. Runtime asset URLs are retained; reviewed local patches are identified below.', '']
|
||||
for (const group of manifest.groups) {
|
||||
assert(group.notices.length > 0, `Missing upstream notice sources: ${group.name}`)
|
||||
const actual = group.roots.flatMap(relative => list(root, relative)).sort()
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# vConsole lifecycle patch
|
||||
|
||||
The public `docs/assets/js/vconsole.min.js` URL remains vConsole 3.15.0 with a
|
||||
local lifecycle correction. `upstream.js` is the exact npm distribution bundle:
|
||||
SHA-256 `671f47427e1e3048919147c765e9fb71e4ea40d79a8c2829089f499d3e9b9bf4`.
|
||||
The archive identity and upstream LICENSE are pinned in `../manifest.json`.
|
||||
Reference source is Tencent/vConsole commit
|
||||
`05d80398bae35e793774f74e3c052b4e530e293a` (tag v3.15.0):
|
||||
`src/log/log.model.ts`, `src/core/core.ts`, and
|
||||
`src/component/recycleScroller/recycleScroller.svelte`.
|
||||
|
||||
## Ownership and build
|
||||
|
||||
- `lifecycle.ts` owns queued log frames. Removing the last log plugin invalidates
|
||||
the ticket before cancellation, clears its queue/flag, and restores console
|
||||
even when cancellation throws. A late cancelled callback cannot consume a new
|
||||
queue on the reused singleton log model. Ordinary batches retain order and
|
||||
release their flag before flushing, including reentrant logging.
|
||||
- The same helper schedules panel insertion against the original instance and
|
||||
plugin object. Removed or replaced plugins cannot populate a new same-ID tab.
|
||||
- `build.ts` verifies the whole upstream SHA and six unique insertion points.
|
||||
It adds the private helper inside the existing UMD factory, replaces log
|
||||
scheduling/unbinding and panel insertion, and guards two virtual-scroller
|
||||
continuations after their timers. Svelte sets the bound items element to null
|
||||
on destroy; those continuations return before touching removed DOM.
|
||||
- `../../build-vconsole.mjs` (`scripts/build-vconsole.mjs` from the root)
|
||||
is the CLI. TypeScript and esbuild are existing root dev dependencies. The
|
||||
helper is emitted as ES5; it uses an array rather than requiring WeakMap before
|
||||
the upstream bundle installs its own polyfills. The remaining vendor code,
|
||||
CSS and UMD branch selection are retained byte for byte.
|
||||
|
||||
Run `yarn build:vconsole` after editing the helper or reviewed patch hooks, then
|
||||
update the candidate fingerprint in `../manifest.json` and
|
||||
`refactor/third-party.json` with the validation evidence. Run
|
||||
`yarn build:site-notices`. `yarn check:vconsole` is read-only and rejects output
|
||||
drift; it does not update the frozen upstream or bless arbitrary vendor changes.
|
||||
CI checks generation before notices and rebuilds the patch before notice output.
|
||||
Never hand-edit the generated minified file.
|
||||
|
||||
## Validation and limits
|
||||
|
||||
`yarn test:vconsole` covers queue order, reentrancy, cancellation exceptions,
|
||||
late callbacks, panel ownership, upstream drift and deterministic generation.
|
||||
`yarn test:browser test/browser/vconsole-lifecycle.spec.js test/browser/site-vendor.spec.js --workers=1`
|
||||
covers real browser globals, named AMD, CommonJS factory loading, visible logs,
|
||||
immediate destroy/recreate, same-ID plugin replacement, pending scroller layout,
|
||||
and the actual mobile page playing local media. Setting
|
||||
`ARTPLAYER_VCONSOLE_BASELINE=1` serves the frozen original only in the lifecycle
|
||||
test to reproduce failures; normal CI serves the candidate and expects no errors.
|
||||
The scroller case holds zero-delay timers to force the late continuation while
|
||||
using native rendering, ResizeObserver and RAF; this is explicit fault injection.
|
||||
|
||||
This patch does not claim complete upstream lifecycle coverage, device coverage,
|
||||
or permission clearance. The upstream LICENSE is distributed verbatim, but its
|
||||
missing MIT body and bundled dependency notices remain open in VENDOR-07/SITE-07.
|
||||
Do not replace or upgrade the bundle to resolve these issues without separately
|
||||
checking entrypoints, logging, CSS and the full dependency notices.
|
||||
@@ -0,0 +1,40 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { createHash } from 'node:crypto'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { build } from 'esbuild'
|
||||
import ts from 'typescript'
|
||||
|
||||
export const upstreamSha256 = '671f47427e1e3048919147c765e9fb71e4ea40d79a8c2829089f499d3e9b9bf4'
|
||||
export const hooks = {
|
||||
factory: '(this||self,(function(){return function(){',
|
||||
signal: 'e._signalLog=function(t){var n=this;this.flushLogScheduled||(this.flushLogScheduled=!0,window.requestAnimationFrame((function(){n.flushLogScheduled=!1,n._flushLogs()}))),this.logQueue.push(t)}',
|
||||
unbind: '0===this.ADDED_LOG_PLUGIN_ID.length&&this.unmockConsole()',
|
||||
panel: 'o&&setTimeout((function(){var e=document.querySelector("#__vc_plug_"+t.id);n.HD(o)?e.innerHTML+=o:n.mf(o.appendTo)?o.appendTo(e):n.kK(o)&&e.insertAdjacentElement("beforeend",o)}),0)',
|
||||
resizeResume: 'case 9:F(p),X(p,W.getPosition(),k),0!==k&&H(N&&L),K();',
|
||||
itemResume: 'case 12:X(p,W.getPosition(),k),e(6,s.style.height=P+"px",s),K();',
|
||||
}
|
||||
|
||||
function replaceOne(source: string, from: string, to: string): string {
|
||||
assert.equal(source.split(from).length, 2, 'Frozen vConsole hook must occur exactly once')
|
||||
return source.replace(from, to)
|
||||
}
|
||||
|
||||
export async function generateVconsole(root: string): Promise<string> {
|
||||
const directory = path.join(root, 'scripts/site-vendor/vconsole')
|
||||
const upstream = fs.readFileSync(path.join(directory, 'upstream.js'), 'utf8')
|
||||
assert.equal(createHash('sha256').update(upstream).digest('hex'), upstreamSha256, 'Frozen vConsole source changed')
|
||||
const source = fs.readFileSync(path.join(directory, 'lifecycle.ts'), 'utf8')
|
||||
const compiled = ts.transpileModule(source, { compilerOptions: { target: ts.ScriptTarget.ES5, module: ts.ModuleKind.ESNext } }).outputText
|
||||
const result = await build({ stdin: { contents: compiled, loader: 'js' }, bundle: true, write: false, platform: 'browser', format: 'iife', globalName: '__artplayerVConsoleLogLifecycle', target: 'es5', minify: true, legalComments: 'none', logLevel: 'silent' })
|
||||
const helper = result.outputFiles?.[0]?.text.trim()
|
||||
assert(helper, 'Missing vConsole lifecycle helper')
|
||||
let candidate = replaceOne(upstream, hooks.signal, 'e._signalLog=function(t){__artplayerVConsoleLogLifecycle.enqueue(this,t,window)}')
|
||||
candidate = replaceOne(candidate, hooks.unbind, '0===this.ADDED_LOG_PLUGIN_ID.length&&__artplayerVConsoleLogLifecycle.unbind(this,window)')
|
||||
candidate = replaceOne(candidate, hooks.panel, 'o&&__artplayerVConsoleLogLifecycle.renderTab(e,t,(function(){var e=document.querySelector("#__vc_plug_"+t.id);n.HD(o)?e.innerHTML+=o:n.mf(o.appendTo)?o.appendTo(e):n.kK(o)&&e.insertAdjacentElement("beforeend",o)}))')
|
||||
// Svelte clears the bound items element on destroy; these continuations resume after a timer.
|
||||
candidate = replaceOne(candidate, hooks.resizeResume, 'case 9:if(!s)return n.abrupt("return");F(p),X(p,W.getPosition(),k),0!==k&&H(N&&L),K();')
|
||||
candidate = replaceOne(candidate, hooks.itemResume, 'case 12:if(!s)return o.abrupt("return");X(p,W.getPosition(),k),e(6,s.style.height=P+"px",s),K();')
|
||||
candidate = replaceOne(candidate, hooks.factory, `(this||self,(function(){${helper}return function(){`)
|
||||
return candidate
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
interface LogHost {
|
||||
ADDED_LOG_PLUGIN_ID: string[]
|
||||
flushLogScheduled: boolean
|
||||
logQueue: unknown[]
|
||||
_flushLogs: () => void
|
||||
unmockConsole: () => void
|
||||
}
|
||||
interface Frames {
|
||||
requestAnimationFrame: (callback: FrameRequestCallback) => number
|
||||
cancelAnimationFrame: (id: number) => void
|
||||
}
|
||||
const pending: { host: LogHost, id: number }[] = []
|
||||
|
||||
interface PanelHost {
|
||||
isInited: boolean
|
||||
pluginList: Record<string, { id: string }>
|
||||
}
|
||||
|
||||
export function renderTab(host: PanelHost, plugin: { id: string }, render: () => void): void {
|
||||
setTimeout(() => {
|
||||
if (host.isInited && host.pluginList[plugin.id] === plugin)
|
||||
render()
|
||||
}, 0)
|
||||
}
|
||||
|
||||
export function enqueue(host: LogHost, log: unknown, frames: Frames): void {
|
||||
if (host.ADDED_LOG_PLUGIN_ID.length === 0)
|
||||
return
|
||||
if (!host.flushLogScheduled) {
|
||||
const ticket = { host, id: 0 }
|
||||
pending.push(ticket)
|
||||
host.flushLogScheduled = true
|
||||
ticket.id = frames.requestAnimationFrame(() => {
|
||||
const index = pending.indexOf(ticket)
|
||||
if (index === -1)
|
||||
return
|
||||
pending.splice(index, 1)
|
||||
host.flushLogScheduled = false
|
||||
host._flushLogs()
|
||||
})
|
||||
}
|
||||
host.logQueue.push(log)
|
||||
}
|
||||
|
||||
export function unbind(host: LogHost, frames: Frames): void {
|
||||
let ticket
|
||||
for (let index = 0; index < pending.length; index++) {
|
||||
if (pending[index]!.host === host) {
|
||||
ticket = pending[index]
|
||||
pending.splice(index, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
host.logQueue = []
|
||||
host.flushLogScheduled = false
|
||||
try {
|
||||
if (ticket && typeof frames.cancelAnimationFrame === 'function')
|
||||
frames.cancelAnimationFrame(ticket.id)
|
||||
}
|
||||
finally {
|
||||
host.unmockConsole()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*!
|
||||
* vConsole v3.15.0 (https://github.com/Tencent/vConsole)
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making vConsole available.
|
||||
* Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
* Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define("VConsole",[],n):"object"==typeof exports?exports.VConsole=n():t.VConsole=n()}(this||self,(function(){return function(){var __webpack_modules__={4264:function(t,n,e){t.exports=e(7588)},5036:function(t,n,e){e(1719),e(5677),e(6394),e(5334),e(6969),e(2021),e(8328),e(2129);var o=e(1287);t.exports=o.Promise},2582:function(t,n,e){e(1646),e(6394),e(2004),e(462),e(8407),e(2429),e(1172),e(8288),e(1274),e(8201),e(6626),e(3211),e(9952),e(15),e(9831),e(7521),e(2972),e(6956),e(5222),e(2257);var o=e(1287);t.exports=o.Symbol},8257:function(t,n,e){var o=e(7583),r=e(9212),i=e(5637),a=o.TypeError;t.exports=function(t){if(r(t))return t;throw a(i(t)+" is not a function")}},1186:function(t,n,e){var o=e(7583),r=e(2097),i=e(5637),a=o.TypeError;t.exports=function(t){if(r(t))return t;throw a(i(t)+" is not a constructor")}},9882:function(t,n,e){var o=e(7583),r=e(9212),i=o.String,a=o.TypeError;t.exports=function(t){if("object"==typeof t||r(t))return t;throw a("Can't set "+i(t)+" as a prototype")}},6288:function(t,n,e){var o=e(3649),r=e(3590),i=e(4615),a=o("unscopables"),c=Array.prototype;null==c[a]&&i.f(c,a,{configurable:!0,value:r(null)}),t.exports=function(t){c[a][t]=!0}},4761:function(t,n,e){var o=e(7583),r=e(2447),i=o.TypeError;t.exports=function(t,n){if(r(n,t))return t;throw i("Incorrect invocation")}},2569:function(t,n,e){var o=e(7583),r=e(794),i=o.String,a=o.TypeError;t.exports=function(t){if(r(t))return t;throw a(i(t)+" is not an object")}},5766:function(t,n,e){var o=e(2977),r=e(6782),i=e(1825),a=function(t){return function(n,e,a){var c,u=o(n),s=i(u),l=r(a,s);if(t&&e!=e){for(;s>l;)if((c=u[l++])!=c)return!0}else for(;s>l;l++)if((t||l in u)&&u[l]===e)return t||l||0;return!t&&-1}};t.exports={includes:a(!0),indexOf:a(!1)}},4805:function(t,n,e){var o=e(2938),r=e(7386),i=e(5044),a=e(1324),c=e(1825),u=e(4822),s=r([].push),l=function(t){var n=1==t,e=2==t,r=3==t,l=4==t,f=6==t,d=7==t,v=5==t||f;return function(h,p,g,m){for(var _,b,y=a(h),w=i(y),E=o(p,g),L=c(w),T=0,x=m||u,C=n?x(h,L):e||d?x(h,0):void 0;L>T;T++)if((v||T in w)&&(b=E(_=w[T],T,y),t))if(n)C[T]=b;else if(b)switch(t){case 3:return!0;case 5:return _;case 6:return T;case 2:s(C,_)}else switch(t){case 4:return!1;case 7:s(C,_)}return f?-1:r||l?l:C}};t.exports={forEach:l(0),map:l(1),filter:l(2),some:l(3),every:l(4),find:l(5),findIndex:l(6),filterReject:l(7)}},9269:function(t,n,e){var o=e(6544),r=e(3649),i=e(4061),a=r("species");t.exports=function(t){return i>=51||!o((function(){var n=[];return(n.constructor={})[a]=function(){return{foo:1}},1!==n[t](Boolean).foo}))}},4546:function(t,n,e){var o=e(7583),r=e(6782),i=e(1825),a=e(5999),c=o.Array,u=Math.max;t.exports=function(t,n,e){for(var o=i(t),s=r(n,o),l=r(void 0===e?o:e,o),f=c(u(l-s,0)),d=0;s<l;s++,d++)a(f,d,t[s]);return f.length=d,f}},6917:function(t,n,e){var o=e(7386);t.exports=o([].slice)},5289:function(t,n,e){var o=e(7583),r=e(4521),i=e(2097),a=e(794),c=e(3649)("species"),u=o.Array;t.exports=function(t){var n;return r(t)&&(n=t.constructor,(i(n)&&(n===u||r(n.prototype))||a(n)&&null===(n=n[c]))&&(n=void 0)),void 0===n?u:n}},4822:function(t,n,e){var o=e(5289);t.exports=function(t,n){return new(o(t))(0===n?0:n)}},3616:function(t,n,e){var o=e(3649)("iterator"),r=!1;try{var i=0,a={next:function(){return{done:!!i++}},return:function(){r=!0}};a[o]=function(){return this},Array.from(a,(function(){throw 2}))}catch(t){}t.exports=function(t,n){if(!n&&!r)return!1;var e=!1;try{var i={};i[o]=function(){return{next:function(){return{done:e=!0}}}},t(i)}catch(t){}return e}},9624:function(t,n,e){var o=e(7386),r=o({}.toString),i=o("".slice);t.exports=function(t){return i(r(t),8,-1)}},3058:function(t,n,e){var o=e(7583),r=e(8191),i=e(9212),a=e(9624),c=e(3649)("toStringTag"),u=o.Object,s="Arguments"==a(function(){return arguments}());t.exports=r?a:function(t){var n,e,o;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(e=function(t,n){try{return t[n]}catch(t){}}(n=u(t),c))?e:s?a(n):"Object"==(o=a(n))&&i(n.callee)?"Arguments":o}},1509:function(t,n,e){var o=e(7386)("".replace),r=String(Error("zxcasd").stack),i=/\n\s*at [^:]*:[^\n]*/,a=i.test(r);t.exports=function(t,n){if(a&&"string"==typeof t)for(;n--;)t=o(t,i,"");return t}},3478:function(t,n,e){var o=e(2870),r=e(929),i=e(6683),a=e(4615);t.exports=function(t,n,e){for(var c=r(n),u=a.f,s=i.f,l=0;l<c.length;l++){var f=c[l];o(t,f)||e&&o(e,f)||u(t,f,s(n,f))}}},926:function(t,n,e){var o=e(6544);t.exports=!o((function(){function t(){}return t.prototype.constructor=null,Object.getPrototypeOf(new t)!==t.prototype}))},4683:function(t,n,e){"use strict";var o=e(2365).IteratorPrototype,r=e(3590),i=e(4677),a=e(8821),c=e(339),u=function(){return this};t.exports=function(t,n,e,s){var l=n+" Iterator";return t.prototype=r(o,{next:i(+!s,e)}),a(t,l,!1,!0),c[l]=u,t}},57:function(t,n,e){var o=e(8494),r=e(4615),i=e(4677);t.exports=o?function(t,n,e){return r.f(t,n,i(1,e))}:function(t,n,e){reLine truncated
|
||||
@@ -28,6 +28,7 @@
|
||||
"build-site-notices.mjs",
|
||||
"build-i18n.js",
|
||||
"build-docs.js",
|
||||
"../packages/artplayer-vitepress/build/**/*.ts"
|
||||
"../packages/artplayer-vitepress/build/**/*.ts",
|
||||
"build-vconsole.mjs"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/* eslint-disable no-console -- Exercise the vendor's public log interception. */
|
||||
import { createHash } from 'node:crypto'
|
||||
import fs from 'node:fs'
|
||||
import process from 'node:process'
|
||||
import { expect, test } from '@playwright/test'
|
||||
|
||||
const artifact = process.env.ARTPLAYER_VCONSOLE_BASELINE === '1' ? 'scripts/site-vendor/vconsole/upstream.js' : 'docs/assets/js/vconsole.min.js'
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.route('**/assets/js/vconsole.min.js', route => route.fulfill({ contentType: 'application/javascript', body: fs.readFileSync(artifact) }))
|
||||
})
|
||||
|
||||
for (const recreate of [false, true]) {
|
||||
test(`vConsole cancels queued logging on last plugin removal, recreate=${recreate}`, async ({ page, browser }, testInfo) => {
|
||||
const errors = []
|
||||
page.on('pageerror', error => errors.push(error.message))
|
||||
await page.route('**/vconsole-lifecycle.html', route => route.fulfill({ contentType: 'text/html', body: '<!doctype html><script src="/assets/js/vconsole.min.js"></script>' }))
|
||||
await page.goto('/vconsole-lifecycle.html')
|
||||
const result = await page.evaluate(async (recreate) => {
|
||||
const raf = window.requestAnimationFrame.bind(window)
|
||||
const cancel = window.cancelAnimationFrame.bind(window)
|
||||
const callbacks = []
|
||||
const cancellations = []
|
||||
window.requestAnimationFrame = (callback) => {
|
||||
const id = raf(callback)
|
||||
callbacks.push({ id, callback })
|
||||
return id
|
||||
}
|
||||
window.cancelAnimationFrame = (id) => {
|
||||
cancellations.push(id)
|
||||
cancel(id)
|
||||
}
|
||||
const first = new window.VConsole({ defaultPlugins: [] })
|
||||
console.log('stale-before-destroy')
|
||||
first.destroy()
|
||||
const late = callbacks.slice()
|
||||
let next
|
||||
if (recreate) {
|
||||
await new Promise((resolve) => {
|
||||
next = new window.VConsole({ defaultPlugins: [], onReady: resolve })
|
||||
})
|
||||
next.show()
|
||||
console.log('fresh-after-recreate')
|
||||
}
|
||||
// Native frames allow the original queued callback to reproduce the failure.
|
||||
await new Promise(resolve => raf(() => raf(resolve)))
|
||||
// A cancelled callback delivered late must also be inert after recreation.
|
||||
const staleErrors = []
|
||||
for (const entry of late) {
|
||||
if (cancellations.includes(entry.id)) {
|
||||
try {
|
||||
entry.callback(performance.now())
|
||||
}
|
||||
catch (error) {
|
||||
staleErrors.push(String(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
const logs = document.querySelector('#__vconsole')?.textContent || ''
|
||||
window.requestAnimationFrame = raf
|
||||
window.cancelAnimationFrame = cancel
|
||||
next?.destroy()
|
||||
await new Promise(resolve => raf(resolve))
|
||||
return { scheduled: callbacks.length, cancelled: cancellations.length, staleErrors, logs, roots: document.querySelectorAll('#__vconsole').length }
|
||||
}, recreate)
|
||||
await testInfo.attach('vconsole-lifecycle', { contentType: 'application/json', body: JSON.stringify({ result, errors, browser: browser.version(), artifactSha256: createHash('sha256').update(fs.readFileSync(artifact)).digest('hex') }) })
|
||||
expect(result.scheduled).toBeGreaterThan(0)
|
||||
expect(result.cancelled).toBeGreaterThan(0)
|
||||
expect(errors).toEqual([])
|
||||
expect(result.staleErrors).toEqual([])
|
||||
expect(result.roots).toBe(0)
|
||||
if (recreate) {
|
||||
expect(result.logs).toContain('fresh-after-recreate')
|
||||
expect(result.logs).not.toContain('stale-before-destroy')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
test('vConsole removed plugin panels cannot attach to a replacement with the same id', async ({ page }) => {
|
||||
const errors = []
|
||||
page.on('pageerror', error => errors.push(error.message))
|
||||
await page.route('**/vconsole-lifecycle.html', route => route.fulfill({ contentType: 'text/html', body: '<!doctype html><script src="/assets/js/vconsole.min.js"></script>' }))
|
||||
await page.goto('/vconsole-lifecycle.html')
|
||||
const result = await page.evaluate(async () => {
|
||||
let instance
|
||||
await new Promise((resolve) => {
|
||||
instance = new window.VConsole({ defaultPlugins: [], onReady: resolve })
|
||||
})
|
||||
const makePlugin = (text) => {
|
||||
const plugin = new window.VConsole.VConsolePlugin('replacement', 'Replacement')
|
||||
plugin.on('renderTab', callback => callback(`<div class="panel-ownership-probe">${text}</div>`))
|
||||
return plugin
|
||||
}
|
||||
instance.addPlugin(makePlugin('stale-panel'))
|
||||
instance.removePlugin('replacement')
|
||||
instance.addPlugin(makePlugin('fresh-panel'))
|
||||
await new Promise(resolve => setTimeout(resolve, 30))
|
||||
const panels = Array.from(document.querySelectorAll('.panel-ownership-probe'), element => element.textContent)
|
||||
instance.destroy()
|
||||
return panels
|
||||
})
|
||||
expect(result).toEqual(['fresh-panel'])
|
||||
expect(errors).toEqual([])
|
||||
})
|
||||
|
||||
for (const mode of ['amd', 'commonjs']) {
|
||||
test(`vConsole retains ${mode} factory and private patch state`, async ({ page }) => {
|
||||
const errors = []
|
||||
page.on('pageerror', error => errors.push(error.message))
|
||||
const setup = mode === 'amd'
|
||||
? 'window.define=function(name,deps,factory){window.testModuleName=name;window.testConstructor=factory()};window.define.amd=true;'
|
||||
: 'window.module={exports:{}};window.exports=window.module.exports;'
|
||||
await page.route('**/vconsole-lifecycle.html', route => route.fulfill({ contentType: 'text/html', body: `<!doctype html><script>${setup}</script><script src="/assets/js/vconsole.min.js"></script>` }))
|
||||
await page.goto('/vconsole-lifecycle.html')
|
||||
const result = await page.evaluate(async (mode) => {
|
||||
const Constructor = mode === 'amd' ? window.testConstructor : window.module.exports
|
||||
let instance
|
||||
await new Promise((resolve) => {
|
||||
instance = new Constructor({ defaultPlugins: [], onReady: resolve })
|
||||
})
|
||||
instance.show()
|
||||
console.log('factory-contract-log')
|
||||
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))
|
||||
const logs = document.querySelector('#__vconsole').textContent
|
||||
instance.destroy()
|
||||
return { logs, global: typeof window.VConsole, helper: typeof window.__artplayerVConsoleLogLifecycle, roots: document.querySelectorAll('#__vconsole').length }
|
||||
}, mode)
|
||||
expect(result.logs).toContain('factory-contract-log')
|
||||
expect(result.global).toBe('undefined')
|
||||
expect(result.helper).toBe('undefined')
|
||||
expect(result.roots).toBe(0)
|
||||
expect(errors).toEqual([])
|
||||
})
|
||||
}
|
||||
|
||||
test('vConsole scroller resumes pending layout work safely after destruction', async ({ page }, testInfo) => {
|
||||
const errors = []
|
||||
page.on('pageerror', error => errors.push(error.message))
|
||||
await page.route('**/vconsole-lifecycle.html', route => route.fulfill({ contentType: 'text/html', body: '<!doctype html><script src="/assets/js/vconsole.min.js"></script>' }))
|
||||
await page.goto('/vconsole-lifecycle.html')
|
||||
await page.evaluate(async () => {
|
||||
await new Promise((resolve) => {
|
||||
window.scrollerInstance = new window.VConsole({ defaultPlugins: [], onReady: resolve })
|
||||
})
|
||||
window.scrollerInstance.show()
|
||||
console.log('scroller-ready')
|
||||
})
|
||||
await expect(page.getByText('scroller-ready', { exact: true })).toBeVisible()
|
||||
const result = await page.evaluate(async () => {
|
||||
const instance = window.scrollerInstance
|
||||
const nativeTimer = window.setTimeout.bind(window)
|
||||
const pending = []
|
||||
window.setTimeout = (callback, delay, ...args) => {
|
||||
if (delay === 0) {
|
||||
pending.push(() => callback(...args))
|
||||
return 0
|
||||
}
|
||||
return nativeTimer(callback, delay, ...args)
|
||||
}
|
||||
document.querySelector('.vc-scroller-viewport').style.height = '137px'
|
||||
for (let index = 0; index < 20; index++)
|
||||
console.log(`layout-row-${index}`)
|
||||
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))
|
||||
const rows = document.querySelectorAll('.vc-log-row').length
|
||||
instance.destroy()
|
||||
window.setTimeout = nativeTimer
|
||||
for (const callback of pending)
|
||||
callback()
|
||||
await new Promise(resolve => nativeTimer(resolve, 30))
|
||||
return { pending: pending.length, rows, roots: document.querySelectorAll('#__vconsole').length }
|
||||
})
|
||||
await testInfo.attach('vconsole-scroller', { contentType: 'application/json', body: JSON.stringify({ result, errors, artifactSha256: createHash('sha256').update(fs.readFileSync(artifact)).digest('hex') }) })
|
||||
expect(result.pending).toBeGreaterThan(0)
|
||||
expect(result.rows).toBeGreaterThan(0)
|
||||
expect(result.roots).toBe(0)
|
||||
expect(errors).toEqual([])
|
||||
})
|
||||
@@ -0,0 +1,141 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Exercise scheduling ownership and cleanup failure semantics.
|
||||
import test from 'node:test'
|
||||
import { generateVconsole, hooks } from '../scripts/site-vendor/vconsole/build.ts'
|
||||
import { enqueue, renderTab, unbind } from '../scripts/site-vendor/vconsole/lifecycle.ts'
|
||||
|
||||
function fixture() {
|
||||
const queued = new Map()
|
||||
const cancelled = []
|
||||
const flushed = []
|
||||
let sequence = 0
|
||||
let restored = 0
|
||||
const host = {
|
||||
ADDED_LOG_PLUGIN_ID: ['default'],
|
||||
logQueue: [],
|
||||
flushLogScheduled: false,
|
||||
_flushLogs() {
|
||||
flushed.push(this.logQueue)
|
||||
this.logQueue = []
|
||||
},
|
||||
unmockConsole() { restored++ },
|
||||
}
|
||||
const frames = {
|
||||
requestAnimationFrame(callback) {
|
||||
queued.set(++sequence, callback)
|
||||
return sequence
|
||||
},
|
||||
cancelAnimationFrame(id) { cancelled.push(id) },
|
||||
}
|
||||
return { host, frames, queued, cancelled, flushed, restored: () => restored }
|
||||
}
|
||||
|
||||
test('vConsole batches in original order and releases the scheduling flag before flushing', () => {
|
||||
const { host, frames, queued, flushed } = fixture()
|
||||
enqueue(host, 'one', frames)
|
||||
enqueue(host, 'two', frames)
|
||||
assert.equal(queued.size, 1)
|
||||
queued.get(1)()
|
||||
assert.equal(host.flushLogScheduled, false)
|
||||
assert.deepEqual(flushed, [['one', 'two']])
|
||||
enqueue(host, 'three', frames)
|
||||
queued.get(2)()
|
||||
assert.deepEqual(flushed, [['one', 'two'], ['three']])
|
||||
})
|
||||
|
||||
test('vConsole cancelled callbacks cannot flush or consume a new instance queue', () => {
|
||||
const { host, frames, queued, cancelled, flushed, restored } = fixture()
|
||||
enqueue(host, 'old', frames)
|
||||
host.ADDED_LOG_PLUGIN_ID = []
|
||||
unbind(host, frames)
|
||||
enqueue(host, 'captured old logger after destroy', frames)
|
||||
assert.deepEqual(host.logQueue, [])
|
||||
assert.deepEqual(cancelled, [1])
|
||||
assert.equal(restored(), 1)
|
||||
host.ADDED_LOG_PLUGIN_ID = ['default']
|
||||
enqueue(host, 'new', frames)
|
||||
queued.get(1)()
|
||||
assert.equal(host.flushLogScheduled, true)
|
||||
assert.deepEqual(host.logQueue, ['new'])
|
||||
queued.get(2)()
|
||||
assert.deepEqual(flushed, [['new']])
|
||||
})
|
||||
|
||||
test('vConsole releases ownership and restores console even if native cancellation throws', () => {
|
||||
const { host, frames, queued, restored, flushed } = fixture()
|
||||
const failure = new Error('cancel failed')
|
||||
enqueue(host, 'old', frames)
|
||||
frames.cancelAnimationFrame = () => {
|
||||
throw failure
|
||||
}
|
||||
assert.throws(() => unbind(host, frames), error => error === failure)
|
||||
assert.equal(restored(), 1)
|
||||
assert.equal(host.flushLogScheduled, false)
|
||||
queued.get(1)()
|
||||
assert.deepEqual(flushed, [])
|
||||
})
|
||||
|
||||
test('vConsole allows reentrant logging while a prior batch is being flushed', () => {
|
||||
const { host, frames, queued } = fixture()
|
||||
const batches = []
|
||||
host._flushLogs = () => {
|
||||
batches.push(host.logQueue)
|
||||
host.logQueue = []
|
||||
if (batches.length === 1)
|
||||
enqueue(host, 'reentrant', frames)
|
||||
}
|
||||
enqueue(host, 'initial', frames)
|
||||
queued.get(1)()
|
||||
queued.get(2)()
|
||||
assert.deepEqual(batches, [['initial'], ['reentrant']])
|
||||
})
|
||||
|
||||
test('vConsole delayed panels retain their original instance and plugin ownership', async () => {
|
||||
const plugin = { id: 'custom' }
|
||||
const host = { isInited: true, pluginList: { custom: plugin } }
|
||||
const rendered = []
|
||||
renderTab(host, plugin, () => rendered.push('removed'))
|
||||
host.pluginList.custom = { id: 'custom' }
|
||||
renderTab(host, host.pluginList.custom, () => rendered.push('replacement'))
|
||||
const destroyed = { isInited: true, pluginList: { custom: plugin } }
|
||||
renderTab(destroyed, plugin, () => rendered.push('destroyed'))
|
||||
destroyed.isInited = false
|
||||
await new Promise(resolve => setTimeout(resolve, 10))
|
||||
assert.deepEqual(rendered, ['replacement'])
|
||||
})
|
||||
|
||||
test('vConsole generation is deterministic and preserves all bytes outside pinned hooks', async () => {
|
||||
const root = path.resolve('.')
|
||||
const candidate = await generateVconsole(root)
|
||||
assert.equal(candidate, await generateVconsole(root))
|
||||
const upstream = fs.readFileSync('scripts/site-vendor/vconsole/upstream.js', 'utf8')
|
||||
const original = upstream.split(hooks.factory)
|
||||
assert.equal(original.length, 2)
|
||||
const suffix = original[1]
|
||||
.replace(hooks.signal, 'e._signalLog=function(t){__artplayerVConsoleLogLifecycle.enqueue(this,t,window)}')
|
||||
.replace(hooks.unbind, '0===this.ADDED_LOG_PLUGIN_ID.length&&__artplayerVConsoleLogLifecycle.unbind(this,window)')
|
||||
.replace(hooks.panel, 'o&&__artplayerVConsoleLogLifecycle.renderTab(e,t,(function(){var e=document.querySelector("#__vc_plug_"+t.id);n.HD(o)?e.innerHTML+=o:n.mf(o.appendTo)?o.appendTo(e):n.kK(o)&&e.insertAdjacentElement("beforeend",o)}))')
|
||||
.replace(hooks.resizeResume, 'case 9:if(!s)return n.abrupt("return");F(p),X(p,W.getPosition(),k),0!==k&&H(N&&L),K();')
|
||||
.replace(hooks.itemResume, 'case 12:if(!s)return o.abrupt("return");X(p,W.getPosition(),k),e(6,s.style.height=P+"px",s),K();')
|
||||
assert(candidate.startsWith(`${original[0]}(this||self,(function(){var __artplayerVConsoleLogLifecycle=`))
|
||||
assert(candidate.endsWith(`return function(){${suffix}`))
|
||||
assert.equal(candidate, fs.readFileSync('docs/assets/js/vconsole.min.js', 'utf8'))
|
||||
})
|
||||
|
||||
test('vConsole generator rejects changed upstream input before producing a candidate', async () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'artplayer-vconsole-'))
|
||||
try {
|
||||
const directory = path.join(root, 'scripts/site-vendor/vconsole')
|
||||
fs.mkdirSync(directory, { recursive: true })
|
||||
fs.writeFileSync(path.join(directory, 'upstream.js'), 'unexpected source')
|
||||
await assert.rejects(generateVconsole(root), /Frozen vConsole source changed/)
|
||||
}
|
||||
finally {
|
||||
assert.equal(path.dirname(root), path.resolve(os.tmpdir()))
|
||||
assert(path.basename(root).startsWith('artplayer-vconsole-'))
|
||||
fs.rmSync(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user