Appearance
Shared Packages
Jubiloop packages expose code that is stable and useful across applications. App-specific routes, business workflows, and presentation compositions stay in their applications.
Package Contracts
| Package | Public contract | Consumers |
|---|---|---|
@jubiloop/auth-client | Better Auth client factory, auth and organization hook factories, query options, and query keys | Webapp and other React clients that configure auth |
@jubiloop/ui | React UI primitives, icons, hooks, class utilities, stylesheet, and PostCSS configuration | Webapp and marketing |
@jubiloop/shared-types | API response envelopes, field-error types, and runtime error-code catalogs | Server and frontend error handling |
@jubiloop/logger | Framework-independent logger, adaptors, context, and log types | Server, webapp, marketing, and packages |
@jubiloop/eslint-config | Shared ESLint configurations | Workspace applications and packages |
@jubiloop/typescript-config | Shared TypeScript base, app, and library configurations | Workspace applications and packages |
@jubiloop/vitest-config | Shared base and browser-like Vitest presets and coverage scripts | Packages that opt into those presets |
@jubiloop/auth-client
This package configures Better Auth's organization plugin and exposes factory-based TanStack Query integration. It does not create an application-global client. Each consumer supplies its configured client and decides how to present navigation, toasts, and request errors.
The public surface includes:
createBetterAuthClient()and local auth and organization types;createAuthHooks()andcreateOrganizationHooks();- reusable session and organization query options for hooks and route loaders;
- session helpers used by route guards;
defaultQueryKeysfor auth and organization cache operations.
ts
import {
createAuthHooks,
createBetterAuthClient,
getSessionData,
isAuthenticated,
} from '@jubiloop/auth-client'
const authClient = createBetterAuthClient({
baseURL: 'https://api.jubiloop.localhost',
})
const authHooks = createAuthHooks(authClient)General server routes, including events, health checks, and newsletter subscriptions, use Tuyau instead. Endpoint request and response types should flow from the server registry rather than this package.
@jubiloop/auth-client builds ESM, CommonJS, and declaration output with tsup. Its root export and module subpaths resolve to that output.
@jubiloop/ui
The UI package is consumed as TypeScript source. It exposes components and helpers through wildcard subpaths, plus the shared stylesheet and PostCSS configuration. Applications own their route and domain compositions, stylesheet wrapper, source scan, font imports, and theme activation.
See UI Components Package for the public API, component rules, token layers, typography, contrast, class merging, and validation commands.
@jubiloop/shared-types
This zero-runtime-dependency package exposes shared API envelope and error contracts through api/responses/* and api/errors/* subpaths:
ts
import type { TErrorCode } from '@jubiloop/shared-types/api/errors/codes'
import { CommonErrors } from '@jubiloop/shared-types/api/errors/common'
import type { IApiResponse } from '@jubiloop/shared-types/api/responses/common'IApiResponse<TData, TMeta> can carry data, user-facing messages, structured errors, and response meta. IApiError carries a message and code, with optional field and metadata. Error codes follow the three-part layer.context.specific format and the package exports runtime catalogs for shared common, event, event-plan, and member errors.
Field-Error Exposure
The api/errors/field-error subpath exports the server-facing field-error contract:
ts
import { EventPlanErrors } from '@jubiloop/shared-types/api/errors/event'
import type { IFieldError } from '@jubiloop/shared-types/api/errors/field-error'
const duplicateName: IFieldError = {
field: 'name',
message: 'An event plan with this name already exists',
code: EventPlanErrors.DUPLICATE_NAME,
expose: { level: 'partial' },
}IFieldError carries the internal field, message, model error code, optional metadata, and a required TExposeStrategy. The strategy controls what a server exception converts into IApiError values:
| Level | Client-facing result |
|---|---|
full | Field, original message, code, and metadata |
partial | Field, original message, and code; metadata is removed |
minimal | Field and code with a generic message |
none | Generic message and common internal code only |
custom | A transform returns an IApiError or omits the field error entirely |
These types define the disclosure contract; the server exception implementation applies it. See App Exception Architecture for the server response path and Request Errors for the webapp consumer boundary.
Do not add endpoint DTOs that Tuyau can infer from server routes, controllers, transformers, and VineJS validators. Do not add framework types, schemas, I/O, environment access, or imports from applications. Dates in a shared HTTP type are ISO 8601 strings, not Date or framework objects.
The package builds ESM, CommonJS, and declaration output with tsup. Production builds clear dist first; development runs tsup watch and declaration generation together.
@jubiloop/logger
The logger package exports Logger, createLogger, ConsoleAdaptor, NoopAdaptor, and its public log types from the package root. A logger delegates output to a TLogAdaptor, so applications can select environment behavior or bridge to a framework logger without changing call sites.
ts
import { createLogger } from '@jubiloop/logger'
const logger = createLogger({ appEnv: 'local' })
logger.error('Event creation failed', {
key: 'events:create',
source: 'http',
organizationId,
error,
})ConsoleAdaptor handles local, development, and QA output. NoopAdaptor suppresses output for production and test when no custom adaptor is supplied. The server provides its own Pino adaptor; browser apps configure the package from their public APP_ENV values.
Log context supports a filterable action key, execution source, identifiers, an error, and other structured fields. Use withContext() for a message prefix and withDefaults() for fields shared by related log calls.
Tooling Packages
Tooling packages publish configuration rather than runtime application features:
@jubiloop/eslint-configprovides the shared base, Next.js, internal React, and TanStack rule sets used by package and app ESLint configurations.@jubiloop/typescript-configprovides reusable compiler configurations such asbase.json,nextjs.json, andreact-library.json.@jubiloop/vitest-configprovides test presets and report scripts. A package must import a preset explicitly; declaring the dependency alone does not activate it.
Adding To A Shared Package
- Confirm at least two consumers need the contract or that the package is already the correct cross-application owner.
- Keep application routes, side effects, and domain presentation out of generic packages.
- Add the smallest public subpath needed by consumers; do not mirror every internal module.
- Build and type-check packages that emit
dist, or run the UI package's source-contract checks. - Verify affected consumers because package changes can alter both server and browser builds.