Skip to content

Capture

Capture intercepts fetch, XMLHttpRequest, WebSocket, and console.* calls (or, on iOS/Android, native URLProtocol/OkHttp traffic) and normalizes each into a NetworkRequest. Every captured request runs through mock/breakpoint/throttle checks, redaction, and body-size capping before it reaches the ring buffer and any registered sink.

import {
enableFetchInterceptor,
enableXHRInterceptor,
enableWebSocketInterceptor,
ConsoleInterceptor,
} from 'hakka-core'
const disable = enableFetchInterceptor(onRequest, maxBodySize, redactHeaders)
const disableXhr = enableXHRInterceptor(onRequest, maxBodySize, redactHeaders)
const disableWs = enableWebSocketInterceptor(onRequest)
ConsoleInterceptor.enable()
ConsoleInterceptor.disable()

Each enableXInterceptor returns a teardown function. All three are wired up by Hakka.start() — direct use is for custom hosts only.

Body decoding pipeline (BodyDecoder, SPEC §5 row “BodyDecoder”):

import { bodyDecoders, decodeSse, decodeProtobuf, decodeGrpcWeb } from 'hakka-core'
bodyDecoders.register({ id: 'my-decoder', decode: (body, contentType, contentEncoding) => string | null })
bodyDecoders.decode(body, contentType, contentEncoding) // first non-null decoder wins; passthrough is last resort

Built-in decoders (registered on module load): gzip, deflate, protobuf, sse, grpc-web.

Verified against DEFAULT_CONFIG (packages/hakka-core/src/model/types.ts):

Key Default Description
mode 'auto' 'auto' | 'native' | 'js' | 'store' — see Core overview.
maxRequests 500 Ring-buffer capacity.
maxBodySize 262144 (256 KB) Per-request/response body capture cap; bodies over this are truncated.
maxBufferBytes 16 * 1024 * 1024 (16 MiB) Byte ceiling for retained bodies across the whole buffer — see Retention.
redactHeaders ['authorization', 'proxy-authorization', 'cookie', 'set-cookie'] See Redaction.
ignoreHosts / ignorePatterns [] Skip capture entirely for matching hosts/URLs (wildcard-capable).
enabled true Kill switch.

Note: limitBodySize’s own default (DEFAULT_MAX_BODY_SIZE, utils/bodySizeLimit.ts) is 100 KB — a standalone utility default, distinct from the engine’s maxBodySize config default of 256 KB that the interceptors actually use.

Capability RN iOS Android Web
Native capture
JS capture
BodyDecoder

RN’s 'auto' mode prefers the native HakkaMonitor TurboModule and falls back to JS monkey-patches; Web and Next.js are JS-capture only. iOS/Android BodyDecoder is a Swift/Kotlin port matched against core’s decoders.test.ts fixtures (SPEC §5 footnote 10).

SPEC §5 row “Cache-status tags” (footnote 15), hakka-node only:

Capability RN iOS Android Web
Cache-status tags

NetworkRequest.cacheStatus is never set by the core fetch/XHR interceptors themselves — it’s populated after capture by hakka-node’s server capture (next/serverCapture.ts), which reads a framework cache-status response header (Next.js x-nextjs-cache wins when present, else Vercel x-vercel-cache). hakka-browser renders the tag (RequestRow.tsx’s .hakka-rt-tag hakka-cache-<status> pill, Detail.tsx’s Cache KVRow); no RN/iOS/Android interceptor reads or surfaces a framework cache-status header today.

Captured requests are normalized to ContractRecord (kind 'network.request') via networkRequestToRecord() before reaching a RecordSink — see Core overview for the shape.

  • packages/hakka-core/src/capture/fetchBasics.test.ts
  • packages/hakka-core/src/capture/fetchSafety.test.ts
  • packages/hakka-core/src/capture/maxBodySize.test.ts
  • packages/hakka-core/src/capture/xhr.test.ts
  • packages/hakka-core/src/capture/rewrite.test.ts
  • packages/hakka-core/src/capture/readCappedBody.test.ts
  • packages/hakka-core/src/capture/sseCapture.test.ts
  • packages/hakka-core/src/capture/console.test.ts
  • packages/hakka-core/src/capture/bodyCapture.test.ts
  • packages/hakka-core/src/engine/decoders.test.ts
  • The fetch interceptor fails open: any internal error (redaction, mock matching, header copies) sends the app’s original request through uncaptured rather than break it.
  • application/wasm responses are never cloned for capture (breaks WebAssembly.instantiateStreaming) — recorded headers-only.
  • SSE (text/event-stream) bodies are captured incrementally up to maxBodySize, then the record stops growing; the stream itself is never cut off for the app.
  • No production cohort/sampling gate is part of the public config surface today — an internal shouldCapture hook exists on the fetch interceptor but is not wired to HakkaConfig.