refactor(mask): [PKG-MASK-04] migrate strict types and preserve public consumers

This commit is contained in:
Harvey Zhao committed 2026-09-14 00:05:14 +08:00
1 parent aba125262a
commit aca4bc1cd2
30 files changed
+1848 -72

No files matched your search

+4
View File
@@ -6,6 +6,10 @@ Use the pinned Node/Yarn toolchain from `../refactor/toolchain-setup.md`.
frozen historical defects and public contracts. Candidate tests also run in
`test:unit`; their controlled SDK, RAF and canvas hosts do not prove native model
inference, GPU disposal or browser mask geometry. See the Mask architecture map.
`yarn test:danmuku-mask-types-package` installs both published versions and the
candidate tarball outside the workspace for old/current compiler compatibility,
negative cases and CJS/legacy registration without model startup. The ordinary
baseline suite also checks public declaration identity and editor generation.
`yarn test:mediabunny` runs historical lifecycle observations, candidate load cancellation,
and real SDK input parsing/track contracts; all are in `test:unit`. Candidate failures
+2 -1
View File
@@ -1,10 +1,11 @@
import assert from 'node:assert/strict'
import vm from 'node:vm'
import { build } from 'esbuild'
import { resolveSource } from './load.js'
export const flush = () => new Promise(resolve => setImmediate(resolve))
export async function maskCandidate() {
const result = await build({ entryPoints: ['packages/artplayer-plugin-danmuku-mask/src/index.js'], bundle: true, write: false, format: 'cjs', platform: 'browser', external: ['@tensorflow-models/body-segmentation', '@tensorflow/tfjs-core', '@tensorflow/tfjs-backend-webgl', '@tensorflow/tfjs-backend-cpu'] })
const result = await build({ entryPoints: [resolveSource('packages/artplayer-plugin-danmuku-mask/src/index')], bundle: true, write: false, format: 'cjs', platform: 'browser', external: ['@tensorflow-models/body-segmentation', '@tensorflow/tfjs-core', '@tensorflow/tfjs-backend-webgl', '@tensorflow/tfjs-backend-cpu'] })
return result.outputFiles[0].text
}
+34
View File
@@ -0,0 +1,34 @@
import Artplayer from 'artplayer'
import mask from 'artplayer-plugin-danmuku-mask'
import legacy from 'artplayer-plugin-danmuku-mask/legacy'
interface Option {
solutionPath?: string
modelSelection?: number
smoothSegmentation?: boolean
minDetectionConfidence?: number
minTrackingConfidence?: number
selfieMode?: boolean
drawContour?: boolean
foregroundThreshold?: number
opacity?: number
maskBlurAmount?: number
}
interface Result { name: 'artplayerPluginDanmukuMask', start: () => Promise<void>, stop: () => void }
type Factory = (option?: Option) => (art: Artplayer) => Result
type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false
type Assert<T extends true> = T
type Arguments = Assert<Equal<Parameters<typeof mask>, [option?: Option]>>
type Registration = Assert<Equal<ReturnType<ReturnType<typeof mask>>, Result>>
const replacement: Factory = () => () => ({ name: 'artplayerPluginDanmukuMask', async start() {}, stop() {} })
const toOld: Factory = mask
const fromOld: typeof mask = replacement
const legacyReplacement: typeof legacy = replacement
const option: Option = { solutionPath: '/models', modelSelection: 0, smoothSegmentation: false, minDetectionConfidence: 0, minTrackingConfidence: 0, selfieMode: false, drawContour: false, foregroundThreshold: 0, opacity: 0, maskBlurAmount: 0 }
const art = new Artplayer({ container: '#player', url: '/video.mp4', plugins: [mask(), mask(option), legacy(undefined)] })
const result = mask(option)(art)
const name: 'artplayerPluginDanmukuMask' = result.name
const starting: Promise<void> = result.start()
const stopped: void = result.stop()
void [toOld, fromOld, legacyReplacement, name, starting, stopped]
export type { Arguments, Registration }