> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lodemc.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Lifecycle

> Load, hot reload, errors, logs, disable, uninstall

## Loading

<Steps>
  <Step title="Read the manifest">
    The app reads `meta.json` and `config.json`. Problems stop here, all reported at once.
  </Step>

  <Step title="Check sdkVersion">
    `sdkVersion` is checked against the app's SDK.
  </Step>

  <Step title="Verify the key">
    The `apiKey` is verified with NMCrate. See [Publishing](/publishing).
  </Step>

  <Step title="Wait for approval">
    If permissions are declared and not yet approved, it waits at **Needs approval**.
  </Step>

  <Step title="Build the module graph">
    The module graph is built from the start file: every import resolved, every file compiled.
  </Step>

  <Step title="Start">
    The start file runs, and whatever it exports as default is [started](/writing-style#the-start-file).
  </Step>
</Steps>

## Hot reload

Save any file and the app rebuilds and swaps your expansion in place.

1. `onUnload` runs.
2. Everything registered through `sdk` is removed: pages, sidebar buttons, injections, editors, creators, item types, settings tabs, listeners, timers, styles and CSS variables.
3. The files that changed, the files importing them, and every file that uses `sdk` run again. Modules that did none of those keep their state.
4. Mounted UI re-renders with the new components.

Some consequences worth knowing:

* Module-level state in a re-run file is reset; state on a class instance goes with it. State saved with `useProjectState` / `useGlobalState`, or written to the project, survives.
* Editing `meta.json` re-validates and re-verifies, so a changed id or permission set takes effect immediately.
* A rebuild that fails leaves the **running** version alone and reports the error, so a typo does not take your expansion down mid-edit.

## Stopping

Disable, uninstall and app shutdown all run the same teardown as a reload, without the re-run. Uninstalling also forgets the permission grant, so reinstalling asks again.

`onUnload` is for what the SDK does not know about: flushing a buffer you kept, closing something you opened. Everything registered through `sdk` is already handled.

## When something throws

**In a component.** Only that spot is replaced, by a card naming the file and line with a Retry button. The rest of the app, and the rest of your expansion, keep running.

**In the start file or `onLoad`.** Nothing it registered is left behind. The Expansions page marks the expansion **Crashed** and shows the message and location.

**In an event listener.** Logged against your expansion; other listeners still run.

Because compiled output keeps your line numbers, the file and line in these messages are the ones you wrote.

## The Expansions page

Project Settings → Expansions is where a user sees all of this:

|                    |                                                                     |
| ------------------ | ------------------------------------------------------------------- |
| Badges             | Untrusted, Needs approval, Disabled, Crashed, Legacy, Source, Error |
| Review permissions | Approve what the expansion asks for                                 |
| Revoke permissions | Take approval back; the expansion stops and asks again              |
| Restart            | Stop and start it, without touching the files                       |
| Disable / Enable   | Keep it installed but off; persists across restarts                 |
| Logs               | Your expansion's own log: `sdk.log.*`, `console.*`, and errors      |
| Uninstall          | Remove it (packaged expansions; a source folder is deleted by hand) |

## Timers and listeners

```ts theme={null}
setInterval(() => poll(), 10_000)          // cleared automatically on unload
const off = sdk.events.on('item:open', fn) // removed automatically; call off() to remove early
```

Timers you create are tracked and cleared for you, which is why a hot reload does not leave two intervals running.
