@legenki/studio-core
v0.3.0
Published
Shared auth, paywall and utilities for the Legenki browser studios (Grafema, Ritmo, Divix, Lumen).
Maintainers
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-coreOr 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 lintReleasing
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 --tagsThat 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.
