LangENKO

mandu.config.ts Reference

Full schema for ManduConfig — every top-level field and sub-field with types, defaults, and examples.

since v0.22
On this page

mandu.config.ts Reference

The canonical configuration surface for a Mandu project. The schema is the ManduConfig type exported from @mandujs/core/src/config/mandu.ts. Every field on this page is defined there; fields not on this page are not part of the supported config surface at v0.22 / v0.23.

This list reflects v0.22 / v0.23. For later fields, check the @mandujs/core release notes.

File lookup

loadManduConfig(rootDir) probes these paths, in order, and uses the first that exists:

  1. mandu.config.ts
  2. mandu.config.js
  3. mandu.config.json
  4. .mandu/guard.json

A .mandu/guard.json whose root object does not contain a guard key is coerced to { guard: <root> }. A malformed file yields {}Mandu does not throw on parse errors; it treats the project as unconfigured.

// mandu.config.ts
import type { ManduConfig } from "@mandujs/core";

export default {
  server: { port: 3333 },
  guard: { preset: "mandu" },
} satisfies ManduConfig;

Top-level fields

Every top-level field is optional.

Field Type Purpose
adapter ManduAdapter Runtime adapter override (advanced)
server object Listener, CORS, streaming, rate-limit
dev object Dev-server behavior
build object Production build behavior
guard object Architecture guard configuration
fsRoutes object FS Routes discovery rules
seo object Default SEO metadata
plugins ManduPlugin[] Plugin array
hooks Partial<ManduHooks> Lifecycle hooks

adapter

Type: ManduAdapter (from @mandujs/core).

Swap out the runtime adapter. Most projects leave this unset and use Mandu's default Bun adapter.

server

server?: {
  port?: number;
  hostname?: string;
  cors?: boolean | {
    origin?: string | string[];
    methods?: string[];
    credentials?: boolean;
  };
  streaming?: boolean;
  rateLimit?: boolean | {
    windowMs?: number;
    max?: number;
    message?: string;
    statusCode?: number;
    headers?: boolean;
  };
};
Field Type Notes
port number Overridden by PORT env and --port CLI flag
hostname string Listener hostname
cors boolean | object true for permissive defaults, object for fine control
cors.origin string | string[] Allowed origin(s)
cors.methods string[] Allowed methods
cors.credentials boolean Allow credentials
streaming boolean Enable streaming responses
rateLimit boolean | object true for default limit, object for fine control
rateLimit.windowMs number Window length in ms
rateLimit.max number Max requests per window
rateLimit.message string Response body on limit
rateLimit.statusCode number Response status on limit
rateLimit.headers boolean Emit rate-limit headers

Example:

server: {
  port: 3333,
  hostname: "0.0.0.0",
  cors: { origin: ["https://example.com"], credentials: true },
  rateLimit: { windowMs: 60_000, max: 120, headers: true },
}

dev

dev?: {
  hmr?: boolean;
  watchDirs?: string[];
  /** Observability SQLite persistent store (default: true) */
  observability?: boolean;
};
Field Type Default Notes
hmr boolean true (runtime-default) Hot module replacement
watchDirs string[] Extra directories to watch
observability boolean true Enable the observability SQLite store

Example:

dev: { hmr: true, watchDirs: ["content", "packages/shared"] }

build

build?: {
  outDir?: string;
  minify?: boolean;
  sourcemap?: boolean;
  splitting?: boolean;
};
Field Type Notes
outDir string Output directory (default follows .mandu/client conventions)
minify boolean Minify output
sourcemap boolean Emit sourcemaps
splitting boolean Code-splitting

Example:

build: { outDir: "dist", minify: true, sourcemap: true, splitting: true }

guard

guard?: {
  preset?: "mandu" | "fsd" | "clean" | "hexagonal" | "atomic" | "cqrs";
  srcDir?: string;
  exclude?: string[];
  realtime?: boolean;
  rules?: Record<string, GuardRuleSeverity>;
  contractRequired?: GuardRuleSeverity;
};
Field Type Notes
preset enum "mandu" | "fsd" | "clean" | "hexagonal" | "atomic" | "cqrs"
srcDir string Source root to scan
exclude string[] Path globs to skip
realtime boolean Enable realtime Guard watcher
rules Record<string, GuardRuleSeverity> Per-rule severity override
contractRequired GuardRuleSeverity Severity when a route lacks a contract

GuardRuleSeverity = "error" | "warn" | "warning" | "off".

Example:

guard: {
  preset: "fsd",
  srcDir: "src",
  exclude: ["**/*.test.ts"],
  realtime: true,
  rules: { "no-cross-feature-import": "error" },
  contractRequired: "warn",
}

A guard-only file is also valid:

// .mandu/guard.json
{
  "preset": "mandu",
  "rules": { "no-cross-feature-import": "error" }
}

The loader will wrap this into { guard: ... } automatically.

fsRoutes

fsRoutes?: {
  routesDir?: string;
  extensions?: string[];
  exclude?: string[];
  islandSuffix?: string;
};
Field Type Notes
routesDir string Routes root (defaults to app/)
extensions string[] File extensions considered routable
exclude string[] Path globs to skip
islandSuffix string Interactive-component suffix (conventionally .island.tsx)

Example:

fsRoutes: {
  routesDir: "app",
  extensions: [".tsx", ".ts"],
  islandSuffix: ".island.tsx",
}

seo

seo?: {
  enabled?: boolean;
  defaultTitle?: string;
  titleTemplate?: string;
};
Field Type Notes
enabled boolean Turn SEO defaults on/off
defaultTitle string Fallback <title>
titleTemplate string Template applied to per-route titles

Example:

seo: {
  enabled: true,
  defaultTitle: "Mandu",
  titleTemplate: "%s — Mandu",
}

plugins

Type: ManduPlugin[] (from @mandujs/core).

plugins: [myPlugin(), anotherPlugin({ verbose: true })]

hooks

Type: Partial<ManduHooks> (from @mandujs/core).

hooks: {
  // populate the hook handlers you need; others are left to Mandu defaults
}

Full example

// mandu.config.ts
import type { ManduConfig } from "@mandujs/core";

export default {
  server: {
    port: 3333,
    cors: { origin: ["https://example.com"], credentials: true },
    streaming: true,
    rateLimit: { windowMs: 60_000, max: 120, headers: true },
  },
  dev: { hmr: true, observability: true, watchDirs: ["content"] },
  build: { outDir: "dist", minify: true, sourcemap: true, splitting: true },
  guard: {
    preset: "mandu",
    srcDir: "src",
    exclude: ["**/*.test.ts"],
    realtime: true,
    rules: { "no-cross-feature-import": "error" },
    contractRequired: "warn",
  },
  fsRoutes: {
    routesDir: "app",
    extensions: [".tsx", ".ts"],
    islandSuffix: ".island.tsx",
  },
  seo: { enabled: true, defaultTitle: "Mandu", titleTemplate: "%s — Mandu" },
  plugins: [],
  hooks: {},
} satisfies ManduConfig;

🤖 Agent Prompt

🤖 Agent Prompt — mandu.config.ts Reference
Apply the guidance from the Mandu docs page at https://mandujs.com/docs/reference/config to my project.

Summary of the page:
ManduConfig schema from @mandujs/core/src/config/mandu.ts. Use this to know which fields are allowed in mandu.config.ts, what types they take, and which files Mandu will load. Do not invent fields not listed here.

Required invariants — must hold after your changes:
- All top-level fields on ManduConfig are optional — a config file may be empty
- Mandu searches, in order: mandu.config.ts, mandu.config.js, mandu.config.json, .mandu/guard.json
- .mandu/guard.json may contain either the full config or a guard-only object; in the guard-only case it is coerced to { guard: ... }
- When a config file exists but fails to load or parse, loadManduConfig returns {} — invalid configs do not throw
- guard.preset must be one of: mandu, fsd, clean, hexagonal, atomic, cqrs
- GuardRuleSeverity is limited to: error, warn, warning, off

Then:
1. Make the change in my codebase consistent with the page.
2. Run `bun run guard` and `bun run check` to verify nothing
   in src/ or app/ breaks Mandu's invariants.
3. Show me the diff and any guard violations.

For Agents

AI hint

ManduConfig schema from @mandujs/core/src/config/mandu.ts. Use this to know which fields are allowed in mandu.config.ts, what types they take, and which files Mandu will load. Do not invent fields not listed here.

Invariants
  • All top-level fields on ManduConfig are optional — a config file may be empty
  • Mandu searches, in order: mandu.config.ts, mandu.config.js, mandu.config.json, .mandu/guard.json
  • .mandu/guard.json may contain either the full config or a guard-only object; in the guard-only case it is coerced to { guard: ... }
  • When a config file exists but fails to load or parse, loadManduConfig returns {} — invalid configs do not throw
  • guard.preset must be one of: mandu, fsd, clean, hexagonal, atomic, cqrs
  • GuardRuleSeverity is limited to: error, warn, warning, off
Guard scope
reference