> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/mercuryworkshop/scramjet/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration

> Understanding Scramjet's configuration system, flags, and runtime options

Scramjet's behavior is controlled through a comprehensive configuration system. This page explains all configuration options, feature flags, and how to modify settings at runtime.

## Configuration structure

The `ScramjetConfig` interface defines all available options:

```typescript theme={null}
interface ScramjetConfig {
  prefix: string;                                    // URL prefix for routing
  globals: { /* ... */ };                           // Runtime global function names
  files: { wasm: string; all: string; sync: string; }; // Script file paths
  flags: ScramjetFlags;                             // Feature flags
  siteFlags: Record<string, Partial<ScramjetFlags>>; // Per-site flag overrides
  codec: { encode: string; decode: string; };       // URL codec functions
}
```

### Initialization config

When creating a `ScramjetController`, you provide a `ScramjetInitConfig` which has slightly different types:

```typescript theme={null}
interface ScramjetInitConfig extends Omit<ScramjetConfig, "codec" | "flags"> {
  flags: Partial<ScramjetFlags>;  // Flags are optional
  codec: {
    encode: (url: string) => string;  // Actual functions, not strings
    decode: (url: string) => string;
  };
}
```

<Info>
  The codec functions are serialized to strings when stored in config. This allows the Service Worker (which runs in a separate context) to deserialize and execute them.
</Info>

## Core options

### prefix

The URL path prefix that identifies proxied requests.

```typescript theme={null}
const scramjet = new ScramjetController({
  prefix: "/scramjet/",
});
```

* Must start and end with `/`
* Should be unique to avoid conflicts
* Used by the Service Worker to route requests

**Default:** `/scramjet/`

### files

Paths to Scramjet's runtime files:

```typescript theme={null}
files: {
  wasm: "/scramjet.wasm.wasm",  // WASM rewriter module
  all: "/scramjet.all.js",       // Main client bundle
  sync: "/scramjet.sync.js",     // Synchronous XHR support
}
```

These files are:

* Injected into HTML documents
* Served by your web server
* Generated during the build process

<Warning>
  Make sure these files are publicly accessible at the specified paths.
</Warning>

### codec

The URL encoding/decoding functions:

```typescript theme={null}
codec: {
  encode: (url: string) => string;
  decode: (url: string) => string;
}
```

**Default codec:**

```typescript theme={null}
codec: {
  encode: (url: string) => encodeURIComponent(url),
  decode: (url: string) => decodeURIComponent(url),
}
```

**Custom codec example:**

```typescript theme={null}
codec: {
  encode: (url: string) => btoa(url),
  decode: (url: string) => atob(url),
}
```

<Tip>
  Use base64 encoding for cleaner-looking URLs, but be aware it can make debugging harder.
</Tip>

### globals

Names of global runtime functions injected into proxied pages:

```typescript theme={null}
globals: {
  wrapfn: "$scramjet$wrap",
  wrappropertybase: "$scramjet__",
  wrappropertyfn: "$scramjet$prop",
  cleanrestfn: "$scramjet$clean",
  importfn: "$scramjet$import",
  rewritefn: "$scramjet$rewrite",
  metafn: "$scramjet$meta",
  setrealmfn: "$scramjet$setrealm",
  pushsourcemapfn: "$scramjet$pushsourcemap",
  trysetfn: "$scramjet$tryset",
  templocid: "$scramjet$temploc",
  tempunusedid: "$scramjet$tempunused",
}
```

<Warning>
  Don't change these unless you're modifying Scramjet's internals. The JS rewriter expects these exact names.
</Warning>

## Feature flags

Flags control specific behaviors and features:

```typescript theme={null}
type ScramjetFlags = {
  serviceworkers: boolean;        // Enable nested Service Worker support
  syncxhr: boolean;               // Enable synchronous XMLHttpRequest rewriting
  strictRewrites: boolean;        // Enforce strict URL rewriting
  rewriterLogs: boolean;          // Log rewriter timing information
  captureErrors: boolean;         // Capture and log errors from rewriting
  cleanErrors: boolean;           // Clean Scramjet internals from stack traces
  scramitize: boolean;            // Enable experimental scramitize feature
  sourcemaps: boolean;            // Generate source maps for rewritten JS
  destructureRewrites: boolean;   // Use destructuring in JS rewrites
  interceptDownloads: boolean;    // Intercept file downloads
  allowInvalidJs: boolean;        // Allow malformed JavaScript
  allowFailedIntercepts: boolean; // Continue if API interception fails
};
```

### Flag details

<AccordionGroup>
  <Accordion title="serviceworkers" defaultOpen>
    **Type:** `boolean` | **Default:** `false`

    Enables support for nested Service Workers within proxied pages. When enabled, sites can register their own Service Workers that will be emulated.

    ```typescript theme={null}
    flags: {
      serviceworkers: true
    }
    ```

    <Warning>
      This is experimental and may not work with all sites.
    </Warning>
  </Accordion>

  <Accordion title="syncxhr">
    **Type:** `boolean` | **Default:** `false`

    Enables synchronous XMLHttpRequest rewriting. When enabled, synchronous XHR calls are intercepted and proxied.

    ```typescript theme={null}
    flags: {
      syncxhr: true
    }
    ```

    <Note>
      Synchronous XHR is deprecated in browsers but some legacy sites still use it.
    </Note>
  </Accordion>

  <Accordion title="strictRewrites">
    **Type:** `boolean` | **Default:** `true`

    When enabled, enforces strict URL rewriting. Invalid URLs will cause errors instead of being silently ignored.

    <Tip>
      Disable this for compatibility with sites that have malformed URLs.
    </Tip>
  </Accordion>

  <Accordion title="rewriterLogs">
    **Type:** `boolean` | **Default:** `false`

    Logs timing information for rewriting operations to the console:

    ```
    [Scramjet] HTML rewrite: 45ms
    [Scramjet] oxc rewrite for "app.js": 12ms
    ```

    <Tip>
      Enable this during development to identify performance bottlenecks.
    </Tip>
  </Accordion>

  <Accordion title="captureErrors">
    **Type:** `boolean` | **Default:** `true`

    Captures errors that occur during rewriting and logs them. Prevents errors in Scramjet from breaking the proxied page.
  </Accordion>

  <Accordion title="cleanErrors">
    **Type:** `boolean` | **Default:** `false`

    Removes Scramjet internals from error stack traces shown to the proxied page. Makes debugging easier for end users but harder for Scramjet developers.
  </Accordion>

  <Accordion title="scramitize">
    **Type:** `boolean` | **Default:** `false`

    Experimental feature for enhanced obfuscation.

    <Warning>
      This is experimental and may break compatibility.
    </Warning>
  </Accordion>

  <Accordion title="sourcemaps">
    **Type:** `boolean` | **Default:** `true`

    Generates source maps for rewritten JavaScript. Allows browser DevTools to show original code.

    ```typescript theme={null}
    flags: {
      sourcemaps: true  // You can debug rewritten code
    }
    ```
  </Accordion>

  <Accordion title="destructureRewrites">
    **Type:** `boolean` | **Default:** `false`

    Uses destructuring assignment in rewritten JavaScript for potential performance improvements.
  </Accordion>

  <Accordion title="interceptDownloads">
    **Type:** `boolean` | **Default:** `false`

    Intercepts file downloads and dispatches them as events instead of triggering browser downloads:

    ```typescript theme={null}
    flags: {
      interceptDownloads: true
    }

    scramjet.addEventListener("download", (event) => {
      console.log("Downloading:", event.download.filename);
      // Handle download programmatically
    });
    ```
  </Accordion>

  <Accordion title="allowInvalidJs">
    **Type:** `boolean` | **Default:** `true`

    Allows malformed JavaScript to be served without rewriting. If the JS rewriter fails to parse code, the original code is returned.

    <Warning>
      Disabling this may break sites with syntax errors in their JavaScript.
    </Warning>
  </Accordion>

  <Accordion title="allowFailedIntercepts">
    **Type:** `boolean` | **Default:** `true`

    If API interception fails (e.g., due to browser quirks), continue instead of throwing an error.
  </Accordion>
</AccordionGroup>

## Site-specific flags

You can override flags for specific sites using regular expressions:

```typescript theme={null}
const scramjet = new ScramjetController({
  prefix: "/scramjet/",
  flags: {
    interceptDownloads: false,  // Global default
  },
  siteFlags: {
    "example\\.com": {
      interceptDownloads: true,  // Override for example.com
    },
    "cdn\\..*": {
      sourcemaps: false,  // Disable sourcemaps for CDN resources
    },
  },
});
```

The `flagEnabled()` helper checks site-specific overrides:

```typescript theme={null}
import { flagEnabled } from "@/shared";

if (flagEnabled("interceptDownloads", url)) {
  // Handle download interception
}
```

<Tip>
  Site flags are checked in order, and the first match wins. Be careful with overlapping patterns.
</Tip>

## Runtime configuration

### Modifying config

You can update configuration at runtime using `modifyConfig()`:

```typescript theme={null}
await scramjet.modifyConfig({
  flags: {
    rewriterLogs: true,
  },
});
```

This:

1. Updates the in-memory config
2. Saves to IndexedDB
3. Notifies the Service Worker via `postMessage`
4. Reloads codec functions

<Warning>
  Config changes only affect new requests. Existing pages won't be affected until they reload.
</Warning>

### Reading config

Access the current config via the global `config` variable:

```typescript theme={null}
import { config } from "@/shared";

console.log("Current prefix:", config.prefix);
console.log("Sourcemaps enabled?", config.flags.sourcemaps);
```

### Loading config in Service Worker

The Service Worker loads config from IndexedDB:

```typescript theme={null}
const scramjet = new ScramjetServiceWorker();

// Config is loaded automatically on first fetch
self.addEventListener("fetch", (event) => {
  if (scramjet.route(event)) {
    event.respondWith(scramjet.fetch(event));
  }
});
```

Manual loading:

```typescript theme={null}
await scramjet.loadConfig();
```

## Codec configuration

### Loading codecs

Codec functions are serialized to strings in config and deserialized at runtime:

```typescript theme={null}
export function loadCodecs() {
  codecEncode = new Function(`return ${config.codec.encode}`)() as any;
  codecDecode = new Function(`return ${config.codec.decode}`)() as any;
}
```

<Info>
  This allows the codec to work in the Service Worker context, which doesn't have access to the original function objects.
</Info>

### Using codecs

Use the global codec functions for encoding/decoding:

```typescript theme={null}
import { codecEncode, codecDecode } from "@/shared";

const encoded = codecEncode("https://example.com");
const decoded = codecDecode(encoded);
```

## Default configuration

Here's the complete default config:

```typescript theme={null}
const defaultConfig: ScramjetInitConfig = {
  prefix: "/scramjet/",
  globals: {
    wrapfn: "$scramjet$wrap",
    wrappropertybase: "$scramjet__",
    wrappropertyfn: "$scramjet$prop",
    cleanrestfn: "$scramjet$clean",
    importfn: "$scramjet$import",
    rewritefn: "$scramjet$rewrite",
    metafn: "$scramjet$meta",
    setrealmfn: "$scramjet$setrealm",
    pushsourcemapfn: "$scramjet$pushsourcemap",
    trysetfn: "$scramjet$tryset",
    templocid: "$scramjet$temploc",
    tempunusedid: "$scramjet$tempunused",
  },
  files: {
    wasm: "/scramjet.wasm.wasm",
    all: "/scramjet.all.js",
    sync: "/scramjet.sync.js",
  },
  flags: {
    serviceworkers: false,
    syncxhr: false,
    strictRewrites: true,
    rewriterLogs: false,
    captureErrors: true,
    cleanErrors: false,
    scramitize: false,
    sourcemaps: true,
    destructureRewrites: false,
    interceptDownloads: false,
    allowInvalidJs: true,
    allowFailedIntercepts: true,
  },
  siteFlags: {},
  codec: {
    encode: (url: string) => encodeURIComponent(url),
    decode: (url: string) => decodeURIComponent(url),
  },
};
```

## Configuration persistence

Configuration is stored in IndexedDB under the `$scramjet` database:

```typescript theme={null}
const db = await openDB<ScramjetDB>("$scramjet", 1, {
  upgrade(db) {
    if (!db.objectStoreNames.contains("config")) {
      db.createObjectStore("config");
    }
    // ... other stores
  },
});

await db.put("config", config, "config");
```

This ensures:

* Config persists across page reloads
* Service Worker and clients use the same config
* Runtime changes are preserved

## Best practices

<Tip>
  Start with default config and only change what you need. Most flags are optimized for compatibility.
</Tip>

<Warning>
  Don't modify `globals` unless you're changing Scramjet's internals. The rewriter depends on specific names.
</Warning>

<Tip>
  Use site-specific flags to handle edge cases without affecting all sites:

  ```typescript theme={null}
  siteFlags: {
    "problematic-site\\.com": {
      strictRewrites: false,
      allowInvalidJs: true,
    },
  }
  ```
</Tip>

## Debugging config issues

Enable logging to see config-related information:

```typescript theme={null}
scramjet.modifyConfig({
  flags: {
    rewriterLogs: true,
    captureErrors: true,
  },
});
```

Check the current config in DevTools:

```javascript theme={null}
// In the console
import("@/shared").then(m => console.log(m.config));
```

## Next steps

<CardGroup cols={2}>
  <Card title="Architecture" icon="diagram-project" href="/concepts/architecture">
    Understand how configuration flows through components
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/controller">
    See the full ScramjetController API
  </Card>
</CardGroup>
