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

extractbrand

v0.1.0

Published

Design-system-grade brand extraction from any domain — the ExtractBrand engine.

Readme

extractbrand

The ExtractBrand extraction engine, as an installable package. Given a URL it returns design-system-grade brand data: colours with roles, typography with availability, logo variants, visual style, brand intelligence, copywriting tone, spacing and accessibility targets.

Closed source. Published from packages/extractbrand in the extractbrand repo.

Install

Published to the public npm registry — no git access to this repo required:

npm install extractbrand
# or: yarn add extractbrand / bun add extractbrand

dist (ESM + .d.ts) ships pre-built in the package; consumers never compile the engine themselves.

Use

import { createBrandOrchestrator, stripV2Fields } from "extractbrand";

const orchestrator = createBrandOrchestrator();
const result = await orchestrator.run({
  url: "https://stripe.com",
  ctx: { user_id, organization_id },
});

ctx is the tenant the run is attributed to — the two fields are stamped on the session row and repeated in the read filter, so a session written under one organization cannot be read from another.

Entry points

| Subpath | What it is | | ---------- | ----------------------------------------------------------------------- | | . | The server barrel: orchestrator factory, adapters, schemas, error types | | ./client | Types and Zod schemas only — safe in a browser bundle | | ./colour | Colour maths (contrast, harmony, snapping). No imports, browser-safe | | ./usage | Token/cost arithmetic and the model price table | | ./ai-sdk | The generation facade the specialists call | | ./* | Any module by its source path, e.g. ./domain/schema |

What the host supplies

The engine ships its own AI, Playwright, Mongo and Blob adapters, so createBrandOrchestrator() works with no wiring. Two things are the host's:

Progress. createBrandOrchestrator({ lifecycle }) takes an ILifecycleEmitter; omitted, progress goes nowhere. A host publishing over Inngest Realtime builds its own publisher and wraps it — the package maps stages to progress frames but never names a channel, because the channel and its topic vocabulary belong to the application:

const rt = createRealtimePublisher(publish, userId); // yours
const lifecycle = new InngestLifecycleEmitter(rt, sessionId); // ours
createBrandOrchestrator({ lifecycle });

Cost. costMeter is optional. Without one a run records unmeasured rather than a misleading zero.

Peer dependencies

zod, ai, @ai-sdk/google, mongoose, playwright-core, sharp and node-vibrant are peers so the host owns one copy of each — zod especially, where two copies mean two sets of schema identities. @vercel/blob and braintrust are optional: without them screenshot upload and eval recording are the only things that degrade.

Runtime requirements

GOOGLE_GENERATIVE_AI_API_KEY and BROWSERLESS_API_KEY are the whole list. LOGO_DEV_PUBLISHABLE_KEY is deliberately never set — see PLAN.md; with it absent the provider is inert and a test pins that.

The engine is Node-only. internal/utils/log-context imports node:async_hooks statically, so an accidental import from a client component is a build error naming that module rather than a silently disabled logger. ./client and ./colour have no path to it.

Vendored, not shared

internal/ holds helpers that were shared/ modules in the origin repo — the logger, colour maths, cost arithmetic, the AI facade. They are copied in rather than imported so the package stands alone. The consequence to know: the logger, log-context and date exist in both this package and the ExtractBrand app. That duplication is deliberate (a package cannot import its host) and bounded to infrastructure boilerplate that encodes no product rule. Anything that does encode one — colour maths, the price table — has its single home here, and the app imports it back through ./colour and ./usage.

Development

bun run build      # tsc → dist, then rewrite relative specifiers for Node ESM
bun run typecheck

tsc emits ESM but never adds file extensions, which a bundler tolerates and Node does not. scripts/package/fix-esm-specifiers.mjs closes that and fails the build on any specifier it cannot resolve, so the artifact runs under plain node and not only inside a bundler.