crumbtrail-core 0.1.0

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.
@@ -0,0 +1,960 @@
1
+ interface PersistedSession {
2
+ id: string;
3
+ lastActivity: number;
4
+ }
5
+ interface SessionStore {
6
+ read(): PersistedSession | undefined;
7
+ write(session: PersistedSession): void;
8
+ }
9
+ declare const DEFAULT_SESSION_STORAGE_KEY = "__crumbtrail_session";
10
+ declare function createWebSessionStore(storage?: Pick<Storage, "getItem" | "setItem">, key?: string): SessionStore | undefined;
11
+
12
+ declare const CRUMBTRAIL_SCHEMA_VERSION: 1;
13
+ declare const CRUMBTRAIL_EVENT_KINDS: {
14
+ readonly navigation: "navigation";
15
+ readonly appLifecycle: "app-lifecycle";
16
+ readonly nativeCrash: "native-crash";
17
+ readonly viewSnapshot: "view-snapshot";
18
+ };
19
+ type CrumbtrailPlatform = "web" | "react-native" | "ios" | "android" | "flutter" | "webview" | "node";
20
+ interface CrumbtrailSdkDescriptor {
21
+ name: string;
22
+ version?: string;
23
+ }
24
+ type CrumbtrailCapabilities = string[];
25
+ type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> & {
26
+ [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>;
27
+ }[Keys];
28
+ type TargetDescriptorIdentityKey = "role" | "label" | "testID" | "accessibilityId" | "componentName" | "routePath" | "ancestryHash";
29
+ interface TargetDescriptorBase {
30
+ /** Semantic role of the target, e.g. button, link, textbox, screen, view. */
31
+ role?: string;
32
+ /** Human-readable label visible to or announced for the user. */
33
+ label?: string;
34
+ /** Stable test identifier supplied by the app. */
35
+ testID?: string;
36
+ /** Stable accessibility/native identifier supplied by the app. */
37
+ accessibilityId?: string;
38
+ /** Framework component or native view name. */
39
+ componentName?: string;
40
+ /** App route or screen path where the target appears. */
41
+ routePath?: string;
42
+ /** Privacy-safe hash of the target's ancestor path. */
43
+ ancestryHash?: string;
44
+ bounds?: {
45
+ x: number;
46
+ y: number;
47
+ width: number;
48
+ height: number;
49
+ };
50
+ redaction?: unknown;
51
+ /** @deprecated Use testID. Accepted while legacy web/mobile emitters migrate. */
52
+ testId?: string;
53
+ /** @deprecated Use accessibilityId or label. Accepted while legacy emitters migrate. */
54
+ accessibilityLabel?: string;
55
+ /** @deprecated Use label. Accepted while legacy web emitters migrate. */
56
+ text?: string;
57
+ /** @deprecated Use componentName. Accepted while legacy emitters migrate. */
58
+ viewName?: string;
59
+ /** @deprecated Use routePath. Accepted while legacy emitters migrate. */
60
+ screen?: string;
61
+ /** @deprecated Accepted while legacy web emitters migrate. */
62
+ selector?: string;
63
+ }
64
+ type TargetDescriptor = RequireAtLeastOne<TargetDescriptorBase, TargetDescriptorIdentityKey>;
65
+ interface BugEvent {
66
+ /** Unix timestamp in milliseconds */
67
+ t: number;
68
+ /** Event category short code */
69
+ k: string;
70
+ /** Type-specific payload */
71
+ d: Record<string, unknown>;
72
+ /** Version of the shared event envelope. Missing means v1 for backward compatibility. */
73
+ schemaVersion?: typeof CRUMBTRAIL_SCHEMA_VERSION;
74
+ /** Source runtime. Missing means `web` for backward compatibility with current browser SDKs. */
75
+ platform?: CrumbtrailPlatform;
76
+ /** SDK identity for non-browser and future SDKs. */
77
+ sdk?: CrumbtrailSdkDescriptor;
78
+ /** Optional capability names enabled by the emitting SDK/session. */
79
+ capabilities?: CrumbtrailCapabilities;
80
+ /** Optional normalized target reference for web/mobile events. */
81
+ target?: TargetDescriptor;
82
+ /** Active recording session identifier when an extension workflow session owns the event. */
83
+ sessionId?: string;
84
+ /** Milliseconds elapsed from the active recording session's canonical startedAt timestamp. */
85
+ offsetMs?: number;
86
+ }
87
+ interface BugReport {
88
+ bugId: string;
89
+ sessionId: string;
90
+ flaggedAt: number;
91
+ windowMs: number;
92
+ note?: string;
93
+ voiceNote?: string;
94
+ url: string;
95
+ userAgent: string;
96
+ tags?: string[];
97
+ summary: {
98
+ errorCount: number;
99
+ failedRequestCount: number;
100
+ eventCount: number;
101
+ eventKinds: Record<string, number>;
102
+ durationMs: number;
103
+ stateProviderCount?: number;
104
+ };
105
+ }
106
+ /** Canonical event kind for a database row diff (`k:'db.diff'`). */
107
+ declare const DB_DIFF_EVENT_KIND: "db.diff";
108
+ /**
109
+ * Canonical event kind for an aggregate capped database diff summary (`k:'db.diff.bulk'`).
110
+ * The comparator intentionally ignores this kind: over-cap statements are treated as batch work,
111
+ * while per-row `db.diff` events remain the UI-flow comparison signal.
112
+ */
113
+ declare const DB_DIFF_BULK_EVENT_KIND: "db.diff.bulk";
114
+ /** Canonical event kind for a capped database read row (`k:'db.read'`). */
115
+ declare const DB_READ_EVENT_KIND: "db.read";
116
+ /** Canonical event kind for an aggregate capped database read summary (`k:'db.read.bulk'`). */
117
+ declare const DB_READ_BULK_EVENT_KIND: "db.read.bulk";
118
+ /** Mutating operation a `db.diff` event records. */
119
+ type DbDiffOp = "insert" | "update" | "delete";
120
+ /**
121
+ * Database engine that produced a `db.diff` / `db.read` event. Downstream consumers (evidence
122
+ * index, fix-context, comparator) treat every engine identically — the engine tag exists so
123
+ * agents and humans know which dialect the captured statement ran against.
124
+ */
125
+ type DbEngine = "postgres" | "mysql" | "mssql" | "sqlite";
126
+ /**
127
+ * Type-specific payload (`d`) of a `k:'db.diff'` event: the row(s) that changed for one
128
+ * mutating statement, correlated to the request that caused them via `requestId` (which equals
129
+ * the active request's traceId per the correlation bridge in `correlation.ts`). `after` carries
130
+ * the post-image (insert/update); `before` is the pre-image, only present when before-capture is
131
+ * enabled (and for deletes it carries the removed row). Sensitive columns are redacted out of
132
+ * `after`/`before`/`pk` before the event ever rests. The shape is identical across engines; only
133
+ * the `engine` tag differs.
134
+ */
135
+ interface DbDiffEventData {
136
+ engine: DbEngine;
137
+ op: DbDiffOp;
138
+ table: string;
139
+ /** Primary-key column→value map identifying the affected row, or `null` when unresolved. */
140
+ pk: Record<string, unknown> | null;
141
+ /** Post-image of the affected row (insert/update); omitted for deletes. */
142
+ after?: Record<string, unknown>;
143
+ /** Pre-image of the affected row; only captured behind the before-capture flag (and for deletes). */
144
+ before?: Record<string, unknown>;
145
+ /**
146
+ * Present only on an image-less statement-level fallback event where per-row images were
147
+ * unobtainable (e.g. a MySQL multi-row insert). Such events carry `pk: null` and no
148
+ * `after`/`before`; this records how many rows the statement changed so the write stays visible.
149
+ */
150
+ rowCount?: number;
151
+ /** Correlation id; equals the active request's traceId so it lands in the same evidence window. */
152
+ requestId: string;
153
+ /** Redaction metadata for any column-level values dropped/masked before rest. */
154
+ redaction?: unknown;
155
+ }
156
+ /**
157
+ * Type-specific payload (`d`) of a `k:'db.diff.bulk'` event emitted when a mutating statement
158
+ * affects more rows than the configured per-statement `db.diff` cap. It summarizes truncation
159
+ * without duplicating every changed row payload.
160
+ */
161
+ interface DbDiffBulkEventData {
162
+ engine: DbEngine;
163
+ op: DbDiffOp;
164
+ table: string;
165
+ requestId: string;
166
+ rowCount: number;
167
+ emittedRows: number;
168
+ truncatedRows: number;
169
+ samplePks: Array<Record<string, unknown>>;
170
+ }
171
+ /**
172
+ * Type-specific payload (`d`) of a `k:'db.read'` event: one redacted row read by a SELECT within
173
+ * an active request scope. Disabled by default because read capture can increase PII surface.
174
+ */
175
+ interface DbReadEventData {
176
+ engine: DbEngine;
177
+ table: string;
178
+ pk: Record<string, unknown> | null;
179
+ row: Record<string, unknown>;
180
+ requestId: string;
181
+ redaction?: unknown;
182
+ }
183
+ /**
184
+ * Type-specific payload (`d`) of a `k:'db.read.bulk'` event emitted when a SELECT returns more rows
185
+ * than the configured read cap. It proves read volume without resting every row.
186
+ */
187
+ interface DbReadBulkEventData {
188
+ engine: DbEngine;
189
+ table: string;
190
+ requestId: string;
191
+ rowCount: number;
192
+ emittedRows: number;
193
+ truncatedRows: number;
194
+ samplePks: Array<Record<string, unknown>>;
195
+ }
196
+ type InteractionElementDescriptor = Record<string, unknown>;
197
+ type InteractionElementDescriptorFactory = (element: Element) => InteractionElementDescriptor;
198
+ /**
199
+ * Declarative environment input the host app passes to `logger.setEnv`. Both fields are
200
+ * vendor-agnostic free-form maps (no LaunchDarkly/PostHog adapters). Values are redacted
201
+ * through the browser redaction policy before they rest in any `k:'env'` event.
202
+ */
203
+ interface EnvDeclaration {
204
+ /** Active feature flags, e.g. `{ newCheckout: true, plan: 'pro' }`. */
205
+ flags?: Record<string, unknown>;
206
+ /** Runtime config the app wants attached to the session, e.g. `{ region: 'eu' }`. */
207
+ config?: Record<string, unknown>;
208
+ }
209
+ /**
210
+ * Redaction-aware environment snapshot captured once at session start (the `d` payload of a
211
+ * `k:'env'` event with `kind:'snapshot'`). Browser/device fields are best-effort and guarded
212
+ * for non-browser/SSR runtimes; `locale`/`timezone` are available in Node via `Intl`.
213
+ */
214
+ interface EnvSnapshot {
215
+ /** Discriminates the initial full snapshot from later `setEnv` deltas. */
216
+ kind: "snapshot" | "delta";
217
+ userAgent?: string;
218
+ browser?: {
219
+ name: string;
220
+ version?: string;
221
+ };
222
+ os?: string;
223
+ viewport?: {
224
+ w: number;
225
+ h: number;
226
+ };
227
+ locale?: string;
228
+ timezone?: string;
229
+ /** Redacted feature flags declared via `setEnv`. */
230
+ flags?: Record<string, unknown>;
231
+ /** Redacted runtime config declared via `setEnv`. */
232
+ config?: Record<string, unknown>;
233
+ /** Browser redaction metadata for any redacted flag/config values. */
234
+ redaction?: unknown;
235
+ }
236
+ interface FlagBugOptions {
237
+ note?: string;
238
+ windowMs?: number;
239
+ tags?: string[];
240
+ voiceBlob?: Blob;
241
+ }
242
+ interface AddBugEventOptions {
243
+ type: string;
244
+ data: Record<string, unknown>;
245
+ schemaVersion?: BugEvent["schemaVersion"];
246
+ platform?: BugEvent["platform"];
247
+ sdk?: BugEvent["sdk"];
248
+ capabilities?: BugEvent["capabilities"];
249
+ target?: BugEvent["target"];
250
+ sessionId?: BugEvent["sessionId"];
251
+ offsetMs?: BugEvent["offsetMs"];
252
+ }
253
+ interface CrumbtrailConfig {
254
+ console: boolean;
255
+ network: boolean;
256
+ interactions: boolean;
257
+ keystrokes: boolean;
258
+ scroll: boolean;
259
+ visibility: boolean;
260
+ clipboard: boolean;
261
+ errors: boolean;
262
+ performance: boolean;
263
+ cookies: boolean;
264
+ storage: boolean;
265
+ video: boolean;
266
+ audio: boolean;
267
+ networkMaxBodySize: number;
268
+ networkExcludeUrls: string[];
269
+ networkCaptureHeaders: boolean;
270
+ networkCorrelationHeaders: boolean;
271
+ networkCorrelationAllowedOrigins: string[];
272
+ maskInputTypes: string[];
273
+ ignoreSelectors: string[];
274
+ describeInteractionElement?: InteractionElementDescriptorFactory;
275
+ keystrokeThrottleMs: number;
276
+ scrollThrottleMs: number;
277
+ scrollElements: string[];
278
+ clipboardMaxLength: number;
279
+ captureRawClipboard: boolean;
280
+ cookiePollIntervalMs: number;
281
+ cookieMaskNames: string[];
282
+ cookieValueMaxLength: number;
283
+ storageValueMaxLength: number;
284
+ storageExcludeKeys: string[];
285
+ captureIdb: boolean;
286
+ captureCacheApi: boolean;
287
+ videoBitsPerSecond: number;
288
+ audioBitsPerSecond: number;
289
+ mediaChunkIntervalMs: number;
290
+ ringBufferMs: number;
291
+ ringBufferMaxEvents: number;
292
+ heartbeat: boolean;
293
+ environment: boolean;
294
+ widget: boolean;
295
+ stateMaxBytes: number;
296
+ captureRawState: boolean;
297
+ autoFlagOnError: boolean;
298
+ autoFlagDebounceMs: number;
299
+ autoFlagMaxPerSession: number;
300
+ autoFlagOnSignals: boolean;
301
+ rageClickThreshold: number;
302
+ rageClickWindowMs: number;
303
+ retryStormThreshold: number;
304
+ retryStormWindowMs: number;
305
+ retryStormFailThreshold: number;
306
+ slowRequestMs: number;
307
+ slowRequestCount: number;
308
+ slowRequestWindowMs: number;
309
+ abandonedFlowWindowMs: number;
310
+ abandonedFlowMinInputs: number;
311
+ domSnapshot: boolean;
312
+ domSnapshotMaxBytes: number;
313
+ captureRawConsole: boolean;
314
+ captureRawErrors: boolean;
315
+ transport: "auto" | "tauri" | "http";
316
+ transportInstance?: CrumbtrailTransport;
317
+ httpEndpoint: string;
318
+ httpAuthToken?: string;
319
+ flushIntervalMs: number;
320
+ flushBufferSize: number;
321
+ sendBlob?: (name: string, blob: Blob) => void;
322
+ sessionPersistence: "session" | "memory" | "none";
323
+ sessionIdleMs: number;
324
+ sessionId?: string;
325
+ sessionStore?: SessionStore;
326
+ }
327
+ declare const DEFAULT_CONFIG: CrumbtrailConfig;
328
+ type CrumbtrailPreset = "full" | "light" | "passive";
329
+ declare const PRESET_FULL: Partial<CrumbtrailConfig>;
330
+ declare const PRESET_LIGHT: Partial<CrumbtrailConfig>;
331
+ declare const PRESET_PASSIVE: Partial<CrumbtrailConfig>;
332
+ type CollectorCleanup = () => void;
333
+ interface CollectorContext {
334
+ sessionId: string;
335
+ /**
336
+ * Returns env declared via `setEnv` before the snapshot is emitted so the environment
337
+ * collector can fold it into the initial `k:'env'` snapshot. Absent for other collectors.
338
+ */
339
+ getDeclaredEnv?: () => EnvDeclaration;
340
+ /** Called by the environment collector once the initial snapshot has been emitted. */
341
+ onEnvEmitted?: () => void;
342
+ /**
343
+ * Lets a collector expose live diagnostic state (e.g. in-flight requests) that is snapshotted
344
+ * at flag time through the same redaction/truncation path as app state providers.
345
+ */
346
+ registerStateProvider?: (name: string, provider: () => unknown) => () => void;
347
+ }
348
+ interface CrumbtrailTransport {
349
+ sendEvents(events: BugEvent[]): Promise<void>;
350
+ sendBlob(name: string, blob: Blob, metadata?: Record<string, unknown>): Promise<void>;
351
+ startSession(sessionId: string, metadata: Record<string, unknown>): Promise<void>;
352
+ endSession(sessionId: string): Promise<void>;
353
+ sendBugReport(report: BugReport, events: BugEvent[], voiceBlob?: Blob): Promise<void>;
354
+ }
355
+
356
+ declare class Crumbtrail {
357
+ private bus;
358
+ private transport;
359
+ private ringBuffer;
360
+ private cleanups;
361
+ private config;
362
+ private sessionId;
363
+ private widgetCleanup?;
364
+ private stateProviders;
365
+ private declaredFlags;
366
+ private declaredConfig;
367
+ private envEmitted;
368
+ private constructor();
369
+ static init(presetOrConfig?: CrumbtrailPreset | Partial<CrumbtrailConfig>): Crumbtrail;
370
+ flagBug(options?: FlagBugOptions): Promise<{
371
+ bugId: string;
372
+ }>;
373
+ mark(label: string): void;
374
+ addEvent(partial: AddBugEventOptions): void;
375
+ getSessionId(): string;
376
+ createRequestHeaders(requestId?: string): Record<string, string>;
377
+ pause(): void;
378
+ resume(): void;
379
+ registerStateProvider(name: string, provider: () => unknown): () => void;
380
+ /**
381
+ * Declaratively attach vendor-agnostic feature flags / config to the session environment.
382
+ * Values are redacted before they rest. Merges into the declared env; if the initial
383
+ * `k:'env'` snapshot has already been emitted (the normal case, since `setEnv` is called
384
+ * after `init`), it emits a `k:'env'` delta event ({ kind:'delta' }). If called before the
385
+ * snapshot is emitted (e.g. environment collector disabled or not yet run), the values are
386
+ * folded into the snapshot instead.
387
+ */
388
+ setEnv(declaration: EnvDeclaration): void;
389
+ stop(): Promise<{
390
+ sessionId: string;
391
+ }>;
392
+ }
393
+
394
+ declare class EventBus {
395
+ private listeners;
396
+ private taps;
397
+ private buffer;
398
+ private flushTimer;
399
+ private paused;
400
+ private flushBufferSize;
401
+ emit(event: BugEvent): void;
402
+ /**
403
+ * Observe every event synchronously at emit time, before batching. Unlike `subscribe`,
404
+ * taps see events immediately (triggers can't wait out a flush interval) and never
405
+ * receive batches.
406
+ */
407
+ tap(fn: (event: BugEvent) => void): () => void;
408
+ subscribe(fn: (events: BugEvent[]) => void): () => void;
409
+ flush(): void;
410
+ start(flushIntervalMs: number, flushBufferSize: number): void;
411
+ stop(): void;
412
+ pause(): void;
413
+ resume(): void;
414
+ }
415
+
416
+ declare class RingBuffer {
417
+ private events;
418
+ private maxMs;
419
+ private maxEvents;
420
+ constructor(maxMs?: number, maxEvents?: number);
421
+ push(event: BugEvent): void;
422
+ pushBatch(events: BugEvent[]): void;
423
+ snapshot(windowMs?: number): BugEvent[];
424
+ clear(): void;
425
+ get size(): number;
426
+ private evict;
427
+ }
428
+
429
+ interface HttpTransportOptions {
430
+ authToken?: string;
431
+ }
432
+ declare class HttpTransport implements CrumbtrailTransport {
433
+ private sessionId;
434
+ private authToken?;
435
+ private endpoint;
436
+ constructor(endpoint: string, options?: HttpTransportOptions);
437
+ private withAuthHeaders;
438
+ sendEvents(events: BugEvent[]): Promise<void>;
439
+ sendBlob(name: string, blob: Blob, metadata?: Record<string, unknown>): Promise<void>;
440
+ startSession(sessionId: string, metadata: Record<string, unknown>): Promise<void>;
441
+ sendBugReport(report: BugReport, events: BugEvent[], voiceBlob?: Blob): Promise<void>;
442
+ endSession(sessionId: string): Promise<void>;
443
+ }
444
+
445
+ /**
446
+ * Converts a runtime-specific target object into the shared, privacy-safe target descriptor.
447
+ * Mobile SDKs can implement this against native view metadata without depending on DOM APIs.
448
+ */
449
+ interface TargetDescriptorResolver<TTarget = unknown> {
450
+ resolve(target: TTarget): TargetDescriptor | undefined;
451
+ }
452
+ declare class WebTargetDescriptorResolver implements TargetDescriptorResolver<Element> {
453
+ resolve(element: Element): TargetDescriptor | undefined;
454
+ }
455
+ declare const webTargetDescriptorResolver: WebTargetDescriptorResolver;
456
+
457
+ declare const BROWSER_REDACTION_POLICY = "crumbtrail.browser-redaction.v1";
458
+ declare const REDACTED_VALUE = "[REDACTED]";
459
+ declare const REDACTED_STORAGE_KEY = "[REDACTED_KEY]";
460
+ type RedactionAction = "redacted" | "dropped" | "summarized";
461
+ interface RedactionField {
462
+ path: string;
463
+ reason: string;
464
+ action: RedactionAction;
465
+ }
466
+ interface PayloadSummary {
467
+ kind: "json" | "text" | "form" | "binary" | "stream" | "storage" | "cookie" | "input" | "unknown";
468
+ action: RedactionAction;
469
+ reason: string;
470
+ originalLength?: number;
471
+ contentLength?: string;
472
+ limit?: number;
473
+ redactedFields?: number;
474
+ }
475
+ interface RedactionMetadata {
476
+ policy: typeof BROWSER_REDACTION_POLICY;
477
+ fields: RedactionField[];
478
+ summaries?: PayloadSummary[];
479
+ }
480
+ interface RedactionResult<T> {
481
+ value: T;
482
+ metadata?: RedactionMetadata;
483
+ summary?: PayloadSummary;
484
+ }
485
+ interface BodyRedactionResult {
486
+ body?: string;
487
+ bodySummary?: PayloadSummary;
488
+ metadata?: RedactionMetadata;
489
+ }
490
+ interface BodyRedactionOptions {
491
+ contentType?: string | null;
492
+ maxLength?: number;
493
+ path?: string;
494
+ }
495
+ interface StoredValueRedactionOptions {
496
+ key?: string;
497
+ maxLength?: number;
498
+ path?: string;
499
+ }
500
+ interface InputValueRedactionOptions {
501
+ name?: string;
502
+ type?: string;
503
+ path?: string;
504
+ }
505
+ declare function mergeRedactionMetadata(...items: Array<RedactionMetadata | undefined>): RedactionMetadata | undefined;
506
+ declare function attachRedactionMetadata(target: Record<string, unknown>, ...items: Array<RedactionMetadata | undefined>): void;
507
+ declare function redactTokenLikeString(value: string, path?: string): RedactionResult<string>;
508
+ declare function redactUrl(url: string, path?: string): RedactionResult<string>;
509
+ /**
510
+ * Scrub secrets from `http(s)://…` URL substrings embedded in FREE TEXT, reusing
511
+ * the SAME query-key-aware policy {@link redactUrl} applies to `ref.url`.
512
+ *
513
+ * The token-shape patterns in {@link redactTokenLikeString} catch Bearer/JWT/
514
+ * prefixed/long-hex/long-alnum secrets, but MISS a short/medium secret carried as
515
+ * a URL query param (`?token=abc123def456`, ~12–26 chars) — while `redactUrl` is
516
+ * query-aware and drops every query value. This finds each URL substring and runs
517
+ * it through `redactUrl`, so a tokenized URL sitting in an adapter's `after`/
518
+ * `brief`/gap text loses its query secret while keeping its origin + path as
519
+ * provenance. Non-URL text is left untouched (fast-path bail when no `://`).
520
+ *
521
+ * This shares one implementation with `ref.url` redaction — there is no second
522
+ * URL-redaction policy.
523
+ */
524
+ declare function redactUrlsInText(value: string, path?: string): RedactionResult<string>;
525
+ declare function redactHeaders(headers: Record<string, string>, path?: string): RedactionResult<Record<string, string>>;
526
+ declare function redactCookieValue(name: string, value: string, path?: string, configuredMaskNames?: string[]): RedactionResult<string>;
527
+ declare function redactCookieName(name: string): string;
528
+ declare function redactCookieMap(cookies: Record<string, string>, path?: string, configuredMaskNames?: string[]): RedactionResult<Record<string, string>>;
529
+ /**
530
+ * Redacts an arbitrary JSON-like value (object/array/scalar) through the browser redaction
531
+ * policy: sensitive-looking key names are masked and token-like string values are scrubbed.
532
+ * Used to sanitize declarative env flags/config before they rest in a `k:'env'` event.
533
+ */
534
+ declare function redactValue<T>(value: T, path?: string): RedactionResult<T>;
535
+ declare function redactNetworkTextBody(body: string, options?: BodyRedactionOptions): BodyRedactionResult;
536
+ declare function summarizeBinaryPayload(contentType: string | null | undefined, contentLength: string | null | undefined, path?: string): BodyRedactionResult;
537
+ declare function summarizeOmittedPayload(reason: "stream_payload" | "body_read_failed" | "non_text_request_body", path?: string): BodyRedactionResult;
538
+ declare function redactStorageKey(key: string, path?: string): RedactionResult<string>;
539
+ declare function redactStoredValue(value: string | null | undefined, options?: StoredValueRedactionOptions): RedactionResult<string | undefined>;
540
+ declare function redactInputValue(value: string, options?: InputValueRedactionOptions): RedactionResult<string>;
541
+
542
+ declare const CRUMBTRAIL_SESSION_HEADER: "X-Crumbtrail-Session-Id";
543
+ declare const CRUMBTRAIL_REQUEST_HEADER: "X-Crumbtrail-Request-Id";
544
+ declare const CRUMBTRAIL_SESSION_HEADER_LOWER: string;
545
+ declare const CRUMBTRAIL_REQUEST_HEADER_LOWER: string;
546
+ declare const CRUMBTRAIL_REQUEST_ID_MAX_LENGTH = 64;
547
+ declare function generateRequestId(): string;
548
+ declare function createCrumbtrailRequestHeaders(sessionId: string, requestId?: string): Record<typeof CRUMBTRAIL_SESSION_HEADER | typeof CRUMBTRAIL_REQUEST_HEADER, string>;
549
+ declare const W3C_TRACEPARENT_HEADER: "traceparent";
550
+ interface W3CTraceContext {
551
+ traceId: string;
552
+ spanId: string;
553
+ flags: number;
554
+ }
555
+ declare function parseTraceparent(value: string | undefined): W3CTraceContext | undefined;
556
+ declare function formatTraceparent(ctx: W3CTraceContext): string;
557
+ /** 16-byte (32 hex) W3C trace id; never all-zero. */
558
+ declare function generateTraceId(): string;
559
+ /** 8-byte (16 hex) W3C span id; never all-zero. */
560
+ declare function generateSpanId(): string;
561
+ /** Fresh, sampled W3C trace context the browser controls. */
562
+ declare function generateTraceContext(): W3CTraceContext;
563
+ interface OutboundCorrelation {
564
+ sessionId: string;
565
+ /** Unified correlation key. Equals `traceId` unless the caller pinned a request id. */
566
+ requestId: string;
567
+ traceId: string;
568
+ spanId: string;
569
+ /** Spec-valid `traceparent` value to emit on the outbound request. */
570
+ traceparent: string;
571
+ }
572
+ /**
573
+ * Decides whether Crumbtrail may stamp outbound correlation headers.
574
+ *
575
+ * Same-origin requests are allowed automatically. Cross-origin requests must match an
576
+ * explicit backend-origin allowlist so the default does not trigger CORS preflights or
577
+ * leak trace context to third-party services.
578
+ */
579
+ declare function canInjectCorrelationHeaders(requestUrl: string, allowedOrigins?: readonly string[]): boolean;
580
+ /**
581
+ * Resolves the correlation identity for one outbound request.
582
+ *
583
+ * - If the caller already set a valid `traceparent` (an app doing W3C propagation),
584
+ * we adopt its trace context so we join on the user's existing trace.
585
+ * - Otherwise we mint a fresh sampled trace context.
586
+ *
587
+ * The trace id doubles as the Crumbtrail request id (`X-Crumbtrail-Request-Id`), so the
588
+ * native Express path and the OTLP `traceId → requestId` bridge join on one shared key.
589
+ * An explicit caller-provided request id is honored as-is for backwards compatibility.
590
+ */
591
+ declare function resolveOutboundCorrelation(input: {
592
+ sessionId: string;
593
+ existingRequestId?: string;
594
+ existingTraceparent?: string;
595
+ }): OutboundCorrelation;
596
+
597
+ /**
598
+ * Environment collector. Emits exactly one `k:'env'` snapshot event at session start with a
599
+ * redaction-aware view of the runtime: userAgent/browser/os/viewport (best-effort, browser
600
+ * only) plus locale/timezone (available in Node via `Intl`). Any feature flags / config the
601
+ * app declared via `logger.setEnv` before the snapshot is folded in (redacted). Guarded so it
602
+ * never throws in non-browser/SSR/test runtimes — it degrades to whatever IS available.
603
+ *
604
+ * Out of scope (CP3): privileged enumeration of installed browser extensions (absent in SDK
605
+ * mode) and continuous mid-session env polling beyond this snapshot + `setEnv` deltas.
606
+ */
607
+ declare function environmentCollector(bus: EventBus, _config: CrumbtrailConfig, context: CollectorContext): CollectorCleanup;
608
+ /** Builds the redaction-aware snapshot payload. Exported for direct unit testing. */
609
+ declare function buildEnvSnapshot(flags?: Record<string, unknown>, config?: Record<string, unknown>): EnvSnapshot;
610
+ /**
611
+ * Builds a `k:'env'` delta payload from a `setEnv` call made after the snapshot was emitted.
612
+ * Values are redacted before they rest. Exported for direct unit testing.
613
+ */
614
+ declare function buildEnvDelta(flags?: Record<string, unknown>, config?: Record<string, unknown>): EnvSnapshot;
615
+
616
+ interface ElementSignature {
617
+ /** Deterministic structural path from a uniquely-identified ancestor (or root) to the element. */
618
+ path: string;
619
+ /** Short stable hash of `path` — the element's identity across sessions. */
620
+ sig: string;
621
+ }
622
+ /** Build a deterministic path, stopping early at the first uniquely-identified ancestor. */
623
+ declare function computeElementPath(el: Element): string;
624
+ /** FNV-1a 32-bit, rendered base36. Deterministic, dependency-free. */
625
+ declare function hashString(input: string): string;
626
+ declare function computeElementSignature(el: Element): ElementSignature;
627
+
628
+ /**
629
+ * Shared evidence contract. Both crumbtrail-node (compare engine) and
630
+ * crumbtrail-cloud (fusion/ranking) speak this. Evidence is neutral and
631
+ * complete — no ranking or opinion lives here.
632
+ */
633
+ declare const EVIDENCE_SCHEMA_VERSION: "evidence.v1";
634
+ type EvidenceLane = "flow" | "network" | "db" | "env" | "browser" | "logs" | "memory" | "code";
635
+ interface EvidenceRef {
636
+ sessionId?: string;
637
+ requestId?: string;
638
+ table?: string;
639
+ pk?: Record<string, unknown>;
640
+ sig?: string;
641
+ /**
642
+ * Provider deep-link back to the source system (Sentry issue URL, CloudWatch
643
+ * Logs Insights link, etc.) so a human can verify provenance. Shared by every
644
+ * evidence adapter. A URL carrying an embedded token/credential is scrubbed at
645
+ * the redaction boundary (see node `redact.ts`); a plain issue URL survives as
646
+ * provenance. Optional and additive — session-derived evidence omits it.
647
+ */
648
+ url?: string;
649
+ /** Source provider id for a deep-linked item, e.g. "sentry" | "cloudwatch". */
650
+ provider?: string;
651
+ /** Provider-native record id for the deep-linked item (issue id, event id). */
652
+ id?: string;
653
+ }
654
+ interface EvidenceItem {
655
+ /** Stable id used by IntentSignal.evidenceId to correlate. */
656
+ id: string;
657
+ lane: EvidenceLane;
658
+ /** Discriminating kind, e.g. "net.status", "db.row-value", "flow.step-missing". */
659
+ kind: string;
660
+ brief: string;
661
+ ref: EvidenceRef;
662
+ before: unknown;
663
+ after: unknown;
664
+ /** ms epoch when observed, if known. */
665
+ whenObserved?: number;
666
+ }
667
+ interface IntentSignal {
668
+ /** Foreign key to EvidenceItem.id. */
669
+ evidenceId: string;
670
+ explainedByCommit?: {
671
+ sha: string;
672
+ pr?: string;
673
+ message: string;
674
+ };
675
+ prIntent?: string;
676
+ }
677
+
678
+ /** A commit range on a git host (e.g. a release tag pair). */
679
+ interface GitHostRef {
680
+ baseRef: string;
681
+ headRef: string;
682
+ }
683
+ /** A single commit in a compared range, as reported by a git host. */
684
+ interface CommitInfo {
685
+ sha: string;
686
+ message: string;
687
+ pr?: string;
688
+ files: string[];
689
+ }
690
+ /** Correlates behavior-change evidence to git-host commits. No network. */
691
+ interface GitHostClient {
692
+ /** Commits in (baseRef, headRef] that touch the repo. Implementations may prefilter. */
693
+ listCommits(ref: GitHostRef): Promise<CommitInfo[]>;
694
+ }
695
+ /**
696
+ * Pure correlation: an evidence item is "explained" when a commit in range
697
+ * plausibly caused it, via either of two paths:
698
+ * - STRONG: an evidence identity token (table name or a brief's route
699
+ * segment, minus structural noise) appears among the commit's
700
+ * file-path tokens (minus structural noise). A single such match
701
+ * qualifies.
702
+ * - WEAK (fallback): total shared tokens across all evidence fields and
703
+ * all commit fields (message + file paths) is >= MIN_SHARED.
704
+ * The best commit per evidence item is the one with the highest
705
+ * (strong-match-count * 100 + weak-shared-count), ties broken by
706
+ * lexicographically smallest sha. Deterministic, never throws. Returns
707
+ * signals in the input evidence order.
708
+ */
709
+ declare function inferIntent(evidence: EvidenceItem[], commits: CommitInfo[]): IntentSignal[];
710
+
711
+ /**
712
+ * A leading indicator that a session is going wrong, raised by a {@link SignalDetector}.
713
+ * The controller turns a raised signal into an auto-flagged report.
714
+ */
715
+ interface Signal {
716
+ /** Tag applied to the auto-flagged report, e.g. `"auto:rage-click"`. */
717
+ tag: string;
718
+ /** Dedup key — a given key auto-flags at most once per session. */
719
+ key: string;
720
+ /** Human-readable note attached to the report. */
721
+ note: string;
722
+ }
723
+ /**
724
+ * Inspects the live event stream and raises a {@link Signal} the moment a leading
725
+ * indicator trips. Detectors are stateful (they track rolling windows) and pure of
726
+ * side effects — they never flag directly; the controller owns coalescing and caps.
727
+ */
728
+ interface SignalDetector {
729
+ inspect(event: BugEvent): Signal | null;
730
+ }
731
+ /** Stable signature for an `err`/`rej` event, from its kind, message, and top stack frames. */
732
+ declare function errorSignature(event: BugEvent): string;
733
+ /**
734
+ * Reactive baseline detector: an uncaught error or unhandled rejection. Each distinct error
735
+ * signature flags once per session (dedup is enforced by the controller via {@link Signal.key}).
736
+ */
737
+ declare function errorDetector(): SignalDetector;
738
+ interface RageClickOptions {
739
+ /** Clicks on the same target within `windowMs` required to trip. */
740
+ threshold: number;
741
+ windowMs: number;
742
+ }
743
+ /**
744
+ * Precognitive detector: the user hammering the same control (a dead button, a stuck submit)
745
+ * is a silent failure that throws no error. Trips after `threshold` clicks on one target
746
+ * inside `windowMs`, then resets that target's window.
747
+ */
748
+ declare function rageClickDetector(opts: RageClickOptions): SignalDetector;
749
+ interface RetryStormOptions {
750
+ /** Requests to the same endpoint within `windowMs` required to trip. */
751
+ threshold: number;
752
+ windowMs: number;
753
+ /** Failed responses (status >= 400) to the same endpoint within `windowMs` required to trip. Defaults to 2. */
754
+ failThreshold?: number;
755
+ }
756
+ declare function retryStormDetector(opts: RetryStormOptions): SignalDetector;
757
+ interface SlowResponseOptions {
758
+ /** A response is "slow" at or above this duration in ms. */
759
+ thresholdMs: number;
760
+ /** Slow responses within `windowMs` required to trip. */
761
+ count: number;
762
+ windowMs: number;
763
+ }
764
+ /**
765
+ * Precognitive detector: a session where responses are piling up slow is degrading before any
766
+ * timeout throws. Trips after `count` responses at or above `thresholdMs` inside `windowMs`.
767
+ * Session-scoped (not per-endpoint) so it stays allocation-light — the captured events carry the
768
+ * per-request detail. Flags once per session (dedup key is stable); the per-session cap bounds it.
769
+ */
770
+ declare function slowResponseDetector(opts: SlowResponseOptions): SignalDetector;
771
+ interface AbandonedFlowOptions {
772
+ /** Max ms from the last input to a page-hide that still counts as abandonment. */
773
+ windowMs: number;
774
+ /** Minimum input events before an interaction is treated as a "flow" worth flagging. */
775
+ minInputs: number;
776
+ }
777
+ /**
778
+ * Precognitive detector: the user filled a form, then left (hid/closed the tab) without submitting
779
+ * — a silent abandonment that throws nothing and never reaches support. Trips when the page is
780
+ * hidden within `windowMs` of the last of at least `minInputs` inputs, with no mutating request
781
+ * (submit) in between. A mutating `net.req` clears the pending flow. Flags once per session.
782
+ */
783
+ declare function abandonedFlowDetector(opts: AbandonedFlowOptions): SignalDetector;
784
+
785
+ interface AutoFlagOptions {
786
+ /** Quiet period after the last new signal before the flag fires, so a cascade coalesces into one report. */
787
+ debounceMs: number;
788
+ /** Hard cap on auto-captured reports per session (shared across all detectors). */
789
+ maxPerSession: number;
790
+ flag: (options: FlagBugOptions) => Promise<unknown>;
791
+ /**
792
+ * Signal detectors that decide when to auto-flag. Defaults to error-only (`errorDetector`),
793
+ * preserving the original reactive-on-error behavior. Pass behavioral detectors
794
+ * (rage-click, retry-storm, …) to capture silent failures before an error throws.
795
+ */
796
+ detectors?: SignalDetector[];
797
+ }
798
+ interface AutoFlagController {
799
+ handleEvent(event: BugEvent): void;
800
+ dispose(): void;
801
+ }
802
+ /**
803
+ * Turns raised {@link Signal}s into automatic `flagBug` snapshots. Each signal key is flagged
804
+ * once per session, and a burst of signals settles into a single report (the debounce doubles
805
+ * as post-roll so the ring buffer snapshot includes the cascade's aftermath). The first signal
806
+ * to open a debounce window owns the report's tag and note; the total report count is capped by
807
+ * `maxPerSession` across every detector.
808
+ */
809
+ declare function createAutoFlagController(options: AutoFlagOptions): AutoFlagController;
810
+
811
+ /**
812
+ * @stability stable
813
+ * Version-bump policy (additive-on-v1 vs fusion.v2) is War-game 01 Fork A's
814
+ * decision, not this file's — see wargames/wargames/01-solve-context-wargame-fields.md.
815
+ */
816
+ declare const FUSION_SCHEMA_VERSION: "fusion.v1";
817
+ type HypothesisKind = "regression" | "latent" | "environment" | "client-side" | "intentional-change" | "inconclusive";
818
+ interface Symptom {
819
+ title: string;
820
+ description?: string;
821
+ release?: string;
822
+ url?: string;
823
+ user?: string;
824
+ errorSig?: string;
825
+ source?: string;
826
+ }
827
+ interface Hypothesis {
828
+ kind: HypothesisKind;
829
+ /** 0..1 advisory confidence. Not a probability; a ranking signal. */
830
+ confidence: number;
831
+ rationale: string;
832
+ /** EvidenceItem.id values backing this hypothesis. */
833
+ evidenceIds: string[];
834
+ }
835
+ interface EvidenceGap {
836
+ lane: EvidenceLane;
837
+ reason: string;
838
+ suggestion?: string;
839
+ /**
840
+ * Optional structured severity marker. Additive and defaults to an ordinary
841
+ * informational gap when absent (e.g. a missing join key, or a partial
842
+ * enrichment/secondary gap) — those never affect a source's health.
843
+ *
844
+ * `"source-unavailable"` marks a HARD failure: the adapter could not deliver
845
+ * its primary evidence at all (dispatch/auth failure, or a timeout that
846
+ * retrieved zero items) and self-degraded to a gap instead of throwing. The
847
+ * evidence framework reads this typed marker — never the free-text `reason` —
848
+ * to decide `stats.ok`, so a self-degrading source and a throwing source emit
849
+ * the same health signal. See node `fetch-all.ts`.
850
+ */
851
+ kind?: "source-unavailable";
852
+ }
853
+ interface CaptureDirective {
854
+ /** The bug signature this raises capture for. */
855
+ signature: string;
856
+ /** Lanes to collect more deeply next time. */
857
+ raise: EvidenceLane[];
858
+ scope: "signature" | "session";
859
+ reason: string;
860
+ }
861
+ interface RankedBundle {
862
+ schemaVersion: typeof FUSION_SCHEMA_VERSION;
863
+ symptom: Symptom;
864
+ /** Complete, neutral evidence in ranked order. Never filtered. */
865
+ evidence: EvidenceItem[];
866
+ /** Advisory only. Consumers may ignore and use evidence directly. */
867
+ opinion: {
868
+ stance: "advisory";
869
+ hypotheses: Hypothesis[];
870
+ };
871
+ gaps: EvidenceGap[];
872
+ /** Advisory-only: suggested capture escalations when evidence is thin. */
873
+ directives: CaptureDirective[];
874
+ }
875
+ interface AssembleBundleInput {
876
+ symptom: Symptom;
877
+ evidence: EvidenceItem[];
878
+ intent: IntentSignal[];
879
+ gaps?: EvidenceGap[];
880
+ }
881
+ /**
882
+ * Compose the RankedBundle: rank the complete evidence set (nothing dropped),
883
+ * classify advisory hypotheses, and pass through any evidence gaps. Pure.
884
+ */
885
+ declare function assembleBundle(input: AssembleBundleInput): RankedBundle;
886
+
887
+ /**
888
+ * Provider-agnostic evidence-source contract (types only — browser-safe, no I/O).
889
+ *
890
+ * This is the query-at-incident-time pull surface: when a ticket arrives, the
891
+ * runtime asks a client's existing observability tools (Sentry, CloudWatch,
892
+ * Splunk, Datadog, PostHog, Cloudflare) for evidence inside the located incident
893
+ * window and normalizes each hit into the neutral `evidence.v1` contract
894
+ * ({@link EvidenceItem} / {@link EvidenceGap}). Evidence stays neutral and
895
+ * complete here — ranking/opinion happens exactly once downstream in
896
+ * `assembleBundle` (fusion.v1), never in adapter output.
897
+ *
898
+ * Deliberately distinct from the OTLP dual-export path: adapters are zero-copy
899
+ * and derived-artifacts-only; only the assembled bundle persists.
900
+ */
901
+
902
+ /**
903
+ * Schema version string const, matching the repo convention
904
+ * (`evidence.v1` / `fusion.v1` / `fix-context.v1`).
905
+ */
906
+ declare const EVIDENCE_SOURCE_SCHEMA_VERSION: "evidence-source.v1";
907
+ /**
908
+ * Correlation keys an adapter can filter by. The Varicent lesson: most clients
909
+ * do not propagate a trace end-to-end, so an adapter must declare (in its
910
+ * descriptor) which keys it can actually use and honestly report — via an
911
+ * {@link EvidenceGap} — when a requested key is unavailable and it fell back to
912
+ * a time window only. Note `traceId` doubles as `requestId` in the repo's
913
+ * correlation model.
914
+ */
915
+ type EvidenceJoinKey = "traceId" | "requestId" | "sessionId" | "time" | "release" | "url" | "user" | "service";
916
+ interface EvidenceSourceDescriptor {
917
+ /** Stable provider id, e.g. "sentry" | "cloudwatch" | "splunk". */
918
+ provider: string;
919
+ /** Human-facing name for doctor output and docs. */
920
+ displayName: string;
921
+ /** Which evidence.v1 lanes this source can populate. */
922
+ lanes: EvidenceLane[];
923
+ /** Keys it can actually filter by, best-first (drives query construction). */
924
+ joinKeys: EvidenceJoinKey[];
925
+ /** Env-var names carrying this source's credentials, for doctor + docs. */
926
+ authFields: string[];
927
+ }
928
+ interface EvidenceQuery {
929
+ /** ms-epoch incident window, already located upstream. */
930
+ window: {
931
+ start: number;
932
+ end: number;
933
+ };
934
+ /** Correlation keys known for this incident; adapters use what they support. */
935
+ keys: Partial<Record<EvidenceJoinKey, string>>;
936
+ /** fusion.v1 Symptom, for text-relevance hints (optional). */
937
+ symptom?: Symptom;
938
+ /** Egress + token discipline: hard bounds every adapter must honor. */
939
+ limits: {
940
+ maxItems: number;
941
+ maxBytes: number;
942
+ timeoutMs: number;
943
+ };
944
+ }
945
+ interface EvidenceSourceResult {
946
+ schemaVersion: typeof EVIDENCE_SOURCE_SCHEMA_VERSION;
947
+ /** Neutral evidence.v1 items — no ranking, no opinion. */
948
+ items: EvidenceItem[];
949
+ /** e.g. "splunk cannot filter by traceId; used time window only". */
950
+ gaps: EvidenceGap[];
951
+ stats: {
952
+ provider: string;
953
+ fetched: number;
954
+ returned: number;
955
+ truncated: boolean;
956
+ latencyMs: number;
957
+ };
958
+ }
959
+
960
+ export { type AbandonedFlowOptions, type AddBugEventOptions, type AssembleBundleInput, type AutoFlagController, type AutoFlagOptions, BROWSER_REDACTION_POLICY, type BodyRedactionOptions, type BodyRedactionResult, type BugEvent, type BugReport, CRUMBTRAIL_EVENT_KINDS, CRUMBTRAIL_REQUEST_HEADER, CRUMBTRAIL_REQUEST_HEADER_LOWER, CRUMBTRAIL_REQUEST_ID_MAX_LENGTH, CRUMBTRAIL_SCHEMA_VERSION, CRUMBTRAIL_SESSION_HEADER, CRUMBTRAIL_SESSION_HEADER_LOWER, type CaptureDirective, type CollectorCleanup, type CollectorContext, type CommitInfo, Crumbtrail, type CrumbtrailCapabilities, type CrumbtrailConfig, type CrumbtrailPlatform, type CrumbtrailPreset, type CrumbtrailSdkDescriptor, type CrumbtrailTransport, DB_DIFF_BULK_EVENT_KIND, DB_DIFF_EVENT_KIND, DB_READ_BULK_EVENT_KIND, DB_READ_EVENT_KIND, DEFAULT_CONFIG, DEFAULT_SESSION_STORAGE_KEY, type DbDiffBulkEventData, type DbDiffEventData, type DbDiffOp, type DbEngine, type DbReadBulkEventData, type DbReadEventData, EVIDENCE_SCHEMA_VERSION, EVIDENCE_SOURCE_SCHEMA_VERSION, type ElementSignature, type EnvDeclaration, type EnvSnapshot, EventBus, type EvidenceGap, type EvidenceItem, type EvidenceJoinKey, type EvidenceLane, type EvidenceQuery, type EvidenceRef, type EvidenceSourceDescriptor, type EvidenceSourceResult, FUSION_SCHEMA_VERSION, type FlagBugOptions, type GitHostClient, type GitHostRef, HttpTransport, type Hypothesis, type HypothesisKind, type InputValueRedactionOptions, type IntentSignal, type InteractionElementDescriptor, type InteractionElementDescriptorFactory, type OutboundCorrelation, PRESET_FULL, PRESET_LIGHT, PRESET_PASSIVE, type PayloadSummary, type PersistedSession, REDACTED_STORAGE_KEY, REDACTED_VALUE, type RageClickOptions, type RankedBundle, type RedactionAction, type RedactionField, type RedactionMetadata, type RedactionResult, type RetryStormOptions, RingBuffer, type SessionStore, type Signal, type SignalDetector, type SlowResponseOptions, type StoredValueRedactionOptions, type Symptom, type TargetDescriptor, type TargetDescriptorResolver, type W3CTraceContext, W3C_TRACEPARENT_HEADER, WebTargetDescriptorResolver, abandonedFlowDetector, assembleBundle, attachRedactionMetadata, buildEnvDelta, buildEnvSnapshot, canInjectCorrelationHeaders, computeElementPath, computeElementSignature, createAutoFlagController, createCrumbtrailRequestHeaders, createWebSessionStore, environmentCollector, errorDetector, errorSignature, formatTraceparent, generateRequestId, generateSpanId, generateTraceContext, generateTraceId, hashString, inferIntent, mergeRedactionMetadata, parseTraceparent, rageClickDetector, redactCookieMap, redactCookieName, redactCookieValue, redactHeaders, redactInputValue, redactNetworkTextBody, redactStorageKey, redactStoredValue, redactTokenLikeString, redactUrl, redactUrlsInText, redactValue, resolveOutboundCorrelation, retryStormDetector, slowResponseDetector, summarizeBinaryPayload, summarizeOmittedPayload, webTargetDescriptorResolver };