mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
chore(root): refresh agent skills and docs (#1835)
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
# @videojs/spf source guide
|
||||
|
||||
Treat source and colocated tests as implementation truth. Use internal/design/spf only for durable rationale and conventions.
|
||||
|
||||
## Layers
|
||||
|
||||
- core: framework-neutral composition, signals, tasks, actors, and reactors. It must not depend on media, network, or playback.
|
||||
- media: media models, algorithms, and format parsing; browser media helpers live under media/dom.
|
||||
- network: fetch and bandwidth primitives.
|
||||
- playback: playback actors, behaviors, and engines; browser-bound playback code lives in nested dom directories.
|
||||
- index.ts and dom.ts are the primary entry points. Package exports also expose HLS and background-video compositions.
|
||||
|
||||
Place code in the lowest layer that satisfies its dependencies. Keep browser APIs out of core and review public entry-point changes deliberately.
|
||||
|
||||
## Working rules
|
||||
|
||||
- Read only the relevant file under internal/design/spf/conventions.
|
||||
- Follow nearby ownership, lifecycle, cleanup, and test patterns.
|
||||
- Add public exports intentionally and check their bundle impact when relevant.
|
||||
- Prefer clear expressions over mutable intermediate state when both remain readable.
|
||||
|
||||
## Verification
|
||||
|
||||
Use the narrowest relevant test while iterating, then run as appropriate:
|
||||
|
||||
```bash
|
||||
pnpm -F @videojs/spf test
|
||||
pnpm -F @videojs/spf build
|
||||
pnpm -F @videojs/spf size
|
||||
```
|
||||
|
||||
Load one specialized SPF skill only when the requested workflow needs it.
|
||||
+1
-104
@@ -1,104 +1 @@
|
||||
# `@videojs/spf` source layout
|
||||
|
||||
Guidance for AI agents and contributors working in `packages/spf/src/`.
|
||||
|
||||
## Top-level layout
|
||||
|
||||
```
|
||||
src/
|
||||
core/ SPF runtime primitives — signals, effects, tasks, actors, reactors, composition
|
||||
media/ CML-like media building blocks — types, parsers, ABR, buffer logic, MSE/VTT primitives
|
||||
network/ HTTP fetch utilities, chunked-stream iterables
|
||||
playback/ Playback domain — composes core+media+network into engines
|
||||
primitives/ Signal-aware playback building blocks composed by behaviors (no behavior lifecycle)
|
||||
behaviors/ Compositional behaviors that drive playback (use signals/effects)
|
||||
actors/ Actor factories used inside behaviors
|
||||
engines/ Assembled playback engines (currently just hls/)
|
||||
```
|
||||
|
||||
`media/`, `network/`, and `playback/behaviors/` each have a `dom/` subdirectory for DOM-bound code (browser APIs like `HTMLMediaElement`, `MediaSource`, `VTTCue`). DOM-free code lives outside those subdirs.
|
||||
|
||||
## Dependency rules
|
||||
|
||||
| From → To | Allowed? | Enforced by |
|
||||
|---|---|---|
|
||||
| `media/` → `core/` | ❌ | `media/tsconfig.json` does not reference `core` |
|
||||
| `media/` → `network/` | ❌ | `media/tsconfig.json` does not reference `network` |
|
||||
| `media/` → `playback/` | ❌ | `playback/` not referenced |
|
||||
| `network/` → `core/` | ❌ | `network/tsconfig.json` has empty `references` |
|
||||
| `network/` → `media/` | ❌ | same |
|
||||
| `network/` → `playback/` | ❌ | same |
|
||||
| `core/` → `media/`, `network/`, `playback/` | ❌ | `core/tsconfig.json` does not reference downstream subtrees |
|
||||
| `core/` → DOM | ❌ | `core/tsconfig.json` `lib` excludes `DOM` |
|
||||
| `media/` (non-dom) → DOM | ❌ | `media/tsconfig.json` `lib` excludes `DOM` |
|
||||
| `network/` → DOM | ❌ | `network/tsconfig.json` `lib` excludes `DOM` |
|
||||
| `playback/primitives/` → DOM | ❌ | tsconfig `lib` excludes `DOM` |
|
||||
| `playback/behaviors/` (non-dom) → DOM | ❌ | tsconfig `lib` excludes `DOM` |
|
||||
| `playback/actors/` (non-dom) → DOM | ❌ | tsconfig `lib` excludes `DOM` |
|
||||
| `playback/behaviors/` → `playback/primitives/` | ✅ | references |
|
||||
| `playback/` → `core/`, `network/`, `media/` | ✅ | references in playback tsconfigs |
|
||||
| `playback/engines/hls/` → `playback/behaviors/`, `playback/actors/` | ✅ | references |
|
||||
| `playback/engines/hls/` → `core/`, `media/` | ✅ — engines compose primitives directly | references |
|
||||
|
||||
The substance: `core/`, `media/`, `network/` are framework-agnostic foundations. `playback/` is the SPF-integrated layer that composes them. Engines live at the top of the dependency tree and may reach into any foundation.
|
||||
|
||||
## Where to put new code
|
||||
|
||||
- **Pure media/streaming logic** (parsers, types, selection algorithms, MSE/VTT helpers without signals): `media/` or `media/dom/`. Must not import from `core/`.
|
||||
- **Signal-aware playback primitive** (a composable building block that reads/writes `core` signals but has no behavior lifecycle — no `effect`/`computed`/`subscribe`, not invoked by composition): `playback/primitives/`. These are composed *by* behaviors/actors/engines (e.g. `failoverFetch`, a fetch decorator that reads selected-track signals at fetch time; `track-types`, the per-type config bundles). They live at the `playback/` layer rather than `media/`/`network/` precisely because they touch `core/` — that's the line below.
|
||||
- **Compositional behavior driving state** (uses `effect`/`computed`/`update` against owners or state signals): `playback/behaviors/` or `playback/behaviors/dom/`.
|
||||
- **Actor factories** (long-lived stateful units that receive messages): `playback/actors/` or `playback/actors/dom/`.
|
||||
- **Engine compositions** (wiring behaviors+actors+config into a `createComposition` call): `playback/engines/<name>/`.
|
||||
- **Generic, framework-agnostic utilities** that aren't media-specific: prefer `@videojs/utils` over creating new homes inside spf.
|
||||
|
||||
If a module looks like a `media/`/`network/` primitive but reaches into `core/`, that's a smell — first consider whether the signal binding can move to the call site (see `onMediaSourceReadyStateChange` for a callback-shaped primitive that lets the consumer create the signal), keeping the primitive itself `core`-free. Only when a building block genuinely needs *live* signal access — e.g. `failoverFetch` is constructed once but its returned fetch reads `presentation`/selected-track signals lazily on every call — does it belong at the `playback/` layer, in `playback/primitives/`.
|
||||
|
||||
Conversely: if a function inside `playback/behaviors/` (or `playback/actors/`) has no `core/` dependency — no signals, effects, or reactors — it probably belongs in a layer below (`media/`, `network/`, or `@videojs/utils`). Same layering principle, opposite direction. When reviewing a behavior or actor file, scan its top-level helpers; any pure data-manipulation / lookup / format-handling code with no reactive concerns is a candidate to extract.
|
||||
|
||||
## DOM-vs-not enforcement
|
||||
|
||||
The DOM/no-DOM split is enforced by `lib` settings on each subtree's `tsconfig.json`:
|
||||
- DOM-free subtrees use `lib: ["ES2022", "WebWorker"]` — referencing `HTMLMediaElement`, `document`, etc. fails typecheck.
|
||||
- DOM subtrees use `lib: ["ES2022", "DOM", "DOM.Iterable"]`.
|
||||
|
||||
This is structural — you cannot accidentally import DOM types into a non-DOM module without surfacing a typecheck error.
|
||||
|
||||
## Public exports
|
||||
|
||||
External consumers go through three entry points (defined in `packages/spf/package.json`):
|
||||
- `@videojs/spf` — main entry (`src/index.ts`): runtime-agnostic primitives + media/network helpers.
|
||||
- `@videojs/spf/dom` — DOM-bound exports (`src/dom.ts`): re-exports from `playback/behaviors/dom/` and `media/dom/`.
|
||||
- `@videojs/spf/hls` — HLS engine (`src/playback/engines/hls/index.ts`).
|
||||
|
||||
Internal paths are not part of the public API. Don't import from `@videojs/spf/playback/...` or similar deep paths in other workspace packages.
|
||||
|
||||
## Vitest projects
|
||||
|
||||
`packages/spf/vitest.config.ts` shards tests by area:
|
||||
- `core`, `media`, `network`, `behaviors` — Node, no browser (the `behaviors` project also covers `playback/actors/` and `playback/primitives/`)
|
||||
- `dom` — Chromium via Playwright, covers all `**/dom/**/*.test.ts` across subtrees
|
||||
- `playback-engines` — Chromium, covers engines/
|
||||
- `types` — type-only tests via tsgo
|
||||
|
||||
When adding a test in a new location, check the project globs match.
|
||||
|
||||
## Code style
|
||||
|
||||
SPF-specific style preferences. These extend the workspace-wide rules in the root `CLAUDE.md` — they apply across `packages/spf/src/`, not just behaviors. (Style guidance specific to a single domain — behavior naming, reactor state names, signal access patterns — lives in `internal/design/spf/conventions/` instead.)
|
||||
|
||||
### Prefer `const` + ternary over `let` + conditional reassignment
|
||||
|
||||
For single-branch derivations of one value, use a `const` with a ternary rather than a `let` that gets conditionally overwritten:
|
||||
|
||||
```ts
|
||||
// Good
|
||||
const duration = maxBufferedEnd > target ? maxBufferedEnd : target;
|
||||
|
||||
// Avoid
|
||||
let duration = target;
|
||||
if (maxBufferedEnd > duration) {
|
||||
duration = maxBufferedEnd;
|
||||
}
|
||||
```
|
||||
|
||||
The `const`-ternary form makes the value's identity immutable from its first binding — readers don't have to scan ahead for reassignment, and the dependency on each branch's inputs is structurally clear. `let` is appropriate when the variable is genuinely mutable across multiple statements (accumulators, loop indices, state that flips on conditions other than a single-shot derivation); reserve it for those cases.
|
||||
@AGENTS.md
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
* Inert where it shouldn't act: live (the MediaSource never reaches `ended` while the
|
||||
* window grows; `duration` is `Infinity`) and streams that end cleanly (no `waiting`).
|
||||
*
|
||||
* See `internal/decisions/end-of-stream-av-skew-recovery.md`.
|
||||
* See `internal/decisions/spf/end-of-stream-av-skew-recovery.md`.
|
||||
*/
|
||||
import { listen } from '@videojs/utils/dom';
|
||||
import { defineBehavior } from '../../../core/composition/create-composition';
|
||||
|
||||
Reference in New Issue
Block a user