Skip to content

⏱️ Outpost Timeout

Prevent outposts from hanging navigation indefinitely.

⚙️ Configuration

typescript
const citadel = createNavigationCitadel(router, {
  defaultTimeout: 5000, // 5 seconds for all outposts
  onTimeout: (name, ctx) => ({ name: 'error' }), // redirect on timeout
});

📊 How Outpost Timeout is Determined

📋 Priority Table

outpost.timeoutdefaultTimeoutResult
undefinedundefinedNo timeout
undefined50005 seconds
10000500010 seconds (override)
05000No timeout (disabled)

💡 Examples

No Timeout (Default)

typescript
const citadel = createNavigationCitadel(router);
// defaultTimeout = undefined — no timeouts

citadel.deployOutpost({
  name: 'slow-api',
  handler: async () => {
    await fetch('/api/slow'); // can hang forever
    return verdicts.ALLOW;
  },
});

WARNING

If API doesn't respond — navigation hangs indefinitely.

Global Timeout

typescript
const citadel = createNavigationCitadel(router, {
  defaultTimeout: 5000, // 5 seconds for all outposts
});

citadel.deployOutpost({
  name: 'slow-api',
  handler: async () => {
    await fetch('/api/slow'); // takes 10 seconds
    return verdicts.ALLOW;
  },
});

Result after 5 seconds: navigation blocked (BLOCK).

Global Timeout with Custom Handler

typescript
const citadel = createNavigationCitadel(router, {
  defaultTimeout: 5000,
  onTimeout: (outpostName, ctx) => {
    console.log(`${outpostName} timed out, redirecting to /error`);
    return { name: 'error' }; // redirect instead of BLOCK
  },
});

Result after 5 seconds: redirect to /error.

Per-Outpost Override

typescript
const citadel = createNavigationCitadel(router, {
  defaultTimeout: 5000, // global 5 seconds
});

// auth — uses defaultTimeout (5s)
citadel.deployOutpost({
  name: 'auth',
  handler: async ({ verdicts }) => {
    await checkAuth(); // must complete within 5 seconds
    return verdicts.ALLOW;
  },
});

// data-loader — needs more time (30s)
citadel.deployOutpost({
  name: 'data-loader',
  timeout: 30000, // override: 30 seconds
  handler: async ({ verdicts }) => {
    await loadHeavyData(); // can take up to 30 seconds
    return verdicts.ALLOW;
  },
});

// analytics — no timeout (runs in afterEach, shouldn't block)
citadel.deployOutpost({
  name: 'analytics',
  timeout: 0, // disabled
  hooks: [NavigationHooks.AFTER_EACH],
  handler: async ({ verdicts }) => {
    await sendAnalytics(); // can take as long as needed
    return verdicts.ALLOW;
  },
});
Diagram Legend
ColorMeaning
🟢Success, ALLOW, continue
🟡Warning, redirect, deduplicate
🔴Error, BLOCK, cancel
🔵Logging (when logger is enabled)
🟣Named debug breakpoint (debug: true)

Released under the MIT License.