refactor(chromecast): [PKG-CAST-04] preserve published types and add runtime entry

This commit is contained in:
Harvey Zhao committed 2026-09-13 17:34:49 +08:00
1 parent b0cfbfe3a0
commit 8ec9fbe83a
27 files changed
+994 -52

No files matched your search

@@ -2,4 +2,5 @@ node_modules
.DS_Store
yarn.lock
package-lock.json
src
src
tsconfig.json
@@ -20,9 +20,11 @@ The control HTML is captured at registration, as before.
Dependencies point from the factory to the controller, then to the SDK loader and
media builder. The loader and media builder do not receive an ArtPlayer instance.
Public declarations remain in `types/`; their historical mismatch with async
registration is tracked separately by PKG-CAST-04. Internal strict typing does not
silently change those declarations.
Public declarations remain in `types/`. The root and legacy entries retain npm
1.1.0's required option argument and synchronous name-only factory type, including
ordinary replacement-function assignment. `/runtime` describes the actual async
registration and callbacks using the same JavaScript files. Internal option and
result contracts reference these precise public types to prevent another drift.
## Initialization and ownership
@@ -111,6 +113,9 @@ node --test test/chromecast.test.js test/chromecast-failures.test.js test/chrome
yarn exec tsc -p packages/artplayer-plugin-chromecast/tsconfig.json
yarn exec eslint packages/artplayer-plugin-chromecast/src test/chromecast-runtime.test.js
yarn build artplayer-plugin-chromecast
yarn build:ts artplayer-plugin-chromecast
node --test refactor/scripts/chromecast-types.test.mjs
yarn test:chromecast-types-package
```
`ARTPLAYER_CHROMECAST_ARTIFACT` selects a built main or legacy file for the candidate
@@ -129,3 +134,25 @@ receiver playback. PKG-CAST-05 still requires the supported Chrome/HTTPS sender,
actual receiver, network-reachable media, source changes and disconnection evidence.
The default SDK URL is mutable; no remote SDK version, physical receiver or Safari
support is established by this source migration.
## Type and module compatibility
ADR-027 preserves the latest actual npm declaration, not a claim that all past
incompatible declarations can coexist. npm 1.0.0 used `export =` and its CommonJS
module exposed `.default`; npm 1.1.0 uses a default declaration and direct CommonJS
factory. Both legal JavaScript forms work in the candidate through a writable
self `.default`. Root types remain a plain factory without that required member.
The historical types-first root mapping also retains the latest declaration's
NodeNext ESM namespace shape and its existing direct-call diagnostic. Its legal
namespace consumers and full module replacement are tested independently. Choose
`/runtime` for a directly callable modern ESM default and precise async results;
its `.d.mts`, `.d.cts` and Node10 `export =` declarations share eight named types.
The option argument remains required on both type surfaces. Callbacks have the
live option object as `this`; errors are unknown, raw session state is string or
null, and `isCasting()` does not establish receiver playback.
The installed consumer verifier compares both actual historical tarballs and an
isolated candidate package, validates individual negative statement diagnostics,
and checks runtime entry identity. The editor declaration is generated semantically
from the root declaration. Keep internal `tsconfig.json` and source out of npm packs.
@@ -6,6 +6,76 @@ Chromecast plugin for ArtPlayer
[https://artplayer.org](https://artplayer.org/?libs=./uncompiled/artplayer-plugin-chromecast/index.js&example=chromecast)
## Usage
```js
import Artplayer from 'artplayer'
import artplayerPluginChromecast from 'artplayer-plugin-chromecast'
const art = new Artplayer({
container: '.artplayer-app',
url: 'https://example.com/video.mp4',
plugins: [artplayerPluginChromecast({
onStateChange(state) {
console.log(state)
},
onError(error) {
console.error(error)
},
})],
})
```
Replace the example URL with media reachable by your receiver. SDK loading starts
when the Cast control is clicked. Use a supported Chrome sender on HTTPS and an
available Cast receiver; loading the demo or a local video alone does not verify
receiver playback. The optional `url`, `sdk`, `icon` and `mimeType` override the
media URL, SDK script URL, control HTML and inferred media type respectively.
Callbacks are `onStateChange`, `onCastAvailable`, `onCastStart` and `onError`.
## TypeScript compatibility
The root and `legacy` declarations preserve the latest published 1.1.0 type shape:
a required options object and a synchronous name-only registration type. JavaScript
registration is actually asynchronous. New TypeScript consumers can use the
additive `artplayer-plugin-chromecast/runtime` entry for precise callbacks and a
`Promise<RuntimeResult>`, without loading another implementation:
```ts
import artplayerPluginChromecast from 'artplayer-plugin-chromecast/runtime'
import type { RuntimeOption } from 'artplayer-plugin-chromecast/runtime'
const options: RuntimeOption = {
onStateChange(state) {
// disconnected | connecting | connected | disconnecting
console.log(state, this.url)
},
onError(error) {
// unknown: SDK and user callbacks can throw arbitrary values.
console.error(error)
},
}
const register = artplayerPluginChromecast(options)
// In an existing ArtPlayer setup, register(art) returns Promise<RuntimeResult>.
// Supply register once through the player's plugins option.
```
The resolved result contains `name`, `getCastState(): string | null` and
`isCasting(): boolean`. The raw state is initially null and differs from the
normalized callback state. A retained session does not prove successful playback.
Version 1.0.0 used `export =` declarations and a CommonJS `.default` factory;
1.1.0 changed to a default declaration and direct CommonJS factory. Those old type
shapes cannot both describe the same root. The candidate keeps 1.1.0's root type
and supports both historical JavaScript factory forms. For older `import = require`
TypeScript consumers, migrate to `import cast = require('artplayer-plugin-chromecast/runtime')`.
Native ESM consumers should use the runtime default import shown above. Root
NodeNext namespace typing retains 1.1.0's existing behavior; its direct ESM default
call was already a type error. The runtime entry fixes that use without changing
the old root's module replacement contracts.
Maintenance and validation details are in [ARCHITECTURE.md](ARCHITECTURE.md).
## License
MIT © Harvey Zhao
@@ -27,11 +27,27 @@
"types": "./types/artplayer-plugin-chromecast.d.ts",
"import": "./dist/artplayer-plugin-chromecast.legacy.js",
"require": "./dist/artplayer-plugin-chromecast.legacy.js"
},
"./runtime": {
"import": {
"types": "./types/runtime.d.mts",
"default": "./dist/artplayer-plugin-chromecast.mjs"
},
"require": {
"types": "./types/runtime.d.cts",
"default": "./dist/artplayer-plugin-chromecast.js"
}
}
},
"main": "./dist/artplayer-plugin-chromecast.js",
"module": "./dist/artplayer-plugin-chromecast.mjs",
"types": "./types/artplayer-plugin-chromecast.d.ts",
"legacy": "./dist/artplayer-plugin-chromecast.legacy.js",
"browserslist": "last 1 Chrome version"
"browserslist": "last 1 Chrome version",
"typesVersions": {
"*": {
"legacy": ["types/artplayer-plugin-chromecast.d.ts"],
"runtime": ["types/runtime.d.ts"]
}
}
}
@@ -1,10 +1,11 @@
import type Artplayer from 'artplayer'
import type { RuntimeResult } from '../types/artplayer-plugin-chromecast'
import type { AvailabilityEvent, CastContext, CastSdk, CastSession, ChromecastOptions, ConnectionState, SessionEvent } from './types'
import icon from './cast.svg?raw'
import { loadMedia } from './media'
import { configure, loadSdk } from './sdk'
export function createController(art: Artplayer, option: ChromecastOptions) {
export function createController(art: Artplayer, option: ChromecastOptions): RuntimeResult {
let disposed = false
let sdk: CastSdk | undefined
let context: CastContext | undefined
@@ -1,15 +1,5 @@
export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'disconnecting'
export interface ChromecastOptions {
url?: string
sdk?: string
icon?: string
mimeType?: string
onStateChange?: (this: ChromecastOptions, state: ConnectionState) => void
onCastAvailable?: (this: ChromecastOptions, available: boolean) => void
onCastStart?: (this: ChromecastOptions) => void
onError?: (this: ChromecastOptions, error: unknown) => void
}
export type { ConnectionState } from '../types/artplayer-plugin-chromecast'
export type ChromecastOptions = import('../types/artplayer-plugin-chromecast').RuntimeOption
export interface MediaInfo {
contentId: string
@@ -1,16 +1,41 @@
import type Artplayer from 'artplayer'
interface Option {
export interface Option {
url?: string
sdk?: string
icon?: string
mimeType?: string
}
interface Chromecast {
export interface Chromecast {
name: 'artplayerPluginChromecast'
}
/** Published 1.1.0 result; actual registration is asynchronous. */
export type Result = Chromecast
export type Factory = (option: Option) => (art: Artplayer) => Chromecast
export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'disconnecting'
export interface RuntimeOption extends Option {
onStateChange?: (this: RuntimeOption, state: ConnectionState) => void
onCastAvailable?: (this: RuntimeOption, available: boolean) => void
onCastStart?: (this: RuntimeOption) => void
onError?: (this: RuntimeOption, error: unknown) => void
}
export interface RuntimeResult extends Chromecast {
/** Last raw SDK SessionState, initially null; not the normalized callback state. */
getCastState: () => string | null
/** Whether this controller retains a session; does not prove receiver playback. */
isCasting: () => boolean
}
export interface RuntimeFactory {
(option: RuntimeOption): (art: Artplayer) => Promise<RuntimeResult>
default: RuntimeFactory
}
declare const artplayerPluginChromecast: (option: Option) => (art: Artplayer) => Chromecast
export default artplayerPluginChromecast
@@ -0,0 +1,15 @@
import type { RuntimeFactory } from './artplayer-plugin-chromecast.js'
declare const artplayerPluginChromecast: RuntimeFactory
// eslint-disable-next-line ts/no-redeclare -- Ambient merge exposes types on the CommonJS factory.
declare namespace artplayerPluginChromecast {
type Chromecast = import('./artplayer-plugin-chromecast.js').Chromecast
type ConnectionState = import('./artplayer-plugin-chromecast.js').ConnectionState
type Factory = import('./artplayer-plugin-chromecast.js').Factory
type Option = import('./artplayer-plugin-chromecast.js').Option
type Result = import('./artplayer-plugin-chromecast.js').Result
type RuntimeFactory = import('./artplayer-plugin-chromecast.js').RuntimeFactory
type RuntimeOption = import('./artplayer-plugin-chromecast.js').RuntimeOption
type RuntimeResult = import('./artplayer-plugin-chromecast.js').RuntimeResult
}
export = artplayerPluginChromecast
@@ -0,0 +1,5 @@
import type { RuntimeFactory } from './artplayer-plugin-chromecast.js'
declare const artplayerPluginChromecast: RuntimeFactory
export default artplayerPluginChromecast
export type { Chromecast, ConnectionState, Factory, Option, Result, RuntimeFactory, RuntimeOption, RuntimeResult } from './artplayer-plugin-chromecast.js'
+15
View File
@@ -0,0 +1,15 @@
import type { RuntimeFactory } from './artplayer-plugin-chromecast.js'
declare const artplayerPluginChromecast: RuntimeFactory
// eslint-disable-next-line ts/no-redeclare -- Ambient merge exposes types on the CommonJS factory.
declare namespace artplayerPluginChromecast {
type Chromecast = import('./artplayer-plugin-chromecast.js').Chromecast
type ConnectionState = import('./artplayer-plugin-chromecast.js').ConnectionState
type Factory = import('./artplayer-plugin-chromecast.js').Factory
type Option = import('./artplayer-plugin-chromecast.js').Option
type Result = import('./artplayer-plugin-chromecast.js').Result
type RuntimeFactory = import('./artplayer-plugin-chromecast.js').RuntimeFactory
type RuntimeOption = import('./artplayer-plugin-chromecast.js').RuntimeOption
type RuntimeResult = import('./artplayer-plugin-chromecast.js').RuntimeResult
}
export = artplayerPluginChromecast