Skip to content

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

PackagePublic contractConsumers
@jubiloop/auth-clientBetter Auth client factory, auth and organization hook factories, query options, and query keysWebapp and other React clients that configure auth
@jubiloop/uiReact UI primitives, icons, hooks, class utilities, stylesheet, and PostCSS configurationWebapp and marketing
@jubiloop/shared-typesAPI response envelopes, field-error types, and runtime error-code catalogsServer and frontend error handling
@jubiloop/loggerFramework-independent logger, adaptors, context, and log typesServer, webapp, marketing, and packages
@jubiloop/eslint-configShared ESLint configurationsWorkspace applications and packages
@jubiloop/typescript-configShared TypeScript base, app, and library configurationsWorkspace applications and packages
@jubiloop/vitest-configShared base and browser-like Vitest presets and coverage scriptsPackages 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() and createOrganizationHooks();
  • reusable session and organization query options for hooks and route loaders;
  • session helpers used by route guards;
  • defaultQueryKeys for 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:

LevelClient-facing result
fullField, original message, code, and metadata
partialField, original message, and code; metadata is removed
minimalField and code with a generic message
noneGeneric message and common internal code only
customA 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-config provides the shared base, Next.js, internal React, and TanStack rule sets used by package and app ESLint configurations.
  • @jubiloop/typescript-config provides reusable compiler configurations such as base.json, nextjs.json, and react-library.json.
  • @jubiloop/vitest-config provides 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

  1. Confirm at least two consumers need the contract or that the package is already the correct cross-application owner.
  2. Keep application routes, side effects, and domain presentation out of generic packages.
  3. Add the smallest public subpath needed by consumers; do not mirror every internal module.
  4. Build and type-check packages that emit dist, or run the UI package's source-contract checks.
  5. Verify affected consumers because package changes can alter both server and browser builds.

Built with ❤️ by the Jubiloop team