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

cpixts

v0.2.0

Published

Isomorphic TypeScript library for CPIX (DASH-IF Content Protection Information Exchange) documents, with Widevine/PlayReady DRM and AWS SPEKE v2 support

Readme

cpix

A TypeScript library for working with CPIX 2.2 (DASH-IF Content Protection Information Exchange) documents.

  • CPIX 2.2: create and parse documents — content keys, DRM systems, usage rules, filters, periods, delivery data.
  • DRM helpers: Widevine (CENC header + PSSH box, key-server) and PlayReady (content-key derivation, checksum, WRMHEADER, PSSH box).
  • Isomorphic: runs in Node and the browser.
  • Byte-exact XML and tree-shakable ESM.

Usage

import { CPIX, ContentKey, ContentKeyList, DRMSystem, DRMSystemList } from "cpixts";

const doc = new CPIX({
  contentKeys: new ContentKeyList(
    new ContentKey({
      kid: "0DC3EC4F-7683-548B-81E7-3C64E582E136",
      cek: "WADwG2qCqkq5TVml+U5PXw==",
    }),
  ),
  drmSystems: new DRMSystemList(
    new DRMSystem({
      kid: "0DC3EC4F-7683-548B-81E7-3C64E582E136",
      systemId: "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED",
      pssh: "AAAAxnBzc2gBAAAA…",
    }),
  ),
});

doc.toString();                     // serialize to XML

import { parse, validate } from "cpixts";
const parsed = parse(xml);          // dispatches on the root element
const [ok, error] = validate(xml);  // XSD validation (CPIX 2.3 by default)

CPIX versions (serialize + validate)

Serialization and validation both take a target version, so building and validating stay coherent. Target a version on toString()/prettyPrint(); the output is shaped for it (e.g. 2.2 omits commonEncryptionScheme; the HLS multivariant playlist is master for ≤2.3 and multiVariant for 2.4):

const xml = doc.toString({ version: "2.2" });  // default: "2.3"
validate(xml, { version: "2.2" });             // "2.2" | "2.3" | "2.3.1" | "2.4"

validate(xml);                                 // default: CPIX 2.3
validate(xml, { schema: myXsd });              // any custom XSD (any version)

import { SUPPORTED_CPIX_VERSIONS, LATEST_CPIX_VERSION } from "cpixts";

Bundled schemas are the official DASH-IF CPIX 2.2–2.4 XSDs. The version attribute on a CPIX document is a separate free-string label you set as needed — it does not by itself change serialization.

DRM crypto helpers (drm.widevine, drm.playready) are async — they use WebCrypto.

SPEKE v2

The cpixts/speke entry point provides an AWS SPEKE v2 request handler and a cross-field validator for the SPEKE CPIX profile:

import { handleSpeke, validateSpekeV2, type SpekeKeyProvider } from "cpixts/speke";

const keyProvider: SpekeKeyProvider = {
  getKey: (kid) => ({ cek: /* base64 key from your key store */ "…" }),
};

// requestXml: a CPIX request (kids without keys, DRM systems without PSSH)
const responseXml = await handleSpeke(requestXml, {
  keyProvider,
  widevineProvider: "example",
  playreadyLaUrl: "https://example.com/playready",
});

handleSpeke mints missing content keys and generates Widevine/PlayReady PSSH; validateSpekeV2(cpix) checks the SPEKE encryption-contract rules (uniform commonEncryptionScheme, unique/mandatory intendedTrackType, filter presence, HLS media/multivariant pairing). Validation uses the official CPIX 2.3 schema.

The library ships no server — handleSpeke/validateSpekeV2 are the reusable pieces you build a SPEKE proxy, key server, or test harness on. The examples/ show the wiring: speke-handler.ts holds the shared request→response logic, and speke.node.ts / speke.bun.ts each add only the serve glue.

buildSpekeRequest (also in cpixts/speke) builds a SPEKE request document. A ContentKeyPeriodList/KeyPeriodFilter block is emitted only when you pass rotation — key rotation is standard CPIX and works in both SPEKE v1 and v2; version affects only serialization/namespace, never whether the period block appears. validateSpekeRequest(cpix, policy?) checks the request side and lets a caller assert a profile rule (e.g. { allowRotation: false }) — server-specific strictness stays with the caller, not baked into the builder. Remember to send the X-Speke-Version header on the HTTP request.

Calling a key server

cpixts/client is the caller-side counterpart to handleSpeke — POST a CPIX document and get the parsed response. It is deliberately vendor-neutral: endpoints and auth are injected by you, never baked in. Provider-specific request signing lives in your CpixAuth, via headerAuth — not in the library.

import { CpixClient, headerAuth } from "cpixts/client";
import { buildSpekeRequest } from "cpixts/speke";

const client = new CpixClient({
  endpoint: "https://keys.example.com/cpix",
  // any provider's own auth scheme goes here — the library stays generic:
  auth: headerAuth("X-Vendor-Token", () => mintToken()),
  // basicAuth(user, pass) and bearerAuth(token) are also provided (RFC-standard)
});

const request = buildSpekeRequest({
  version: "2.0",
  contentId: "movie-1",
  keyIds: ["0dc3ec4f-7683-548b-81e7-3c64e582e136"],
  drmSystems: [{ systemId: "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" }],
  commonEncryptionScheme: "cbcs",
});

const res = await client.request(request);        // throws CpixHttpError on non-2xx
const cek = res.cpix.keyFor("0dc3ec4f-7683-548b-81e7-3c64e582e136")?.cek;

Parsed documents expose ergonomic accessors — keyFor(kid), psshFor(systemId), hlsKeyUriFor(systemId), systems() — and contentKey.decrypt(documentKey) unwraps a document-key-encrypted CEK (DASH-IF CPIX §9), returning the raw key bytes.

Development

npm install
npm test        # vitest
npm run build   # tsc -> dist/