mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-28 18:41:20 -08:00
fix(thumbnail): [PKG-TOOL-THUMB-04] supply public types and installed module entries
This commit is contained in:
1 parent
15485b6420
commit
3bff236145
33 files changed
+2466
-164
No files matched your search
@@ -1,8 +1,9 @@
|
||||
# Thumbnail tool maintenance map
|
||||
|
||||
The runtime responsibilities were separated under PKG-TOOL-THUMB-03 and are now
|
||||
strict TypeScript under PKG-TOOL-THUMB-04. Published declarations and installed
|
||||
entrypoint checks are still in progress; runtime migration is not release readiness.
|
||||
strict TypeScript under PKG-TOOL-THUMB-04. Public declarations and isolated installed
|
||||
entrypoints now have checks; the historical default-policy decision and final
|
||||
integration/release gates remain open.
|
||||
|
||||
| File | Responsibility |
|
||||
| --- | --- |
|
||||
@@ -15,6 +16,7 @@ entrypoint checks are still in progress; runtime migration is not release readin
|
||||
| src/emitter.ts | Typed local adaptation of tiny-emitter; preserves on/once/emit/off behavior |
|
||||
| src/utils.ts | Pure clamp and filename helpers; unused sleep/serial helpers removed |
|
||||
| src/types.ts | Internal option, frame, event tuple, job and lifecycle contracts; no runtime output |
|
||||
| types/artplayer-tool-thumbnail.d.ts | Public class/namespace; d.cts/d.mts wrappers share its identity |
|
||||
|
||||
The entry delegates to input/source/extraction/sheet. Helpers import its type only;
|
||||
the imports are erased and do not create a runtime dependency on the entry.
|
||||
@@ -100,7 +102,8 @@ Safari/WebKit evidence in 05. The independent tool example is
|
||||
callback failures, reentrancy and private resources. Browser tests hold real PNG
|
||||
callbacks and replace a selected file during encoding; only the latest job may
|
||||
complete. Continue published declarations and installed consumers in 04; preserve
|
||||
old tests and the default delay/height boundary.
|
||||
old tests and the default delay/height boundary. The [public type guide](types/README.md)
|
||||
documents declaration ownership, module entries, events and installed consumers.
|
||||
See [task plan](../../refactor/plan.md) and [risk ledger](../../refactor/risks.json).
|
||||
|
||||
## Type and provenance boundaries
|
||||
@@ -110,9 +113,12 @@ strict, noUncheckedIndexedAccess, noImplicitOverride, skipLibCheck=false and no
|
||||
ambient Node/test globals. Public fields use declare so TypeScript does not add
|
||||
early undefined properties or change constructor property order. File/URL/density
|
||||
fields retain their absence until the corresponding operation publishes them.
|
||||
Unknown option fields remain unknown, and custom string/number/symbol events keep
|
||||
an open payload boundary; built-in events have precise tuples. This source typing
|
||||
is not yet the missing public npm declaration entrypoint.
|
||||
Unknown option fields remain unknown. Custom string/number/symbol events infer
|
||||
caller-defined callback tuples; built-in events have precise tuples. Their
|
||||
heterogeneous registry erases tuple types at storage and applies a local assertion
|
||||
when dispatching. Public and source constructors are checked in both directions.
|
||||
Error payloads remain unknown because callbacks can throw arbitrary message
|
||||
values. This does not change runtime error delivery.
|
||||
|
||||
Assertions are limited to existing runtime boundaries: a temporary empty option
|
||||
object before setup validation; the input/wrapper conversion; event target and
|
||||
@@ -129,7 +135,8 @@ The emitter structurally corresponds to tiny-emitter 2.1.0, which the recovered
|
||||
The pinned [reference files](../../refactor/baselines/thumbnail-vendor/sources.json)
|
||||
and [MIT notice](THIRD_PARTY_NOTICES) retain upstream attribution. The repository
|
||||
build includes the complete notice in main, legacy and ESM bundle headers.
|
||||
Final packed contents remain a distribution gate. Do not replace this local
|
||||
Current packed contents include the complete notice and no implementation source;
|
||||
new release candidates must repeat the installed check. Do not replace this local
|
||||
emitter with the core emitter as part of a type-only change: dispatch semantics
|
||||
and historical prototype-key handling need their own behavior review.
|
||||
|
||||
|
||||
@@ -19,11 +19,40 @@
|
||||
"player"
|
||||
],
|
||||
"exports": {
|
||||
"import": "./dist/artplayer-tool-thumbnail.esm.js",
|
||||
"require": "./dist/artplayer-tool-thumbnail.js",
|
||||
"default": "./dist/artplayer-tool-thumbnail.js"
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./types/artplayer-tool-thumbnail.d.mts",
|
||||
"default": "./dist/artplayer-tool-thumbnail.mjs"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/artplayer-tool-thumbnail.d.cts",
|
||||
"default": "./dist/artplayer-tool-thumbnail.js"
|
||||
},
|
||||
"default": "./dist/artplayer-tool-thumbnail.js"
|
||||
},
|
||||
"./legacy": {
|
||||
"types": "./types/artplayer-tool-thumbnail.d.cts",
|
||||
"import": "./dist/artplayer-tool-thumbnail.legacy.js",
|
||||
"require": "./dist/artplayer-tool-thumbnail.legacy.js",
|
||||
"default": "./dist/artplayer-tool-thumbnail.legacy.js"
|
||||
}
|
||||
},
|
||||
"module": "dist/artplayer-tool-thumbnail.esm.js",
|
||||
"types": "types/artplayer-tool-thumbnail.d.ts",
|
||||
"browserslist": "last 1 Chrome version"
|
||||
"main": "./dist/artplayer-tool-thumbnail.js",
|
||||
"module": "./dist/artplayer-tool-thumbnail.mjs",
|
||||
"types": "./types/artplayer-tool-thumbnail.d.ts",
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"legacy": [
|
||||
"types/artplayer-tool-thumbnail.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"ARCHITECTURE.md",
|
||||
"README.md",
|
||||
"THIRD_PARTY_NOTICES",
|
||||
"dist",
|
||||
"types"
|
||||
]
|
||||
}
|
||||
@@ -1,26 +1,27 @@
|
||||
// Locally adapted from tiny-emitter; upstream reference 2.1.0. See THIRD_PARTY_NOTICES.
|
||||
type EventMap<Events> = { [Name in keyof Events]: readonly unknown[] }
|
||||
type EventArgs<Events extends EventMap<Events>, Name extends PropertyKey, Custom extends unknown[]> = Name extends keyof Events ? [...Events[Name]] : Custom
|
||||
type Listener<Args extends unknown[]> = ((...args: Args) => unknown) & { _?: (...args: Args) => unknown }
|
||||
interface Registration<Args extends unknown[]> {
|
||||
fn: Listener<Args>
|
||||
ctx: unknown
|
||||
}
|
||||
type Registry<Events extends EventMap<Events>> = { [Name in keyof Events]?: Registration<[...Events[Name]]>[] }
|
||||
type Registry = Partial<Record<PropertyKey, Registration<never[]>[]>>
|
||||
|
||||
export default class Emitter<Events extends EventMap<Events> = Record<PropertyKey, unknown[]>> {
|
||||
declare e?: Registry<Events>
|
||||
declare e?: Registry
|
||||
|
||||
on<Name extends keyof Events, Context>(name: Name, fn: (this: Context, ...args: [...Events[Name]]) => unknown, ctx?: Context): this {
|
||||
const e: Registry<Events> = this.e || (this.e = {});
|
||||
(e[name] || (e[name] = [])).push({ fn, ctx })
|
||||
on<Name extends PropertyKey, Custom extends unknown[], Context>(name: Name, fn: (this: Context, ...args: EventArgs<Events, Name, Custom>) => unknown, ctx?: Context): this {
|
||||
const e: Registry = this.e || (this.e = {});
|
||||
(e[name] || (e[name] = [] as Registration<never[]>[])).push({ fn, ctx })
|
||||
return this
|
||||
}
|
||||
|
||||
once<Name extends keyof Events, Context>(name: Name, fn: (this: Context, ...args: [...Events[Name]]) => unknown, ctx?: Context): this {
|
||||
once<Name extends PropertyKey, Custom extends unknown[], Context>(name: Name, fn: (this: Context, ...args: EventArgs<Events, Name, Custom>) => unknown, ctx?: Context): this {
|
||||
// eslint-disable-next-line ts/no-this-alias -- Preserve the owning emitter independently of callback ctx.
|
||||
const self = this
|
||||
const callback: Listener<[...Events[Name]]> = fn
|
||||
function listener(...args: [...Events[Name]]) {
|
||||
const callback: Listener<EventArgs<Events, Name, Custom>> = fn
|
||||
function listener(...args: EventArgs<Events, Name, Custom>) {
|
||||
self.off(name, listener)
|
||||
callback.apply(ctx, args)
|
||||
}
|
||||
@@ -28,19 +29,20 @@ export default class Emitter<Events extends EventMap<Events> = Record<PropertyKe
|
||||
return this.on(name, listener, ctx)
|
||||
}
|
||||
|
||||
emit<Name extends keyof Events>(name: Name, ...data: [...Events[Name]]): this {
|
||||
const e: Registry<Events> = this.e || (this.e = {})
|
||||
emit<Name extends PropertyKey, Custom extends unknown[]>(name: Name, ...data: EventArgs<Events, Name, Custom>): this {
|
||||
const e: Registry = this.e || (this.e = {})
|
||||
const evtArr = (e[name] || []).slice()
|
||||
for (let i = 0; i < evtArr.length; i += 1) {
|
||||
evtArr[i]!.fn.apply(evtArr[i]!.ctx, data)
|
||||
// The registry erases heterogeneous listener tuples; emit checks its own payload.
|
||||
(evtArr[i]!.fn as Listener<typeof data>).apply(evtArr[i]!.ctx, data)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
off<Name extends keyof Events>(name: Name, callback?: Listener<[...Events[Name]]>): this {
|
||||
const e: Registry<Events> = this.e || (this.e = {})
|
||||
off<Name extends PropertyKey, Custom extends unknown[]>(name: Name, callback?: Listener<EventArgs<Events, Name, Custom>>): this {
|
||||
const e: Registry = this.e || (this.e = {})
|
||||
const evts = e[name]
|
||||
const liveEvents: Registration<[...Events[Name]]>[] = []
|
||||
const liveEvents: Registration<never[]>[] = []
|
||||
if (evts && callback) {
|
||||
for (let i = 0, len = evts.length; i < len; i += 1) {
|
||||
if (evts[i]!.fn !== callback && evts[i]!.fn._ !== callback)
|
||||
|
||||
@@ -75,7 +75,7 @@ function createJob(tool: ArtplayerToolThumbnail, state: LifecycleState) {
|
||||
}
|
||||
if (report) {
|
||||
try {
|
||||
tool.emit('error', (error as { message?: string } | null | undefined)?.message)
|
||||
tool.emit('error', (error as { message?: unknown } | null | undefined)?.message)
|
||||
}
|
||||
catch (listenerError) {
|
||||
error = listenerError
|
||||
@@ -94,7 +94,7 @@ function createJob(tool: ArtplayerToolThumbnail, state: LifecycleState) {
|
||||
catch (error) {
|
||||
let failure = error
|
||||
try {
|
||||
tool.emit('error', (error as { message?: string } | null | undefined)?.message)
|
||||
tool.emit('error', (error as { message?: unknown } | null | undefined)?.message)
|
||||
}
|
||||
catch (listenerError) {
|
||||
failure = listenerError
|
||||
|
||||
@@ -23,14 +23,14 @@ export interface ScreenshotPoint {
|
||||
y: number
|
||||
}
|
||||
|
||||
export interface ThumbnailEvents extends Record<PropertyKey, unknown[]> {
|
||||
export interface ThumbnailEvents {
|
||||
file: [file: File]
|
||||
video: [video: HTMLVideoElement]
|
||||
canvas: [canvas: HTMLCanvasElement]
|
||||
update: [url: string, progress: number]
|
||||
done: []
|
||||
download: [name: string]
|
||||
error: [message: string | undefined]
|
||||
error: [message: unknown]
|
||||
destroy: []
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": { "allowJs": false },
|
||||
"include": ["src/**/*.ts"]
|
||||
"include": ["src/**/*.ts", "types/**/*.d.ts"]
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
# Public thumbnail types
|
||||
|
||||
The declaration describes the existing tool class, not an ArtPlayer plugin factory.
|
||||
The previous workspace manifest pointed to a missing declaration. There is no
|
||||
recovered legacy declaration to claim as an exact TypeScript baseline.
|
||||
|
||||
| Entry | Declaration | Runtime |
|
||||
| --- | --- | --- |
|
||||
| Root ESM import | artplayer-tool-thumbnail.d.mts | Default constructor in dist/artplayer-tool-thumbnail.mjs |
|
||||
| Root require | artplayer-tool-thumbnail.d.cts | Direct constructor in dist/artplayer-tool-thumbnail.js |
|
||||
| Older Node resolution / TS 4.3 | artplayer-tool-thumbnail.d.ts | main points to the direct CommonJS constructor |
|
||||
| artplayer-tool-thumbnail/legacy | d.cts or typesVersions fallback | Direct legacy constructor; native ESM gets its default |
|
||||
| Browser script / editor | Generated docs/assets/ts declaration | ArtplayerToolThumbnail global |
|
||||
|
||||
The d.ts file is the public source of truth. d.cts forwards its export assignment;
|
||||
d.mts exposes the default constructor and type-only named exports. No runtime
|
||||
named exports or .default self-alias exist. Keep the wrappers sharing one class.
|
||||
|
||||
```ts
|
||||
import Thumbnail, { Option } from 'artplayer-tool-thumbnail'
|
||||
|
||||
const input = document.createElement('input')
|
||||
input.type = 'file'
|
||||
const options: Option = { fileInput: input, number: 10 }
|
||||
const tool = new Thumbnail(options)
|
||||
tool.on('update', (url, progress) => console.log(url, progress))
|
||||
tool.on('custom', (value: number) => console.log(value))
|
||||
tool.emit('custom', 1)
|
||||
```
|
||||
|
||||
CommonJS TS can use `import Thumbnail = require('artplayer-tool-thumbnail')` without
|
||||
esModuleInterop. The optional constructor argument mirrors the JS signature, but
|
||||
successful initialization requires fileInput to be an actual file input or Element
|
||||
wrapper. Missing input throws synchronously. setup accepts partial options and
|
||||
retains unknown fields. Numeric range/clamp validation remains a runtime condition.
|
||||
|
||||
Known event tuples are precise. Custom string/number/symbol events infer annotated
|
||||
callback arguments; those protocols remain the caller's responsibility. ctx is
|
||||
inferred from the optional third argument. Dispatch through emit to retain event
|
||||
argument checks rather than directly invoking the heterogeneous e registry.
|
||||
file/videoUrl/thumbnailUrl/density may be absent before their operation.
|
||||
start returns Promise<void>; ready-metadata preflight may throw synchronously,
|
||||
and source replacement/destroy reject with AbortError. Error events usually contain
|
||||
strings, but callback throws can supply any message value, so narrow unknown first.
|
||||
|
||||
Source/declaration constructors are mutually checked in test/types/thumbnail-source.ts.
|
||||
Current and TS 4.3.5 consumers include ten invalid uses. Installed checks pack/install
|
||||
outside the workspace, offline with an unchanged frozen lock, verify every file
|
||||
and exercise native Node require/import without a DOM.
|
||||
|
||||
```sh
|
||||
yarn test:thumbnail
|
||||
yarn test:thumbnail-types-package
|
||||
yarn build:ts
|
||||
yarn test:browser test/browser/thumbnail-editor-types.spec.js
|
||||
```
|
||||
|
||||
build:ts selects package declaration entries rather than auxiliary types and emits
|
||||
the uppercase global. Actual file extraction, old-core composition and the full
|
||||
demo remain separate checks. The 3.5.31 versus 4.4.0 default policy remains pending.
|
||||
@@ -0,0 +1,4 @@
|
||||
// eslint-disable-next-line ts/no-require-imports -- Match the direct CommonJS constructor without a self-default alias.
|
||||
import ArtplayerToolThumbnail = require('./artplayer-tool-thumbnail.js')
|
||||
|
||||
export = ArtplayerToolThumbnail
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default } from './artplayer-tool-thumbnail.cjs'
|
||||
export type { EventArgs, EventRegistry, Events, Listener, Option, ResolvedOption, ScreenshotPoint, SheetOptions } from './artplayer-tool-thumbnail.js'
|
||||
@@ -0,0 +1,83 @@
|
||||
/** Extract PNG thumbnail sheets from a locally selected video file. */
|
||||
declare class ArtplayerToolThumbnail {
|
||||
constructor(option?: ArtplayerToolThumbnail.Option)
|
||||
|
||||
static readonly DEFAULTS: ArtplayerToolThumbnail.SheetOptions
|
||||
static ondragover(event: DragEvent): void
|
||||
static creatVideo(): HTMLVideoElement
|
||||
|
||||
processing: boolean
|
||||
option: ArtplayerToolThumbnail.ResolvedOption
|
||||
video: HTMLVideoElement
|
||||
duration: number
|
||||
density: number | undefined
|
||||
file: File | undefined
|
||||
videoUrl: string | undefined
|
||||
thumbnailUrl: string | undefined
|
||||
e?: ArtplayerToolThumbnail.EventRegistry
|
||||
|
||||
setup(option?: ArtplayerToolThumbnail.Option): this
|
||||
inputChange(event: Event): void
|
||||
ondrop(event: DragEvent): void
|
||||
loadVideo(file?: File | null): void
|
||||
/** Ready-metadata preflight may throw synchronously. Cancellation rejects with AbortError. */
|
||||
start(): Promise<void>
|
||||
creatScreenshotDate(): ArtplayerToolThumbnail.ScreenshotPoint[]
|
||||
creatCanvas(): HTMLCanvasElement
|
||||
download(): this
|
||||
errorHandle(condition: unknown, message: string): void
|
||||
destroy(): void
|
||||
|
||||
on<Name extends PropertyKey, Custom extends unknown[], Context>(name: Name, callback: (this: Context, ...args: ArtplayerToolThumbnail.EventArgs<Name, Custom>) => unknown, ctx?: Context): this
|
||||
once<Name extends PropertyKey, Custom extends unknown[], Context>(name: Name, callback: (this: Context, ...args: ArtplayerToolThumbnail.EventArgs<Name, Custom>) => unknown, ctx?: Context): this
|
||||
emit<Name extends PropertyKey, Custom extends unknown[]>(name: Name, ...args: ArtplayerToolThumbnail.EventArgs<Name, Custom>): this
|
||||
off<Name extends PropertyKey, Custom extends unknown[]>(name: Name, callback?: ArtplayerToolThumbnail.Listener<ArtplayerToolThumbnail.EventArgs<Name, Custom>>): this
|
||||
}
|
||||
|
||||
declare namespace ArtplayerToolThumbnail {
|
||||
interface SheetOptions {
|
||||
number: number
|
||||
width: number
|
||||
height: number
|
||||
column: number
|
||||
begin: number
|
||||
end: number
|
||||
}
|
||||
|
||||
/** fileInput must be a file input or an Element wrapper when constructing. */
|
||||
interface Option extends Partial<SheetOptions> {
|
||||
fileInput?: Element
|
||||
[name: string]: unknown
|
||||
}
|
||||
|
||||
interface ResolvedOption extends SheetOptions {
|
||||
fileInput: HTMLInputElement
|
||||
[name: string]: unknown
|
||||
}
|
||||
|
||||
interface ScreenshotPoint {
|
||||
time: number
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
|
||||
interface Events {
|
||||
file: [file: File]
|
||||
video: [video: HTMLVideoElement]
|
||||
canvas: [canvas: HTMLCanvasElement]
|
||||
update: [url: string, progress: number]
|
||||
done: []
|
||||
download: [name: string]
|
||||
/** Built-in failures are strings; user callbacks may throw any message value. */
|
||||
error: [message: unknown]
|
||||
destroy: []
|
||||
}
|
||||
|
||||
type EventArgs<Name extends PropertyKey, Custom extends unknown[] = unknown[]> = Name extends keyof Events ? [...Events[Name]] : Custom
|
||||
type Listener<Args extends unknown[]> = ((...args: Args) => unknown) & { _?: (...args: Args) => unknown }
|
||||
/** Heterogeneous listener storage; dispatch through emit to retain event argument checks. */
|
||||
type EventRegistry = Partial<Record<PropertyKey, { fn: Listener<never[]>, ctx: unknown }[]>>
|
||||
}
|
||||
|
||||
export = ArtplayerToolThumbnail
|
||||
export as namespace ArtplayerToolThumbnail
|
||||
Reference in new issue
Block a user