Build and dev/bionicjs.config.ts

bionicjs.config.ts

The file, the plugins, and the runtime exports it drives.

The file

Every BionicJS project has a bionicjs.config.ts at its root. This is the single configuration surface for the framework. There is no vite.config.ts, no nitro.config.ts, and no tooling configuration the developer needs to manage.

bionicjs.config.ts
1import { defineConfig } from "@bionicjs/core";
2import { betterAuth } from "@bionicjs/better-auth";
3import { drizzle } from "@bionicjs/drizzle";
4import { anthropic } from "@bionicjs/anthropic";
5import { celery } from "@bionicjs/celery";
6
7export default defineConfig({
8  auth: betterAuth({ emailAndPassword: { enabled: true } }),
9  database: drizzle({ provider: "sqlite", url: "file:./db.sqlite" }),
10  ai: anthropic({ model: "claude-sonnet-4-5" }),
11  jobs: celery({ broker: "redis://localhost:6379" }),
12});

Each top-level key - auth, database, ai, jobs - is a plugin. The provider packages ( @bionicjs/better-auth, @bionicjs/drizzle, @bionicjs/anthropic, @bionicjs/celery, and the rest) export the factory you call with your options. The framework reads the file at startup and wires each plugin into the dev server, the build, and the runtime exports.

Plugins

Every first-party plugin is a thin add-on package built on one of four factories exported from bionicjs - one per capability:

CapabilityFactoryKeyPackages
authcreateAuthPluginauthbetter-auth · clerk · firebase · supabase · workos
databasecreateDatabasePlugindatabaseprisma · drizzle · kysely · sql
aicreateAiPluginaianthropic · openai · google
jobscreateJobsPluginjobscelery · rq · dramatiq

The provider package stays thin and the shared logic lives in the factory. The factory takes options (the provider, model, path, and feature flags) plus optional setup and generateExports hooks.

A thin plugin package
1import { createAuthPlugin } from "@bionicjs/core";
2
3export function betterAuth(options: BetterAuthConfig) {
4  return createAuthPlugin({
5    name: "better-auth",
6    options,
7    path: "/api/auth/[...auth]",
8    generateExports: () =>
9      `export const auth = {
10  getSession: async () => null,
11  requireAuth: async () => null,
12};`,
13  });
14}

defineConfig

defineConfig is a typed helper exported from bionicjs. It returns its input unchanged - its value is TypeScript autocompletion and type safety, not runtime transformation.

Implementation
1export interface BionicJSPlugin<TName extends string = string, TExports = any> {
2  name: TName;
3  setup: (app: any) => void | Promise<void>;
4  generateExports?: () => string | Promise<string>;
5}
6
7export type BionicJSConfig = {
8  [key: string]: BionicJSPlugin | any;
9};
10
11export function defineConfig(config: BionicJSConfig): BionicJSConfig {
12  return config;
13}

This is the same pattern used by Vite's own defineConfig, Nitro's defineNitroConfig, and virtually every modern TypeScript tool. The developer gets IDE help; the framework reads the file at startup. The config object is deliberately permissive - any key may hold a plugin or a plain value (a port, a flag), so additions don't require a config type change.

Runtime exports

Plugins exist to make capabilities importable from real code. The client-side RPC helper is api, exported from bionicjs:

app/page.tsx
1import { api } from "@bionicjs/core";
2
3const result = await api.users.sayHello({ name: "bionicjs" });

The server-side handles - auth, the database client, the AI connection, and the jobs client - come from a single import, @bionicjs/core/server. Their implementations are generated at dev/build time from the generateExports return value of whichever plugins you composed:

server/anything.ts
1import { auth, db, ai, jobs } from "@bionicjs/core/server";
2
3const session = await auth.getSession();
4const row = await db.query.users.findFirst();
5const reply = await ai.chat("hello");
6await jobs.enqueue("send-email", { to: "you@bionicjs.dev" });
ⓘ
@bionicjs/core/server is a generated boundary: import it in your server-side code, never from the client. Compose only the plugins you use, and only those exports exist.