Documentation
IntegrationAdvanced Usage

Advanced Usage

beforeSend Hook Patterns

Basic Pattern Matching

const noiseGateFilter = async (event: Sentry.Event): Promise<Sentry.Event | null> => {
  const patterns = await getPatterns();
  const message = event.exception?.values?.[0]?.value || "";

  for (const p of patterns) {
    if (p.type === "regex") {
      if (new RegExp(p.pattern).test(message)) return null;
    } else {
      if (message.includes(p.pattern)) return null;
    }
  }

  return event;
};

Match Against Stack Traces

const noiseGateFilter = async (event: Sentry.Event): Promise<Sentry.Event | null> => {
  const patterns = await getPatterns();

  // Check error message
  const message = event.exception?.values?.[0]?.value || "";

  // Check stack frames
  const frames = event.exception?.values?.[0]?.stacktrace?.frames || [];
  const stackText = frames.map(f => `${f.filename}:${f.function}`).join("\n");

  const textToCheck = `${message}\n${stackText}`;

  for (const p of patterns) {
    const matches = p.type === "regex"
      ? new RegExp(p.pattern).test(textToCheck)
      : textToCheck.includes(p.pattern);

    if (matches) return null;
  }

  return event;
};

Combine with Existing beforeSend

const existingBeforeSend = (event: Sentry.Event) => {
  // Your existing logic
  event.tags = { ...event.tags, app_version: "1.0.0" };
  return event;
};

Sentry.init({
  dsn: "...",
  beforeSend: async (event) => {
    // Run NoiseGate filter first
    const filtered = await noiseGateFilter(event);
    if (!filtered) return null;

    // Then run existing logic
    return existingBeforeSend(filtered);
  },
});

Caching Strategies

class PatternCache {
  private patterns: Pattern[] | null = null;
  private expiry = 0;
  private pending: Promise<Pattern[]> | null = null;

  async get(): Promise<Pattern[]> {
    // Return cached if valid
    if (this.patterns && Date.now() < this.expiry) {
      return this.patterns;
    }

    // Deduplicate concurrent requests
    if (this.pending) {
      return this.pending;
    }

    this.pending = this.fetch();
    const patterns = await this.pending;
    this.pending = null;
    return patterns;
  }

  private async fetch(): Promise<Pattern[]> {
    const response = await fetch("https://api.noisegate.dev/v1/filters", {
      headers: { "Authorization": `Bearer ${API_KEY}` }
    });
    const data = await response.json();

    this.patterns = data.patterns;
    this.expiry = Date.now() + 5 * 60 * 1000; // 5 minutes

    return this.patterns;
  }
}

const cache = new PatternCache();

localStorage Cache (Browser)

const CACHE_KEY = "noisegate_patterns";
const CACHE_TTL = 5 * 60 * 1000;

async function getPatterns(): Promise<Pattern[]> {
  // Check localStorage
  const cached = localStorage.getItem(CACHE_KEY);
  if (cached) {
    const { patterns, expiry } = JSON.parse(cached);
    if (Date.now() < expiry) {
      return patterns;
    }
  }

  // Fetch fresh
  const response = await fetch("https://api.noisegate.dev/v1/filters", {
    headers: { "Authorization": `Bearer ${API_KEY}` }
  });
  const data = await response.json();

  // Store in localStorage
  localStorage.setItem(CACHE_KEY, JSON.stringify({
    patterns: data.patterns,
    expiry: Date.now() + CACHE_TTL
  }));

  return data.patterns;
}

Background Refresh

class PatternCache {
  private patterns: Pattern[] = [];
  private refreshInterval: number;

  constructor() {
    this.refresh(); // Initial fetch
    this.refreshInterval = setInterval(() => this.refresh(), 5 * 60 * 1000);
  }

  private async refresh() {
    try {
      const response = await fetch("https://api.noisegate.dev/v1/filters", {
        headers: { "Authorization": `Bearer ${API_KEY}` }
      });
      const data = await response.json();
      this.patterns = data.patterns;
    } catch {
      // Keep existing patterns on error
    }
  }

  get(): Pattern[] {
    return this.patterns; // Always synchronous
  }

  destroy() {
    clearInterval(this.refreshInterval);
  }
}

Fallback Handling

Graceful Degradation

const noiseGateFilter = async (event: Sentry.Event): Promise<Sentry.Event | null> => {
  try {
    const patterns = await getPatterns();
    // ... filtering logic
    return shouldFilter ? null : event;
  } catch (error) {
    // Always send event if NoiseGate fails
    console.warn("NoiseGate unavailable, sending unfiltered event");
    return event;
  }
};

Fallback Patterns

const FALLBACK_PATTERNS: Pattern[] = [
  { pattern: "ResizeObserver loop", type: "string" },
  { pattern: "Script error", type: "string" },
  { pattern: "ChunkLoadError", type: "string" },
];

async function getPatterns(): Promise<Pattern[]> {
  try {
    const response = await fetch("https://api.noisegate.dev/v1/filters", {
      headers: { "Authorization": `Bearer ${API_KEY}` },
      signal: AbortSignal.timeout(3000), // 3s timeout
    });

    if (!response.ok) throw new Error("API error");

    const data = await response.json();
    return data.patterns;
  } catch {
    return FALLBACK_PATTERNS;
  }
}

Timeout with Cached Fallback

async function getPatterns(): Promise<Pattern[]> {
  const cached = getCachedPatterns(); // From localStorage or memory

  try {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 2000);

    const response = await fetch("https://api.noisegate.dev/v1/filters", {
      headers: { "Authorization": `Bearer ${API_KEY}` },
      signal: controller.signal,
    });

    clearTimeout(timeout);

    const data = await response.json();
    setCachedPatterns(data.patterns);
    return data.patterns;
  } catch {
    // Return stale cache if available, empty array otherwise
    return cached || [];
  }
}

Performance Tips

1. Prefetch on App Load

// Fetch patterns before any errors occur
if (typeof window !== "undefined") {
  getPatterns(); // Fire and forget
}

2. Use Synchronous Fallback

Sentry.init({
  dsn: "...",
  beforeSend: (event) => {
    // Synchronous check against cached patterns
    const patterns = cache.get(); // Returns immediately
    if (patterns.length === 0) return event; // No patterns yet

    const message = event.exception?.values?.[0]?.value || "";
    const shouldFilter = patterns.some(p =>
      p.type === "regex"
        ? new RegExp(p.pattern).test(message)
        : message.includes(p.pattern)
    );

    return shouldFilter ? null : event;
  },
});

3. Batch Pattern Compilation

class CompiledPatterns {
  private regexPatterns: RegExp[] = [];
  private stringPatterns: string[] = [];

  load(patterns: Pattern[]) {
    this.regexPatterns = patterns
      .filter(p => p.type === "regex")
      .map(p => new RegExp(p.pattern));

    this.stringPatterns = patterns
      .filter(p => p.type === "string")
      .map(p => p.pattern);
  }

  matches(text: string): boolean {
    return (
      this.stringPatterns.some(p => text.includes(p)) ||
      this.regexPatterns.some(p => p.test(text))
    );
  }
}

Type Definitions

interface Pattern {
  id: string;
  pattern: string;
  type: "string" | "regex";
  category: string;
  confidence: number;
  source: string;
}

interface FiltersResponse {
  version: string;
  patterns: Pattern[];
}