# Dev tools and errors

Source: https://signy.dev/documentation/1/application/dev-tools/

Log levels, readable messages and the signy-dev tools.

## Log ids

Signy logs and throws with short ids instead of sentences, so the framework stays small. A warning looks like this in the console:

```json
[signy][WARN] view:unsafe-attribute { name: 'href' }
```

The id names the part of Signy and the problem. The object after it holds the details, here the attribute that was left out.

## Turn warnings on

The default log level is `error`, so warnings and info do not show. Raise it while you develop:

```js
import { globalConfig } from 'signy';

globalConfig.configure({ logLevel: 'warn' });
```

The levels, from quiet to loud, are `'error'`, `'warn'`, `'info'`, `'debug'` and `'trace'`. With the global build it is `Signy.globalConfig.configure(...)`.

Some warnings, such as `view:unsafe-attribute` and `view:lazy-outside-suspense`, only exist in dev builds. Production builds still leave the unsafe attribute out, they just do not say so. The npm build is a production build when your bundler sets `process.env.NODE_ENV` to `'production'`, and the `.min.js` global builds are production builds.

## Common ids

| Id | What happened |
| --- | --- |
| `view:unsafe-attribute` | An `on` attribute got a string, or a URL attribute got a `javascript:` URL. It was left out. See [Security](https://signy.dev/documentation/1/application/security/index.html.md). |
| `view:lazy-outside-suspense` | A `lazy()` component rendered outside `Suspense`, so nothing shows while it loads. |
| `reactivity:cleanup-unowned` | `onCleanup()` ran outside a component, effect or root. The cleanup never runs. |
| `reactivity:cleanup-error` | A cleanup function threw. The other cleanups still ran. |
| `router:redirect-loop` | Redirects and guards kept redirecting past 10 steps. `push()` rejects with it. |
| `router:loader-failed` | A route loader threw. The page shows without data. |
| `app:invalid-state` | An app was mounted twice. Create a new one. |
| `installable:already-installed` | The same plugin was passed to `use()` twice. |
| `component:app-required` | A component was mounted by hand. Mount the root with `application(App).mount()`. |

## signy-dev

`signy-dev` is not on npm yet. It will be installed with `npm install signy-dev --save-dev`.

Importing `signy-dev` registers a sentence for each of the 44 log ids. Logs and errors then carry it after the id, and `getLogMessage(id)` returns it.

```js
import 'signy-dev';
import { getLogMessage } from 'signy';

getLogMessage('view:lazy-outside-suspense');
// 'Lazy component rendered outside Suspense boundary. ...'
```

`enableDevTools()` also installs a profiler, an inspector and an event logger, and creates the global hook `window.__SIGNY_DEVTOOLS_HOOK__` that browser extensions connect to.

```js
import { enableDevTools } from 'signy-dev';

const devTools = enableDevTools({
  profiler: true,     // component renders, signal writes, effect runs
  inspector: true,    // mounted components and apps, signals, effects
  eventLogger: false, // a log of every reactive event
  globalHook: true,
});

console.log(devTools.profiler?.report());
console.log(devTools.inspector?.getComponents());

devTools.cleanup();
```

Load it only in development, so production bundles leave it out:

```js
if (import.meta.env.DEV) {
  import('signy-dev').then(({ enableDevTools }) => enableDevTools());
}
```

The tools hold signals and effects weakly, so tracking never keeps one alive. A second `enableDevTools()` call replaces the first, so hot reload can run the setup again.

## Names

Give names to what you want to find in the tools: `new Signal(0, { name: 'count' })`, `computed(fn, { name: 'total' })`, `store(value, { name: 'cart' })`, and `static displayName = 'Cart'` on a component, which keeps its name through a minifier.
