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

@legenki/studio-core

v0.3.0

Published

Shared auth, paywall and utilities for the Legenki browser studios (Grafema, Ritmo, Divix, Lumen).

Readme

@legenki/studio-core

Shared runtime for the Legenki browser studios (Grafema, Ritmo).

This package deliberately holds only the code where a single source of truth matters — auth, billing and the untrusted-input helpers. The apps' shared/ directories have diverged substantially over time, and unifying all of them is a separate refactor. Keeping the surface small keeps the package easy to version.

Consumed by Grafema, Ritmo, Divix and Lumen.

Install

npm install @legenki/studio-core

Or pin to a git tag, which is what the studios currently do — it needs no registry auth and makes the exact revision obvious in the lockfile:

"@legenki/studio-core": "github:legenki/studio-core#v0.2.0"

Either way, pin deliberately: an unpinned dependency means a change here can break an app's deploy without a commit in that app.

Consumer setup

The package ships untranspiled ES modules that read import.meta.env, so it expects a Vite (or equivalent) consumer. Vitest does not transform node_modules by default, so tests need it inlined:

// vite.config.js / vitest.config.js
test: {
  server: { deps: { inline: ['@legenki/studio-core'] } },
}

Without that, the package sees an undefined import.meta.env and throws on import.

The paywall modal expects .paywall-* styles from the host app's stylesheet — the package ships no CSS.

Modules

| Import | Purpose | | --- | --- | | @legenki/studio-core/auth | Supabase session, checkPro() subscription gate | | @legenki/studio-core/paywall | Subscription modal, Stripe payment-link redirect | | @legenki/studio-core/deepMerge | Preset merge, hardened against prototype pollution | | @legenki/studio-core/datetime | Filename-safe timestamp |

auth

import { initAuth, checkPro, signIn, signOut } from '@legenki/studio-core/auth';

initAuth({ onStateChange: (event, session, isPro) => updateUI(isPro) });

Requires VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY. Without them the module still imports cleanly and the app runs signed-out in free mode — this is covered by auth.unconfigured.test.js, which exists because a module-level crash on a build without secrets shipped once before.

Set VITE_DEV_PRO=true to bypass the gate in local development.

Panels are usually built before auth resolves, so checkPro() reads false for everyone at construction time. Subscribe to be told when the real answer arrives, otherwise a subscriber sees the free UI until they reload:

import { onProChange } from '@legenki/studio-core/auth';

const unsubscribe = onProChange((isPro) => rebuildPanel(isPro));

paywall

Copy is per-app, so configure it once at startup before opening:

import { configurePaywall, openPaywall } from '@legenki/studio-core/paywall';

configurePaywall({
  appName: 'Grafema',
  subtitle: 'RETICULA, TEXTURA, RASTRO and MUESTRA, plus MP4 and SVG export.',
  features: ['RETICULA — raster grid drawing', 'MP4 and SVG export'],
  monthly: '$5',
  yearly: '$40',
});

Prices must match the app's landing page entry in legenki-site (src/content/apps/<app>.md) — the same offer is described in both places.

The CTA appends client_reference_id (the Supabase user id) to the Stripe payment link; the stripe-webhook function uses it to map a completed checkout back to a user row.

The modal expects .paywall-* styles from the host app's stylesheet.

Supabase keepalive

.github/workflows/supabase-keepalive.yml queries the shared Supabase project every third day. The free tier pauses a project after ~7 days idle, which would take auth and Pro gating down across all four studios until someone restored it by hand.

It queries through PostgREST rather than just pinging the project URL, because the pause check is based on database activity. An anonymous select returns [] under RLS — that is the healthy response; the point is that Postgres served a query. Needs SUPABASE_URL and SUPABASE_ANON_KEY repo secrets.

Development

npm install
npm test
npm run lint

Releasing

Publishing is tag-driven, and the workflow refuses to publish when the tag and package.json disagree:

npm version patch     # or minor / major
git push && git push --tags

That runs lint and tests, then publishes to npm. Needs an NPM_TOKEN repo secret.

Because all four studios pin this package, a breaking change here should be a minor/major bump and a deliberate update in each consumer — never a silent in-place edit of a re-export inside an app repo.