Contributing
Thanks for helping improve IVKit! Bug reports, ideas and pull requests are all welcome.
Reporting a bug
Open an issue with:
- What you expected and what happened instead.
- The smallest story that shows the problem. Run it through
validateStoryfirst, since many problems are invalid stories. - The IVKit version, from
npm ls @ivkit/playerorVERSIONin@ivkit/core. - Your browser and operating system, and your framework if you use one.
Setting up
You need Node.js 24 and pnpm. Use the pnpm version pinned in package.json, for example through Corepack:
corepack enable
pnpm install --frozen-lockfileProject layout
| Path | What it is |
|---|---|
packages/core | @ivkit/core: the story format, engine, media controller and validation. No DOM dependencies and no runtime dependencies. |
packages/player | @ivkit/player: the <ivkit-player> Web Component, built on core. |
packages/react | @ivkit/react: a React component around the player. |
docs | The documentation site, built with VitePress and published to GitHub Pages. |
docs/examples | The live examples. Their stories are in docs/examples/stories. |
scripts | Checks for bundle size and package exports, and the publish script. |
Commands
| Command | What it does |
|---|---|
pnpm typecheck | Type-checks every package. |
pnpm lint | Checks formatting and lint rules with Biome. |
pnpm format | Formats the code and applies safe lint fixes. |
pnpm test | Runs every test. Core runs in Node, and the player and React component in happy-dom. |
pnpm test:watch | Runs the tests in watch mode. |
pnpm test:coverage | Runs the tests with a coverage report in coverage/. |
pnpm build | Builds every package into its dist folder. |
pnpm check:size | Builds every package and checks them against their size budgets. |
pnpm check:exports | Checks what each runtime resolves from the player's entries, and that the React component stays a client component. Run it after a build. |
pnpm docs:dev | Serves the documentation at http://localhost:5173/ivkit/. |
pnpm docs:build | Builds the documentation site. |
CI runs lint, typecheck, test:coverage, check:size and check:exports on every pull request.
Size budgets
The library is meant to stay small, so pnpm check:size fails when a build grows past its budget:
| Build | Gzip budget |
|---|---|
| Standalone player, including core and CSS | 9.5 KiB |
| All of core | 6 KiB |
StoryEngine only | 2 KiB |
validateStory only | 3 KiB |
The player, core and validation budgets grew by 0.5, 1 and 0.25 KiB for variables and conditions, which are part of the story format and so belong in core rather than in a plugin. Multiple-answer questions, also part of the format, added 0.5 KiB to the player and core and 0.25 KiB to the engine and validation. Question timeouts added another 0.5 KiB to the player and core and 0.25 KiB to validation. Cues and plugin support added 0.25 KiB to the engine and validation, so plugins can build on the format instead of growing it.
It also checks that validation isn't bundled into the player, and that neither package gains a runtime dependency. If a change needs a bigger budget, explain why in the pull request.
Writing code
- Core never touches the DOM. It talks to media through the
MediaElementinterface, so it can run in Node and be tested with fakes. - Keep the player safe to import on the server. Don't use
window,documentor other browser globals at module level. - Test behavior, not implementation. Core tests drive the engine and controller with fake media. Player tests render the element in happy-dom.
- Document public changes. Update the guide or reference page for any change to the story format or the public API, and add an entry to
CHANGELOG.mdunder Unreleased.
Documentation and examples
The documentation is in docs, as Markdown. Pages are written in English, with short sentences and a code example for each feature.
The live examples use placeholder clips, so that hotspots line up exactly with what's drawn in the video. If you replace a clip, keep the hotspots in docs/examples/stories in sync.
Pull requests
- Create a branch from
main. - Make your change, with tests.
- Run
pnpm typecheck,pnpm testandpnpm check:size. - Write commit messages in the Conventional Commits style used in this repository, such as
feat(player): …,fix(core): …ordocs: …. - Open a pull request that explains what changed and why.
Releasing
Maintainers release every package together, with the same version. Publishing is automated: creating a GitHub release runs the release workflow, which runs every check and publishes to npm through trusted publishing, with provenance and no npm token.
In a pull request, set
versionin thepackage.jsonofpackages/core,packages/playerandpackages/react, and move the Unreleased entries inCHANGELOG.mdinto a section for the new version, with its compare link.After it's merged, create the GitHub release from
main, with that version's changelog section as its notes. The tag must bevplus the version:shgh release create v0.1.2 --target main --title v0.1.2 --notes-file notes.mdFor a prerelease such as
1.0.0-beta.1, add--prerelease. It's published under npm'snexttag, so it's never installed by default.Follow the Release run under Actions. If a step fails, fix the cause and re-run the workflow. Versions already on npm are skipped.
If jsDelivr still answers 404 for the new version, clear its cache with
https://purge.jsdelivr.net/npm/@ivkit/player@<version>/dist/ivkit-player.min.js.
To check what would be published without publishing, run pnpm build and then node scripts/publish.mjs --dry-run.
By contributing, you agree that your contributions are licensed under the project's Apache License 2.0.