Appearance
Shared Packages
Monorepo packages for code sharing across applications.
Current Packages
packages/
├── auth-client/ # Auth & org client (Better Auth + TanStack Query hooks)
├── ui/ # Shared UI components (shadcn/ui)
├── eslint-config/ # ESLint configurations
├── logger/ # Structured logging (adaptor pattern, zero dependencies)
├── typescript-config/ # TypeScript configurations
├── shared-types/ # Shared TypeScript types
└── vitest-config/ # Vitest test configurationsPackage Details
@jubiloop/auth-client
Authentication and organization client for frontend apps. Wraps Better Auth with TanStack Query hooks for session management, sign-in/up/out flows, and organization operations.
General API calls (events, health checks, etc.) use Tuyau — an end-to-end type-safe client generated from the AdonisJS backend. auth-client handles only auth and org concerns.
Features
- Better Auth Client Factory (
createBetterAuthClient): Configured with organization plugin - Auth Hooks:
useAuth(sign-in, sign-up, sign-out, session state) - Organization Hooks: list, create, switch, update, delete organizations; manage members
- Session Queries: Cache-first session fetching for route guards (
getSessionData,isAuthenticated) - Centralized Query Keys:
defaultQueryKeysfor consistent cache management across apps
Package Structure
packages/auth-client/
├── src/
│ ├── auth/ # Better Auth integration
│ │ ├── client.ts # Auth client factory
│ │ ├── types.ts # Auth type definitions
│ │ └── index.ts
│ ├── hooks/ # TanStack Query hooks
│ │ ├── auth.ts # Sign-in, sign-up, sign-out, session
│ │ ├── organization.ts # Org CRUD, switching, member management
│ │ └── index.ts
│ ├── queries/ # Query option factories
│ │ ├── session.ts # Session query options
│ │ ├── organization.ts # Organization query options
│ │ └── index.ts
│ ├── keys/ # Query key management
│ │ ├── defaults.ts # Static defaultQueryKeys object
│ │ └── index.ts
│ └── index.ts # Main package exports
├── tsup.config.ts # Build configuration
└── package.jsonBuild System
Uses tsup for bundling:
- Multiple entry points for optimal tree-shaking
- ESM and CommonJS outputs
- TypeScript declarations
@jubiloop/ui
Centralized UI component library using shadcn/ui. See UI Components for full documentation.
@jubiloop/eslint-config
Shared ESLint configurations:
base.js- Base JavaScript/TypeScript rulesnext.js- Next.js-specific rulesreact-internal.js- React rulestanstack.js- TanStack-specific rules
@jubiloop/typescript-config
Shared TypeScript configurations:
base.json- Base configurationnextjs.json- Next.js applicationsreact-library.json- React libraries
@jubiloop/shared-types
Centralized TypeScript type definitions shared between frontend and backend applications, eliminating duplication and ensuring type consistency across the stack.
Purpose
The shared-types package solves the problem of duplicate type definitions between backend and frontend by providing a single source of truth for all shared TypeScript interfaces and types.
Features
- Entity Interfaces: Core domain models (e.g.,
IUser,IOrganization) - API Response Types: Standardized API response structures (
IApiResponse,IApiMessage,IApiError) - DTOs: Legacy DTO interfaces for API communication (
IUserMinimalDTO) — being replaced by auto-generated transformer types - Request Types: API request interfaces (
ISubscribeNewsletterRequest) - Health Check Types: System health monitoring interfaces (
IHealthResponse,IDetailedHealthResponse)
Architecture
The package uses wildcard exports instead of barrel exports to:
- Avoid circular dependency issues
- Enable better tree-shaking
- Provide cleaner import paths
Build System
The package employs a two-step build process:
Production Build (pnpm run build):
- Uses tsup only
- Generates JavaScript and type definitions
- No declaration maps for security (prevents source exposure)
Development Build (pnpm run dev):
- Uses tsup + tsc concurrently
- Generates declaration maps (
.d.ts.mapfiles) - Enables IDE "Go to Definition" navigation to source TypeScript files
Package Structure
packages/shared-types/
├── src/
│ ├── entities/ # Domain entity interfaces
│ │ └── user.ts # IUser interface
│ ├── dtos/ # Data Transfer Objects
│ │ └── user.ts # User DTOs
│ ├── api/
│ │ ├── requests/ # API request types
│ │ │ └── newsletter.ts
│ │ └── responses/ # API response types
│ │ ├── common.ts # IApiResponse, IApiError
│ │ └── health.ts # Health check types
│ └── index.ts # Re-exports (minimal)
├── tsup.config.ts # Build configuration
└── package.json # Package configurationUsage Examples
typescript
// Import from specific paths (recommended)
import type { IApiResponse } from '@jubiloop/shared-types/api/responses/common'
import type { IUserMinimalDTO } from '@jubiloop/shared-types/dtos/user'
import type { IUser } from '@jubiloop/shared-types/entities/user'
// Backend: Type-safe API responses
const response: IApiResponse<IUser> = {
success: true,
data: user,
message: { type: 'success', text: 'User fetched' },
}
// Frontend: Type-safe API consumption
const { data }: IApiResponse<IUser> = await api.get('/users/me')TypeScript Naming Conventions
The package follows these conventions for clarity:
Iprefix for interfaces:IUser,IApiClient,IHealthResponseTprefix for type aliases:TSession,TAuthResponse(if needed)
This helps distinguish between interfaces and types at a glance.
@jubiloop/logger
Zero-dependency structured logging package with adaptor pattern.
Architecture
Loggerclass — framework-agnostic, delegates output to aTLogAdaptorConsoleAdaptor— dev output with structured contextNoopAdaptor— production/test (suppresses all output)- Apps implement their own adaptors (server wraps AdonisJS Pino)
Usage
typescript
import { createLogger } from '@jubiloop/logger'
const logger = createLogger({ appEnv: 'local' })
logger.error('Event creation failed', { key: 'events:create', source: 'http', error })Log Context
Every log call accepts a TLogContext with:
key— filterable composite key:'domain:action'(e.g.,'events:create','auth:signIn')source— execution context:'http','worker','cron','webhook'organizationId,userId— optional identifiers for filteringerror— the error object (if any)
Consumer Setup
| App | Logger file | APP_ENV source |
|---|---|---|
| Webapp | src/utils/logger.ts | import.meta.env.VITE_APP_ENV |
| Marketing | src/utils/logger.ts | process.env.NEXT_PUBLIC_APP_ENV |
| Server | app/lib/app_logger.ts | env.get('APP_ENV') via PinoAdaptor |
@jubiloop/vitest-config
Test configuration for Vitest (currently unused as tests are not yet implemented).
Using Packages
Packages are linked using pnpm workspaces:
json
{
"dependencies": {
"@jubiloop/ui": "*",
"@jubiloop/eslint-config": "*"
}
}Best Practices
- Keep packages focused on truly shared code
- Avoid app-specific logic in packages
- Use TypeScript for all package code
- Document public APIs with JSDoc comments