# Building

Compile your bot for production with seedcord build, then start the output on your host with a health server it can check.

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.
