Skip to content

Monorepo

Prerequisites

Personal experience

Depending on the kind of contribution you’re interested in, the following experience is recommended:

  • Deep experience with TypeScript (incl. type-level programming)
  • Experience with TypeScript monorepo setups
  • Experience with distributed systems
  • Experience with Effect (or willingness to learn)

To make development as easy and consistent across systems and platforms, this project uses Nix to manage “system dependencies” such as Node.js, Bun etc. You can either use Direnv setup (recommended) to automatically load the Nix env or manually use the Nix env (e.g. via nix develop --command pnpm install).

Manual setup

You’ll need to have a recent version the following tools installed:

  • Node.js
  • Bun
  • pnpm

Initial setup

Terminal window
git clone git@github.com:livestorejs/livestore.git
cd livestore
# Load environment variables (including Nix dependencies)
direnv allow
# Install dependencies
pnpm install
# Build the project
pnpm build

General notes

  • TypeScript
    • LiveStore tries to follow the strictest TypeScript rules possible to ensure type safety and avoid subtle bugs.
  • Package management
    • This project uses pnpm to manage the workspace.
  • LiveStore is primarily developed in VSCode/Cursor.
  • Testing
    • LiveStore uses Vitest for most tests and Playwright for browser tests.

Notable used tools / technologies

Environment variables

The .envrc file contains all necessary environment variables for the project. You can create a .envrc.local file to override or add variables for your local setup. You’ll need to run direnv allow to load the environment variables.

VSCode tasks

  • This project is primarily developed in VSCode and makes use of tasks to run commands.
  • Common tasks are:
    • dev:ts to run the TypeScript compiler in watch mode for the entire monorepo
    • pnpm:install to install all dependencies (e.g. when changing a package.json)

Examples

  • Once you’ve set up the monorepo locally, you’ll notice both the src and standalone directories in /examples.
  • The /examples/standalone directory is meant as a starting point for app developers evaluating LiveStore and looking for a ready-to-run example app.
  • The /examples/src directory is meant for LiveStore maintainers and to run as part of the LiveStore monorepo. Compared to /examples/standalone it makes use of local linking features such a workspace:*, TypeScript references etc.
  • Both directories are kept in sync via /examples/patches and /scripts/generate-examples.ts. Usually it’s recommended to work in /examples/src and generate the /examples/standalone version via pnpm -w generate:examples:standalone.

Making changes to examples

  1. Make your desired changes in /examples/src. (You might also need to update some of the patches in /examples/patches.)
  2. Run pnpm generate:examples:standalone to generate the /examples/standalone version
  3. Check whether the changes in /examples/standalone are what you expected.
  4. Commit your changes in both /examples/src and /examples/standalone (and possibly /examples/patches). Note on GitHub, changes to examples/standalone are hidden by default.

OpenTelemetry setup

As a local OpenTelemetry setup, we recommend the docker-otel-lgtm setup.

Add the following to your .envrc.local file:

Terminal window
export VITE_GRAFANA_ENDPOINT="http://localhost:30003"
export GRAFANA_ENDPOINT="http://localhost:30003"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
export VITE_OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"

TypeScript

  • Each package has its own tsconfig.json file which extends the root tsconfig.base.json.
  • This project makes heavy use of TypeScript project references.

Package management

  • This project uses pnpm to manage the workspace.
  • We’re using the workspace:* protocol to link packages together.
  • We should try to keep dependencies to an absolute minimum and only add them if we absolutely need them.
  • We also need to manually add peer dependencies for each package.
  • We should try to avoid duplicate dependencies across the monorepo as much as possible as duplicate dependencies can lead to a lot of issues and pain.
    • We’re also using the resolutions field in the root package.json to force some packages to be the same across the monorepo (ideally not needed but for some packages it’s necessary currently).
  • We’re using syncpack to help maintain consistent dependency versions across the monorepo.
    • See syncpack.config.mjs for the configuration.
    • Common commands:
      • bunx syncpack format to format the package.json files
      • bunx syncpack lint to check all version ranges
      • bunx syncpack fix-mismatches to adjust versions across package.json files (check before with lint)
      • bunx syncpack update to update packages across the monorepo to the latest versions

Notes on external dependencies

LiveStore tries to use as few external dependencies as possible. Given LiveStore is built on top of Effect, which can be considered a standard library for TypeScript, it should handle most use cases.

Notes on some packages

The following packages need to be updated with extra care:

  • react/react-dom as we need to move in lockstep with Expo / React Native (currently pinned to 19.0.0)
  • effect (currently pinned to 3.14.8)

Effect

  • LiveStore makes heavy use of the Effect library and ecosystem throughout the implementation of the various packages.
  • Effect is not imposed on the app developers using LiveStore but where it makes sense, LiveStore is also exposing a Effect-based API (e.g. createStore).

Updating dependencies

  • Either update the versions manually in each package.json file or use bunx syncpack update.

Notes on monorepo structure

  • The @livestore/utils package re-exports many common modules/functions (e.g. from effect) in order to
    • Reduce the number of direct dependencies for other packages
    • Allows for convenient extension of modules (e.g. adding methods to Effect.___, Schema.___, …)

Docs

The LiveStore docs are built with Astro Starlight.