@dylanmerigaud/microsaas-kit
v0.1.3
Published
The invariant machinery of the micro-SaaS template, imported (never copied) by the template and every bet: logger, env factory, oRPC/db/stripe/email seams, structured-data, shadcn ui primitives, and the Claude Code CLI structured-output transport.
Readme
@dylanmerigaud/microsaas-kit
The invariant machinery of the micro-SaaS template, imported (never copied) by the template and every bet as a versioned dependency (the pre-eject react-scripts model). A bet keeps only bet-specific code; shared machinery lives here once. No more copy divergence.
What's in it (subpath exports)
Each subpath is imported directly, e.g. import { cn } from "@dylanmerigaud/microsaas-kit/utils".
| Subpath | What it is |
| ------------------ | ----------------------------------------------------------------------------------------------- |
| /logger | createLogger(isDev) -> the log object (the sanctioned console user) |
| /utils | cn (clsx + tailwind-merge) |
| /email | sendPaymentConfirmation, createEmailDeps({ apiKey, from }), the receipt template |
| /site-url | resolveSiteOrigin(configuredUrl) |
| /stripe | createGetStripe(secretKey) -> memoized getStripe |
| /stripe-events | handleStripeEvent(event, deps, warn) + its types (the webhook money path) |
| /structured-data | softwareApplicationJsonLd(input), faqPageJsonLd(entries), serializeJsonLd |
| /theme-meta | ThemeMeta type + DEFAULT_THEME_META |
| /maker-handle | makerXHandle(url) |
| /orpc | createContextFactory({ getSession }), createOrpcClient<Router>({ siteOrigin }) |
| /db | createGetDb({ connectionString, schema }) -> memoized getDb (drizzle over Neon) |
| /env | baseServerSchema, baseClientSchema, betEnvBaseOptions to spread into a bet's createEnv |
| /claude-cli | runClaudeCliJson, runClaudeCliVision, pickTransport, extractJsonObject, ... (subscription-billed structured output) |
| /ui | shadcn primitives: Button, Card, Input, Label |
| /components | ProductDemo, QueryProvider (the pricing CTA stays per-bet: it wires a bet's billing procedure) |
Consumption pattern (why the template keeps thin shims)
The template (and each bet) keeps THIN SHIM FILES at the existing paths that re-export or bind the
kit: lib/logger.ts becomes export const log = createLogger(env.NODE_ENV !== "production"),
lib/utils.ts becomes export * from "@dylanmerigaud/microsaas-kit/utils", and so on. So
@/lib/logger imports keep working everywhere, the repo rules keep their enforcement anchor
(only lib/logger.ts may console, only lib/env.ts touches process.env), and a bet that must
diverge on one module deletes the shim and owns the file again, explicitly. That is THE divergence
mechanism.
Env-bound and per-bet-typed modules (logger, site-url, stripe, email, db, orpc, structured-data) are exported as FACTORIES the bet binds; pure modules (utils, theme-meta, maker-handle, stripe-events, ui, components) are bare re-exports.
Consumer requirements
- Tailwind v4 must scan the kit's dist. Add to the consumer's
globals.css:@source "../node_modules/@dylanmerigaud/microsaas-kit/dist"; - The kit ships
peerDependencies(react, next, stripe, drizzle-orm, radix-ui, ...); the consumer already owns them, so nothing is bundled twice.
The update flow
- Edit the source here (
packages/microsaas-kit/src/...), with its tests. pnpm build && pnpm test(both are gates;prepublishOnlyruns them).- Bump
version, thennpm publish(access is public). - Bump
@dylanmerigaud/microsaas-kitin each consumer (the template and every bet) and reinstall.
During development in THIS repo the template links the kit through the pnpm workspace (a file:
dependency), so edits are picked up on the next pnpm build without publishing. In a bet clone the
kit resolves from npm, so packages/ here is inert source there.
Build
ESM only (type: module), NodeNext, tsc to dist/ with .d.ts. The React subpaths keep their
"use client" directive through the build (verified in dist output).
