Working with Modules

Build on a Module

Write your own features next to Stardeck's Modules so they keep working when the Modules update or get switched off.

An app has two kinds of code. The Modules under apps/web/src/modules/ belong to Stardeck: every update replaces them, and the editing tools refuse to change them. The rest of apps/web/src/ is yours. This page is for you and your coding agent when you add your own features in your part, so they keep working when the Modules update and when an owner switches one off.

The rules your agent follows are in your app's AGENTS.md § "Modules (reusable capabilities)". This page explains why they exist.

Check what a Module already does

Before you build a feature, read the reference pages for the Modules your app has. A booking calendar, a checkout, a stock count or a staff time clock is probably there already, with tests for the hard cases. Capabilities maps common requests to Modules. Building a second copy means two places to fix every bug, and the Module's copy keeps getting fixes that yours won't.

Reach a Module without pinning it on

An owner can switch a feature Module such as Booking or POS off. If one of your pages imports that Module, it can't be switched off: the Modules tab refuses while app code imports it, and the app's app-layer-imports test fails on an import nobody listed. So your code reaches feature Modules in these ways first:

WayWhat it's for
SlotsYour code adds a section, panel or page to a Module's screen, or your page renders what Modules put in it. Entries go in apps/web/src/lib/modules/app-extensions.ts. See Extend a Module.
Server registrationsEach Module hands the app its sample data, scheduled jobs, home cards, search results and sitemap entries in its server/registrations.ts. App code reads them as one list (@/module-server.gen), so a Module that is off drops out.
Module pages and APIA page or endpoint that belongs to one Module lives in that Module. Your code links to it or calls it like any other route.

Use a Module's public API

When your code does need a Module's functions or types, it imports them only from the Module's root:

  • index.ts, imported as @/modules/<name>: types, hooks and helpers safe in the browser.
  • server/index.ts, imported as @/modules/<name>/server: server-only functions.
  • server/ui.ts and the files in client/: the loaders and hooks a Module's screens use, for an app that customized those screens.

Never import a Module's other files. Every export name is recorded in apps/web/src/lib/modules/__tests__/public-api.json, and an update that removes or renames one is a major version whose changelog tells your agent what replaces it.

Importing a feature Module from a page, layout, route or lib file keeps that Module on. List the file in apps/web/src/app-layer-imports.allowlist.json with a reason, and the Modules tab won't switch the Module off while the import is there. Your own website pages are the usual case: see AGENTS.md § "Website (the public site)". Library Modules (Formatting, i18n, Identity, Infra, Standard UI) are always on, so any app code may import them through their root.

Check that a Module is on

A Module can be off in two places: left out of the build in the dashboard's Modules tab, or hidden by the owner in Features in use (see Two switches). Code that depends on a Module checks isModuleEnabled from @/lib/modules/registry before it uses it. A link to a Module's admin screen asks useCanReach(href) from @/lib/client/admin-reach, which also covers Features in use and the person's permissions. A feature built on Booking should disappear cleanly when Booking is off, not show an error.

Leave the generated files alone

The app's composed pages, API stubs and *.gen.ts files are rebuilt from the Modules on every npm run dev, build, lint, typecheck and test. Git ignores them. An edit to one is gone at the next run, so change the Module's settings or your own code instead.

Permissions

Each Module declares the permission keys its pages and endpoints check, listed under Facts on its reference page. Admin holds every key. Manager, Staff and Viewer start with the keys the Module gives them. A feature you build on a Module checks the same key as the Module's page for the same action, so a person who can't refund at the register can't refund through your page either.

Test your feature through the real routes

Write tests for your own feature in your part of the app, against the real routes and the real database harness Stardeck ships. The Modules' own tests stay Stardeck's: they run in your app and keep proving the Module, but you don't edit them. When your feature changes what a person sees, test it the way the Module tests do: sign in as one person, call the routes the screen calls, and check what they see.

When you need more