Skip to content

GJ Kit

Ten TypeScript libraries where the step you'd forget is a compile error.

Ten packages covering Expo, React Native, NestJS, PostgreSQL and Toss Payments, each built around one boundary where a silent mistake costs money, data, or a user’s session. They are for teams who would rather meet the bug in tsc than in an incident channel.

  • 0 runtime dependencies, in all ten
  • 708 @ts-expect-error guards, run by vitest typecheck
  • 3,700+ tests on one pnpm test — no network, no device
  • Dual ESM + CJS, TypeScript strict, Node 20+, MIT
  • One CI gate for every package: pnpm verify:release

The dangerous default does not exist

The argument you would have let default is required instead. createTossPayments returns a type with no confirm property until you pass an OrderStore, and no billing property until you pass a BillingKeyStore. formatDateTime(instant) does not compile, because timeZone has no default. createTossPaymentsPostgres({ sql }) is a type error, because the encryption protector is a required field. Unlimited uploads have to be spelled 'server-enforced', because Number.POSITIVE_INFINITY is rejected.

You cannot leave the branch out

The result you are handed is a union that names the branch you would otherwise drop. matchRefreshOutcome takes all five endings of a token refresh as handler keys — omit transient and the call does not compile, and transient is exactly the branch that turns a 5xx into a false sign-out. saveWorkout()’s nativeId is TS2339 until status === 'saved' narrows past the locked-device branch, which never appears during development. On a job result, error is unreachable until you switch on status.

The enforcement is itself tested

708 @ts-expect-error directives sit in 65 fixture files under tests/types, and vitest typecheck runs every one of them. TypeScript treats a directive that no longer catches anything as an error in its own right (TS2578), so a guard that quietly loosens fails the suite instead of passing it. A rule here cannot decay into a line of documentation without breaking the build.

The half you own runs on Node

Seven of the ten ship a framework-free ./testing entry point, so the part you still own is checkable without the dangerous thing attached. nest-notifications hands you 30 runnable contract cases to point at your own database and nest-operations-jobs hands you 13. toss-payments-postgresql ships an in-memory double that throws on the nested lock PostgreSQL would silently hang on. expo-workouts fakes the native module seam rather than the API, so the real core code replays all six cursor-reset reasons under vitest.

Four packages for the boundaries where the platform itself fails silently: an IconButton with no accessible name, an upload that ends the process instead of rejecting, a 5xx classified as a sign-out, and a Health Connect upsert that deletes the route you left out.

One package for the code that quietly forks into three copies. Time zone, date separator, ₩ versus 원, byte unit system, and the meaning of zero bytes are all required arguments, so two screens can only disagree if someone typed it that way.

Durable job and notification pipelines where the database stays yours: the library owns the ordering and liveness rules, refuses at boot the tuning that would let a job run twice, and hands you framework-free contract cases to run against your real store.

The Toss Payments core, its Nest DI composition, and its PostgreSQL stores — where a confirm that skipped verification, a billing approve with no idempotency key, and a store assembled without an encryption protector are all compile errors.

These guides exist only where there is a concrete composition boundary. Every package remains independently adoptable without them.

Alongside these pages, GJ Kit publishes llms.txt and an API JSON index. Automation should always check the JSON package version and import path.