Documentation

Configuration

Configure your app through frame-master.config.ts. Export a FrameMasterConfig object to control the HTTP server and plugins.

Overview

import type { FrameMasterConfig } from "frame-master/server/types";
 
const config: FrameMasterConfig = {
  HTTPServer: {
    port: 3000,
    hostname: "localhost",
  },
  plugins: [],
  pluginsOptions: {
    disableHttpServerOptionsConflictWarning: false,
  },
};
 
export default config;

Tip: Use the FrameMasterConfig type for autocomplete and validation.

HTTPServer Configuration

Frame-Master wraps Bun.serve(). Most Bun.serve options are available.

const config: FrameMasterConfig = {
  HTTPServer: {
    // see Bun.serve options
  },
  plugins: [],
};

Docs: https://bun.com/reference/bun/serve

Plugins Configuration

import ReactSSRPlugin from "frame-master-plugin-react-ssr/plugin";
import TailwindPlugin from "frame-master-plugin-tailwind";
import SessionPlugin from "frame-master-plugin-session/plugin";
 
const config: FrameMasterConfig = {
  HTTPServer: { port: 3000 },
  plugins: [
    ReactSSRPlugin({
      pathToShellFile: ".frame-master/shell.tsx",
      pathToClientWrapper: ".frame-master/client-wrapper.tsx",
      pathToBuildDir: ".frame-master/build",
    }),
    TailwindPlugin({
      inputFile: "static/index.css",
      outputFile: "static/tailwind.css",
    }),
    SessionPlugin({ framework: "react", strategy: "cookie" }),
  ],
};

Plugin order matters unless a plugin sets priority. Check dependencies in plugin docs.

Skipping Runtime Requirement Checks

Set pluginsOptions.skipRequirementsCheck to bypass requirement checks during application startup. This is useful for temporary compatibility testing, but requirements are normally enforced to prevent unsupported plugin combinations.

const config: FrameMasterConfig = {
  HTTPServer: { port: 3000 },
  plugins: [/* plugins */],
  pluginsOptions: {
    skipRequirementsCheck: true,
  },
};

This setting only affects runtime initialization. frame-master plugin validate continues to report unmet plugin requirements.

Environment-Specific Configuration

const isDev = process.env.NODE_ENV === "development";
const isProd = process.env.NODE_ENV === "production";
 
const config: FrameMasterConfig = {
  HTTPServer: {
    port: parseInt(process.env.PORT || "3000"),
    hostname: process.env.HOST || "localhost",
    development: isDev,
    ...(isProd && {
      tls: {
        key: Bun.file(process.env.TLS_KEY_PATH!),
        cert: Bun.file(process.env.TLS_CERT_PATH!),
      },
    }),
  },
  plugins: [...(isDev ? [DevToolsPlugin()] : [])],
};

Best practices:

  • Separate .env.development and .env.production
  • Provide defaults with fallbacks
  • Validate required env vars at startup
  • Document variables in .env.example

Validation Snippet

const requiredEnv = ["DATABASE_URL", "API_KEY"];
for (const key of requiredEnv) {
  if (!process.env[key]) throw new Error(`Missing env var: ${key}`);
}
 
const port = parseInt(process.env.PORT || "3000");
if (!Number.isInteger(port) || port < 1 || port > 65535) {
  throw new Error(`Invalid port: ${process.env.PORT}`);
}
 
const config: FrameMasterConfig = { HTTPServer: { port }, plugins: [] };
export default config;

Complete Example

import type { FrameMasterConfig } from "frame-master/server/types";
import ReactSSRPlugin from "frame-master-plugin-react-ssr/plugin";
import TailwindPlugin from "frame-master-plugin-tailwind";
import SessionPlugin from "frame-master-plugin-session/plugin";
import { readFileSync } from "fs";
 
const isDev = process.env.NODE_ENV === "development";
const isProd = process.env.NODE_ENV === "production";
 
const config: FrameMasterConfig = {
  HTTPServer: {
    port: parseInt(process.env.PORT || "3000"),
    hostname: process.env.HOST || "localhost",
    development: isDev,
    maxRequestBodySize: 1024 * 1024 * 50,
    ...(isProd && {
      tls: {
        key: readFileSync(process.env.TLS_KEY_PATH!),
        cert: readFileSync(process.env.TLS_CERT_PATH!),
      },
    }),
    websocket: {
      maxPayloadLength: 16 * 1024 * 1024,
      idleTimeout: 120,
      perMessageDeflate: true,
    },
    error(error: Error) {
      console.error("[Server Error]", error);
      if (isDev) {
        return new Response(
          JSON.stringify({ error: error.message, stack: error.stack }),
          {
            status: 500,
            headers: { "Content-Type": "application/json" },
          },
        );
      }
      return new Response("Internal Server Error", {
        status: 500,
        headers: { "Content-Type": "text/plain" },
      });
    },
  },
  plugins: [
    ReactSSRPlugin({
      pathToShellFile: ".frame-master/shell.tsx",
      pathToClientWrapper: ".frame-master/client-wrapper.tsx",
      pathToBuildDir: ".frame-master/build",
      pathToPagesDir: "src/pages",
    }),
    TailwindPlugin({
      inputFile: "static/index.css",
      outputFile: "static/tailwind.css",
    }),
    SessionPlugin({ framework: "react", strategy: "cookie" }),
  ],
  pluginsOptions: {
    disableHttpServerOptionsConflictWarning: false,
  },
};
 
export default config;

Best Practices

  • Use environment variables for secrets and environment-specific values
  • Keep the config minimal; add options only when needed
  • Validate config early to fail fast
  • Document custom options for your team

Next Steps