Shared @jainparichay/* packages for all JainParichay apps, published to GitHub Packages.
| Package | What it is |
|---|---|
@jainparichay/db |
PostgreSQL connection handler (bring your own Prisma schema) |
@jainparichay/ui |
Shared React components (CVA + Tailwind) |
@jainparichay/i18n |
Localization helpers |
@jainparichay/storage |
Storage helpers |
@jainparichay/ai |
OpenAI-compatible client wrapper |
@jainparichay/config-typescript |
Reusable tsconfig presets |
These packages contain mechanism only — no database schemas, no domain types, no visual design. Anything specific to one app's data model or brand belongs in that app.
@jainparichay/typeswas removed for this reason: its contents were one app's domain.
GitHub Packages requires a token even for public packages. The app's committed .npmrc should carry the registry mapping only:
@jainparichay:registry=https://npm.pkg.github.com
Do not put the token in that file. It's committed to the repo, and pnpm deliberately refuses to expand environment variables in credentials that come from a project-level .npmrc — putting //npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN} there will fail with a 401, not read the variable. Supply the token from a trusted source instead:
- Local development, once per machine:
pnpm config set "//npm.pkg.github.com/:_authToken" "<your PAT>"(a classic PAT withread:packages). This writes to pnpm's user-level config store, which pnpm does trust to expand. - GitHub Actions: use
actions/setup-node@v7withregistry-url: https://npm.pkg.github.com, and setNODE_AUTH_TOKENfrom a secret on the install step — this is the mechanism this repo's ownrelease.ymlalready uses successfully. - Docker or a deploy server: write the user-level file at build/deploy time from a secret, e.g.
echo "//npm.pkg.github.com/:_authToken=$NODE_AUTH_TOKEN" > ~/.npmrc, never committed and never baked into an image layer.
If the app sets minimumReleaseAge, also add minimumReleaseAgeExclude: ['@jainparichay/*'] — otherwise new releases are unresolvable until the buffer elapses.
Then: pnpm add @jainparichay/db @jainparichay/ui
@jainparichay/db and @jainparichay/ui ship both ESM and CommonJS (each has a require condition alongside import); the rest are ESM-only. From a CommonJS app depending on an ESM-only package, load it with await import('@jainparichay/…').
This package only handles the database connection — it owns no schema. Each consuming app defines its own prisma/schema.prisma, runs its own prisma generate, and passes its own generated PrismaClient in:
import { createDatabaseClient } from '@jainparichay/db';
import { PrismaClient } from '@prisma/client'; // the app's own generated client
export const db = createDatabaseClient(
(adapter) => new PrismaClient({ adapter }),
);createDatabaseClient validates DATABASE_URL (or an explicit connectionString option), builds a PrismaPg adapter, and — outside production — caches the resulting client on globalThis so hot reloads don't open a new connection pool each time. Pass cache: false to opt out, or a globalKey to namespace multiple databases in one process (e.g. when Astro and NestJS share a process but talk to different databases). Call resetDatabaseClient with the same connectionString or globalKey to clear a cached entry, which is useful between test files.
Caching is not unconditional. The cache defaults to
cache: process.env.NODE_ENV !== 'production'— it's on in development (to survive hot reloads) and off in production. TheglobalThiscache itself is shared correctly across the package's CJS and ESM entry points (bothrequire()andimportresolve to the same module-level state, so there's no dual-package hazard from the dual build). But with caching off, a process that loads both entries — for example a graph where one dependencyrequire()s@jainparichay/dband anotherimports it — will construct two separatePrismaClientinstances and two separate connection pools underNODE_ENV=production, since nothing is deduplicating uncached calls. This isn't reachable in today's single-importer app graphs, but it becomes possible now that arequirecondition exists, so don't assume the singleton holds underNODE_ENV=productionwithout checking whether every entry point in your dependency graph actually uses the same module condition.
@prisma/adapter-pg is a peer dependency of this package — install it in the app alongside @prisma/client and prisma, matching major versions. The package re-exports its PrismaPg type so callbacks can be typed from the app's own installed copy:
import { createDatabaseClient, type PrismaPg } from '@jainparichay/db';These packages hold generic, cross-app code only. An app may still keep its own packages/ui, packages/types, etc. under its own scope (e.g. @myapp/ui) that wrap, extend, or re-export the shared ones. Promote code here only once a second app needs it.
pnpm changeset— describe the change, pick bump levels.- Merge the PR; the release workflow opens a "Version Packages" PR.
- Merge the version PR; packages publish automatically.
How releasing actually works here.
changesets/actionpublishes whenever there are no pending changesets — it treats the versions currently inpackage.jsonas ready to ship. Always land your changeset in the same push as your code, and let the generated Version Packages PR perform the version bump.Because of that, the
versionfield in everypackage.jsonis owned by the changesets bot. Never edit a version by hand onmain: the next push tomainwould publish it immediately, with no review step and no way to take it back. A published version can only be superseded, never corrected.