Skip to content

Commit 87fbd59

Browse files
committed
refactor(core): Extract shared modules and utilities
- fetchTimer function moved to @hawk.so/core - StackParser moved to @hawk.so/core - Transport moved to @hawk.so/core - event utility functions moved to @hawk.so/core - selector utility functions moved to @hawk.so/core - EventRejectedError moved to @hawk.so/core - suppress error-logs by StackParser in tests
1 parent eaf77a6 commit 87fbd59

24 files changed

+105
-143
lines changed

packages/core/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,9 @@ export type { Logger, LogType } from './logger/logger';
55
export { isLoggerSet, setLogger, resetLogger, log } from './logger/logger';
66
export { isPlainObject, validateUser, validateContext, isValidEventPayload, isValidBreadcrumb } from './utils/validation';
77
export { Sanitizer } from './modules/sanitizer';
8+
export type { SanitizerTypeHandler } from './modules/sanitizer';
9+
export { StackParser } from './modules/stack-parser';
10+
export { buildElementSelector } from './utils/selector';
11+
export type { Transport } from './transports/transport';
12+
export { EventRejectedError } from './errors';
13+
export { isErrorProcessed, markErrorAsProcessed } from './utils/event';

packages/javascript/src/modules/fetchTimer.ts renamed to packages/core/src/modules/fetch-timer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { log } from '@hawk.so/core';
1+
import { log } from '../logger/logger';
22

33
/**
44
* Sends AJAX request and wait for some time.

packages/javascript/src/modules/stackParser.ts renamed to packages/core/src/modules/stack-parser.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import type { StackFrame } from 'error-stack-parser';
22
import ErrorStackParser from 'error-stack-parser';
33
import type { BacktraceFrame, SourceCodeLine } from '@hawk.so/types';
4-
import fetchTimer from './fetchTimer';
4+
import fetchTimer from './fetch-timer';
55

66
/**
77
* This module prepares parsed backtrace
88
*/
9-
export default class StackParser {
9+
export class StackParser {
1010
/**
1111
* Prevents loading one file several times
1212
* name -> content
@@ -48,7 +48,7 @@ export default class StackParser {
4848
try {
4949
if (!frame.fileName) {
5050
return null;
51-
};
51+
}
5252

5353
if (!this.isValidUrl(frame.fileName)) {
5454
return null;
@@ -118,9 +118,9 @@ export default class StackParser {
118118
/**
119119
* Downloads source file
120120
*
121-
* @param {string} fileName - name of file to download
121+
* @param fileName - name of file to download
122122
*/
123-
private async loadSourceFile(fileName): Promise<string | null> {
123+
private async loadSourceFile(fileName: string): Promise<string | null> {
124124
if (this.sourceFilesCache[fileName] !== undefined) {
125125
return this.sourceFilesCache[fileName];
126126
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import type { CatcherMessage, CatcherMessageType } from '@hawk.so/types';
2+
3+
/**
4+
* Transport interface — anything that can send a CatcherMessage
5+
*/
6+
export interface Transport<T extends CatcherMessageType> {
7+
send(message: CatcherMessage<T>): Promise<void>;
8+
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { log } from '@hawk.so/core';
1+
import { log } from '../logger/logger';
22

33
/**
44
* Symbol to mark error as processed by Hawk
55
*/
66
const errorSentShadowProperty = Symbol('__hawk_processed__');
77

88
/**
9-
* Check if the error has alrady been sent to Hawk.
9+
* Check if the error has already been sent to Hawk.
1010
*
1111
* Motivation:
1212
* Some integrations may catch errors on their own side and then normally re-throw them down.
@@ -20,7 +20,7 @@ export function isErrorProcessed(error: unknown): boolean {
2020
return false;
2121
}
2222

23-
return error[errorSentShadowProperty] === true;
23+
return (error as Record<symbol, unknown>)[errorSentShadowProperty] === true;
2424
}
2525

2626
/**
@@ -35,7 +35,7 @@ export function markErrorAsProcessed(error: unknown): void {
3535
}
3636

3737
Object.defineProperty(error, errorSentShadowProperty, {
38-
enumerable: false, // Prevent from beight collected by Hawk
38+
enumerable: false, // Prevent from being collected by Hawk
3939
value: true,
4040
writable: true,
4141
configurable: true,

packages/core/tests/modules/sanitizer.test.ts

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { describe, expect, it } from 'vitest';
2+
<<<<<<<< HEAD:packages/core/tests/modules/sanitizer.test.ts
23
import { Sanitizer } from '../../src';
34

45
describe('Sanitizer', () => {
@@ -87,32 +88,30 @@ describe('Sanitizer', () => {
8788

8889
it('should format a class (not constructed) as "<class Name>"', () => {
8990
class Foo {}
91+
========
92+
import { Sanitizer } from '@hawk.so/core';
93+
import '../../src/modules/element-sanitizer';
9094

91-
expect(Sanitizer.sanitize(Foo)).toBe('<class Foo>');
92-
});
93-
94-
it('should format a class instance as "<instance of Name>"', () => {
95-
class Foo {}
95+
describe('Browser Sanitizer handlers', () => {
96+
describe('Element handler', () => {
97+
it('should format an empty HTML element as its outer HTML', () => {
98+
const el = document.createElement('div');
99+
const result = Sanitizer.sanitize(el);
96100

97-
expect(Sanitizer.sanitize(new Foo())).toBe('<instance of Foo>');
101+
expect(typeof result).toBe('string');
102+
expect(result).toMatch(/^<div/);
98103
});
99104

100-
it('should replace circular references with placeholder', () => {
101-
const obj: any = { a: 1 };
105+
it('should replace inner HTML content with ellipsis', () => {
106+
const el = document.createElement('div');
107+
>>>>>>>> 8070fac (refactor(core): Extract shared modules and utilities):packages/javascript/tests/modules/sanitizer.test.ts
102108

103-
obj.self = obj;
109+
el.innerHTML = '<span>some long content</span>';
104110

105-
const result = Sanitizer.sanitize(obj);
106-
107-
expect(result.self).toBe('<circular>');
108-
});
111+
const result = Sanitizer.sanitize(el);
109112

110-
it.each([
111-
{ label: 'number', value: 42 },
112-
{ label: 'boolean', value: true },
113-
{ label: 'null', value: null },
114-
])('should pass through $label primitives unchanged', ({ value }) => {
115-
expect(Sanitizer.sanitize(value)).toBe(value);
113+
expect(result).toContain('…');
114+
expect(result).not.toContain('some long content');
116115
});
117116
});
118117
});

packages/javascript/src/addons/breadcrumbs.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +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 { isValidBreadcrumb, log, Sanitizer } from '@hawk.so/core';
6-
import { buildElementSelector } from '../utils/selector';
5+
import { buildElementSelector, isValidBreadcrumb, log, Sanitizer } from '@hawk.so/core';
76

87
/**
98
* Default maximum number of breadcrumbs to store

packages/javascript/src/catcher.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import './modules/element-sanitizer';
22
import Socket from './modules/socket';
3-
import StackParser from './modules/stackParser';
43
import type { BreadcrumbsAPI, CatcherMessage, HawkInitialSettings, HawkJavaScriptEvent, Transport } from './types';
54
import { VueIntegration } from './integrations/vue';
65
import type {
@@ -12,24 +11,26 @@ import type {
1211
Json,
1312
VueIntegrationAddons
1413
} from '@hawk.so/types';
15-
import type { JavaScriptCatcherIntegrations } from './types/integrations';
16-
import { EventRejectedError } from './errors';
17-
import { isErrorProcessed, markErrorAsProcessed } from './utils/event';
18-
import { BrowserRandomGenerator } from './utils/random';
14+
import type { JavaScriptCatcherIntegrations } from '@/types';
1915
import { ConsoleCatcher } from './addons/consoleCatcher';
2016
import { BreadcrumbManager } from './addons/breadcrumbs';
2117
import {
18+
EventRejectedError,
2219
HawkUserManager,
20+
isErrorProcessed,
2321
isLoggerSet,
2422
isValidEventPayload,
2523
log,
24+
markErrorAsProcessed,
2625
Sanitizer,
2726
setLogger,
27+
StackParser,
2828
validateContext,
2929
validateUser
3030
} from '@hawk.so/core';
3131
import { HawkLocalStorage } from './storages/hawk-local-storage';
3232
import { createBrowserLogger } from './logger/logger';
33+
import { BrowserRandomGenerator } from './utils/random';
3334

3435
/**
3536
* Allow to use global VERSION, that will be overwritten by Webpack
@@ -63,7 +64,7 @@ export default class Catcher {
6364
/**
6465
* Catcher Type
6566
*/
66-
private readonly type: string = 'errors/javascript';
67+
private readonly type = 'errors/javascript' as const;
6768

6869
/**
6970
* User project's Integration Token
@@ -516,7 +517,7 @@ export default class Catcher {
516517
* and reject() provided with text reason instead of Error()
517518
*/
518519
if (notAnError) {
519-
return null;
520+
return undefined;
520521
}
521522

522523
return (error as Error).name;
@@ -526,7 +527,7 @@ export default class Catcher {
526527
* Release version
527528
*/
528529
private getRelease(): HawkJavaScriptEvent['release'] {
529-
return this.release !== undefined ? String(this.release) : null;
530+
return this.release !== undefined ? String(this.release) : undefined;
530531
}
531532

532533
/**
@@ -579,7 +580,7 @@ export default class Catcher {
579580
private getBreadcrumbsForEvent(): HawkJavaScriptEvent['breadcrumbs'] {
580581
const breadcrumbs = this.breadcrumbManager?.getBreadcrumbs();
581582

582-
return breadcrumbs && breadcrumbs.length > 0 ? breadcrumbs : null;
583+
return breadcrumbs && breadcrumbs.length > 0 ? breadcrumbs : undefined;
583584
}
584585

585586
/**
@@ -619,15 +620,15 @@ export default class Catcher {
619620
* and reject() provided with text reason instead of Error()
620621
*/
621622
if (notAnError) {
622-
return null;
623+
return undefined;
623624
}
624625

625626
try {
626627
return await this.stackParser.parse(error as Error);
627628
} catch (e) {
628629
log('Can not parse stack:', 'warn', e);
629630

630-
return null;
631+
return undefined;
631632
}
632633
}
633634

@@ -694,6 +695,6 @@ export default class Catcher {
694695
* @param integrationAddons - extra addons
695696
*/
696697
private appendIntegrationAddons(errorFormatted: CatcherMessage, integrationAddons: JavaScriptCatcherIntegrations): void {
697-
Object.assign(errorFormatted.payload.addons, integrationAddons);
698+
Object.assign(errorFormatted.payload.addons!, integrationAddons);
698699
}
699700
}

0 commit comments

Comments
 (0)