npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@luceosports/play-rendering

v2.13.2

Published

Core canvas rendering library for the LuceoSports platform. Renders play JSON (players, lines, shapes, animations) onto an HTML5 canvas. Published as a UMD bundle via webpack. Consumed by the `animation-playback` and `drawing-animation` frontend submodule

Readme

@luceosports/play-rendering

Core canvas rendering library for the LuceoSports platform. Renders play JSON (players, lines, shapes, animations) onto an HTML5 canvas. Published as a UMD bundle via webpack. Consumed by the animation-playback and drawing-animation frontend submodules.


Install

npm install @luceosports/play-rendering

Usage

import { PlayModel, AnimationModel, PlayConstructData } from '@luceosports/play-rendering';

await PlayModel.init();                         // load shared assets once
const play = new PlayModel(data, { width: 800 });
const ctx = canvas.getContext('2d')!;
const anim = new AnimationModel(ctx, play);
anim.start(onFinish, onProgress);

The complete public type surface lives in src/play-rendering.ts (source) and dist/types/play-rendering.d.ts (emitted). Anything not exported there is internal — do not deep-import from dist/types/... subpaths.


Development

npm install                  # install deps
npm run build                # webpack -> dist/play-rendering.js
npm run build:dev            # webpack --mode=development
npm run build:types          # rimraf dist/types && tsc && node scripts/prune-types.js
npm run check:types-fresh    # verify committed dist/types matches a fresh emit
npm run lint                 # eslint
npm run lint:fix             # eslint --fix
npm run verify:pack          # build everything + npm pack, prints next steps

There are no automated tests. Verification is via:

  1. npx tsc --noEmit --emitDeclarationOnly false — type-check source
  2. npm run lint
  3. Building both consumer repos (drawing-animation, animation-playback) against an npm link-ed copy of this lib

The Husky pre-commit hook runs lint-staged plus check:types-fresh. It will reject any commit where dist/types/ would differ from a fresh build:types emit — i.e. source changes that didn't regenerate the published types are caught before they land.


Release process

Releases go directly from master to npm. The library is published as @luceosports/play-rendering and the dist/ folder is the only thing shipped in the tarball (controlled by "files": ["dist"] in package.json).

Step 1 — Merge work to master

If you've been working on a feature branch, fast-forward merge to master:

git checkout master
git pull
git merge --ff-only <feature-branch>

Confirm working tree is clean before continuing.

Step 2 — Smoke-test the actual publishable tarball

npm run verify:pack

That runs npm run build, npm run build:types, then npm pack. It prints the absolute path to the produced .tgz plus copy-paste commands for installing into the consumer repos. Run those commands and build each consumer:

cd ../drawing-animation
npm install --no-save "<path-to-tgz>"
npm run build

cd ../animation-playback
npm install --no-save "<path-to-tgz>"
npm run build

If both build clean, the tarball is safe to publish. The point of this step over npm link is that npm pack builds the exact tarball npm publish would ship (respecting "files" and .npmignore), so it catches missing-dist regressions that a symlinked dev install would mask.

After the smoke-test you can either re-link to keep editing locally:

cd ../drawing-animation && npm link @luceosports/play-rendering
cd ../animation-playback && npm link @luceosports/play-rendering

…or leave them on the tarball install until you bump and publish.

Step 3 — Bump version

npm version <patch|minor|major>

That edits package.json, creates a commit (default message is the new version, e.g. 2.9.0), and creates an annotated git tag (v2.9.0).

Semver guidance for this library:

  • patch — internal fixes, no public-API change. Bug fixes inside model methods, performance improvements, comment-only docs changes.
  • minor — additive public-API change (new exported class/method) or cleanup that's transparent for the known consumers but technically shifts the type surface (e.g. a stricter return type on an existing method).
  • major — breaking change to the exports from src/play-rendering.ts: removed identifier, removed method, changed required parameter, return type widened in a way consumers can't handle. Per .claude/CLAUDE.md's rule: treat src/play-rendering.ts as a public contract.

Step 4 — Push commit + tag

git push --follow-tags

--follow-tags pushes the v<version> tag along with the version-bump commit.

Step 5 — Publish to npm

Run every npm command that touches your account through a modern npm — login above all. This repo pins node 16.15.0 with volta, and volta's default package manager is npm 8.19.4, so a bare npm here is npm 8. It predates npm's current two-factor flow: npm login asks for a typed one-time password that the account can no longer produce (see below), and npm whoami answers a misleading 401 even with a perfectly good token. Override the pin from inside the repo instead of leaving the directory:

volta run --node 24.13.0 --npm 11.6.2 npm login
volta run --node 24.13.0 --npm 11.6.2 npm whoami
volta run --node 24.13.0 --npm 11.6.2 npm publish --dry-run
volta run --node 24.13.0 --npm 11.6.2 npm publish

(volta list all shows which runtimes and package managers are installed; volta install npm@11 would make the modern one the default everywhere.)

Second factor is WebAuthn only — npm no longer supports authenticator apps. Both npm login and npm publish go through it: the CLI prints Open https://www.npmjs.com/login/<id> to use your security key; confirm on that page and the terminal continues on its own. Two traps:

  • Run it in a real terminal window. Without a TTY — CI, an agent shell, a piped command — npm skips the browser step and fails immediately with EOTP, asking for an --otp=<code> that the account can no longer produce.
  • Never type a 2FA recovery code into the OTP prompt. Using one is a "security-sensitive action": npm puts the account into a 72-hour read-only state that blocks publishing, token management, package visibility and org/team changes. It lifts on its own and each new recovery code restarts the clock. If your security key isn't on the machine you publish from, add a second one (Windows Hello / Touch ID) at npmjs.com → Two-Factor Authentication → Add Security Key.

If the account is already in that state, the publish fails after the 2FA step with 403 ... temporarily suspended due to a recent security-sensitive action. Nothing is half-published — confirm with npm view @luceosports/play-rendering version. To unblock consumers while it lasts, point them at the pushed tag instead of the registry:

"@luceosports/play-rendering": "git+ssh://[email protected]/luceosports/playbook-play-rendering-library.git#v<version>"

That works because dist/ is committed here and there is no prepare / prepack / postinstall script — npm installs the built package as-is. Swap the semver range back once the version is on the registry.

Long-lived 2FA-bypass automation tokens are not a way around this: since August 2026 they no longer skip 2FA for sensitive operations, and npm is removing their publish rights around January 2027.

A dry-run is cheap and surfaces what would upload:

volta run --node 24.13.0 --npm 11.6.2 npm publish --dry-run

The output lists every file going into the tarball plus the final shasum. When you're satisfied, do the real publish:

volta run --node 24.13.0 --npm 11.6.2 npm publish

A prebuilt tarball can be published directly, which sidesteps the node pin entirely — useful when a publish has to be retried later from anywhere:

volta run --node 24.13.0 --npm 11.6.2 npm publish <path-to>.tgz

That uploads to https://registry.npmjs.org/@luceosports/play-rendering/-/play-rendering-<version>.tgz and updates the latest dist-tag. Published versions are effectively permanent — npm unpublish only works for the first 72 hours and gets increasingly restricted. Treat publish as irreversible.

Step 6 — Verify the publish landed

npm view @luceosports/play-rendering version
npm view @luceosports/play-rendering@<version> dist.shasum

The shasum should match what npm publish --dry-run reported in Step 5 — if it does, what landed on npm is byte-identical to what you tested.

Step 7 — Update consumers

Bump the version range in each consumer's package.json and reinstall. For a clean install (not symlinked), make sure any prior npm link is gone first.

# in each consumer repo:
npm install --save '@luceosports/play-rendering@^<new-version>'
npm run build      # smoke-test against the published version

If a consumer still has a leftover npm link symlink, npm install --save of a real version replaces the symlink with the published tarball. The package.json and package-lock.json diffs there should be:

  • package.json: bumped semver range (^2.8.0 → ^2.9.0)
  • package-lock.json: new integrity hash + resolved URL pointing at registry.npmjs.org

Commit those in the consumer repo per its own convention.

Optional — pre-release / RC channel

For a soak period before promoting to latest, use the next dist-tag:

npm version prerelease --preid=rc      # 2.9.0 -> 2.10.0-rc.0
git push --follow-tags
npm publish --tag next
# consumers opt in: npm install @luceosports/play-rendering@next
# after the soak:
npm dist-tag add @luceosports/play-rendering@<version> latest

This is overkill for the current scale (~2 known consumers, both maintained in this workspace). Skip unless you're shipping something risky enough to want a multi-day bake-in window.


Architecture pointers

  • Source layout: src/index.ts is the runtime entry (webpack reads it for the JS bundle); src/play-rendering.ts is the type entry (tsc reads it for the published declarations). Both files must export the same identifier set — the consumer build (Phase 2 verification) catches drift; long-term the structural const X: { new (...): I } = XImpl assignment inside play-rendering.ts catches impl-vs-interface drift at compile time.
  • Pruning: scripts/prune-types.js runs after tsc and deletes any dist/types/*.d.ts file the public entry can't reach. Without it, tsc ships ~200 internal layer/sport/trait declaration files that no consumer can ever import.
  • Curated classes: PlayerModel, LineModel, ShapeModel, NoteModel, FrameModel, AnimationModel, PlayModel, Bezier — each has an explicit interface in src/play-rendering.ts that the impl class must satisfy. Adding a method to one of these requires updating the interface, rebuilding dist/types/, and committing both together.

License

ISC. See package.json.