Skip to content

@open-learning-hub/server shared package

Problem

Several server primitives are duplicated or only present in one app:

  • bcrypt password hashing (helper in CMS, inline calls in LMS — see T-012).
  • Security headers (only CMS — see T-001).
  • Structured redacting logger (only LMS, sparsely used; CMS has none).
  • API error helper / ApiError (CMS only — converged in T-003 for LMS).
  • Rate-limit driver interface (now shared; see notes).

Consolidating these into a workspace package keeps the apps thin without forcing schema or mutation-transport convergence (which is a standing decision to leave alone).

Proposal (partially implemented)

  1. Add/expand packages/server/ workspace package with name: "@open-learning-hub/server" and per-feature subpath exports (no barrel):
    ./password   → bcrypt cost 12 helpers
    ./errors     → ApiError + handleApiError + ErrorCode union
    ./headers    → getSecurityHeaders()
    ./log        → structured redacting logger (port from either app's src/lib/log.ts per .cursor/rules/091-structured-logging.mdc parity contract)
    ./rate-limit → driver interface + memory + upstash drivers
    
  2. Configure as source-only (no dist/), consumed via transpilePackages in both apps' next.config.ts (matches widget-wire-schemas pattern).
  3. Strict TS, noUncheckedIndexedAccess: true (matches other packages).
  4. Migrate consumers:
    • CMS: replace apps/cms/src/lib/security/headers.ts, apps/cms/src/lib/api/errors.ts, apps/cms/src/lib/auth/password.ts imports with @open-learning-hub/server/* re-exports (keep thin compatibility shims for one release if helpful).
    • LMS: replace the modules landed by T-001, T-003, T-012 with @open-learning-hub/server/* imports.
  5. Add unit tests in packages/server/src/**/__tests__/ mirroring the original locations.

Acceptance criteria

  • New packages/server/ rate-limit surface transpiles and is consumed by both apps.
  • CMS and LMS import remaining primitives from @open-learning-hub/server/* instead of app-local modules (legacy paths either deleted or shimmed to the package).
  • All pre-existing tests still pass; new package-level tests added.
  • npm run check passes.

Out of scope

  • Converging the rate-limit concrete driver implementations (memory is shared; upstash stays deferred per T-014).
  • Sharing Auth.js config (auth models intentionally diverge).
  • Sharing DB / Kysely schemas.

Notes / decisions log

  • 2026-05-24: Ticket created from monorepo audit. Scheduled to land after T-001, T-003, T-012 so the API shapes are stable before extraction.
  • 2026-05-25: Deferred for a later effort. App-local primitives (T-001, T-003, T-012) can land without extraction; reactivate when monorepo consolidation is reprioritized.
  • 2026-05-25: Moved to tickets/deferred/ alongside T-014 and T-015.
  • 2026-05-25: T-009 (CMS server-only guards) deferred as companion work when this package effort resumes.
  • 2026-07-07: Implemented @open-learning-hub/server/rate-limit subpath and migrated LMS/CMS rate-limit drivers to it. Remaining shared-package work (./errors, ./log, and consolidation of existing standalone packages) stays deferred.