- TypeScript 100%
| .github/workflows | ||
| src | ||
| .gitignore | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| tsconfig.json | ||
| tsup.config.ts | ||
@mihai205/sharevent-contracts
Single source of truth for the types, enums, and API request/response
contracts shared between FE/sharevent-fe and BE/sharevent-be.
It exists to kill the duplication where the FE (src/types, src/schemas) and
the BE (src/schemas) used to redefine the same shapes. Change a field here and
both sides recompile against it — a mismatch becomes a compile error, not a
runtime surprise.
What's inside
| Path | Contents |
|---|---|
enums.ts |
Domain enums mirrored 1:1 from prisma/schema.prisma (Role, InviteStatus, RsvpAttending, PaymentStatus, …). Declared once as zod enums; TS unions derived with z.infer. |
common.ts |
Shared request primitives (intId, idParam, slugParam, paginationQuery) and envelopes (Paginated<T>, ApiErrorBody). |
schemas/* |
Canonical zod request schemas — the validation source of truth (auth, events, eventSettings, guest, invites, monetization, upload, missions, program/lists/todos, RSVP). |
contracts/* |
Serialized API response DTOs — what the API actually returns over the wire. These deliberately do not mirror Prisma models (dates are ISO strings, no secrets, signed URLs resolved). |
Import either flat or namespaced:
import { submitRsvpBody, type SubmitRsvpInput, type EventDto, RoleEnum } from "@mihai205/sharevent-contracts";
// or
import { schemas, contracts, enums } from "@mihai205/sharevent-contracts";
schemas.submitRsvpBody.parse(body);
zod is a peer dependency — the consumer provides it, so FE and BE each use
their own single zod instance.
Build
pnpm install
pnpm build # tsup → dist/{index.js (ESM), index.cjs (CJS), index.d.ts}
pnpm typecheck # tsc --noEmit
Outputs ESM + CJS + .d.ts so it works in the FE (Vite/ESM, moduleResolution: bundler) and the BE (CommonJS/ts-node) alike.
Consumption modes (multi-repo)
Both modes are supported; pick per environment.
(a) Local development — relative file: dependency
Each consuming repo already references it as a workspace-relative file
dependency in its package.json:
// FE/sharevent-fe/package.json and BE/sharevent-be/package.json
"dependencies": {
"@mihai205/sharevent-contracts": "file:../../PACKAGES/sharevent-contracts"
}
pnpm install in either repo symlinks the local package. After changing a
schema, rebuild the package so the dist consumers read is fresh:
cd PACKAGES/sharevent-contracts && pnpm build
# or, while iterating:
pnpm dev # tsup --watch
The root dev.ps1 builds the package before launching BE/FE, so a normal local
bring-up always has a current dist.
(b) Versioned publishing — private registry or git tag
For CI/CD and deployed builds, consume a pinned version instead of a symlink.
Private registry (e.g. GitHub Packages / Verdaccio):
cd PACKAGES/sharevent-contracts
npm version patch # bump 0.1.0 → 0.1.1
pnpm build
npm publish # respects publishConfig.access = "restricted"
Consumers pin the version and point the scope at the registry via .npmrc:
// package.json
"@mihai205/sharevent-contracts": "^0.1.1"
# .npmrc
@sharevent:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
Git tag (no registry needed):
git tag contracts-v0.1.1 && git push origin contracts-v0.1.1
// package.json — install straight from the tagged repo subdirectory
"@mihai205/sharevent-contracts": "github:youruser/sharevent#contracts-v0.1.1"
Switching a repo from mode (a) to (b) is a one-line change in its
package.jsondependency spec — no source code changes, since the import specifier (@mihai205/sharevent-contracts) is identical in both modes.
Versioning rules
- Breaking a schema/DTO (rename/remove a field, tighten a type) → minor or major bump; both repos must update together.
- Mirror any
prisma/schema.prismaenum change here in the same change set. - Never import Prisma types into this package — DTOs describe the serialized API, not the database.