
# Configuration 

## The Config File

All application configuration lives in a **single file**: `config.mjs`.

To configure an app:

1. Copy the shipped template: `cp config.default.mjs config.mjs`
2. Edit `config.mjs`.
3. `index.html` imports `config.mjs` and passes it to `apiMain.init()`. Backends and
   islands declared in the config are registered automatically — you do not add
   wiring to `index.html`.

`config.default.mjs` is the pristine reference and lists every option with a comment. It is a **plain
object** — no factory, no merge, no hidden defaults. What you read is exactly what runs.

## Three opt-in layers

| You need | Config | Downloads |
|----------|--------|-----------|
| A static website | `routes`, `components`, `appMainComponent` | vue, vue-router, vue3-sfc-loader, core |
| Plain HTTP calls to an API | nothing: `api.http.getJson('/api/x')` | same |
| Named servers with `exec`/`exec2` (HTTP or WebSocket) | `backends: {...}` | + rxjs (HTTP) / + rxjs, xstate, msgpack (WebSocket) |
| A server that serves pages and a node tree | `islands: [...]` | same as backends |

### A static website

```js
// config.mjs
import { defaultVueComponents } from 'ConfigUtils'

export default {
    appName: "My Site",
    appMainComponent: "/core/PageMain.vue",
    frameworkBase: "",                       // "" = same origin, or a CDN root
    routes: [
        { path: '/', location: '/pages/Home.vue' },
        { path: '/about', location: '/pages/About.vue' },
        { path: '/:pathMatch(.*)*', name: '404', location: '/core/Page404.vue' },
    ],
    components: defaultVueComponents(""),
}
```

There is no `instanceId`, no `root` tree and no `/default/` in URLs. Every page is declared once, in `routes`. The sidebar only exists when `showSidebar: true`, when `nav` is set or when islands are mounted.

### Plain HTTP calls

```js
import {api} from 'GlobalStore'
const resp = await api.http.getJson('/api/status')    // {isOk, result, status}, never throws
if (resp.isOk) console.log(resp.result)
```

### Backends: the same calls over HTTP or WebSocket

```js
backends: {
    api:    { transport: 'http', url: '/api' },      // server implements POST /exec and /exec2
    server: { transport: 'ws',   url: 'https://host:1995',
              auth: { username: 'anonymous', password: 'anonymous' } },
},
```

```js
const be = api.backend('server')            // or 'api': the code does not change
const resp = await be.exec('/user', ['userContext'])      // {isOk, result}
be.exec2Result('/jte/api/prj', args).subscribe(e => console.log(e.type, e.result))
```

See [Island-Interface.md](./Island-Interface.md) for every option and the HTTP wire format.

### Islands: a backend mounted at `/<id>`

```js
islands: [
    { id: 'admin', kind: 'server',
      backend: { transport: 'ws', url: 'https://host:1995', auth: { username: 'root', password: 'root' } } },
    { id: 'docs2', kind: 'static', backend: 'api', routes: [...], nav: {...} },   // `backend` may name a declared backend
],
```

Pages of an island are served by the island and appear at `/admin/...`. Island ids must not collide with a top level folder (`core`, `components`, `css`, `js_ext`, `docs`, `examples`, `_c0ckp1t`, ...) or an existing route.

### Framework pages (opt-in)

Connections, Cache, Traffic, Notifies, the component showcase and the examples are not part of a site unless you ask:

```js
import { defaultVueComponents, devRoutes, devNav } from 'ConfigUtils'
routes: [ ...myRoutes, ...devRoutes(frameworkBase) ],     // mounted at /_c0ckp1t/...
nav: devNav('/docs'),                                     // optional sidebar tree; the argument adds a
                                                          // Documentation node (use config.docs.path, or omit)
```

`/_c0ckp1t/examples/<name>` runs `examples/<name>/index.html` in an iframe and shows its source files. The examples come from this web server when `frameworkBase` is local and from `https://c0ckp1t.com` when it is a CDN; override with `devRoutes(frameworkBase, 'https://my.site')`. See [examples/README.md](https://github.com/c0ckp1t/c0ckp1t-webroot/blob/main/examples/README.md).

### Local or CDN: `frameworkBase()`

`frameworkBase()` (ConfigUtils) returns where `core/` was loaded from: `""` when the import map points at this web server, `https://cdn.jsdelivr.net/npm/c0ckp1t@<version>` when it points at the CDN. A config that uses it works unchanged with `index.html` and with the generated `index-cdn.html`:

```js
import { defaultVueComponents, frameworkBase as findFrameworkBase } from 'ConfigUtils'
const frameworkBase = findFrameworkBase()
```

`index-cdn.html` files are never edited by hand: `make cdn` generates them from `index.html` with the `version` of `package.json`.

### Documentation (opt-in)

Markdown pages rendered by `core/pages/Documentation.vue`. Leave `docs` out (or `null`) and nothing of the feature is downloaded. Every key is optional:

```js
docs: {
    path: '/docs',               // URL prefix: '/docs', '/documentation', '/d', ...
    source: '/docs',             // folder the .md files are read from, or an absolute URL (CDN)
    instanceId: null,            // island to read from; null = this web server (plain fetch)
    homepage: '/Introduction.md',
    toc: false,                  // table of contents next to the document
    showNav: true,               // Home / Back / Reload header
    showTrail: false,            // current document path above the document
    allowWrite: true,            // HTML / Markdown editor toggles
    allowReload: true,           // Reload button
},
```

- `/docs` redirects to `/docs/Introduction.md`; `/docs/guides/a.md` reads `<source>/guides/a.md`.
- With `instanceId` the files come from that island (`kind: 'static'` over HTTP, or `kind: 'server'` over WebSocket). The page waits for the island to be registered.
- Saving from the editor only works on a server island with `accessLevel <= 500`; on other sources the editor is a local preview.
- A `path` that is already used by `config.routes` is rejected at startup.
- The old top level keys `showDocNav`, `showDocReload`, `showDocTrail`, `allowDocWrite`, `allowDocReload` are ignored (a warning tells you).

`config.docs` is the only switch: `devRoutes()` has no documentation route. To show the framework's own documentation use ``docs: {path: '/docs', source: `${frameworkBase}/docs`}``. For a sidebar entry pass the path to `devNav(docs.path)` (or add a node to your own `nav`); for the top bar add `{name: 'Documentation', path: '/docs'}` to `navItems`.

Node applications embed the page directly with the same options as props (`routerPath`, `remotePathMapping`, `instanceId`, `homepage`, `enableToc`, `showNav`, `showTrail`, `allowWrite`, `allowReload`):

```html
<Documentation homepage="/Introduction.md" remotePathMapping="/v3/actions/cli/docs"
               :routerPath="`/${instanceId}/cli/docs`" :instanceId="instanceId" />
```

## Required Modules

These core modules are required for the app to run, so they need to be included:

* C0ckp1t Core Modules (required):
    * Logging.mjs
    * Global.mjs (import name `GlobalStore`)
    * NotifyUtils.mjs
    * VueUtils.mjs
    * JsUtils.mjs
    * ConfigUtils.mjs
    * Site.mjs, EventBus.mjs, net/Http.mjs (small, no dependencies; imported by relative path)
    * PageMain.vue
* External Libraries (required):
    * vue3-sfc-loader (1,794.1 KB)
    * vue (128.6 KB)
    * vue-router (29.6 KB)
    * mitt (0.3 KB)
* Loaded on demand (only when `backends` / `islands` are declared, or an SFC imports them):
    * `core/net/*Backend.mjs`, `core/islands/*`, rxjs, xstate, msgpack, `WsUtils`, `WsProtocol`, `WsLogUtils`, `Stash`
    * `core/pages/documentation/*` (only when `docs` is set or `Documentation.vue` is used)

Everything else is optional and lazy loaded. Modules inside `core/` import each other by relative path, so a new core module does not need a new entry in your `index.html` import map.

**Note:** The vue3-sfc-loader will be optional soon. Right now the use cases are for compiling in browser and the 2MB is not a problem.

## index.html Configuration

Anything inside the `index.html` will be loaded immediately. If you want something more lightweight don't include dependencies you won't use.

For example if you are not using fontawesome brand icons don't include the brands.min.css file:

* https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@7.2.0/css/brands.min.css


You can mix and match files including the core modules with your own implementation, this is common if you want to fine-tune the application. For example, you can replace the `Logging` implementation with your own custom one by replacing the `Logging` location in the `importmap`. It is common is to download and modify it and then serve it from your own server instead of the CDN. For example in your `index.html`:

* "Logging": "https://cdn.jsdelivr.net/npm/c0ckp1t@latest/core/Logging.mjs",

becomes

* "Logging": "./Logging.mjs"

Now you can modify the `Logging.mjs` file as you want. Only modules listed in the import map can be replaced this way; modules that `core/` imports by relative path (`net/*`, `islands/*`, `Site.mjs`) are loaded from the framework's own location.


## The Application 

The main page that determines the layout and contains the `<RouterView/>` element is [PageMain.vue](https://github.com/c0ckp1t/c0ckp1t-webroot/blob/main/core/PageMain.vue). You can replace it with your own component if you want to customize the app. There is a configuration filed for that:

`appMainComponent: "https://cdn.jsdelivr.net/npm/c0ckp1t@1.0.14/core/PageMain.vue"`

See [config.default.mjs](https://github.com/c0ckp1t/c0ckp1t-webroot/blob/main/config.default.mjs) for the complete, commented list of every configuration option. The framework helpers (`defaultVueComponents`, `devRoutes`, `devNav`, `validateAppConfig`, `validateBackendConfig`, `validateIslandConfig`, `findHostnamePortProtocol`, `frameworkBase`) live in [core/ConfigUtils.mjs](https://github.com/c0ckp1t/c0ckp1t-webroot/blob/main/core/ConfigUtils.mjs).


