Skip to content
On this page

module.json ​

module.json is the per-module manifest in the registry. add, diff, and check parse it with Zod (moduleManifestSchema). Each feature folder (and api) ships one file next to its service. Core packages types, constants, and contracts have no module.json; the CLI synthesizes a manifest for them.

This page is the field catalog, how copy globs work, and which fields check actually enforces.

Where it lives ​

In the registry: <registry>/<name>/module.json. After add, the same file is copied into the host at <paths.modules>/<name>/module.json.

check errors when a copied name does not match the folder. It loads dependency and peer data from the registry manifest, not only from the host copy.

The registry may also have a root registry.json:

json
{
  "core": ["api", "types", "constants", "contracts"],
  "modules": ["auth", "account", "notification", "creem", "subscription"]
}

If that file is missing, the CLI infers core as whichever of api / types / constants / contracts exist as directories, and modules as other directories that contain module.json. list prints core then modules in that order.

Implied core manifests ​

For types, constants, and contracts, loadManifest builds:

  • description: Shared <name> package
  • files: ["**/*"] (still ignoring stubs/, tests/, node_modules/)
  • empty stubs, dependencies, registryDependencies, and the other arrays

api is core in registry.json but does have a real module.json (registryDependencies on those three packages).

Field catalog ​

Every key below is optional in Zod except name (min length 1). Omitted arrays default to []. Omitted description defaults to "". Omitted dependencies defaults to {}.

FieldTypeRole
namestringFolder identity; must match after copy
descriptionstringlist text
filesstring[]Domain-slice globs (relative to the module folder)
stubsstring[]Host-file globs under stubs/
dependenciesobjectnpm name → version spec to install
registryDependenciesstring[]Other modules add must copy first
peerModelsstring[]#models/… files the host must have
configKeysstring[]Keys expected in config/modules.ts
envstring[]Names add looks for (not written)
eventsstring[]Names merged into config/modules.ts emits
hostFacingExceptionsstring[]Documented host codes (not checked)
hostTestsstring[]Documented host specs (not copied)

Empty files means “all files” (**/*), still excluding stubs/**, tests/**, and node_modules/**. Empty stubs means no stub copies even when you pass --with-*.

files and stubs ​

files are fast-glob patterns from the module root. They land under <paths.modules>/<name>/. Typical domain set:

json
"files": [
  "service.ts",
  "events.ts",
  "options.ts",
  "contracts/**",
  "module.json"
]

stubs patterns must stay under stubs/<folder>/…. The first path segment after stubs/ selects the destination:

FolderDestination
modelspaths.models
migrationspaths.migrations
controllerspaths.controllers
validatorspaths.validators
adaptersapp/adapters/
providersproviders/
startstart/
routesstart/routes/

A stub with no folder (stubs/readme.ts) is skipped with a warning. Unknown folder names are skipped with a warning. CLI flags that select those folders are on add and CLI flags.

dependencies and registryDependencies ​

dependencies is npm packages this module imports. add installs missing ones with the host package manager (pnpm / yarn / npm). The bundled registry only uses this for creem (creem_io: ^1.0.0).

registryDependencies is other module names. add expands the graph and prints Plan: …. Cycles throw. api also pulls types, constants, and contracts even when you add api directly.

check errors when an installed module lists a dependency that is not in adonia.json installed.

Bundled graph:

text
api → types, constants, contracts
auth → api
account → api, auth
notification → api
creem → api
subscription → api, creem

peerModels ​

Each entry is an import alias. check strips #models/ and resolves <paths.models>/<rest>.ts. Example: #models/user → app/models/user.ts.

Plain add auth does not copy models, so check fails until --with-models / --with-stubs (or your own files) provide #models/user and #models/password_reset.

configKeys, env, and events ​

configKeys entries such as modules.auth are checked as a leaf in config/modules.ts: the segment after the last . must appear as auth: (word boundary). Missing file or missing leaf is a warning.

env names are scanned in .env, .env.example, and start/env.ts. add prints Missing env keys for <name> (not written): …. It never writes values. APP_KEY (auth, account) and the CREEM_* keys (creem) are the bundled examples.

events are merged into config/modules.ts under that module's emits array. emitSafe only fires names listed there. check warns when a declared event string is absent from the file. api and notification declare none.

hostFacingExceptions and hostTests ​

These arrays are reserved. check does not validate them (comment in the check command: not checked yet).

hostFacingExceptions names catalog keys in #constants/exceptions that host middleware is expected to use (auth guards, limiters, upload limits). Feature services may throw a different set; see each module page.

hostTests points at integration specs that stay in a host tests/ tree. --with-tests copies colocatedmodules/<name>/tests/ from the registry. It does not copy hostTests paths.

Example (auth) ​

Trimmed from the bundled auth manifest:

json
{
  "name": "auth",
  "description": "Email registration, login, verification, password reset",
  "files": [
    "service.ts",
    "events.ts",
    "options.ts",
    "contracts/**",
    "module.json"
  ],
  "stubs": [
    "stubs/models/user.ts",
    "stubs/migrations/*.ts",
    "stubs/adapters/**/*",
    "stubs/routes/auth.ts"
  ],
  "dependencies": {},
  "registryDependencies": ["api"],
  "peerModels": ["#models/user", "#models/password_reset"],
  "configKeys": ["modules.auth"],
  "env": ["APP_KEY"],
  "events": ["Auth:RegisterUser", "Auth:Login"],
  "hostFacingExceptions": ["INVALID_CREDENTIALS", "UNAUTHENTICATED"],
  "hostTests": ["tests/integration/lucid_adapters.spec.ts"]
}

Author a new folder the same way, then list it in registry.json. See local development.

Next steps ​

Wire fields to commands and slices:

  1. adonia.json for host installed and paths.
  2. add for how files / stubs copy.
  3. A feature page such as auth.