Skip to content

Commit 8df48ec

Browse files
committed
refactor(core): BreadcrumbStore abstraction and browser implementation added
1 parent ad0fd34 commit 8df48ec

File tree

14 files changed

+134
-113
lines changed

14 files changed

+134
-113
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { Breadcrumb } from '@hawk.so/types';
2+
3+
/**
4+
* Hint passed to beforeBreadcrumb callback.
5+
*/
6+
export interface BreadcrumbHint {
7+
[key: string]: unknown;
8+
}
9+
10+
/**
11+
* Breadcrumb input type - breadcrumb data with optional timestamp.
12+
*/
13+
export type BreadcrumbInput = Omit<Breadcrumb, 'timestamp'> & { timestamp?: number };
14+
15+
/**
16+
* Contract for breadcrumb storage. Also serves as public breadcrumbs API.
17+
*/
18+
export interface BreadcrumbStore {
19+
add(breadcrumb: BreadcrumbInput, hint?: BreadcrumbHint): void;
20+
get(): Breadcrumb[];
21+
clear(): void;
22+
}
23+
24+
/**
25+
* @deprecated Use {@link BreadcrumbStore} instead.
26+
*/
27+
export type BreadcrumbsAPI = BreadcrumbStore;

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ export { StackParser } from './modules/stack-parser';
1212
export { buildElementSelector } from './utils/selector';
1313
export { EventRejectedError } from './errors';
1414
export { isErrorProcessed, markErrorAsProcessed } from './utils/event';
15+
export type { BreadcrumbStore, BreadcrumbsAPI, BreadcrumbHint, BreadcrumbInput } from './breadcrumbs/breadcrumb-store';

packages/javascript/src/addons/breadcrumbs.ts

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* @file Breadcrumbs module - captures chronological trail of events before an error
33
*/
44
import type { Breadcrumb, BreadcrumbLevel, BreadcrumbType, Json, JsonNode } from '@hawk.so/types';
5+
import type { BreadcrumbHint, BreadcrumbInput, BreadcrumbStore } from '@hawk.so/core';
56
import { buildElementSelector, isValidBreadcrumb, log, Sanitizer } from '@hawk.so/core';
67

78
/**
@@ -10,9 +11,10 @@ import { buildElementSelector, isValidBreadcrumb, log, Sanitizer } from '@hawk.s
1011
const DEFAULT_MAX_BREADCRUMBS = 15;
1112

1213
/**
13-
* Hint object passed to beforeBreadcrumb callback
14+
* Hint object passed to beforeBreadcrumb callback.
15+
* Extends generic {@link BreadcrumbHint} with browser-specific data.
1416
*/
15-
export interface BreadcrumbHint {
17+
export interface BrowserBreadcrumbHint extends BreadcrumbHint {
1618
/**
1719
* Original event that triggered the breadcrumb (if any)
1820
*/
@@ -51,7 +53,7 @@ export interface BreadcrumbsOptions {
5153
* - Return `false` — the breadcrumb will be discarded.
5254
* - Any other value is invalid — the original breadcrumb is stored as-is (a warning is logged).
5355
*/
54-
beforeBreadcrumb?: (breadcrumb: Breadcrumb, hint?: BreadcrumbHint) => Breadcrumb | false | void;
56+
beforeBreadcrumb?: (breadcrumb: Breadcrumb, hint?: BrowserBreadcrumbHint) => Breadcrumb | false | void;
5557

5658
/**
5759
* Enable automatic fetch/XHR breadcrumbs
@@ -75,12 +77,6 @@ export interface BreadcrumbsOptions {
7577
trackClicks?: boolean;
7678
}
7779

78-
/**
79-
* Breadcrumb input type - breadcrumb data with optional timestamp
80-
* (timestamp will be auto-generated if not provided)
81-
*/
82-
export type BreadcrumbInput = Omit<Breadcrumb, 'timestamp'> & { timestamp?: Breadcrumb['timestamp'] };
83-
8480
/**
8581
* Internal breadcrumbs options - all fields except 'beforeBreadcrumb' are required
8682
* (they have default values and are always set during init)
@@ -90,17 +86,18 @@ interface InternalBreadcrumbsOptions {
9086
trackFetch: boolean;
9187
trackNavigation: boolean;
9288
trackClicks: boolean;
93-
beforeBreadcrumb?: (breadcrumb: Breadcrumb, hint?: BreadcrumbHint) => Breadcrumb | false | void;
89+
beforeBreadcrumb?: (breadcrumb: Breadcrumb, hint?: BrowserBreadcrumbHint) => Breadcrumb | false | void;
9490
}
9591

9692
/**
97-
* BreadcrumbManager - singleton that manages breadcrumb collection and storage
93+
* Browser implementation of BreadcrumbStore.
94+
* Singleton that manages breadcrumb collection and storage.
9895
*/
99-
export class BreadcrumbManager {
96+
export class BrowserBreadcrumbStore implements BreadcrumbStore {
10097
/**
10198
* Singleton instance
10299
*/
103-
private static instance: BreadcrumbManager | null = null;
100+
private static instance: BrowserBreadcrumbStore | null = null;
104101

105102
/**
106103
* Breadcrumbs buffer (FIFO)
@@ -167,10 +164,10 @@ export class BreadcrumbManager {
167164
/**
168165
* Get singleton instance
169166
*/
170-
public static getInstance(): BreadcrumbManager {
171-
BreadcrumbManager.instance ??= new BreadcrumbManager();
167+
public static getInstance(): BrowserBreadcrumbStore {
168+
BrowserBreadcrumbStore.instance ??= new BrowserBreadcrumbStore();
172169

173-
return BreadcrumbManager.instance;
170+
return BrowserBreadcrumbStore.instance;
174171
}
175172

176173
/**
@@ -180,7 +177,7 @@ export class BreadcrumbManager {
180177
*/
181178
public init(options: BreadcrumbsOptions = {}): void {
182179
if (this.isInitialized) {
183-
log('[BreadcrumbManager] init has already been called; breadcrumb configuration is global and subsequent init options are ignored.', 'warn');
180+
log('[BrowserBreadcrumbStore] init has already been called; breadcrumb configuration is global and subsequent init options are ignored.', 'warn');
184181

185182
return;
186183
}
@@ -219,7 +216,7 @@ export class BreadcrumbManager {
219216
* @param hint - Optional hint object with original event data (Event, Response, XMLHttpRequest, etc.)
220217
* Used by beforeBreadcrumb callback to access original event context
221218
*/
222-
public addBreadcrumb(breadcrumb: BreadcrumbInput, hint?: BreadcrumbHint): void {
219+
public add(breadcrumb: BreadcrumbInput, hint?: BrowserBreadcrumbHint): void {
223220
/**
224221
* Ensure timestamp
225222
*/
@@ -293,14 +290,14 @@ export class BreadcrumbManager {
293290
/**
294291
* Get current breadcrumbs snapshot (oldest to newest)
295292
*/
296-
public getBreadcrumbs(): Breadcrumb[] {
293+
public get(): Breadcrumb[] {
297294
return [ ...this.breadcrumbs ];
298295
}
299296

300297
/**
301298
* Clear all breadcrumbs
302299
*/
303-
public clearBreadcrumbs(): void {
300+
public clear(): void {
304301
this.breadcrumbs.length = 0;
305302
}
306303

@@ -358,9 +355,9 @@ export class BreadcrumbManager {
358355
this.popstateHandler = null;
359356
}
360357

361-
this.clearBreadcrumbs();
358+
this.clear();
362359
this.isInitialized = false;
363-
BreadcrumbManager.instance = null;
360+
BrowserBreadcrumbStore.instance = null;
364361
}
365362

366363

@@ -399,7 +396,7 @@ export class BreadcrumbManager {
399396

400397
const duration = Date.now() - startTime;
401398

402-
manager.addBreadcrumb({
399+
manager.add({
403400
type: 'request',
404401
category: 'fetch',
405402
message: `${response.status} ${method} ${url}`,
@@ -419,7 +416,7 @@ export class BreadcrumbManager {
419416
} catch (error) {
420417
const duration = Date.now() - startTime;
421418

422-
manager.addBreadcrumb({
419+
manager.add({
423420
type: 'request',
424421
category: 'fetch',
425422
message: `[FAIL] ${method} ${url}`,
@@ -483,7 +480,7 @@ export class BreadcrumbManager {
483480
const url = this.hawkUrl || '';
484481
const status = this.status;
485482

486-
manager.addBreadcrumb({
483+
manager.add({
487484
type: 'request',
488485
category: 'xhr',
489486
message: `${status} ${method} ${url}`,
@@ -529,7 +526,7 @@ export class BreadcrumbManager {
529526

530527
lastUrl = to;
531528

532-
manager.addBreadcrumb({
529+
manager.add({
533530
type: 'navigation',
534531
category: 'navigation',
535532
message: `Navigated to ${to}`,
@@ -599,7 +596,7 @@ export class BreadcrumbManager {
599596
*/
600597
const text = (target.textContent || target.innerText || '').trim().substring(0, 50);
601598

602-
manager.addBreadcrumb({
599+
manager.add({
603600
type: 'ui',
604601
category: 'ui.click',
605602
message: `Click on ${selector}`,

packages/javascript/src/catcher.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import './modules/element-sanitizer';
22
import Socket from './modules/socket';
3-
import type { BreadcrumbsAPI, CatcherMessage, HawkInitialSettings, HawkJavaScriptEvent, Transport } from './types';
3+
import type { CatcherMessage, HawkInitialSettings, HawkJavaScriptEvent, Transport } from './types';
44
import { VueIntegration } from './integrations/vue';
55
import type {
66
AffectedUser,
@@ -13,7 +13,8 @@ import type {
1313
} from '@hawk.so/types';
1414
import type { JavaScriptCatcherIntegrations } from '@/types';
1515
import { ConsoleCatcher } from './addons/consoleCatcher';
16-
import { BreadcrumbManager } from './addons/breadcrumbs';
16+
import { BrowserBreadcrumbStore } from './addons/breadcrumbs';
17+
import type { BreadcrumbStore } from '@hawk.so/core';
1718
import {
1819
EventRejectedError,
1920
HawkUserManager,
@@ -123,7 +124,7 @@ export default class Catcher {
123124
/**
124125
* Breadcrumb manager instance
125126
*/
126-
private readonly breadcrumbManager: BreadcrumbManager | null;
127+
private readonly breadcrumbStore: BrowserBreadcrumbStore | null;
127128

128129
/**
129130
* Manages currently authenticated user identity.
@@ -195,10 +196,10 @@ export default class Catcher {
195196
* Initialize breadcrumbs
196197
*/
197198
if (settings.breadcrumbs !== false) {
198-
this.breadcrumbManager = BreadcrumbManager.getInstance();
199-
this.breadcrumbManager.init(settings.breadcrumbs ?? {});
199+
this.breadcrumbStore = BrowserBreadcrumbStore.getInstance();
200+
this.breadcrumbStore.init(settings.breadcrumbs ?? {});
200201
} else {
201-
this.breadcrumbManager = null;
202+
this.breadcrumbStore = null;
202203
}
203204

204205
/**
@@ -297,11 +298,11 @@ export default class Catcher {
297298
* data: { userId: '123' }
298299
* });
299300
*/
300-
public get breadcrumbs(): BreadcrumbsAPI {
301+
public get breadcrumbs(): BreadcrumbStore {
301302
return {
302-
add: (breadcrumb, hint) => this.breadcrumbManager?.addBreadcrumb(breadcrumb, hint),
303-
get: () => this.breadcrumbManager?.getBreadcrumbs() ?? [],
304-
clear: () => this.breadcrumbManager?.clearBreadcrumbs(),
303+
add: (breadcrumb, hint) => this.breadcrumbStore?.add(breadcrumb, hint),
304+
get: () => this.breadcrumbStore?.get() ?? [],
305+
clear: () => this.breadcrumbStore?.clear(),
305306
};
306307
}
307308

@@ -578,7 +579,7 @@ export default class Catcher {
578579
* Get breadcrumbs for event payload
579580
*/
580581
private getBreadcrumbsForEvent(): HawkJavaScriptEvent['breadcrumbs'] {
581-
const breadcrumbs = this.breadcrumbManager?.getBreadcrumbs();
582+
const breadcrumbs = this.breadcrumbStore?.get();
582583

583584
return breadcrumbs && breadcrumbs.length > 0 ? breadcrumbs : undefined;
584585
}

packages/javascript/src/types/breadcrumbs-api.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.

packages/javascript/src/types/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import type { CatcherMessage } from './catcher-message';
22
import type { HawkInitialSettings } from './hawk-initial-settings';
33
import type { Transport } from '@hawk.so/core';
4+
import type { BreadcrumbsAPI, BreadcrumbStore } from '@hawk.so/core';
45
import type { HawkJavaScriptEvent } from './event';
5-
import type { VueIntegrationData, NuxtIntegrationData, NuxtIntegrationAddons, JavaScriptCatcherIntegrations } from './integrations';
6-
import type { BreadcrumbsAPI } from './breadcrumbs-api';
6+
import type {
7+
JavaScriptCatcherIntegrations,
8+
NuxtIntegrationAddons,
9+
NuxtIntegrationData,
10+
VueIntegrationData
11+
} from './integrations';
712

813
export type {
914
CatcherMessage,
@@ -14,5 +19,6 @@ export type {
1419
NuxtIntegrationData,
1520
NuxtIntegrationAddons,
1621
JavaScriptCatcherIntegrations,
22+
BreadcrumbStore,
1723
BreadcrumbsAPI
1824
};

0 commit comments

Comments
 (0)