# Building

Compile your bot for production with seedcord build. Covers the steps it runs, starting the output, its config keys, build failures, the runtime key, and the health server your host calls.

Node can't run your `src` folder in production as it is. Your handlers use decorators, and node's built-in TypeScript support stops with a parser error on decorator syntax. Running `tsc` yourself compiles the decorators, but node then fails on any extensionless import or path alias that `tsc` copied into the output as written.

`seedcord build` compiles the decorators and rewrites those imports, turning `src` into a `dist` folder that plain node can run. It doesn't take flags, so everything it needs comes from `seedcord.config.ts`.

```sh
pnpm run build
```

It starts by checking that your `entry` file exists, and a missing one throws `CliEntryNotFound`. After that check it runs these steps in order.

1. Your project's own `tsc` compiles the project. seedcord always passes `--inlineSources`, and adds `--sourceMap` unless your tsconfig already sets `inlineSourceMap`. Both give you readable stack traces in production.
2. `tsc-alias` rewrites every import specifier in the output to a full relative path ending in `.js`, since TypeScript emits each one exactly as you wrote it.
3. seedcord writes a bootstrap file that holds a single import of the compiled entry.

seedcord expects the compiled `entry` under `outDir` at the same path it has under `root`, so `src/index.ts` becomes `dist/index.js`. The bootstrap file always has one fixed path, so your start script can point at it however you arrange your source.

## Running what it built

```sh
pnpm run start
```

The scaffold sets that script to run `dist/index.mjs` with node's `--enable-source-maps` flag. Without `--enable-source-maps`, a stack trace points at lines in the compiled `.js` files, and the flag maps them back to your `.ts` lines.

## The build keys

Your CLI config holds these under `build`, all of them optional.

```ts title="seedcord.config.ts"
import { defineConfig } from 'seedcord';

export default defineConfig({
    root: './src',
    instance: './bot.ts',
    entry: './index.ts',
    build: {
        outDir: './dist',
        tsconfig: './tsconfig.build.json',
        bootstrap: 'index.mjs'
    }
});
```

`outDir` defaults to `./dist` beside your config, and `tsconfig` resolves from the same folder.

A relative `bootstrap` resolves against `outDir`, which turns `index.mjs` into `dist/index.mjs`. That's also the default.

With a separate `tsconfig.build.json` you exclude tests and scripts from the build while your editor still type-checks them. Without `tsconfig`, seedcord looks for `tsconfig.build.json` first, then `tsconfig.json`. When neither exists, or the path you set doesn't, it throws `CliBuildTsconfigNotFound`.

## When the build fails

A type error stops the build. seedcord throws `CliBuildFailed` with the compiler's own output, shortened to its first 24,000 characters. seedcord runs the `tsc` from your project's own `typescript`, so your build uses the compiler version you pinned, and a project missing that dependency gets `CliBuildFailed` too.

Once `tsc` finishes, seedcord checks that the compiled entry exists at the path above. Sometimes the compiler reports success while that file is still missing. You get `CliBuildFailed` again, with the path seedcord checked.

Two things usually cause this. Your tsconfig may exclude the entry file, or its `rootDir` may place the output somewhere other than the path under `outDir` that matches `root`.

## Where your bot runs

You set `runtime` on your bot's own config in `src/bot.ts`, beside `bot` and `subscribers`. It says which kind of deployment your bot is built for. Leaving it out means `'server'`.

```ts title="src/bot.ts"
import { resolve } from 'node:path';

import { Seedcord } from '@seedcord/http';

export const seedcord = new Seedcord({
    bot: {
        interactions: {
            path: resolve(import.meta.dirname, './handlers')
        },
        commands: { path: null }
    },
    subscribers: { path: null },
    runtime: 'edge'
});
```

An edge bot has to write `runtime: 'edge'` itself.

> **Gateway and http differ**
>
> A gateway bot only accepts `'server'`, since its websocket needs a process that stays up. An http bot takes `'server'` for a long-running node process, or `'edge'` for a bundled isolate like a Cloudflare Worker.

`seedcord build` doesn't read `runtime` yet, so it compiles both kinds of bot the same way.

## The health server

Most hosting platforms call an http endpoint to check that your process is still alive. A gateway bot opens a small server that answers `/health` on port 6967 once it reaches the Ready phase, and it does this under `seedcord dev` too.

```ts title="src/bot.ts"
import { resolve } from 'node:path';

import { Seedcord } from '@seedcord/gateway';
import { GatewayIntentBits } from 'discord.js';

export const seedcord = new Seedcord({
    bot: {
        clientOptions: { intents: [GatewayIntentBits.Guilds] },
        ...paths
    },
    subscribers: { path: null },
    healthCheck: { port: 8080, path: '/healthz' }
});
```

This bot answers `/healthz` on port 8080. All three keys are optional. Without `port` the server uses 6967, and without `path` it answers `/health`. Without `host` it listens on every network interface. `healthCheck: false` turns the server off.

Change `port` or `path` when your host expects its health check somewhere else, and set `host` when the server should only listen inside a container network. Turn it off when nothing calls it, or when port 6967 is already taken on the machine.

> **Gateway only**
>
> An http bot's config doesn't accept `healthCheck`, because an http bot already answers on a port of its own.
