Skip to content

Latest commit

 

History

History
137 lines (97 loc) · 6.62 KB

File metadata and controls

137 lines (97 loc) · 6.62 KB

Milestones

Milestone 1 — Working end-to-end demo

Build the library from core outward, ending in a runnable demo against real Postgres.


1.1 @kosan/core

ORM-agnostic: registry, cache, context, resolvers. No framework dependencies.

  • TenantRegistry — create / resolve / suspend / delete / update tenants
  • ConnectionCache — LRU eviction, idle-timeout sweeping, per-tenant stats
  • TenantContextAsyncLocalStorage, runWithTenant(), useTenant(), getCurrentTenant()
  • Resolvers — SubdomainResolver, HeaderResolver, PathResolver
  • Types — MasterStore, Adapter, Cipher, LifecycleHooks, Resolver
  • Full Vitest suite — 52/52 tests passing
  • In-memory fakes — InMemoryMasterStore, FakeAdapter (used in tests)

1.2 @kosan/sequelize

Sequelize v6 adapter — connection factory, model registration, master store.

  • SequelizeMasterStore — implements MasterStore using a Sequelize model (Tenant table)
  • SequelizeAdapter — implements Adapter<Sequelize>, handles instance creation, dialect config, pool config, model factory application, graceful disconnect
  • AdapterContext type — { sequelize: Sequelize } passed to model factories
  • Full Vitest suite — 20/20 tests with SQLite in-memory master DB

1.3 @kosan/express

Thin Express middleware — resolves tenant, runs handler inside runWithTenant.

  • tenantMiddleware(options) — calls resolver, calls registry.resolveBySlug, calls runWithTenant, calls next()
  • Error handling — 404 on TenantNotFoundError, 403 on TenantNotActiveError, 500 on unexpected errors
  • Custom onMissingTenant hook
  • Accepts Resolver instance or plain async/sync function
  • Full Vitest suite — 10/10 tests (including concurrent context isolation)

1.4 examples/sequelize-express

Runnable demo — 3 tenants in Postgres via Docker Compose.

  • docker-compose.yml — master Postgres + 3 tenant Postgres instances
  • Subdomain resolution demo — acme.localhost, globex.localhost, initech.localhost
  • Model usage — Order model with CRUD routes (list, create, delete)
  • Tenant provisioning script (pnpm provision) — idempotent, syncs schema via onCreate hook
  • GET /health — cache size + per-tenant stats
  • README — how to run locally

Milestone 2 — CLI migration orchestrator ✅

@kosan/cli — runs Sequelize migrations across all active tenants.

  • kosan migrate command — iterates active tenants, runs migrations per tenant
  • --concurrency N flag — parallel migrations with failure isolation (per-tenant errors captured, siblings continue)
  • --tenant <slug> flag — target a single tenant
  • KosanConfig type — master, migrationsPath, migrationsTableName, concurrency
  • Config loaded via jiti (supports .ts, .js, .mjs config files)
  • Per-tenant result summary — printResults() table with outcome, applied count, duration
  • Migration interface — name, up(qi), down(qi) — decoupled from file loading
  • loadMigrationsFromDir() — reads directory, sorts alphabetically, lazy dynamic import
  • withConcurrency() pool — bounded parallel execution, results in input order
  • Tests — 16/16 (7 pool tests, 9 runner/printer tests with in-memory SQLite)

Milestone 3 — Prisma adapter ✅

@kosan/prisma — adapter for Prisma Client.

  • PrismaAdapter — one PrismaClient per tenant, datasource URL override via buildUrl
  • PrismaMasterStore — master tenant table via structural TenantDelegate type (pass prisma.tenant)
  • usePrisma<TClient>() — typed shortcut that returns the client from useTenant().models.prisma
  • No hard dependency on @prisma/client — fully generic, peer dep only
  • TENANT_PRISMA_SCHEMA export — copy-pasteable Prisma schema snippet
  • PrismaClientConstructor type — typed constructor interface for the generated client
  • Tests — 26/26 (15 store tests, 11 adapter+context tests, full registry integration)

Milestone 4 — Fastify plugin & Koa middleware ✅

  • @kosan/fastify — Fastify plugin (callback-based onRequest hook; wrapWithTenantContext for correct async propagation)
  • @kosan/koa — Koa middleware (runWithTenant wrapping next() — works cleanly with Koa's Promise chain)
  • Core addition: wrapWithTenantContext(value, callback) — calls callback inside storage.run() for frameworks that can't use async closures
  • Tests — 10 Fastify + 11 Koa (happy path, errors, resolver types, concurrent isolation)

Milestone 5 — Observability & health ✅

  • CacheStats extended with tenantSlug and idleMs fields
  • ConnectionCache.maxSize getter — publicly readable
  • TenantRegistry.getStats() — returns { cacheSize, cacheMaxSize, entries }
  • getTenantLogContext() — reads current tenant from AsyncLocalStorage, returns { tenantId, tenantSlug } (empty object outside context)
  • getHealthPayload(registry) — builds a serialisable health payload from cache state
  • SlowQueryInfo type + onSlowQuery callback + slowQueryThresholdMs option in SequelizeAdapterOptions
  • Slow-query detection in SequelizeAdapter via wrapped logging + benchmark: true
  • Tests — 7 observability tests in @kosan/core, 5 slow-query tests in @kosan/sequelize
  • All exports added to @kosan/core and @kosan/sequelize index files

Milestone 6 — Docs site

VitePress documentation.

  • Getting started guide
  • API reference (auto-generated from TypeScript)
  • Adapter authoring guide
  • Deployment guide (subdomain setup, credential encryption, Docker)
  • Migration guide

Milestone 7 — NestJS support ✅

@kosan/nestjs — NestJS module, guard, and decorator integration.

  • KosanModule.forRoot — synchronous registration
  • KosanModule.forRootAsync — async registration (useFactory, inject)
  • TenantMiddleware — NestJS middleware wrapping runWithTenant(ctx, next)
  • TenantGuard — optional guard asserting tenant context is present
  • @CurrentTenant() — parameter decorator returning TenantContextValue
  • @InjectRegistry() — constructor decorator injecting the TenantRegistry
  • Tests — 16/16 (KosanModule structure, TenantGuard, TenantMiddleware with context isolation)
  • Example — examples/sequelize-nestjs/ — runnable NestJS app with Docker Compose, 3 tenants
  • Docs — docs/docs/packages/nestjs.md updated + docs/docs/examples/sequelize-nestjs.md