BitaKit
How-to guides

Configuration

Give plugins defaults and apply explicit host overrides.

Owner: @bitakit/core. Configuration is an immutable snapshot resolved before services are created. Use it for developer defaults and deployment composition. Mutable user preferences belong to Core Settings; App Preferences renders their optional UI.

Define defaults

Each file under configs/ defines the subtree named by its path:

// src/plugins/todos/configs/limits.ts  →  app.config.todos.limits
import { defineConfig } from '@bitakit/core';

export default defineConfig({
  defaults: { maxItems: 100, pageSize: 20 },
});
// src/plugins/todos/configs/actions/preview.ts  →  config.action inside todos.preview
import { defineConfig } from '@bitakit/core';

export default defineConfig({ defaults: { pageSize: 10 } });

The plugin folder supplies the module ID todos. A plain object export works too. Do not define the same leaf twice; that is an error, not an override.

Read the correct scope

// src/plugins/todos/actions/preview.ts
import { defineAction } from '@bitakit/core';

export default defineAction({
  title: 'Preview todos',
  execute({ services, config }) {
    const pageSize = config.action.get('pageSize', 10);
    return services.todos.list.state.get().todos.slice(0, pageSize);
  },
});

config.plugin.get('limits.pageSize', 20) reads the plugin's whole tree; config.action.get('pageSize', 10) reads actions.preview.pageSize for this action. There is no implicit fallback between the two scopes. Service factories receive the same config object with the plugin scope.

A fallback supplies a value and TypeScript inference, not runtime validation. Dot paths address nested values; segment arrays address literal keys containing dots.

Override from the host

// bitakit.config.ts
import { defineBitaKit } from '@bitakit/discovery';

export default defineBitaKit({
  configOverrides: {
    todos: { limits: { maxItems: 50 }, actions: { preview: { pageSize: 50 } } },
  },
});

The same configOverrides field exists on a manual BitaKitRuntimeConfig. Objects merge recursively, arrays replace arrays, undefined retains defaults and null is explicit. Unknown module IDs are rejected. Overrides for disabled plugins are removed with their contributions.

Configure an installed package

Some installed packages read their options from a config file of your app. Shell reads src/configs/shell.ts; Quick Search reads src/configs/quick-search.ts, for example to disable plugin results by default:

// src/configs/quick-search.ts
import { defineConfig } from '@bitakit/core';
import type { QuickSearchOptions } from '@bitakit/quick-search';

export default defineConfig({
  defaults: { results: { plugins: false } } satisfies QuickSearchOptions,
});

Quick Search source switches are initial defaults; saved user source preferences take precedence. See Quick Search for plugin-owned sources.

Without the file, the package uses its defaults. A package names the file it reads with bitakit.configuration in its package.json, and discovery passes the resolved values to the package's plugin when the app starts. The package README lists its options.

Optional schemas

defineConfig({ schema, defaults }) accepts a schema with synchronous parse(unknown) (zod, valibot, Standard Schema). Core calls it on the merged values before services start; invalid configuration is a startup error attributed to the plugin. A root schema (configs/index.ts) owns the complete config and cannot be combined with other config files; subtrees may carry their own schemas.

Reading the snapshot

With generated types, app.config.todos.limits.maxItems reads the resolved module snapshot, and usePluginConfig('todos', 'limits.maxItems', 100) does the same in React. Low-level callers use runtime.configuration.plugin('todos'). Plain objects and arrays are frozen. There is no configuration write API; replacing the runtime composition supplies a new snapshot.

Configuration shipped in client contributions is client-visible. Keep secrets in explicit server-only composition.

On this page