appdoctor-rn 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,6 +10,10 @@ npm install appdoctor-rn
10
10
 
11
11
  Peer dependencies: `react`, `react-native`. Optional: `@react-navigation/native` for stack/tab navigation listeners.
12
12
 
13
+ ## Package
14
+
15
+ - npm: [appdoctor-rn](https://www.npmjs.com/package/appdoctor-rn)
16
+
13
17
  ## Quick start
14
18
 
15
19
  1. Wrap your app with `AppDoctorProvider` and pass transports (for example `createConsoleTransport()`).
package/dist/index.cjs CHANGED
@@ -236,6 +236,7 @@ function createConsoleTransport(options = {}) {
236
236
  const label = options.label ?? "AppDoctor";
237
237
  const slowScreenThresholdMs = options.slowScreenThresholdMs ?? 1e3;
238
238
  const slowApiThresholdMs = options.slowApiThresholdMs ?? 800;
239
+ const format = options.format ?? "pretty";
239
240
  function toHint(event) {
240
241
  if (event.name === "screen_load" && event.phase === "ready" && event.durationMs >= slowScreenThresholdMs) {
241
242
  return `Slow screen "${event.screen}" (${event.durationMs}ms). Check expensive effects and repeated renders.`;
@@ -245,6 +246,19 @@ function createConsoleTransport(options = {}) {
245
246
  }
246
247
  return void 0;
247
248
  }
249
+ function toPrettyLine(event) {
250
+ if (event.name === "screen_load") {
251
+ return `screen ${event.screen} phase=${event.phase} duration=${event.durationMs}ms`;
252
+ }
253
+ if (event.name === "api_request") {
254
+ const status = event.status ?? "n/a";
255
+ return `api ${event.method} ${event.url} status=${status} duration=${event.durationMs}ms success=${event.success}`;
256
+ }
257
+ if (event.name === "render_event") {
258
+ return `render ${event.component} count=${event.renderCount}`;
259
+ }
260
+ return `sdk_error ${event.message}`;
261
+ }
248
262
  return {
249
263
  send(events) {
250
264
  if (events.length === 0) return;
@@ -253,8 +267,13 @@ function createConsoleTransport(options = {}) {
253
267
  if (hint) {
254
268
  console.warn(`[${label}] ${hint}`);
255
269
  }
270
+ if (format === "pretty") {
271
+ console.log(`[${label}] ${toPrettyLine(event)}`);
272
+ }
273
+ }
274
+ if (format === "raw") {
275
+ console.log(`[${label}]`, events);
256
276
  }
257
- console.log(`[${label}]`, events);
258
277
  }
259
278
  };
260
279
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/types.ts","../src/core/sampling.ts","../src/core/client.ts","../src/transports/console-transport.ts","../src/transports/http-transport.ts","../src/navigation/route-name.ts","../src/navigation/create-navigation-listener.ts","../src/network/axios-instrumentation.ts","../src/utils/track-api.ts","../src/provider/context.ts","../src/provider/AppDoctorProvider.tsx","../src/hooks/useAppDoctor.ts","../src/hooks/useTrackScreen.ts","../src/hooks/useTrackRender.ts"],"names":["createContext","useRef","useEffect","useContext"],"mappings":"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","file":"index.cjs","sourcesContent":["export type AppDoctorEventName =\n | \"screen_load\"\n | \"api_request\"\n | \"render_event\"\n | \"sdk_error\";\n\nexport interface EventContext {\n route?: string;\n component?: string;\n platform?: string;\n appVersion?: string;\n metadata?: Record<string, string | number | boolean>;\n}\n\nexport interface AppDoctorEventBase {\n name: AppDoctorEventName;\n /** Unix ms */\n timestamp: number;\n sessionId: string;\n tags?: Record<string, string>;\n context?: EventContext;\n}\n\nexport interface ScreenLoadEvent extends AppDoctorEventBase {\n name: \"screen_load\";\n screen: string;\n phase: \"start\" | \"ready\";\n /** Time from navigation intent to ready signal (ms), if known */\n durationMs: number;\n}\n\nexport interface NetworkRequestEvent extends AppDoctorEventBase {\n name: \"api_request\";\n method: string;\n url: string;\n status?: number;\n durationMs: number;\n success: boolean;\n errorMessage?: string;\n}\n\nexport interface RenderCountEvent extends AppDoctorEventBase {\n name: \"render_event\";\n component: string;\n renderCount: number;\n}\n\nexport interface SdkErrorEvent extends AppDoctorEventBase {\n name: \"sdk_error\";\n message: string;\n stack?: string;\n errorContext?: string;\n}\n\nexport type AppDoctorEvent =\n | ScreenLoadEvent\n | NetworkRequestEvent\n | RenderCountEvent\n | SdkErrorEvent;\n\n/** Payload accepted by `AppDoctorClient.emit` (union; avoids `Omit` pitfalls on unions). */\nexport type AppDoctorEmitInput =\n | Omit<ScreenLoadEvent, \"timestamp\" | \"sessionId\">\n | Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\">\n | Omit<RenderCountEvent, \"timestamp\" | \"sessionId\">\n | Omit<SdkErrorEvent, \"timestamp\" | \"sessionId\">;\n\nexport interface Transport {\n send(events: readonly AppDoctorEvent[]): void | Promise<void>;\n flush?: () => void | Promise<void>;\n shutdown?: () => void | Promise<void>;\n}\n\nexport interface AppDoctorConfig {\n enabled?: boolean;\n /** Shortcut to disable all internal work while preserving integration calls */\n noop?: boolean;\n /** 0–1, default 1 */\n sampleRate?: number;\n flushIntervalMs?: number;\n maxQueueSize?: number;\n transports?: Transport[];\n tags?: Record<string, string>;\n /** Add context to every emitted event */\n context?: EventContext;\n /** When true, wraps global fetch (restore on shutdown) */\n instrumentFetch?: boolean;\n /**\n * Redacts/sanitizes network event fields before emission.\n * Returning partial values overrides the computed payload.\n */\n redactNetworkEvent?: (\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ) => Partial<Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">>;\n}\n\nexport const DEFAULT_FLUSH_INTERVAL_MS = 2000;\nexport const DEFAULT_MAX_QUEUE_SIZE = 200;\n","export function shouldSample(rate: number): boolean {\n if (rate >= 1) return true;\n if (rate <= 0) return false;\n return Math.random() < rate;\n}\n","import type {\n AppDoctorConfig,\n AppDoctorEmitInput,\n AppDoctorEvent,\n NetworkRequestEvent,\n SdkErrorEvent,\n Transport,\n} from \"./types.js\";\nimport {\n DEFAULT_FLUSH_INTERVAL_MS,\n DEFAULT_MAX_QUEUE_SIZE,\n} from \"./types.js\";\nimport { shouldSample } from \"./sampling.js\";\n\nfunction now(): number {\n return Date.now();\n}\n\nfunction newSessionId(): string {\n return `${now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nexport class AppDoctorClient {\n readonly sessionId: string;\n private readonly config: Required<\n Pick<\n AppDoctorConfig,\n | \"enabled\"\n | \"noop\"\n | \"sampleRate\"\n | \"flushIntervalMs\"\n | \"maxQueueSize\"\n | \"instrumentFetch\"\n >\n > &\n Pick<AppDoctorConfig, \"tags\" | \"context\" | \"redactNetworkEvent\">;\n private readonly transports: Transport[];\n private queue: AppDoctorEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | undefined;\n private fetchPatched = false;\n private originalFetch: typeof fetch | undefined;\n\n constructor(config: AppDoctorConfig = {}) {\n this.sessionId = newSessionId();\n this.config = {\n enabled: config.enabled ?? true,\n noop: config.noop ?? false,\n sampleRate: config.sampleRate ?? 1,\n flushIntervalMs: config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS,\n maxQueueSize: config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,\n instrumentFetch: config.instrumentFetch ?? true,\n tags: config.tags,\n context: config.context,\n redactNetworkEvent: config.redactNetworkEvent,\n };\n this.transports = [...(config.transports ?? [])];\n if (this.config.enabled) {\n this.startFlushTimer();\n if (this.config.instrumentFetch && typeof globalThis.fetch === \"function\") {\n this.patchFetch();\n }\n }\n }\n\n updateConfig(partial: Partial<AppDoctorConfig>): void {\n if (partial.enabled !== undefined) {\n (this.config as { enabled: boolean }).enabled = partial.enabled;\n }\n if (partial.noop !== undefined) {\n (this.config as { noop: boolean }).noop = partial.noop;\n }\n if (partial.sampleRate !== undefined) {\n (this.config as { sampleRate: number }).sampleRate = partial.sampleRate;\n }\n if (partial.flushIntervalMs !== undefined) {\n (this.config as { flushIntervalMs: number }).flushIntervalMs =\n partial.flushIntervalMs;\n this.restartFlushTimer();\n }\n if (partial.maxQueueSize !== undefined) {\n (this.config as { maxQueueSize: number }).maxQueueSize =\n partial.maxQueueSize;\n }\n if (partial.tags !== undefined) {\n this.config.tags = partial.tags;\n }\n if (partial.context !== undefined) {\n this.config.context = partial.context;\n }\n if (partial.instrumentFetch !== undefined) {\n const next = partial.instrumentFetch;\n (this.config as { instrumentFetch: boolean }).instrumentFetch = next;\n if (next && !this.fetchPatched) this.patchFetch();\n if (!next && this.fetchPatched) this.restoreFetch();\n }\n if (partial.transports !== undefined) {\n this.transports.length = 0;\n this.transports.push(...partial.transports);\n }\n if (partial.redactNetworkEvent !== undefined) {\n this.config.redactNetworkEvent = partial.redactNetworkEvent;\n }\n }\n\n addTransport(transport: Transport): void {\n this.transports.push(transport);\n }\n\n emit(event: AppDoctorEmitInput): void {\n if (!this.config.enabled) return;\n if (this.config.noop) return;\n if (!shouldSample(this.config.sampleRate)) return;\n\n const { tags: eventTags, ...payload } = event;\n const tags =\n this.config.tags || eventTags\n ? { ...this.config.tags, ...eventTags }\n : undefined;\n\n const full = {\n ...payload,\n timestamp: now(),\n sessionId: this.sessionId,\n tags,\n context: this.config.context,\n } as AppDoctorEvent;\n\n this.enqueue(full);\n }\n\n private enqueue(event: AppDoctorEvent): void {\n if (this.queue.length >= this.config.maxQueueSize) {\n this.queue.shift();\n }\n this.queue.push(event);\n }\n\n captureError(message: string, context?: string, err?: unknown): void {\n const stack =\n err instanceof Error && typeof err.stack === \"string\"\n ? err.stack\n : undefined;\n const sdkErr: Omit<SdkErrorEvent, \"timestamp\" | \"sessionId\" | \"tags\"> = {\n name: \"sdk_error\",\n message,\n stack,\n errorContext: context,\n };\n this.emit(sdkErr);\n }\n\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n const batch = this.queue.splice(0, this.queue.length);\n await Promise.all(\n this.transports.map(async (t) => {\n try {\n await t.send(batch);\n } catch (e) {\n this.safeInternalError(\"transport.send failed\", e);\n }\n }),\n );\n }\n\n async shutdown(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = undefined;\n }\n this.restoreFetch();\n await this.flush();\n await Promise.all(\n this.transports.map(async (t) => {\n if (!t.shutdown) return;\n try {\n await t.shutdown();\n } catch (e) {\n this.safeInternalError(\"transport.shutdown failed\", e);\n }\n }),\n );\n }\n\n private startFlushTimer(): void {\n this.flushTimer = setInterval(() => {\n void this.flush();\n }, this.config.flushIntervalMs);\n if (\n typeof this.flushTimer === \"object\" &&\n this.flushTimer &&\n \"unref\" in this.flushTimer &&\n typeof this.flushTimer.unref === \"function\"\n ) {\n this.flushTimer.unref();\n }\n }\n\n private restartFlushTimer(): void {\n if (this.flushTimer) clearInterval(this.flushTimer);\n this.startFlushTimer();\n }\n\n private safeInternalError(msg: string, err: unknown): void {\n const dev =\n typeof __DEV__ !== \"undefined\"\n ? __DEV__\n : typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\";\n if (dev) console.warn(`[AppDoctor] ${msg}`, err);\n }\n\n private patchFetch(): void {\n if (this.fetchPatched) return;\n const g = globalThis as { fetch: typeof fetch };\n this.originalFetch = g.fetch.bind(globalThis);\n const originalFetch = this.originalFetch;\n g.fetch = async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n const start = now();\n let method = \"GET\";\n let url = \"\";\n try {\n if (typeof input === \"string\") {\n url = input;\n } else if (input instanceof URL) {\n url = input.href;\n } else if (typeof Request !== \"undefined\" && input instanceof Request) {\n url = input.url;\n method = input.method;\n }\n if (init?.method) method = init.method;\n } catch {\n /* ignore */\n }\n\n const urlForEvent =\n url ||\n (typeof input === \"string\"\n ? input\n : input instanceof URL\n ? input.href\n : typeof Request !== \"undefined\" && input instanceof Request\n ? input.url\n : \"[request]\");\n\n try {\n const res = await originalFetch(input, init);\n const durationMs = now() - start;\n const event = this.applyNetworkRedaction({\n method,\n url: urlForEvent,\n status: res.status,\n durationMs,\n success: true,\n });\n this.emit({\n name: \"api_request\",\n ...event,\n });\n return res;\n } catch (e) {\n const durationMs = now() - start;\n const event = this.applyNetworkRedaction({\n method,\n url: urlForEvent,\n durationMs,\n success: false,\n errorMessage: e instanceof Error ? e.message : String(e),\n });\n this.emit({\n name: \"api_request\",\n ...event,\n });\n throw e;\n }\n };\n this.fetchPatched = true;\n }\n\n private restoreFetch(): void {\n if (!this.fetchPatched || !this.originalFetch) return;\n (globalThis as { fetch: typeof fetch }).fetch = this.originalFetch;\n this.fetchPatched = false;\n this.originalFetch = undefined;\n }\n\n private applyNetworkRedaction(\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ): Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\"> {\n if (!this.config.redactNetworkEvent) return event;\n const redacted = this.config.redactNetworkEvent(event);\n return { ...event, ...redacted };\n }\n}\n","import type { AppDoctorEvent, Transport } from \"../core/types.js\";\n\nexport function createConsoleTransport(\n options: {\n label?: string;\n slowScreenThresholdMs?: number;\n slowApiThresholdMs?: number;\n } = {},\n): Transport {\n const label = options.label ?? \"AppDoctor\";\n const slowScreenThresholdMs = options.slowScreenThresholdMs ?? 1000;\n const slowApiThresholdMs = options.slowApiThresholdMs ?? 800;\n\n function toHint(event: AppDoctorEvent): string | undefined {\n if (\n event.name === \"screen_load\" &&\n event.phase === \"ready\" &&\n event.durationMs >= slowScreenThresholdMs\n ) {\n return `Slow screen \"${event.screen}\" (${event.durationMs}ms). Check expensive effects and repeated renders.`;\n }\n if (event.name === \"api_request\" && event.durationMs >= slowApiThresholdMs) {\n return `Slow API \"${event.method} ${event.url}\" (${event.durationMs}ms). Check server latency, payload size, and retry loops.`;\n }\n return undefined;\n }\n\n return {\n send(events: readonly AppDoctorEvent[]): void {\n if (events.length === 0) return;\n for (const event of events) {\n const hint = toHint(event);\n if (hint) {\n console.warn(`[${label}] ${hint}`);\n }\n }\n console.log(`[${label}]`, events);\n },\n };\n}\n","import type { AppDoctorEvent, Transport } from \"../core/types.js\";\n\nexport interface HttpTransportOptions {\n url: string;\n headers?: Record<string, string>;\n maxRetries?: number;\n /** ms */\n initialBackoffMs?: number;\n getExtraBody?: () => Record<string, unknown>;\n}\n\nasync function sleep(ms: number): Promise<void> {\n await new Promise((r) => setTimeout(r, ms));\n}\n\nexport function createHttpTransport(options: HttpTransportOptions): Transport {\n const maxRetries = options.maxRetries ?? 3;\n const initialBackoffMs = options.initialBackoffMs ?? 500;\n\n return {\n async send(events: readonly AppDoctorEvent[]): Promise<void> {\n if (events.length === 0) return;\n const body = {\n events: [...events],\n ...(options.getExtraBody?.() ?? {}),\n };\n let attempt = 0;\n let backoff = initialBackoffMs;\n for (;;) {\n try {\n const res = await fetch(options.url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n body: JSON.stringify(body),\n });\n if (res.ok) return;\n if (res.status >= 400 && res.status < 500 && res.status !== 429) {\n return;\n }\n } catch {\n /* retry */\n }\n attempt += 1;\n if (attempt > maxRetries) return;\n await sleep(backoff);\n backoff *= 2;\n }\n },\n };\n}\n","/**\n * Best-effort active route name from React Navigation state tree.\n * Avoids a hard dependency on `@react-navigation/native` types at runtime.\n */\nexport function getActiveRouteName(state: unknown): string | undefined {\n if (!state || typeof state !== \"object\") return undefined;\n const s = state as {\n index?: number;\n routes?: Array<{ name?: string; state?: unknown }>;\n };\n if (!Array.isArray(s.routes) || typeof s.index !== \"number\") return undefined;\n const route = s.routes[s.index];\n if (!route || typeof route !== \"object\") return undefined;\n if (route.state !== undefined) return getActiveRouteName(route.state);\n return typeof route.name === \"string\" ? route.name : undefined;\n}\n","import type { AppDoctorClient } from \"../core/client.js\";\nimport { getActiveRouteName } from \"./route-name.js\";\n\nexport interface NavigationTimingState {\n lastScreen?: string;\n}\n\n/**\n * Pass the returned listener to `NavigationContainer` as `onStateChange`.\n * Emits `screen_load` when the active route changes and a ready event after the next microtask\n * (approximates first paint/commit after navigation state updates).\n */\nexport function createNavigationStateListener(client: AppDoctorClient): {\n listener: (state: unknown) => void;\n reset: () => void;\n} {\n const timing: NavigationTimingState = {};\n\n return {\n listener(state: unknown) {\n const screen = getActiveRouteName(state);\n if (!screen || screen === timing.lastScreen) return;\n\n timing.lastScreen = screen;\n const startedAt = Date.now();\n\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"start\",\n durationMs: 0,\n });\n\n queueMicrotask(() => {\n if (timing.lastScreen !== screen) return;\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"ready\",\n durationMs: Date.now() - startedAt,\n });\n });\n },\n reset() {\n timing.lastScreen = undefined;\n },\n };\n}\n","import type { AppDoctorClient } from \"../core/client.js\";\nimport type { NetworkRequestEvent } from \"../core/types.js\";\n\n/** Minimal Axios shape to avoid requiring `axios` as a dependency. */\nexport interface AxiosLike {\n interceptors: {\n request: {\n use: (onFulfilled: (value: unknown) => unknown) => number;\n eject: (id: number) => void;\n };\n response: {\n use: (\n onFulfilled: (value: unknown) => unknown,\n onRejected?: (error: unknown) => unknown,\n ) => number;\n eject: (id: number) => void;\n };\n };\n}\n\nexport interface AxiosInstrumentationOptions {\n redactNetworkEvent?: (\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ) => Partial<Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">>;\n}\n\nfunction getConfig(errorOrResponse: unknown): unknown {\n if (!errorOrResponse || typeof errorOrResponse !== \"object\") return undefined;\n if (\n \"config\" in errorOrResponse &&\n errorOrResponse.config &&\n typeof errorOrResponse.config === \"object\"\n ) {\n return errorOrResponse.config;\n }\n return undefined;\n}\n\nfunction getResponseMeta(response: unknown): {\n status?: number;\n method?: string;\n url?: string;\n} {\n if (!response || typeof response !== \"object\") return {};\n const r = response;\n const status =\n \"status\" in r && typeof r.status === \"number\" ? r.status : undefined;\n const rawConfig = \"config\" in r ? r.config : undefined;\n let method: string | undefined;\n let url: string | undefined;\n if (rawConfig !== null && typeof rawConfig === \"object\") {\n if (\"method\" in rawConfig && typeof rawConfig.method === \"string\") {\n method = rawConfig.method;\n }\n if (\"url\" in rawConfig && typeof rawConfig.url === \"string\") {\n url = rawConfig.url;\n }\n }\n return {\n status,\n method,\n url,\n };\n}\n\n/**\n * Returns an `eject` function to remove interceptors.\n */\nexport function instrumentAxios(\n axios: AxiosLike,\n client: AppDoctorClient,\n options: AxiosInstrumentationOptions = {},\n): () => void {\n function applyRedaction(\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ): Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\"> {\n const redacted = options.redactNetworkEvent?.(event);\n return redacted ? { ...event, ...redacted } : event;\n }\n\n const starts = new WeakMap<object, number>();\n\n const reqId = axios.interceptors.request.use((config) => {\n if (config !== null && typeof config === \"object\") {\n starts.set(config, Date.now());\n }\n return config;\n });\n\n const resId = axios.interceptors.response.use(\n (response) => {\n const cfg = getConfig(response);\n if (cfg !== null && typeof cfg === \"object\") {\n const start = starts.get(cfg);\n if (start !== undefined) {\n const { status, method, url } = getResponseMeta(response);\n const event = applyRedaction({\n method: (method ?? \"GET\").toUpperCase(),\n url: url ?? \"\",\n status,\n durationMs: Date.now() - start,\n success: true,\n });\n client.emit({\n name: \"api_request\",\n ...event,\n });\n starts.delete(cfg);\n }\n }\n return response;\n },\n (error: unknown) => {\n const cfg = getConfig(error);\n if (cfg !== null && typeof cfg === \"object\") {\n const start = starts.get(cfg);\n if (start !== undefined) {\n let method = \"GET\";\n let url = \"\";\n let status: number | undefined;\n let errorMessage = String(error);\n if (error !== null && typeof error === \"object\") {\n if (\"message\" in error && typeof error.message === \"string\") {\n errorMessage = error.message;\n }\n const resp =\n \"response\" in error && error.response !== null &&\n typeof error.response === \"object\"\n ? error.response\n : undefined;\n if (\n resp &&\n \"status\" in resp &&\n typeof resp.status === \"number\"\n ) {\n status = resp.status;\n }\n const errCfg =\n \"config\" in error && error.config !== null &&\n typeof error.config === \"object\"\n ? error.config\n : undefined;\n if (errCfg) {\n if (\"method\" in errCfg && typeof errCfg.method === \"string\") {\n method = errCfg.method.toUpperCase();\n }\n if (\"url\" in errCfg && typeof errCfg.url === \"string\") {\n url = errCfg.url;\n }\n }\n }\n const event = applyRedaction({\n method,\n url,\n status,\n durationMs: Date.now() - start,\n success: false,\n errorMessage,\n });\n client.emit({\n name: \"api_request\",\n ...event,\n });\n starts.delete(cfg);\n }\n }\n const rejectReason =\n error instanceof Error ? error : new Error(String(error));\n return Promise.reject(rejectReason);\n },\n );\n\n return () => {\n axios.interceptors.request.eject(reqId);\n axios.interceptors.response.eject(resId);\n };\n}\n","import type { AppDoctorClient } from \"../core/client.js\";\n\n/**\n * Wraps an async call and emits an `api_request` event with a synthetic URL label.\n */\nexport async function trackApi<T>(\n client: AppDoctorClient,\n label: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const start = Date.now();\n try {\n const result = await fn();\n client.emit({\n name: \"api_request\",\n method: \"TRACK\",\n url: label,\n status: 200,\n durationMs: Date.now() - start,\n success: true,\n });\n return result;\n } catch (e) {\n client.emit({\n name: \"api_request\",\n method: \"TRACK\",\n url: label,\n durationMs: Date.now() - start,\n success: false,\n errorMessage: e instanceof Error ? e.message : String(e),\n });\n throw e;\n }\n}\n","import { createContext } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\n\nexport const AppDoctorContext = createContext<AppDoctorClient | null>(null);\n","import { useEffect, useRef, type ReactElement, type ReactNode } from \"react\";\nimport { AppDoctorClient } from \"../core/client.js\";\nimport type { AppDoctorConfig } from \"../core/types.js\";\nimport { AppDoctorContext } from \"./context.js\";\n\nexport type AppDoctorProviderProps = AppDoctorConfig & {\n children: ReactNode;\n};\n\nexport function AppDoctorProvider({\n children,\n ...config\n}: AppDoctorProviderProps): ReactElement {\n const clientRef = useRef<AppDoctorClient | null>(null);\n if (!clientRef.current) {\n clientRef.current = new AppDoctorClient(config);\n }\n\n const {\n enabled,\n noop,\n sampleRate,\n flushIntervalMs,\n maxQueueSize,\n instrumentFetch,\n tags,\n context,\n transports,\n redactNetworkEvent,\n } = config;\n\n useEffect(() => {\n clientRef.current?.updateConfig({\n enabled,\n noop,\n sampleRate,\n flushIntervalMs,\n maxQueueSize,\n instrumentFetch,\n tags,\n context,\n transports,\n redactNetworkEvent,\n });\n }, [\n enabled,\n noop,\n sampleRate,\n flushIntervalMs,\n maxQueueSize,\n instrumentFetch,\n tags,\n context,\n transports,\n redactNetworkEvent,\n ]);\n\n useEffect(() => {\n return () => {\n void clientRef.current?.shutdown();\n clientRef.current = null;\n };\n }, []);\n\n return (\n <AppDoctorContext.Provider value={clientRef.current}>\n {children}\n </AppDoctorContext.Provider>\n );\n}\n","import { useContext } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\nimport { AppDoctorContext } from \"../provider/context.js\";\n\nexport function useAppDoctor(): AppDoctorClient {\n const client = useContext(AppDoctorContext);\n if (!client) {\n throw new Error(\"useAppDoctor must be used within AppDoctorProvider\");\n }\n return client;\n}\n","import { useContext, useEffect } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\nimport { AppDoctorContext } from \"../provider/context.js\";\n\nexport interface UseTrackScreenOptions {\n client?: AppDoctorClient;\n}\n\n/**\n * Emits `screen_load` lifecycle events on mount and ready.\n */\nexport function useTrackScreen(\n screen: string,\n options: UseTrackScreenOptions = {},\n): void {\n const fromContext = useContext(AppDoctorContext);\n const client = options.client ?? fromContext;\n if (!client) {\n throw new Error(\n \"useTrackScreen requires AppDoctorProvider or options.client\",\n );\n }\n\n useEffect(() => {\n const startedAt = Date.now();\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"start\",\n durationMs: 0,\n });\n queueMicrotask(() => {\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"ready\",\n durationMs: Date.now() - startedAt,\n });\n });\n }, [client, screen]);\n}\n","import { useContext, useEffect, useRef } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\nimport { AppDoctorContext } from \"../provider/context.js\";\n\nexport interface UseTrackRenderOptions {\n client?: AppDoctorClient;\n /** Emit every N renders (default 10) to limit noise */\n every?: number;\n}\n\n/**\n * Emits `render_event` periodically when the component re-renders.\n */\nexport function useTrackRender(\n componentName: string,\n options: UseTrackRenderOptions = {},\n): void {\n const fromContext = useContext(AppDoctorContext);\n const client = options.client ?? fromContext;\n if (!client) {\n throw new Error(\n \"useTrackRender requires AppDoctorProvider or options.client\",\n );\n }\n const every = options.every ?? 10;\n const count = useRef(0);\n\n count.current += 1;\n\n useEffect(() => {\n if (count.current % every !== 0) return;\n client.emit({\n name: \"render_event\",\n component: componentName,\n renderCount: count.current,\n });\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/core/types.ts","../src/core/sampling.ts","../src/core/client.ts","../src/transports/console-transport.ts","../src/transports/http-transport.ts","../src/navigation/route-name.ts","../src/navigation/create-navigation-listener.ts","../src/network/axios-instrumentation.ts","../src/utils/track-api.ts","../src/provider/context.ts","../src/provider/AppDoctorProvider.tsx","../src/hooks/useAppDoctor.ts","../src/hooks/useTrackScreen.ts","../src/hooks/useTrackRender.ts"],"names":["createContext","useRef","useEffect","useContext"],"mappings":"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","file":"index.cjs","sourcesContent":["export type AppDoctorEventName =\n | \"screen_load\"\n | \"api_request\"\n | \"render_event\"\n | \"sdk_error\";\n\nexport interface EventContext {\n route?: string;\n component?: string;\n platform?: string;\n appVersion?: string;\n metadata?: Record<string, string | number | boolean>;\n}\n\nexport interface AppDoctorEventBase {\n name: AppDoctorEventName;\n /** Unix ms */\n timestamp: number;\n sessionId: string;\n tags?: Record<string, string>;\n context?: EventContext;\n}\n\nexport interface ScreenLoadEvent extends AppDoctorEventBase {\n name: \"screen_load\";\n screen: string;\n phase: \"start\" | \"ready\";\n /** Time from navigation intent to ready signal (ms), if known */\n durationMs: number;\n}\n\nexport interface NetworkRequestEvent extends AppDoctorEventBase {\n name: \"api_request\";\n method: string;\n url: string;\n status?: number;\n durationMs: number;\n success: boolean;\n errorMessage?: string;\n}\n\nexport interface RenderCountEvent extends AppDoctorEventBase {\n name: \"render_event\";\n component: string;\n renderCount: number;\n}\n\nexport interface SdkErrorEvent extends AppDoctorEventBase {\n name: \"sdk_error\";\n message: string;\n stack?: string;\n errorContext?: string;\n}\n\nexport type AppDoctorEvent =\n | ScreenLoadEvent\n | NetworkRequestEvent\n | RenderCountEvent\n | SdkErrorEvent;\n\n/** Payload accepted by `AppDoctorClient.emit` (union; avoids `Omit` pitfalls on unions). */\nexport type AppDoctorEmitInput =\n | Omit<ScreenLoadEvent, \"timestamp\" | \"sessionId\">\n | Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\">\n | Omit<RenderCountEvent, \"timestamp\" | \"sessionId\">\n | Omit<SdkErrorEvent, \"timestamp\" | \"sessionId\">;\n\nexport interface Transport {\n send(events: readonly AppDoctorEvent[]): void | Promise<void>;\n flush?: () => void | Promise<void>;\n shutdown?: () => void | Promise<void>;\n}\n\nexport interface AppDoctorConfig {\n enabled?: boolean;\n /** Shortcut to disable all internal work while preserving integration calls */\n noop?: boolean;\n /** 0–1, default 1 */\n sampleRate?: number;\n flushIntervalMs?: number;\n maxQueueSize?: number;\n transports?: Transport[];\n tags?: Record<string, string>;\n /** Add context to every emitted event */\n context?: EventContext;\n /** When true, wraps global fetch (restore on shutdown) */\n instrumentFetch?: boolean;\n /**\n * Redacts/sanitizes network event fields before emission.\n * Returning partial values overrides the computed payload.\n */\n redactNetworkEvent?: (\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ) => Partial<Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">>;\n}\n\nexport const DEFAULT_FLUSH_INTERVAL_MS = 2000;\nexport const DEFAULT_MAX_QUEUE_SIZE = 200;\n","export function shouldSample(rate: number): boolean {\n if (rate >= 1) return true;\n if (rate <= 0) return false;\n return Math.random() < rate;\n}\n","import type {\n AppDoctorConfig,\n AppDoctorEmitInput,\n AppDoctorEvent,\n NetworkRequestEvent,\n SdkErrorEvent,\n Transport,\n} from \"./types.js\";\nimport {\n DEFAULT_FLUSH_INTERVAL_MS,\n DEFAULT_MAX_QUEUE_SIZE,\n} from \"./types.js\";\nimport { shouldSample } from \"./sampling.js\";\n\nfunction now(): number {\n return Date.now();\n}\n\nfunction newSessionId(): string {\n return `${now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nexport class AppDoctorClient {\n readonly sessionId: string;\n private readonly config: Required<\n Pick<\n AppDoctorConfig,\n | \"enabled\"\n | \"noop\"\n | \"sampleRate\"\n | \"flushIntervalMs\"\n | \"maxQueueSize\"\n | \"instrumentFetch\"\n >\n > &\n Pick<AppDoctorConfig, \"tags\" | \"context\" | \"redactNetworkEvent\">;\n private readonly transports: Transport[];\n private queue: AppDoctorEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | undefined;\n private fetchPatched = false;\n private originalFetch: typeof fetch | undefined;\n\n constructor(config: AppDoctorConfig = {}) {\n this.sessionId = newSessionId();\n this.config = {\n enabled: config.enabled ?? true,\n noop: config.noop ?? false,\n sampleRate: config.sampleRate ?? 1,\n flushIntervalMs: config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS,\n maxQueueSize: config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,\n instrumentFetch: config.instrumentFetch ?? true,\n tags: config.tags,\n context: config.context,\n redactNetworkEvent: config.redactNetworkEvent,\n };\n this.transports = [...(config.transports ?? [])];\n if (this.config.enabled) {\n this.startFlushTimer();\n if (this.config.instrumentFetch && typeof globalThis.fetch === \"function\") {\n this.patchFetch();\n }\n }\n }\n\n updateConfig(partial: Partial<AppDoctorConfig>): void {\n if (partial.enabled !== undefined) {\n (this.config as { enabled: boolean }).enabled = partial.enabled;\n }\n if (partial.noop !== undefined) {\n (this.config as { noop: boolean }).noop = partial.noop;\n }\n if (partial.sampleRate !== undefined) {\n (this.config as { sampleRate: number }).sampleRate = partial.sampleRate;\n }\n if (partial.flushIntervalMs !== undefined) {\n (this.config as { flushIntervalMs: number }).flushIntervalMs =\n partial.flushIntervalMs;\n this.restartFlushTimer();\n }\n if (partial.maxQueueSize !== undefined) {\n (this.config as { maxQueueSize: number }).maxQueueSize =\n partial.maxQueueSize;\n }\n if (partial.tags !== undefined) {\n this.config.tags = partial.tags;\n }\n if (partial.context !== undefined) {\n this.config.context = partial.context;\n }\n if (partial.instrumentFetch !== undefined) {\n const next = partial.instrumentFetch;\n (this.config as { instrumentFetch: boolean }).instrumentFetch = next;\n if (next && !this.fetchPatched) this.patchFetch();\n if (!next && this.fetchPatched) this.restoreFetch();\n }\n if (partial.transports !== undefined) {\n this.transports.length = 0;\n this.transports.push(...partial.transports);\n }\n if (partial.redactNetworkEvent !== undefined) {\n this.config.redactNetworkEvent = partial.redactNetworkEvent;\n }\n }\n\n addTransport(transport: Transport): void {\n this.transports.push(transport);\n }\n\n emit(event: AppDoctorEmitInput): void {\n if (!this.config.enabled) return;\n if (this.config.noop) return;\n if (!shouldSample(this.config.sampleRate)) return;\n\n const { tags: eventTags, ...payload } = event;\n const tags =\n this.config.tags || eventTags\n ? { ...this.config.tags, ...eventTags }\n : undefined;\n\n const full = {\n ...payload,\n timestamp: now(),\n sessionId: this.sessionId,\n tags,\n context: this.config.context,\n } as AppDoctorEvent;\n\n this.enqueue(full);\n }\n\n private enqueue(event: AppDoctorEvent): void {\n if (this.queue.length >= this.config.maxQueueSize) {\n this.queue.shift();\n }\n this.queue.push(event);\n }\n\n captureError(message: string, context?: string, err?: unknown): void {\n const stack =\n err instanceof Error && typeof err.stack === \"string\"\n ? err.stack\n : undefined;\n const sdkErr: Omit<SdkErrorEvent, \"timestamp\" | \"sessionId\" | \"tags\"> = {\n name: \"sdk_error\",\n message,\n stack,\n errorContext: context,\n };\n this.emit(sdkErr);\n }\n\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n const batch = this.queue.splice(0, this.queue.length);\n await Promise.all(\n this.transports.map(async (t) => {\n try {\n await t.send(batch);\n } catch (e) {\n this.safeInternalError(\"transport.send failed\", e);\n }\n }),\n );\n }\n\n async shutdown(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = undefined;\n }\n this.restoreFetch();\n await this.flush();\n await Promise.all(\n this.transports.map(async (t) => {\n if (!t.shutdown) return;\n try {\n await t.shutdown();\n } catch (e) {\n this.safeInternalError(\"transport.shutdown failed\", e);\n }\n }),\n );\n }\n\n private startFlushTimer(): void {\n this.flushTimer = setInterval(() => {\n void this.flush();\n }, this.config.flushIntervalMs);\n if (\n typeof this.flushTimer === \"object\" &&\n this.flushTimer &&\n \"unref\" in this.flushTimer &&\n typeof this.flushTimer.unref === \"function\"\n ) {\n this.flushTimer.unref();\n }\n }\n\n private restartFlushTimer(): void {\n if (this.flushTimer) clearInterval(this.flushTimer);\n this.startFlushTimer();\n }\n\n private safeInternalError(msg: string, err: unknown): void {\n const dev =\n typeof __DEV__ !== \"undefined\"\n ? __DEV__\n : typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\";\n if (dev) console.warn(`[AppDoctor] ${msg}`, err);\n }\n\n private patchFetch(): void {\n if (this.fetchPatched) return;\n const g = globalThis as { fetch: typeof fetch };\n this.originalFetch = g.fetch.bind(globalThis);\n const originalFetch = this.originalFetch;\n g.fetch = async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n const start = now();\n let method = \"GET\";\n let url = \"\";\n try {\n if (typeof input === \"string\") {\n url = input;\n } else if (input instanceof URL) {\n url = input.href;\n } else if (typeof Request !== \"undefined\" && input instanceof Request) {\n url = input.url;\n method = input.method;\n }\n if (init?.method) method = init.method;\n } catch {\n /* ignore */\n }\n\n const urlForEvent =\n url ||\n (typeof input === \"string\"\n ? input\n : input instanceof URL\n ? input.href\n : typeof Request !== \"undefined\" && input instanceof Request\n ? input.url\n : \"[request]\");\n\n try {\n const res = await originalFetch(input, init);\n const durationMs = now() - start;\n const event = this.applyNetworkRedaction({\n method,\n url: urlForEvent,\n status: res.status,\n durationMs,\n success: true,\n });\n this.emit({\n name: \"api_request\",\n ...event,\n });\n return res;\n } catch (e) {\n const durationMs = now() - start;\n const event = this.applyNetworkRedaction({\n method,\n url: urlForEvent,\n durationMs,\n success: false,\n errorMessage: e instanceof Error ? e.message : String(e),\n });\n this.emit({\n name: \"api_request\",\n ...event,\n });\n throw e;\n }\n };\n this.fetchPatched = true;\n }\n\n private restoreFetch(): void {\n if (!this.fetchPatched || !this.originalFetch) return;\n (globalThis as { fetch: typeof fetch }).fetch = this.originalFetch;\n this.fetchPatched = false;\n this.originalFetch = undefined;\n }\n\n private applyNetworkRedaction(\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ): Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\"> {\n if (!this.config.redactNetworkEvent) return event;\n const redacted = this.config.redactNetworkEvent(event);\n return { ...event, ...redacted };\n }\n}\n","import type { AppDoctorEvent, Transport } from \"../core/types.js\";\n\nexport interface ConsoleTransportOptions {\n label?: string;\n slowScreenThresholdMs?: number;\n slowApiThresholdMs?: number;\n format?: \"pretty\" | \"raw\";\n}\n\nexport function createConsoleTransport(\n options: ConsoleTransportOptions = {},\n): Transport {\n const label = options.label ?? \"AppDoctor\";\n const slowScreenThresholdMs = options.slowScreenThresholdMs ?? 1000;\n const slowApiThresholdMs = options.slowApiThresholdMs ?? 800;\n const format = options.format ?? \"pretty\";\n\n function toHint(event: AppDoctorEvent): string | undefined {\n if (\n event.name === \"screen_load\" &&\n event.phase === \"ready\" &&\n event.durationMs >= slowScreenThresholdMs\n ) {\n return `Slow screen \"${event.screen}\" (${event.durationMs}ms). Check expensive effects and repeated renders.`;\n }\n if (event.name === \"api_request\" && event.durationMs >= slowApiThresholdMs) {\n return `Slow API \"${event.method} ${event.url}\" (${event.durationMs}ms). Check server latency, payload size, and retry loops.`;\n }\n return undefined;\n }\n\n function toPrettyLine(event: AppDoctorEvent): string {\n if (event.name === \"screen_load\") {\n return `screen ${event.screen} phase=${event.phase} duration=${event.durationMs}ms`;\n }\n if (event.name === \"api_request\") {\n const status = event.status ?? \"n/a\";\n return `api ${event.method} ${event.url} status=${status} duration=${event.durationMs}ms success=${event.success}`;\n }\n if (event.name === \"render_event\") {\n return `render ${event.component} count=${event.renderCount}`;\n }\n return `sdk_error ${event.message}`;\n }\n\n return {\n send(events: readonly AppDoctorEvent[]): void {\n if (events.length === 0) return;\n for (const event of events) {\n const hint = toHint(event);\n if (hint) {\n console.warn(`[${label}] ${hint}`);\n }\n if (format === \"pretty\") {\n console.log(`[${label}] ${toPrettyLine(event)}`);\n }\n }\n if (format === \"raw\") {\n console.log(`[${label}]`, events);\n }\n },\n };\n}\n","import type { AppDoctorEvent, Transport } from \"../core/types.js\";\n\nexport interface HttpTransportOptions {\n url: string;\n headers?: Record<string, string>;\n maxRetries?: number;\n /** ms */\n initialBackoffMs?: number;\n getExtraBody?: () => Record<string, unknown>;\n}\n\nasync function sleep(ms: number): Promise<void> {\n await new Promise((r) => setTimeout(r, ms));\n}\n\nexport function createHttpTransport(options: HttpTransportOptions): Transport {\n const maxRetries = options.maxRetries ?? 3;\n const initialBackoffMs = options.initialBackoffMs ?? 500;\n\n return {\n async send(events: readonly AppDoctorEvent[]): Promise<void> {\n if (events.length === 0) return;\n const body = {\n events: [...events],\n ...(options.getExtraBody?.() ?? {}),\n };\n let attempt = 0;\n let backoff = initialBackoffMs;\n for (;;) {\n try {\n const res = await fetch(options.url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n body: JSON.stringify(body),\n });\n if (res.ok) return;\n if (res.status >= 400 && res.status < 500 && res.status !== 429) {\n return;\n }\n } catch {\n /* retry */\n }\n attempt += 1;\n if (attempt > maxRetries) return;\n await sleep(backoff);\n backoff *= 2;\n }\n },\n };\n}\n","/**\n * Best-effort active route name from React Navigation state tree.\n * Avoids a hard dependency on `@react-navigation/native` types at runtime.\n */\nexport function getActiveRouteName(state: unknown): string | undefined {\n if (!state || typeof state !== \"object\") return undefined;\n const s = state as {\n index?: number;\n routes?: Array<{ name?: string; state?: unknown }>;\n };\n if (!Array.isArray(s.routes) || typeof s.index !== \"number\") return undefined;\n const route = s.routes[s.index];\n if (!route || typeof route !== \"object\") return undefined;\n if (route.state !== undefined) return getActiveRouteName(route.state);\n return typeof route.name === \"string\" ? route.name : undefined;\n}\n","import type { AppDoctorClient } from \"../core/client.js\";\nimport { getActiveRouteName } from \"./route-name.js\";\n\nexport interface NavigationTimingState {\n lastScreen?: string;\n}\n\n/**\n * Pass the returned listener to `NavigationContainer` as `onStateChange`.\n * Emits `screen_load` when the active route changes and a ready event after the next microtask\n * (approximates first paint/commit after navigation state updates).\n */\nexport function createNavigationStateListener(client: AppDoctorClient): {\n listener: (state: unknown) => void;\n reset: () => void;\n} {\n const timing: NavigationTimingState = {};\n\n return {\n listener(state: unknown) {\n const screen = getActiveRouteName(state);\n if (!screen || screen === timing.lastScreen) return;\n\n timing.lastScreen = screen;\n const startedAt = Date.now();\n\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"start\",\n durationMs: 0,\n });\n\n queueMicrotask(() => {\n if (timing.lastScreen !== screen) return;\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"ready\",\n durationMs: Date.now() - startedAt,\n });\n });\n },\n reset() {\n timing.lastScreen = undefined;\n },\n };\n}\n","import type { AppDoctorClient } from \"../core/client.js\";\nimport type { NetworkRequestEvent } from \"../core/types.js\";\n\n/** Minimal Axios shape to avoid requiring `axios` as a dependency. */\nexport interface AxiosLike {\n interceptors: {\n request: {\n use: (onFulfilled: (value: unknown) => unknown) => number;\n eject: (id: number) => void;\n };\n response: {\n use: (\n onFulfilled: (value: unknown) => unknown,\n onRejected?: (error: unknown) => unknown,\n ) => number;\n eject: (id: number) => void;\n };\n };\n}\n\nexport interface AxiosInstrumentationOptions {\n redactNetworkEvent?: (\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ) => Partial<Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">>;\n}\n\nfunction getConfig(errorOrResponse: unknown): unknown {\n if (!errorOrResponse || typeof errorOrResponse !== \"object\") return undefined;\n if (\n \"config\" in errorOrResponse &&\n errorOrResponse.config &&\n typeof errorOrResponse.config === \"object\"\n ) {\n return errorOrResponse.config;\n }\n return undefined;\n}\n\nfunction getResponseMeta(response: unknown): {\n status?: number;\n method?: string;\n url?: string;\n} {\n if (!response || typeof response !== \"object\") return {};\n const r = response;\n const status =\n \"status\" in r && typeof r.status === \"number\" ? r.status : undefined;\n const rawConfig = \"config\" in r ? r.config : undefined;\n let method: string | undefined;\n let url: string | undefined;\n if (rawConfig !== null && typeof rawConfig === \"object\") {\n if (\"method\" in rawConfig && typeof rawConfig.method === \"string\") {\n method = rawConfig.method;\n }\n if (\"url\" in rawConfig && typeof rawConfig.url === \"string\") {\n url = rawConfig.url;\n }\n }\n return {\n status,\n method,\n url,\n };\n}\n\n/**\n * Returns an `eject` function to remove interceptors.\n */\nexport function instrumentAxios(\n axios: AxiosLike,\n client: AppDoctorClient,\n options: AxiosInstrumentationOptions = {},\n): () => void {\n function applyRedaction(\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ): Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\"> {\n const redacted = options.redactNetworkEvent?.(event);\n return redacted ? { ...event, ...redacted } : event;\n }\n\n const starts = new WeakMap<object, number>();\n\n const reqId = axios.interceptors.request.use((config) => {\n if (config !== null && typeof config === \"object\") {\n starts.set(config, Date.now());\n }\n return config;\n });\n\n const resId = axios.interceptors.response.use(\n (response) => {\n const cfg = getConfig(response);\n if (cfg !== null && typeof cfg === \"object\") {\n const start = starts.get(cfg);\n if (start !== undefined) {\n const { status, method, url } = getResponseMeta(response);\n const event = applyRedaction({\n method: (method ?? \"GET\").toUpperCase(),\n url: url ?? \"\",\n status,\n durationMs: Date.now() - start,\n success: true,\n });\n client.emit({\n name: \"api_request\",\n ...event,\n });\n starts.delete(cfg);\n }\n }\n return response;\n },\n (error: unknown) => {\n const cfg = getConfig(error);\n if (cfg !== null && typeof cfg === \"object\") {\n const start = starts.get(cfg);\n if (start !== undefined) {\n let method = \"GET\";\n let url = \"\";\n let status: number | undefined;\n let errorMessage = String(error);\n if (error !== null && typeof error === \"object\") {\n if (\"message\" in error && typeof error.message === \"string\") {\n errorMessage = error.message;\n }\n const resp =\n \"response\" in error && error.response !== null &&\n typeof error.response === \"object\"\n ? error.response\n : undefined;\n if (\n resp &&\n \"status\" in resp &&\n typeof resp.status === \"number\"\n ) {\n status = resp.status;\n }\n const errCfg =\n \"config\" in error && error.config !== null &&\n typeof error.config === \"object\"\n ? error.config\n : undefined;\n if (errCfg) {\n if (\"method\" in errCfg && typeof errCfg.method === \"string\") {\n method = errCfg.method.toUpperCase();\n }\n if (\"url\" in errCfg && typeof errCfg.url === \"string\") {\n url = errCfg.url;\n }\n }\n }\n const event = applyRedaction({\n method,\n url,\n status,\n durationMs: Date.now() - start,\n success: false,\n errorMessage,\n });\n client.emit({\n name: \"api_request\",\n ...event,\n });\n starts.delete(cfg);\n }\n }\n const rejectReason =\n error instanceof Error ? error : new Error(String(error));\n return Promise.reject(rejectReason);\n },\n );\n\n return () => {\n axios.interceptors.request.eject(reqId);\n axios.interceptors.response.eject(resId);\n };\n}\n","import type { AppDoctorClient } from \"../core/client.js\";\n\n/**\n * Wraps an async call and emits an `api_request` event with a synthetic URL label.\n */\nexport async function trackApi<T>(\n client: AppDoctorClient,\n label: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const start = Date.now();\n try {\n const result = await fn();\n client.emit({\n name: \"api_request\",\n method: \"TRACK\",\n url: label,\n status: 200,\n durationMs: Date.now() - start,\n success: true,\n });\n return result;\n } catch (e) {\n client.emit({\n name: \"api_request\",\n method: \"TRACK\",\n url: label,\n durationMs: Date.now() - start,\n success: false,\n errorMessage: e instanceof Error ? e.message : String(e),\n });\n throw e;\n }\n}\n","import { createContext } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\n\nexport const AppDoctorContext = createContext<AppDoctorClient | null>(null);\n","import { useEffect, useRef, type ReactElement, type ReactNode } from \"react\";\nimport { AppDoctorClient } from \"../core/client.js\";\nimport type { AppDoctorConfig } from \"../core/types.js\";\nimport { AppDoctorContext } from \"./context.js\";\n\nexport type AppDoctorProviderProps = AppDoctorConfig & {\n children: ReactNode;\n};\n\nexport function AppDoctorProvider({\n children,\n ...config\n}: AppDoctorProviderProps): ReactElement {\n const clientRef = useRef<AppDoctorClient | null>(null);\n if (!clientRef.current) {\n clientRef.current = new AppDoctorClient(config);\n }\n\n const {\n enabled,\n noop,\n sampleRate,\n flushIntervalMs,\n maxQueueSize,\n instrumentFetch,\n tags,\n context,\n transports,\n redactNetworkEvent,\n } = config;\n\n useEffect(() => {\n clientRef.current?.updateConfig({\n enabled,\n noop,\n sampleRate,\n flushIntervalMs,\n maxQueueSize,\n instrumentFetch,\n tags,\n context,\n transports,\n redactNetworkEvent,\n });\n }, [\n enabled,\n noop,\n sampleRate,\n flushIntervalMs,\n maxQueueSize,\n instrumentFetch,\n tags,\n context,\n transports,\n redactNetworkEvent,\n ]);\n\n useEffect(() => {\n return () => {\n void clientRef.current?.shutdown();\n clientRef.current = null;\n };\n }, []);\n\n return (\n <AppDoctorContext.Provider value={clientRef.current}>\n {children}\n </AppDoctorContext.Provider>\n );\n}\n","import { useContext } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\nimport { AppDoctorContext } from \"../provider/context.js\";\n\nexport function useAppDoctor(): AppDoctorClient {\n const client = useContext(AppDoctorContext);\n if (!client) {\n throw new Error(\"useAppDoctor must be used within AppDoctorProvider\");\n }\n return client;\n}\n","import { useContext, useEffect } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\nimport { AppDoctorContext } from \"../provider/context.js\";\n\nexport interface UseTrackScreenOptions {\n client?: AppDoctorClient;\n}\n\n/**\n * Emits `screen_load` lifecycle events on mount and ready.\n */\nexport function useTrackScreen(\n screen: string,\n options: UseTrackScreenOptions = {},\n): void {\n const fromContext = useContext(AppDoctorContext);\n const client = options.client ?? fromContext;\n if (!client) {\n throw new Error(\n \"useTrackScreen requires AppDoctorProvider or options.client\",\n );\n }\n\n useEffect(() => {\n const startedAt = Date.now();\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"start\",\n durationMs: 0,\n });\n queueMicrotask(() => {\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"ready\",\n durationMs: Date.now() - startedAt,\n });\n });\n }, [client, screen]);\n}\n","import { useContext, useEffect, useRef } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\nimport { AppDoctorContext } from \"../provider/context.js\";\n\nexport interface UseTrackRenderOptions {\n client?: AppDoctorClient;\n /** Emit every N renders (default 10) to limit noise */\n every?: number;\n}\n\n/**\n * Emits `render_event` periodically when the component re-renders.\n */\nexport function useTrackRender(\n componentName: string,\n options: UseTrackRenderOptions = {},\n): void {\n const fromContext = useContext(AppDoctorContext);\n const client = options.client ?? fromContext;\n if (!client) {\n throw new Error(\n \"useTrackRender requires AppDoctorProvider or options.client\",\n );\n }\n const every = options.every ?? 10;\n const count = useRef(0);\n\n count.current += 1;\n\n useEffect(() => {\n if (count.current % every !== 0) return;\n client.emit({\n name: \"render_event\",\n component: componentName,\n renderCount: count.current,\n });\n });\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -99,11 +99,13 @@ declare class AppDoctorClient {
99
99
 
100
100
  declare function shouldSample(rate: number): boolean;
101
101
 
102
- declare function createConsoleTransport(options?: {
102
+ interface ConsoleTransportOptions {
103
103
  label?: string;
104
104
  slowScreenThresholdMs?: number;
105
105
  slowApiThresholdMs?: number;
106
- }): Transport;
106
+ format?: "pretty" | "raw";
107
+ }
108
+ declare function createConsoleTransport(options?: ConsoleTransportOptions): Transport;
107
109
 
108
110
  interface HttpTransportOptions {
109
111
  url: string;
@@ -184,4 +186,4 @@ interface UseTrackRenderOptions {
184
186
  */
185
187
  declare function useTrackRender(componentName: string, options?: UseTrackRenderOptions): void;
186
188
 
187
- export { AppDoctorClient, type AppDoctorConfig, AppDoctorContext, type AppDoctorEmitInput, type AppDoctorEvent, type AppDoctorEventName, AppDoctorProvider, type AppDoctorProviderProps, type AxiosInstrumentationOptions, type AxiosLike, type EventContext, type HttpTransportOptions, type NetworkRequestEvent, type RenderCountEvent, type ScreenLoadEvent, type SdkErrorEvent, type Transport, createConsoleTransport, createHttpTransport, createNavigationStateListener, getActiveRouteName, instrumentAxios, shouldSample, trackApi, useAppDoctor, useTrackRender, useTrackScreen };
189
+ export { AppDoctorClient, type AppDoctorConfig, AppDoctorContext, type AppDoctorEmitInput, type AppDoctorEvent, type AppDoctorEventName, AppDoctorProvider, type AppDoctorProviderProps, type AxiosInstrumentationOptions, type AxiosLike, type ConsoleTransportOptions, type EventContext, type HttpTransportOptions, type NetworkRequestEvent, type RenderCountEvent, type ScreenLoadEvent, type SdkErrorEvent, type Transport, createConsoleTransport, createHttpTransport, createNavigationStateListener, getActiveRouteName, instrumentAxios, shouldSample, trackApi, useAppDoctor, useTrackRender, useTrackScreen };
package/dist/index.d.ts CHANGED
@@ -99,11 +99,13 @@ declare class AppDoctorClient {
99
99
 
100
100
  declare function shouldSample(rate: number): boolean;
101
101
 
102
- declare function createConsoleTransport(options?: {
102
+ interface ConsoleTransportOptions {
103
103
  label?: string;
104
104
  slowScreenThresholdMs?: number;
105
105
  slowApiThresholdMs?: number;
106
- }): Transport;
106
+ format?: "pretty" | "raw";
107
+ }
108
+ declare function createConsoleTransport(options?: ConsoleTransportOptions): Transport;
107
109
 
108
110
  interface HttpTransportOptions {
109
111
  url: string;
@@ -184,4 +186,4 @@ interface UseTrackRenderOptions {
184
186
  */
185
187
  declare function useTrackRender(componentName: string, options?: UseTrackRenderOptions): void;
186
188
 
187
- export { AppDoctorClient, type AppDoctorConfig, AppDoctorContext, type AppDoctorEmitInput, type AppDoctorEvent, type AppDoctorEventName, AppDoctorProvider, type AppDoctorProviderProps, type AxiosInstrumentationOptions, type AxiosLike, type EventContext, type HttpTransportOptions, type NetworkRequestEvent, type RenderCountEvent, type ScreenLoadEvent, type SdkErrorEvent, type Transport, createConsoleTransport, createHttpTransport, createNavigationStateListener, getActiveRouteName, instrumentAxios, shouldSample, trackApi, useAppDoctor, useTrackRender, useTrackScreen };
189
+ export { AppDoctorClient, type AppDoctorConfig, AppDoctorContext, type AppDoctorEmitInput, type AppDoctorEvent, type AppDoctorEventName, AppDoctorProvider, type AppDoctorProviderProps, type AxiosInstrumentationOptions, type AxiosLike, type ConsoleTransportOptions, type EventContext, type HttpTransportOptions, type NetworkRequestEvent, type RenderCountEvent, type ScreenLoadEvent, type SdkErrorEvent, type Transport, createConsoleTransport, createHttpTransport, createNavigationStateListener, getActiveRouteName, instrumentAxios, shouldSample, trackApi, useAppDoctor, useTrackRender, useTrackScreen };
package/dist/index.js CHANGED
@@ -234,6 +234,7 @@ function createConsoleTransport(options = {}) {
234
234
  const label = options.label ?? "AppDoctor";
235
235
  const slowScreenThresholdMs = options.slowScreenThresholdMs ?? 1e3;
236
236
  const slowApiThresholdMs = options.slowApiThresholdMs ?? 800;
237
+ const format = options.format ?? "pretty";
237
238
  function toHint(event) {
238
239
  if (event.name === "screen_load" && event.phase === "ready" && event.durationMs >= slowScreenThresholdMs) {
239
240
  return `Slow screen "${event.screen}" (${event.durationMs}ms). Check expensive effects and repeated renders.`;
@@ -243,6 +244,19 @@ function createConsoleTransport(options = {}) {
243
244
  }
244
245
  return void 0;
245
246
  }
247
+ function toPrettyLine(event) {
248
+ if (event.name === "screen_load") {
249
+ return `screen ${event.screen} phase=${event.phase} duration=${event.durationMs}ms`;
250
+ }
251
+ if (event.name === "api_request") {
252
+ const status = event.status ?? "n/a";
253
+ return `api ${event.method} ${event.url} status=${status} duration=${event.durationMs}ms success=${event.success}`;
254
+ }
255
+ if (event.name === "render_event") {
256
+ return `render ${event.component} count=${event.renderCount}`;
257
+ }
258
+ return `sdk_error ${event.message}`;
259
+ }
246
260
  return {
247
261
  send(events) {
248
262
  if (events.length === 0) return;
@@ -251,8 +265,13 @@ function createConsoleTransport(options = {}) {
251
265
  if (hint) {
252
266
  console.warn(`[${label}] ${hint}`);
253
267
  }
268
+ if (format === "pretty") {
269
+ console.log(`[${label}] ${toPrettyLine(event)}`);
270
+ }
271
+ }
272
+ if (format === "raw") {
273
+ console.log(`[${label}]`, events);
254
274
  }
255
- console.log(`[${label}]`, events);
256
275
  }
257
276
  };
258
277
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/types.ts","../src/core/sampling.ts","../src/core/client.ts","../src/transports/console-transport.ts","../src/transports/http-transport.ts","../src/navigation/route-name.ts","../src/navigation/create-navigation-listener.ts","../src/network/axios-instrumentation.ts","../src/utils/track-api.ts","../src/provider/context.ts","../src/provider/AppDoctorProvider.tsx","../src/hooks/useAppDoctor.ts","../src/hooks/useTrackScreen.ts","../src/hooks/useTrackRender.ts"],"names":["useContext","useEffect","useRef"],"mappings":"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","file":"index.js","sourcesContent":["export type AppDoctorEventName =\n | \"screen_load\"\n | \"api_request\"\n | \"render_event\"\n | \"sdk_error\";\n\nexport interface EventContext {\n route?: string;\n component?: string;\n platform?: string;\n appVersion?: string;\n metadata?: Record<string, string | number | boolean>;\n}\n\nexport interface AppDoctorEventBase {\n name: AppDoctorEventName;\n /** Unix ms */\n timestamp: number;\n sessionId: string;\n tags?: Record<string, string>;\n context?: EventContext;\n}\n\nexport interface ScreenLoadEvent extends AppDoctorEventBase {\n name: \"screen_load\";\n screen: string;\n phase: \"start\" | \"ready\";\n /** Time from navigation intent to ready signal (ms), if known */\n durationMs: number;\n}\n\nexport interface NetworkRequestEvent extends AppDoctorEventBase {\n name: \"api_request\";\n method: string;\n url: string;\n status?: number;\n durationMs: number;\n success: boolean;\n errorMessage?: string;\n}\n\nexport interface RenderCountEvent extends AppDoctorEventBase {\n name: \"render_event\";\n component: string;\n renderCount: number;\n}\n\nexport interface SdkErrorEvent extends AppDoctorEventBase {\n name: \"sdk_error\";\n message: string;\n stack?: string;\n errorContext?: string;\n}\n\nexport type AppDoctorEvent =\n | ScreenLoadEvent\n | NetworkRequestEvent\n | RenderCountEvent\n | SdkErrorEvent;\n\n/** Payload accepted by `AppDoctorClient.emit` (union; avoids `Omit` pitfalls on unions). */\nexport type AppDoctorEmitInput =\n | Omit<ScreenLoadEvent, \"timestamp\" | \"sessionId\">\n | Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\">\n | Omit<RenderCountEvent, \"timestamp\" | \"sessionId\">\n | Omit<SdkErrorEvent, \"timestamp\" | \"sessionId\">;\n\nexport interface Transport {\n send(events: readonly AppDoctorEvent[]): void | Promise<void>;\n flush?: () => void | Promise<void>;\n shutdown?: () => void | Promise<void>;\n}\n\nexport interface AppDoctorConfig {\n enabled?: boolean;\n /** Shortcut to disable all internal work while preserving integration calls */\n noop?: boolean;\n /** 0–1, default 1 */\n sampleRate?: number;\n flushIntervalMs?: number;\n maxQueueSize?: number;\n transports?: Transport[];\n tags?: Record<string, string>;\n /** Add context to every emitted event */\n context?: EventContext;\n /** When true, wraps global fetch (restore on shutdown) */\n instrumentFetch?: boolean;\n /**\n * Redacts/sanitizes network event fields before emission.\n * Returning partial values overrides the computed payload.\n */\n redactNetworkEvent?: (\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ) => Partial<Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">>;\n}\n\nexport const DEFAULT_FLUSH_INTERVAL_MS = 2000;\nexport const DEFAULT_MAX_QUEUE_SIZE = 200;\n","export function shouldSample(rate: number): boolean {\n if (rate >= 1) return true;\n if (rate <= 0) return false;\n return Math.random() < rate;\n}\n","import type {\n AppDoctorConfig,\n AppDoctorEmitInput,\n AppDoctorEvent,\n NetworkRequestEvent,\n SdkErrorEvent,\n Transport,\n} from \"./types.js\";\nimport {\n DEFAULT_FLUSH_INTERVAL_MS,\n DEFAULT_MAX_QUEUE_SIZE,\n} from \"./types.js\";\nimport { shouldSample } from \"./sampling.js\";\n\nfunction now(): number {\n return Date.now();\n}\n\nfunction newSessionId(): string {\n return `${now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nexport class AppDoctorClient {\n readonly sessionId: string;\n private readonly config: Required<\n Pick<\n AppDoctorConfig,\n | \"enabled\"\n | \"noop\"\n | \"sampleRate\"\n | \"flushIntervalMs\"\n | \"maxQueueSize\"\n | \"instrumentFetch\"\n >\n > &\n Pick<AppDoctorConfig, \"tags\" | \"context\" | \"redactNetworkEvent\">;\n private readonly transports: Transport[];\n private queue: AppDoctorEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | undefined;\n private fetchPatched = false;\n private originalFetch: typeof fetch | undefined;\n\n constructor(config: AppDoctorConfig = {}) {\n this.sessionId = newSessionId();\n this.config = {\n enabled: config.enabled ?? true,\n noop: config.noop ?? false,\n sampleRate: config.sampleRate ?? 1,\n flushIntervalMs: config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS,\n maxQueueSize: config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,\n instrumentFetch: config.instrumentFetch ?? true,\n tags: config.tags,\n context: config.context,\n redactNetworkEvent: config.redactNetworkEvent,\n };\n this.transports = [...(config.transports ?? [])];\n if (this.config.enabled) {\n this.startFlushTimer();\n if (this.config.instrumentFetch && typeof globalThis.fetch === \"function\") {\n this.patchFetch();\n }\n }\n }\n\n updateConfig(partial: Partial<AppDoctorConfig>): void {\n if (partial.enabled !== undefined) {\n (this.config as { enabled: boolean }).enabled = partial.enabled;\n }\n if (partial.noop !== undefined) {\n (this.config as { noop: boolean }).noop = partial.noop;\n }\n if (partial.sampleRate !== undefined) {\n (this.config as { sampleRate: number }).sampleRate = partial.sampleRate;\n }\n if (partial.flushIntervalMs !== undefined) {\n (this.config as { flushIntervalMs: number }).flushIntervalMs =\n partial.flushIntervalMs;\n this.restartFlushTimer();\n }\n if (partial.maxQueueSize !== undefined) {\n (this.config as { maxQueueSize: number }).maxQueueSize =\n partial.maxQueueSize;\n }\n if (partial.tags !== undefined) {\n this.config.tags = partial.tags;\n }\n if (partial.context !== undefined) {\n this.config.context = partial.context;\n }\n if (partial.instrumentFetch !== undefined) {\n const next = partial.instrumentFetch;\n (this.config as { instrumentFetch: boolean }).instrumentFetch = next;\n if (next && !this.fetchPatched) this.patchFetch();\n if (!next && this.fetchPatched) this.restoreFetch();\n }\n if (partial.transports !== undefined) {\n this.transports.length = 0;\n this.transports.push(...partial.transports);\n }\n if (partial.redactNetworkEvent !== undefined) {\n this.config.redactNetworkEvent = partial.redactNetworkEvent;\n }\n }\n\n addTransport(transport: Transport): void {\n this.transports.push(transport);\n }\n\n emit(event: AppDoctorEmitInput): void {\n if (!this.config.enabled) return;\n if (this.config.noop) return;\n if (!shouldSample(this.config.sampleRate)) return;\n\n const { tags: eventTags, ...payload } = event;\n const tags =\n this.config.tags || eventTags\n ? { ...this.config.tags, ...eventTags }\n : undefined;\n\n const full = {\n ...payload,\n timestamp: now(),\n sessionId: this.sessionId,\n tags,\n context: this.config.context,\n } as AppDoctorEvent;\n\n this.enqueue(full);\n }\n\n private enqueue(event: AppDoctorEvent): void {\n if (this.queue.length >= this.config.maxQueueSize) {\n this.queue.shift();\n }\n this.queue.push(event);\n }\n\n captureError(message: string, context?: string, err?: unknown): void {\n const stack =\n err instanceof Error && typeof err.stack === \"string\"\n ? err.stack\n : undefined;\n const sdkErr: Omit<SdkErrorEvent, \"timestamp\" | \"sessionId\" | \"tags\"> = {\n name: \"sdk_error\",\n message,\n stack,\n errorContext: context,\n };\n this.emit(sdkErr);\n }\n\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n const batch = this.queue.splice(0, this.queue.length);\n await Promise.all(\n this.transports.map(async (t) => {\n try {\n await t.send(batch);\n } catch (e) {\n this.safeInternalError(\"transport.send failed\", e);\n }\n }),\n );\n }\n\n async shutdown(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = undefined;\n }\n this.restoreFetch();\n await this.flush();\n await Promise.all(\n this.transports.map(async (t) => {\n if (!t.shutdown) return;\n try {\n await t.shutdown();\n } catch (e) {\n this.safeInternalError(\"transport.shutdown failed\", e);\n }\n }),\n );\n }\n\n private startFlushTimer(): void {\n this.flushTimer = setInterval(() => {\n void this.flush();\n }, this.config.flushIntervalMs);\n if (\n typeof this.flushTimer === \"object\" &&\n this.flushTimer &&\n \"unref\" in this.flushTimer &&\n typeof this.flushTimer.unref === \"function\"\n ) {\n this.flushTimer.unref();\n }\n }\n\n private restartFlushTimer(): void {\n if (this.flushTimer) clearInterval(this.flushTimer);\n this.startFlushTimer();\n }\n\n private safeInternalError(msg: string, err: unknown): void {\n const dev =\n typeof __DEV__ !== \"undefined\"\n ? __DEV__\n : typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\";\n if (dev) console.warn(`[AppDoctor] ${msg}`, err);\n }\n\n private patchFetch(): void {\n if (this.fetchPatched) return;\n const g = globalThis as { fetch: typeof fetch };\n this.originalFetch = g.fetch.bind(globalThis);\n const originalFetch = this.originalFetch;\n g.fetch = async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n const start = now();\n let method = \"GET\";\n let url = \"\";\n try {\n if (typeof input === \"string\") {\n url = input;\n } else if (input instanceof URL) {\n url = input.href;\n } else if (typeof Request !== \"undefined\" && input instanceof Request) {\n url = input.url;\n method = input.method;\n }\n if (init?.method) method = init.method;\n } catch {\n /* ignore */\n }\n\n const urlForEvent =\n url ||\n (typeof input === \"string\"\n ? input\n : input instanceof URL\n ? input.href\n : typeof Request !== \"undefined\" && input instanceof Request\n ? input.url\n : \"[request]\");\n\n try {\n const res = await originalFetch(input, init);\n const durationMs = now() - start;\n const event = this.applyNetworkRedaction({\n method,\n url: urlForEvent,\n status: res.status,\n durationMs,\n success: true,\n });\n this.emit({\n name: \"api_request\",\n ...event,\n });\n return res;\n } catch (e) {\n const durationMs = now() - start;\n const event = this.applyNetworkRedaction({\n method,\n url: urlForEvent,\n durationMs,\n success: false,\n errorMessage: e instanceof Error ? e.message : String(e),\n });\n this.emit({\n name: \"api_request\",\n ...event,\n });\n throw e;\n }\n };\n this.fetchPatched = true;\n }\n\n private restoreFetch(): void {\n if (!this.fetchPatched || !this.originalFetch) return;\n (globalThis as { fetch: typeof fetch }).fetch = this.originalFetch;\n this.fetchPatched = false;\n this.originalFetch = undefined;\n }\n\n private applyNetworkRedaction(\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ): Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\"> {\n if (!this.config.redactNetworkEvent) return event;\n const redacted = this.config.redactNetworkEvent(event);\n return { ...event, ...redacted };\n }\n}\n","import type { AppDoctorEvent, Transport } from \"../core/types.js\";\n\nexport function createConsoleTransport(\n options: {\n label?: string;\n slowScreenThresholdMs?: number;\n slowApiThresholdMs?: number;\n } = {},\n): Transport {\n const label = options.label ?? \"AppDoctor\";\n const slowScreenThresholdMs = options.slowScreenThresholdMs ?? 1000;\n const slowApiThresholdMs = options.slowApiThresholdMs ?? 800;\n\n function toHint(event: AppDoctorEvent): string | undefined {\n if (\n event.name === \"screen_load\" &&\n event.phase === \"ready\" &&\n event.durationMs >= slowScreenThresholdMs\n ) {\n return `Slow screen \"${event.screen}\" (${event.durationMs}ms). Check expensive effects and repeated renders.`;\n }\n if (event.name === \"api_request\" && event.durationMs >= slowApiThresholdMs) {\n return `Slow API \"${event.method} ${event.url}\" (${event.durationMs}ms). Check server latency, payload size, and retry loops.`;\n }\n return undefined;\n }\n\n return {\n send(events: readonly AppDoctorEvent[]): void {\n if (events.length === 0) return;\n for (const event of events) {\n const hint = toHint(event);\n if (hint) {\n console.warn(`[${label}] ${hint}`);\n }\n }\n console.log(`[${label}]`, events);\n },\n };\n}\n","import type { AppDoctorEvent, Transport } from \"../core/types.js\";\n\nexport interface HttpTransportOptions {\n url: string;\n headers?: Record<string, string>;\n maxRetries?: number;\n /** ms */\n initialBackoffMs?: number;\n getExtraBody?: () => Record<string, unknown>;\n}\n\nasync function sleep(ms: number): Promise<void> {\n await new Promise((r) => setTimeout(r, ms));\n}\n\nexport function createHttpTransport(options: HttpTransportOptions): Transport {\n const maxRetries = options.maxRetries ?? 3;\n const initialBackoffMs = options.initialBackoffMs ?? 500;\n\n return {\n async send(events: readonly AppDoctorEvent[]): Promise<void> {\n if (events.length === 0) return;\n const body = {\n events: [...events],\n ...(options.getExtraBody?.() ?? {}),\n };\n let attempt = 0;\n let backoff = initialBackoffMs;\n for (;;) {\n try {\n const res = await fetch(options.url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n body: JSON.stringify(body),\n });\n if (res.ok) return;\n if (res.status >= 400 && res.status < 500 && res.status !== 429) {\n return;\n }\n } catch {\n /* retry */\n }\n attempt += 1;\n if (attempt > maxRetries) return;\n await sleep(backoff);\n backoff *= 2;\n }\n },\n };\n}\n","/**\n * Best-effort active route name from React Navigation state tree.\n * Avoids a hard dependency on `@react-navigation/native` types at runtime.\n */\nexport function getActiveRouteName(state: unknown): string | undefined {\n if (!state || typeof state !== \"object\") return undefined;\n const s = state as {\n index?: number;\n routes?: Array<{ name?: string; state?: unknown }>;\n };\n if (!Array.isArray(s.routes) || typeof s.index !== \"number\") return undefined;\n const route = s.routes[s.index];\n if (!route || typeof route !== \"object\") return undefined;\n if (route.state !== undefined) return getActiveRouteName(route.state);\n return typeof route.name === \"string\" ? route.name : undefined;\n}\n","import type { AppDoctorClient } from \"../core/client.js\";\nimport { getActiveRouteName } from \"./route-name.js\";\n\nexport interface NavigationTimingState {\n lastScreen?: string;\n}\n\n/**\n * Pass the returned listener to `NavigationContainer` as `onStateChange`.\n * Emits `screen_load` when the active route changes and a ready event after the next microtask\n * (approximates first paint/commit after navigation state updates).\n */\nexport function createNavigationStateListener(client: AppDoctorClient): {\n listener: (state: unknown) => void;\n reset: () => void;\n} {\n const timing: NavigationTimingState = {};\n\n return {\n listener(state: unknown) {\n const screen = getActiveRouteName(state);\n if (!screen || screen === timing.lastScreen) return;\n\n timing.lastScreen = screen;\n const startedAt = Date.now();\n\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"start\",\n durationMs: 0,\n });\n\n queueMicrotask(() => {\n if (timing.lastScreen !== screen) return;\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"ready\",\n durationMs: Date.now() - startedAt,\n });\n });\n },\n reset() {\n timing.lastScreen = undefined;\n },\n };\n}\n","import type { AppDoctorClient } from \"../core/client.js\";\nimport type { NetworkRequestEvent } from \"../core/types.js\";\n\n/** Minimal Axios shape to avoid requiring `axios` as a dependency. */\nexport interface AxiosLike {\n interceptors: {\n request: {\n use: (onFulfilled: (value: unknown) => unknown) => number;\n eject: (id: number) => void;\n };\n response: {\n use: (\n onFulfilled: (value: unknown) => unknown,\n onRejected?: (error: unknown) => unknown,\n ) => number;\n eject: (id: number) => void;\n };\n };\n}\n\nexport interface AxiosInstrumentationOptions {\n redactNetworkEvent?: (\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ) => Partial<Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">>;\n}\n\nfunction getConfig(errorOrResponse: unknown): unknown {\n if (!errorOrResponse || typeof errorOrResponse !== \"object\") return undefined;\n if (\n \"config\" in errorOrResponse &&\n errorOrResponse.config &&\n typeof errorOrResponse.config === \"object\"\n ) {\n return errorOrResponse.config;\n }\n return undefined;\n}\n\nfunction getResponseMeta(response: unknown): {\n status?: number;\n method?: string;\n url?: string;\n} {\n if (!response || typeof response !== \"object\") return {};\n const r = response;\n const status =\n \"status\" in r && typeof r.status === \"number\" ? r.status : undefined;\n const rawConfig = \"config\" in r ? r.config : undefined;\n let method: string | undefined;\n let url: string | undefined;\n if (rawConfig !== null && typeof rawConfig === \"object\") {\n if (\"method\" in rawConfig && typeof rawConfig.method === \"string\") {\n method = rawConfig.method;\n }\n if (\"url\" in rawConfig && typeof rawConfig.url === \"string\") {\n url = rawConfig.url;\n }\n }\n return {\n status,\n method,\n url,\n };\n}\n\n/**\n * Returns an `eject` function to remove interceptors.\n */\nexport function instrumentAxios(\n axios: AxiosLike,\n client: AppDoctorClient,\n options: AxiosInstrumentationOptions = {},\n): () => void {\n function applyRedaction(\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ): Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\"> {\n const redacted = options.redactNetworkEvent?.(event);\n return redacted ? { ...event, ...redacted } : event;\n }\n\n const starts = new WeakMap<object, number>();\n\n const reqId = axios.interceptors.request.use((config) => {\n if (config !== null && typeof config === \"object\") {\n starts.set(config, Date.now());\n }\n return config;\n });\n\n const resId = axios.interceptors.response.use(\n (response) => {\n const cfg = getConfig(response);\n if (cfg !== null && typeof cfg === \"object\") {\n const start = starts.get(cfg);\n if (start !== undefined) {\n const { status, method, url } = getResponseMeta(response);\n const event = applyRedaction({\n method: (method ?? \"GET\").toUpperCase(),\n url: url ?? \"\",\n status,\n durationMs: Date.now() - start,\n success: true,\n });\n client.emit({\n name: \"api_request\",\n ...event,\n });\n starts.delete(cfg);\n }\n }\n return response;\n },\n (error: unknown) => {\n const cfg = getConfig(error);\n if (cfg !== null && typeof cfg === \"object\") {\n const start = starts.get(cfg);\n if (start !== undefined) {\n let method = \"GET\";\n let url = \"\";\n let status: number | undefined;\n let errorMessage = String(error);\n if (error !== null && typeof error === \"object\") {\n if (\"message\" in error && typeof error.message === \"string\") {\n errorMessage = error.message;\n }\n const resp =\n \"response\" in error && error.response !== null &&\n typeof error.response === \"object\"\n ? error.response\n : undefined;\n if (\n resp &&\n \"status\" in resp &&\n typeof resp.status === \"number\"\n ) {\n status = resp.status;\n }\n const errCfg =\n \"config\" in error && error.config !== null &&\n typeof error.config === \"object\"\n ? error.config\n : undefined;\n if (errCfg) {\n if (\"method\" in errCfg && typeof errCfg.method === \"string\") {\n method = errCfg.method.toUpperCase();\n }\n if (\"url\" in errCfg && typeof errCfg.url === \"string\") {\n url = errCfg.url;\n }\n }\n }\n const event = applyRedaction({\n method,\n url,\n status,\n durationMs: Date.now() - start,\n success: false,\n errorMessage,\n });\n client.emit({\n name: \"api_request\",\n ...event,\n });\n starts.delete(cfg);\n }\n }\n const rejectReason =\n error instanceof Error ? error : new Error(String(error));\n return Promise.reject(rejectReason);\n },\n );\n\n return () => {\n axios.interceptors.request.eject(reqId);\n axios.interceptors.response.eject(resId);\n };\n}\n","import type { AppDoctorClient } from \"../core/client.js\";\n\n/**\n * Wraps an async call and emits an `api_request` event with a synthetic URL label.\n */\nexport async function trackApi<T>(\n client: AppDoctorClient,\n label: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const start = Date.now();\n try {\n const result = await fn();\n client.emit({\n name: \"api_request\",\n method: \"TRACK\",\n url: label,\n status: 200,\n durationMs: Date.now() - start,\n success: true,\n });\n return result;\n } catch (e) {\n client.emit({\n name: \"api_request\",\n method: \"TRACK\",\n url: label,\n durationMs: Date.now() - start,\n success: false,\n errorMessage: e instanceof Error ? e.message : String(e),\n });\n throw e;\n }\n}\n","import { createContext } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\n\nexport const AppDoctorContext = createContext<AppDoctorClient | null>(null);\n","import { useEffect, useRef, type ReactElement, type ReactNode } from \"react\";\nimport { AppDoctorClient } from \"../core/client.js\";\nimport type { AppDoctorConfig } from \"../core/types.js\";\nimport { AppDoctorContext } from \"./context.js\";\n\nexport type AppDoctorProviderProps = AppDoctorConfig & {\n children: ReactNode;\n};\n\nexport function AppDoctorProvider({\n children,\n ...config\n}: AppDoctorProviderProps): ReactElement {\n const clientRef = useRef<AppDoctorClient | null>(null);\n if (!clientRef.current) {\n clientRef.current = new AppDoctorClient(config);\n }\n\n const {\n enabled,\n noop,\n sampleRate,\n flushIntervalMs,\n maxQueueSize,\n instrumentFetch,\n tags,\n context,\n transports,\n redactNetworkEvent,\n } = config;\n\n useEffect(() => {\n clientRef.current?.updateConfig({\n enabled,\n noop,\n sampleRate,\n flushIntervalMs,\n maxQueueSize,\n instrumentFetch,\n tags,\n context,\n transports,\n redactNetworkEvent,\n });\n }, [\n enabled,\n noop,\n sampleRate,\n flushIntervalMs,\n maxQueueSize,\n instrumentFetch,\n tags,\n context,\n transports,\n redactNetworkEvent,\n ]);\n\n useEffect(() => {\n return () => {\n void clientRef.current?.shutdown();\n clientRef.current = null;\n };\n }, []);\n\n return (\n <AppDoctorContext.Provider value={clientRef.current}>\n {children}\n </AppDoctorContext.Provider>\n );\n}\n","import { useContext } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\nimport { AppDoctorContext } from \"../provider/context.js\";\n\nexport function useAppDoctor(): AppDoctorClient {\n const client = useContext(AppDoctorContext);\n if (!client) {\n throw new Error(\"useAppDoctor must be used within AppDoctorProvider\");\n }\n return client;\n}\n","import { useContext, useEffect } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\nimport { AppDoctorContext } from \"../provider/context.js\";\n\nexport interface UseTrackScreenOptions {\n client?: AppDoctorClient;\n}\n\n/**\n * Emits `screen_load` lifecycle events on mount and ready.\n */\nexport function useTrackScreen(\n screen: string,\n options: UseTrackScreenOptions = {},\n): void {\n const fromContext = useContext(AppDoctorContext);\n const client = options.client ?? fromContext;\n if (!client) {\n throw new Error(\n \"useTrackScreen requires AppDoctorProvider or options.client\",\n );\n }\n\n useEffect(() => {\n const startedAt = Date.now();\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"start\",\n durationMs: 0,\n });\n queueMicrotask(() => {\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"ready\",\n durationMs: Date.now() - startedAt,\n });\n });\n }, [client, screen]);\n}\n","import { useContext, useEffect, useRef } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\nimport { AppDoctorContext } from \"../provider/context.js\";\n\nexport interface UseTrackRenderOptions {\n client?: AppDoctorClient;\n /** Emit every N renders (default 10) to limit noise */\n every?: number;\n}\n\n/**\n * Emits `render_event` periodically when the component re-renders.\n */\nexport function useTrackRender(\n componentName: string,\n options: UseTrackRenderOptions = {},\n): void {\n const fromContext = useContext(AppDoctorContext);\n const client = options.client ?? fromContext;\n if (!client) {\n throw new Error(\n \"useTrackRender requires AppDoctorProvider or options.client\",\n );\n }\n const every = options.every ?? 10;\n const count = useRef(0);\n\n count.current += 1;\n\n useEffect(() => {\n if (count.current % every !== 0) return;\n client.emit({\n name: \"render_event\",\n component: componentName,\n renderCount: count.current,\n });\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/core/types.ts","../src/core/sampling.ts","../src/core/client.ts","../src/transports/console-transport.ts","../src/transports/http-transport.ts","../src/navigation/route-name.ts","../src/navigation/create-navigation-listener.ts","../src/network/axios-instrumentation.ts","../src/utils/track-api.ts","../src/provider/context.ts","../src/provider/AppDoctorProvider.tsx","../src/hooks/useAppDoctor.ts","../src/hooks/useTrackScreen.ts","../src/hooks/useTrackRender.ts"],"names":["useContext","useEffect","useRef"],"mappings":"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","file":"index.js","sourcesContent":["export type AppDoctorEventName =\n | \"screen_load\"\n | \"api_request\"\n | \"render_event\"\n | \"sdk_error\";\n\nexport interface EventContext {\n route?: string;\n component?: string;\n platform?: string;\n appVersion?: string;\n metadata?: Record<string, string | number | boolean>;\n}\n\nexport interface AppDoctorEventBase {\n name: AppDoctorEventName;\n /** Unix ms */\n timestamp: number;\n sessionId: string;\n tags?: Record<string, string>;\n context?: EventContext;\n}\n\nexport interface ScreenLoadEvent extends AppDoctorEventBase {\n name: \"screen_load\";\n screen: string;\n phase: \"start\" | \"ready\";\n /** Time from navigation intent to ready signal (ms), if known */\n durationMs: number;\n}\n\nexport interface NetworkRequestEvent extends AppDoctorEventBase {\n name: \"api_request\";\n method: string;\n url: string;\n status?: number;\n durationMs: number;\n success: boolean;\n errorMessage?: string;\n}\n\nexport interface RenderCountEvent extends AppDoctorEventBase {\n name: \"render_event\";\n component: string;\n renderCount: number;\n}\n\nexport interface SdkErrorEvent extends AppDoctorEventBase {\n name: \"sdk_error\";\n message: string;\n stack?: string;\n errorContext?: string;\n}\n\nexport type AppDoctorEvent =\n | ScreenLoadEvent\n | NetworkRequestEvent\n | RenderCountEvent\n | SdkErrorEvent;\n\n/** Payload accepted by `AppDoctorClient.emit` (union; avoids `Omit` pitfalls on unions). */\nexport type AppDoctorEmitInput =\n | Omit<ScreenLoadEvent, \"timestamp\" | \"sessionId\">\n | Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\">\n | Omit<RenderCountEvent, \"timestamp\" | \"sessionId\">\n | Omit<SdkErrorEvent, \"timestamp\" | \"sessionId\">;\n\nexport interface Transport {\n send(events: readonly AppDoctorEvent[]): void | Promise<void>;\n flush?: () => void | Promise<void>;\n shutdown?: () => void | Promise<void>;\n}\n\nexport interface AppDoctorConfig {\n enabled?: boolean;\n /** Shortcut to disable all internal work while preserving integration calls */\n noop?: boolean;\n /** 0–1, default 1 */\n sampleRate?: number;\n flushIntervalMs?: number;\n maxQueueSize?: number;\n transports?: Transport[];\n tags?: Record<string, string>;\n /** Add context to every emitted event */\n context?: EventContext;\n /** When true, wraps global fetch (restore on shutdown) */\n instrumentFetch?: boolean;\n /**\n * Redacts/sanitizes network event fields before emission.\n * Returning partial values overrides the computed payload.\n */\n redactNetworkEvent?: (\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ) => Partial<Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">>;\n}\n\nexport const DEFAULT_FLUSH_INTERVAL_MS = 2000;\nexport const DEFAULT_MAX_QUEUE_SIZE = 200;\n","export function shouldSample(rate: number): boolean {\n if (rate >= 1) return true;\n if (rate <= 0) return false;\n return Math.random() < rate;\n}\n","import type {\n AppDoctorConfig,\n AppDoctorEmitInput,\n AppDoctorEvent,\n NetworkRequestEvent,\n SdkErrorEvent,\n Transport,\n} from \"./types.js\";\nimport {\n DEFAULT_FLUSH_INTERVAL_MS,\n DEFAULT_MAX_QUEUE_SIZE,\n} from \"./types.js\";\nimport { shouldSample } from \"./sampling.js\";\n\nfunction now(): number {\n return Date.now();\n}\n\nfunction newSessionId(): string {\n return `${now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nexport class AppDoctorClient {\n readonly sessionId: string;\n private readonly config: Required<\n Pick<\n AppDoctorConfig,\n | \"enabled\"\n | \"noop\"\n | \"sampleRate\"\n | \"flushIntervalMs\"\n | \"maxQueueSize\"\n | \"instrumentFetch\"\n >\n > &\n Pick<AppDoctorConfig, \"tags\" | \"context\" | \"redactNetworkEvent\">;\n private readonly transports: Transport[];\n private queue: AppDoctorEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | undefined;\n private fetchPatched = false;\n private originalFetch: typeof fetch | undefined;\n\n constructor(config: AppDoctorConfig = {}) {\n this.sessionId = newSessionId();\n this.config = {\n enabled: config.enabled ?? true,\n noop: config.noop ?? false,\n sampleRate: config.sampleRate ?? 1,\n flushIntervalMs: config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS,\n maxQueueSize: config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,\n instrumentFetch: config.instrumentFetch ?? true,\n tags: config.tags,\n context: config.context,\n redactNetworkEvent: config.redactNetworkEvent,\n };\n this.transports = [...(config.transports ?? [])];\n if (this.config.enabled) {\n this.startFlushTimer();\n if (this.config.instrumentFetch && typeof globalThis.fetch === \"function\") {\n this.patchFetch();\n }\n }\n }\n\n updateConfig(partial: Partial<AppDoctorConfig>): void {\n if (partial.enabled !== undefined) {\n (this.config as { enabled: boolean }).enabled = partial.enabled;\n }\n if (partial.noop !== undefined) {\n (this.config as { noop: boolean }).noop = partial.noop;\n }\n if (partial.sampleRate !== undefined) {\n (this.config as { sampleRate: number }).sampleRate = partial.sampleRate;\n }\n if (partial.flushIntervalMs !== undefined) {\n (this.config as { flushIntervalMs: number }).flushIntervalMs =\n partial.flushIntervalMs;\n this.restartFlushTimer();\n }\n if (partial.maxQueueSize !== undefined) {\n (this.config as { maxQueueSize: number }).maxQueueSize =\n partial.maxQueueSize;\n }\n if (partial.tags !== undefined) {\n this.config.tags = partial.tags;\n }\n if (partial.context !== undefined) {\n this.config.context = partial.context;\n }\n if (partial.instrumentFetch !== undefined) {\n const next = partial.instrumentFetch;\n (this.config as { instrumentFetch: boolean }).instrumentFetch = next;\n if (next && !this.fetchPatched) this.patchFetch();\n if (!next && this.fetchPatched) this.restoreFetch();\n }\n if (partial.transports !== undefined) {\n this.transports.length = 0;\n this.transports.push(...partial.transports);\n }\n if (partial.redactNetworkEvent !== undefined) {\n this.config.redactNetworkEvent = partial.redactNetworkEvent;\n }\n }\n\n addTransport(transport: Transport): void {\n this.transports.push(transport);\n }\n\n emit(event: AppDoctorEmitInput): void {\n if (!this.config.enabled) return;\n if (this.config.noop) return;\n if (!shouldSample(this.config.sampleRate)) return;\n\n const { tags: eventTags, ...payload } = event;\n const tags =\n this.config.tags || eventTags\n ? { ...this.config.tags, ...eventTags }\n : undefined;\n\n const full = {\n ...payload,\n timestamp: now(),\n sessionId: this.sessionId,\n tags,\n context: this.config.context,\n } as AppDoctorEvent;\n\n this.enqueue(full);\n }\n\n private enqueue(event: AppDoctorEvent): void {\n if (this.queue.length >= this.config.maxQueueSize) {\n this.queue.shift();\n }\n this.queue.push(event);\n }\n\n captureError(message: string, context?: string, err?: unknown): void {\n const stack =\n err instanceof Error && typeof err.stack === \"string\"\n ? err.stack\n : undefined;\n const sdkErr: Omit<SdkErrorEvent, \"timestamp\" | \"sessionId\" | \"tags\"> = {\n name: \"sdk_error\",\n message,\n stack,\n errorContext: context,\n };\n this.emit(sdkErr);\n }\n\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n const batch = this.queue.splice(0, this.queue.length);\n await Promise.all(\n this.transports.map(async (t) => {\n try {\n await t.send(batch);\n } catch (e) {\n this.safeInternalError(\"transport.send failed\", e);\n }\n }),\n );\n }\n\n async shutdown(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = undefined;\n }\n this.restoreFetch();\n await this.flush();\n await Promise.all(\n this.transports.map(async (t) => {\n if (!t.shutdown) return;\n try {\n await t.shutdown();\n } catch (e) {\n this.safeInternalError(\"transport.shutdown failed\", e);\n }\n }),\n );\n }\n\n private startFlushTimer(): void {\n this.flushTimer = setInterval(() => {\n void this.flush();\n }, this.config.flushIntervalMs);\n if (\n typeof this.flushTimer === \"object\" &&\n this.flushTimer &&\n \"unref\" in this.flushTimer &&\n typeof this.flushTimer.unref === \"function\"\n ) {\n this.flushTimer.unref();\n }\n }\n\n private restartFlushTimer(): void {\n if (this.flushTimer) clearInterval(this.flushTimer);\n this.startFlushTimer();\n }\n\n private safeInternalError(msg: string, err: unknown): void {\n const dev =\n typeof __DEV__ !== \"undefined\"\n ? __DEV__\n : typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\";\n if (dev) console.warn(`[AppDoctor] ${msg}`, err);\n }\n\n private patchFetch(): void {\n if (this.fetchPatched) return;\n const g = globalThis as { fetch: typeof fetch };\n this.originalFetch = g.fetch.bind(globalThis);\n const originalFetch = this.originalFetch;\n g.fetch = async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n const start = now();\n let method = \"GET\";\n let url = \"\";\n try {\n if (typeof input === \"string\") {\n url = input;\n } else if (input instanceof URL) {\n url = input.href;\n } else if (typeof Request !== \"undefined\" && input instanceof Request) {\n url = input.url;\n method = input.method;\n }\n if (init?.method) method = init.method;\n } catch {\n /* ignore */\n }\n\n const urlForEvent =\n url ||\n (typeof input === \"string\"\n ? input\n : input instanceof URL\n ? input.href\n : typeof Request !== \"undefined\" && input instanceof Request\n ? input.url\n : \"[request]\");\n\n try {\n const res = await originalFetch(input, init);\n const durationMs = now() - start;\n const event = this.applyNetworkRedaction({\n method,\n url: urlForEvent,\n status: res.status,\n durationMs,\n success: true,\n });\n this.emit({\n name: \"api_request\",\n ...event,\n });\n return res;\n } catch (e) {\n const durationMs = now() - start;\n const event = this.applyNetworkRedaction({\n method,\n url: urlForEvent,\n durationMs,\n success: false,\n errorMessage: e instanceof Error ? e.message : String(e),\n });\n this.emit({\n name: \"api_request\",\n ...event,\n });\n throw e;\n }\n };\n this.fetchPatched = true;\n }\n\n private restoreFetch(): void {\n if (!this.fetchPatched || !this.originalFetch) return;\n (globalThis as { fetch: typeof fetch }).fetch = this.originalFetch;\n this.fetchPatched = false;\n this.originalFetch = undefined;\n }\n\n private applyNetworkRedaction(\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ): Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\"> {\n if (!this.config.redactNetworkEvent) return event;\n const redacted = this.config.redactNetworkEvent(event);\n return { ...event, ...redacted };\n }\n}\n","import type { AppDoctorEvent, Transport } from \"../core/types.js\";\n\nexport interface ConsoleTransportOptions {\n label?: string;\n slowScreenThresholdMs?: number;\n slowApiThresholdMs?: number;\n format?: \"pretty\" | \"raw\";\n}\n\nexport function createConsoleTransport(\n options: ConsoleTransportOptions = {},\n): Transport {\n const label = options.label ?? \"AppDoctor\";\n const slowScreenThresholdMs = options.slowScreenThresholdMs ?? 1000;\n const slowApiThresholdMs = options.slowApiThresholdMs ?? 800;\n const format = options.format ?? \"pretty\";\n\n function toHint(event: AppDoctorEvent): string | undefined {\n if (\n event.name === \"screen_load\" &&\n event.phase === \"ready\" &&\n event.durationMs >= slowScreenThresholdMs\n ) {\n return `Slow screen \"${event.screen}\" (${event.durationMs}ms). Check expensive effects and repeated renders.`;\n }\n if (event.name === \"api_request\" && event.durationMs >= slowApiThresholdMs) {\n return `Slow API \"${event.method} ${event.url}\" (${event.durationMs}ms). Check server latency, payload size, and retry loops.`;\n }\n return undefined;\n }\n\n function toPrettyLine(event: AppDoctorEvent): string {\n if (event.name === \"screen_load\") {\n return `screen ${event.screen} phase=${event.phase} duration=${event.durationMs}ms`;\n }\n if (event.name === \"api_request\") {\n const status = event.status ?? \"n/a\";\n return `api ${event.method} ${event.url} status=${status} duration=${event.durationMs}ms success=${event.success}`;\n }\n if (event.name === \"render_event\") {\n return `render ${event.component} count=${event.renderCount}`;\n }\n return `sdk_error ${event.message}`;\n }\n\n return {\n send(events: readonly AppDoctorEvent[]): void {\n if (events.length === 0) return;\n for (const event of events) {\n const hint = toHint(event);\n if (hint) {\n console.warn(`[${label}] ${hint}`);\n }\n if (format === \"pretty\") {\n console.log(`[${label}] ${toPrettyLine(event)}`);\n }\n }\n if (format === \"raw\") {\n console.log(`[${label}]`, events);\n }\n },\n };\n}\n","import type { AppDoctorEvent, Transport } from \"../core/types.js\";\n\nexport interface HttpTransportOptions {\n url: string;\n headers?: Record<string, string>;\n maxRetries?: number;\n /** ms */\n initialBackoffMs?: number;\n getExtraBody?: () => Record<string, unknown>;\n}\n\nasync function sleep(ms: number): Promise<void> {\n await new Promise((r) => setTimeout(r, ms));\n}\n\nexport function createHttpTransport(options: HttpTransportOptions): Transport {\n const maxRetries = options.maxRetries ?? 3;\n const initialBackoffMs = options.initialBackoffMs ?? 500;\n\n return {\n async send(events: readonly AppDoctorEvent[]): Promise<void> {\n if (events.length === 0) return;\n const body = {\n events: [...events],\n ...(options.getExtraBody?.() ?? {}),\n };\n let attempt = 0;\n let backoff = initialBackoffMs;\n for (;;) {\n try {\n const res = await fetch(options.url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n body: JSON.stringify(body),\n });\n if (res.ok) return;\n if (res.status >= 400 && res.status < 500 && res.status !== 429) {\n return;\n }\n } catch {\n /* retry */\n }\n attempt += 1;\n if (attempt > maxRetries) return;\n await sleep(backoff);\n backoff *= 2;\n }\n },\n };\n}\n","/**\n * Best-effort active route name from React Navigation state tree.\n * Avoids a hard dependency on `@react-navigation/native` types at runtime.\n */\nexport function getActiveRouteName(state: unknown): string | undefined {\n if (!state || typeof state !== \"object\") return undefined;\n const s = state as {\n index?: number;\n routes?: Array<{ name?: string; state?: unknown }>;\n };\n if (!Array.isArray(s.routes) || typeof s.index !== \"number\") return undefined;\n const route = s.routes[s.index];\n if (!route || typeof route !== \"object\") return undefined;\n if (route.state !== undefined) return getActiveRouteName(route.state);\n return typeof route.name === \"string\" ? route.name : undefined;\n}\n","import type { AppDoctorClient } from \"../core/client.js\";\nimport { getActiveRouteName } from \"./route-name.js\";\n\nexport interface NavigationTimingState {\n lastScreen?: string;\n}\n\n/**\n * Pass the returned listener to `NavigationContainer` as `onStateChange`.\n * Emits `screen_load` when the active route changes and a ready event after the next microtask\n * (approximates first paint/commit after navigation state updates).\n */\nexport function createNavigationStateListener(client: AppDoctorClient): {\n listener: (state: unknown) => void;\n reset: () => void;\n} {\n const timing: NavigationTimingState = {};\n\n return {\n listener(state: unknown) {\n const screen = getActiveRouteName(state);\n if (!screen || screen === timing.lastScreen) return;\n\n timing.lastScreen = screen;\n const startedAt = Date.now();\n\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"start\",\n durationMs: 0,\n });\n\n queueMicrotask(() => {\n if (timing.lastScreen !== screen) return;\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"ready\",\n durationMs: Date.now() - startedAt,\n });\n });\n },\n reset() {\n timing.lastScreen = undefined;\n },\n };\n}\n","import type { AppDoctorClient } from \"../core/client.js\";\nimport type { NetworkRequestEvent } from \"../core/types.js\";\n\n/** Minimal Axios shape to avoid requiring `axios` as a dependency. */\nexport interface AxiosLike {\n interceptors: {\n request: {\n use: (onFulfilled: (value: unknown) => unknown) => number;\n eject: (id: number) => void;\n };\n response: {\n use: (\n onFulfilled: (value: unknown) => unknown,\n onRejected?: (error: unknown) => unknown,\n ) => number;\n eject: (id: number) => void;\n };\n };\n}\n\nexport interface AxiosInstrumentationOptions {\n redactNetworkEvent?: (\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ) => Partial<Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">>;\n}\n\nfunction getConfig(errorOrResponse: unknown): unknown {\n if (!errorOrResponse || typeof errorOrResponse !== \"object\") return undefined;\n if (\n \"config\" in errorOrResponse &&\n errorOrResponse.config &&\n typeof errorOrResponse.config === \"object\"\n ) {\n return errorOrResponse.config;\n }\n return undefined;\n}\n\nfunction getResponseMeta(response: unknown): {\n status?: number;\n method?: string;\n url?: string;\n} {\n if (!response || typeof response !== \"object\") return {};\n const r = response;\n const status =\n \"status\" in r && typeof r.status === \"number\" ? r.status : undefined;\n const rawConfig = \"config\" in r ? r.config : undefined;\n let method: string | undefined;\n let url: string | undefined;\n if (rawConfig !== null && typeof rawConfig === \"object\") {\n if (\"method\" in rawConfig && typeof rawConfig.method === \"string\") {\n method = rawConfig.method;\n }\n if (\"url\" in rawConfig && typeof rawConfig.url === \"string\") {\n url = rawConfig.url;\n }\n }\n return {\n status,\n method,\n url,\n };\n}\n\n/**\n * Returns an `eject` function to remove interceptors.\n */\nexport function instrumentAxios(\n axios: AxiosLike,\n client: AppDoctorClient,\n options: AxiosInstrumentationOptions = {},\n): () => void {\n function applyRedaction(\n event: Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\">,\n ): Omit<NetworkRequestEvent, \"timestamp\" | \"sessionId\" | \"name\"> {\n const redacted = options.redactNetworkEvent?.(event);\n return redacted ? { ...event, ...redacted } : event;\n }\n\n const starts = new WeakMap<object, number>();\n\n const reqId = axios.interceptors.request.use((config) => {\n if (config !== null && typeof config === \"object\") {\n starts.set(config, Date.now());\n }\n return config;\n });\n\n const resId = axios.interceptors.response.use(\n (response) => {\n const cfg = getConfig(response);\n if (cfg !== null && typeof cfg === \"object\") {\n const start = starts.get(cfg);\n if (start !== undefined) {\n const { status, method, url } = getResponseMeta(response);\n const event = applyRedaction({\n method: (method ?? \"GET\").toUpperCase(),\n url: url ?? \"\",\n status,\n durationMs: Date.now() - start,\n success: true,\n });\n client.emit({\n name: \"api_request\",\n ...event,\n });\n starts.delete(cfg);\n }\n }\n return response;\n },\n (error: unknown) => {\n const cfg = getConfig(error);\n if (cfg !== null && typeof cfg === \"object\") {\n const start = starts.get(cfg);\n if (start !== undefined) {\n let method = \"GET\";\n let url = \"\";\n let status: number | undefined;\n let errorMessage = String(error);\n if (error !== null && typeof error === \"object\") {\n if (\"message\" in error && typeof error.message === \"string\") {\n errorMessage = error.message;\n }\n const resp =\n \"response\" in error && error.response !== null &&\n typeof error.response === \"object\"\n ? error.response\n : undefined;\n if (\n resp &&\n \"status\" in resp &&\n typeof resp.status === \"number\"\n ) {\n status = resp.status;\n }\n const errCfg =\n \"config\" in error && error.config !== null &&\n typeof error.config === \"object\"\n ? error.config\n : undefined;\n if (errCfg) {\n if (\"method\" in errCfg && typeof errCfg.method === \"string\") {\n method = errCfg.method.toUpperCase();\n }\n if (\"url\" in errCfg && typeof errCfg.url === \"string\") {\n url = errCfg.url;\n }\n }\n }\n const event = applyRedaction({\n method,\n url,\n status,\n durationMs: Date.now() - start,\n success: false,\n errorMessage,\n });\n client.emit({\n name: \"api_request\",\n ...event,\n });\n starts.delete(cfg);\n }\n }\n const rejectReason =\n error instanceof Error ? error : new Error(String(error));\n return Promise.reject(rejectReason);\n },\n );\n\n return () => {\n axios.interceptors.request.eject(reqId);\n axios.interceptors.response.eject(resId);\n };\n}\n","import type { AppDoctorClient } from \"../core/client.js\";\n\n/**\n * Wraps an async call and emits an `api_request` event with a synthetic URL label.\n */\nexport async function trackApi<T>(\n client: AppDoctorClient,\n label: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const start = Date.now();\n try {\n const result = await fn();\n client.emit({\n name: \"api_request\",\n method: \"TRACK\",\n url: label,\n status: 200,\n durationMs: Date.now() - start,\n success: true,\n });\n return result;\n } catch (e) {\n client.emit({\n name: \"api_request\",\n method: \"TRACK\",\n url: label,\n durationMs: Date.now() - start,\n success: false,\n errorMessage: e instanceof Error ? e.message : String(e),\n });\n throw e;\n }\n}\n","import { createContext } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\n\nexport const AppDoctorContext = createContext<AppDoctorClient | null>(null);\n","import { useEffect, useRef, type ReactElement, type ReactNode } from \"react\";\nimport { AppDoctorClient } from \"../core/client.js\";\nimport type { AppDoctorConfig } from \"../core/types.js\";\nimport { AppDoctorContext } from \"./context.js\";\n\nexport type AppDoctorProviderProps = AppDoctorConfig & {\n children: ReactNode;\n};\n\nexport function AppDoctorProvider({\n children,\n ...config\n}: AppDoctorProviderProps): ReactElement {\n const clientRef = useRef<AppDoctorClient | null>(null);\n if (!clientRef.current) {\n clientRef.current = new AppDoctorClient(config);\n }\n\n const {\n enabled,\n noop,\n sampleRate,\n flushIntervalMs,\n maxQueueSize,\n instrumentFetch,\n tags,\n context,\n transports,\n redactNetworkEvent,\n } = config;\n\n useEffect(() => {\n clientRef.current?.updateConfig({\n enabled,\n noop,\n sampleRate,\n flushIntervalMs,\n maxQueueSize,\n instrumentFetch,\n tags,\n context,\n transports,\n redactNetworkEvent,\n });\n }, [\n enabled,\n noop,\n sampleRate,\n flushIntervalMs,\n maxQueueSize,\n instrumentFetch,\n tags,\n context,\n transports,\n redactNetworkEvent,\n ]);\n\n useEffect(() => {\n return () => {\n void clientRef.current?.shutdown();\n clientRef.current = null;\n };\n }, []);\n\n return (\n <AppDoctorContext.Provider value={clientRef.current}>\n {children}\n </AppDoctorContext.Provider>\n );\n}\n","import { useContext } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\nimport { AppDoctorContext } from \"../provider/context.js\";\n\nexport function useAppDoctor(): AppDoctorClient {\n const client = useContext(AppDoctorContext);\n if (!client) {\n throw new Error(\"useAppDoctor must be used within AppDoctorProvider\");\n }\n return client;\n}\n","import { useContext, useEffect } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\nimport { AppDoctorContext } from \"../provider/context.js\";\n\nexport interface UseTrackScreenOptions {\n client?: AppDoctorClient;\n}\n\n/**\n * Emits `screen_load` lifecycle events on mount and ready.\n */\nexport function useTrackScreen(\n screen: string,\n options: UseTrackScreenOptions = {},\n): void {\n const fromContext = useContext(AppDoctorContext);\n const client = options.client ?? fromContext;\n if (!client) {\n throw new Error(\n \"useTrackScreen requires AppDoctorProvider or options.client\",\n );\n }\n\n useEffect(() => {\n const startedAt = Date.now();\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"start\",\n durationMs: 0,\n });\n queueMicrotask(() => {\n client.emit({\n name: \"screen_load\",\n screen,\n phase: \"ready\",\n durationMs: Date.now() - startedAt,\n });\n });\n }, [client, screen]);\n}\n","import { useContext, useEffect, useRef } from \"react\";\nimport type { AppDoctorClient } from \"../core/client.js\";\nimport { AppDoctorContext } from \"../provider/context.js\";\n\nexport interface UseTrackRenderOptions {\n client?: AppDoctorClient;\n /** Emit every N renders (default 10) to limit noise */\n every?: number;\n}\n\n/**\n * Emits `render_event` periodically when the component re-renders.\n */\nexport function useTrackRender(\n componentName: string,\n options: UseTrackRenderOptions = {},\n): void {\n const fromContext = useContext(AppDoctorContext);\n const client = options.client ?? fromContext;\n if (!client) {\n throw new Error(\n \"useTrackRender requires AppDoctorProvider or options.client\",\n );\n }\n const every = options.every ?? 10;\n const count = useRef(0);\n\n count.current += 1;\n\n useEffect(() => {\n if (count.current % every !== 0) return;\n client.emit({\n name: \"render_event\",\n component: componentName,\n renderCount: count.current,\n });\n });\n}\n"]}
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "appdoctor-rn",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Lightweight React Native performance and observability SDK",
5
5
  "license": "MIT",
6
- "author": "",
6
+ "author": {
7
+ "name": "Kelvin Ajayi",
8
+ "url": "https://github.com/scryptwiz"
9
+ },
7
10
  "repository": {
8
11
  "type": "git",
9
12
  "url": "git+https://github.com/scryptwiz/appdoctor-rn.git"