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 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.
@gj-kit/expo-ui · 0.8.5React Native and web primitives: an unnamed IconButton, Tabs, or Slider is a compile error.
@gj-kit/expo-media · 0.6.2An upload with no size limit, or an iCloud download nobody asked for, is a compile error.
@gj-kit/expo-auth · 0.1.2Token refresh for Expo, React Native and web — a missed transient branch won't compile.
@gj-kit/expo-workouts · 0.1.2HealthKit and Health Connect workouts in Expo, where destroying stored data is a compile error.
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.
@gj-kit/format · 0.1.2Timestamps drift between screens only when someone typed them that way — timeZone has no default.
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.
@gj-kit/toss-payments · 0.6.2A confirm that skipped verification does not compile — `flow.confirm` takes only VerifiedCheckout.
These guides exist only where there is a concrete composition boundary. Every package remains independently adoptable without them.
Build a Toss payment serviceChoose the smallest payment boundary first, then add Nest dependency injection or PostgreSQL stores only when the host application needs them.
Run operations work and notificationsUse protected operations jobs and durable notifications together when one application-owned workflow needs both. Each package still works without the other.