Skip to content

Commit 005f3e1

Browse files
committed
refactor(core): Extract shared modules and utilities
1 parent 8ad3d96 commit 005f3e1

File tree

18 files changed

+83
-136
lines changed

18 files changed

+83
-136
lines changed

packages/core/src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,10 @@ export type { HawkStorage } from './storages/hawk-storage';
22
export { HawkUserManager } from './users/hawk-user-manager';
33
export type { Logger, LogType } from './logger/logger';
44
export { isLoggerSet, setLogger, resetLogger, log } from './logger/logger';
5+
export { Sanitizer } from './modules/sanitizer';
6+
export { StackParser } from './modules/stack-parser';
7+
export { buildElementSelector } from './utils/selector';
8+
export type { Transport } from './transports/transport';
9+
export { EventRejectedError } from './errors';
10+
export { isErrorProcessed, markErrorAsProcessed } from './utils/event';
11+
export { isPlainObject, validateUser, validateContext, isValidEventPayload, isValidBreadcrumb } from './utils/validation';
File renamed without changes.

packages/javascript/src/modules/sanitizer.ts renamed to packages/core/src/modules/sanitizer.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import { isPlainObject } from '../utils/validation';
3+
24
/**
35
* This class provides methods for preparing data to sending to Hawk
46
* - trim long strings
57
* - represent html elements like <div ...> as "<div>" instead of "{}"
68
* - represent big objects as "<big object>"
79
* - represent class as <class SomeClass> or <instance of SomeClass>
810
*/
9-
export default class Sanitizer {
11+
export class Sanitizer {
1012
/**
1113
* Maximum string length
1214
*/
@@ -34,7 +36,7 @@ export default class Sanitizer {
3436
* @param target - variable to check
3537
*/
3638
public static isObject(target: any): boolean {
37-
return Sanitizer.typeOf(target) === 'object';
39+
return isPlainObject(target);
3840
}
3941

4042
/**

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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { CatcherMessage } from '@hawk.so/types';
2+
import { CatcherMessageType } from '@hawk.so/types';
3+
4+
/**
5+
* Transport interface — anything that can send a CatcherMessage
6+
*/
7+
export interface Transport<T extends CatcherMessageType> {
8+
send(message: CatcherMessage<T>): Promise<void>;
9+
}
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,
Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1-
import { log } from '@hawk.so/core';
2-
import type { AffectedUser, Breadcrumb, EventContext, EventData, JavaScriptAddons } from '@hawk.so/types';
3-
import Sanitizer from '../modules/sanitizer';
1+
import { log } from '../logger/logger';
2+
import type { AffectedUser, Breadcrumb, EventAddons, EventContext, EventData } from '@hawk.so/types';
3+
4+
/**
5+
* Returns true if value is a plain object (not null, array, Date, Map, etc.)
6+
*/
7+
export function isPlainObject(value: unknown): value is Record<string, unknown> {
8+
return Object.prototype.toString.call(value) === '[object Object]';
9+
}
410

511
/**
612
* Validates user data - basic security checks
713
*
814
* @param user - user data to validate
915
*/
1016
export function validateUser(user: AffectedUser): boolean {
11-
if (!user || !Sanitizer.isObject(user)) {
17+
if (!user || !isPlainObject(user)) {
1218
log('validateUser: User must be an object', 'warn');
1319

1420
return false;
@@ -30,7 +36,7 @@ export function validateUser(user: AffectedUser): boolean {
3036
* @param context - context data to validate
3137
*/
3238
export function validateContext(context: EventContext | undefined): boolean {
33-
if (context && !Sanitizer.isObject(context)) {
39+
if (context && !isPlainObject(context)) {
3440
log('validateContext: Context must be an object', 'warn');
3541

3642
return false;
@@ -39,23 +45,14 @@ export function validateContext(context: EventContext | undefined): boolean {
3945
return true;
4046
}
4147

42-
/**
43-
* Checks if value is a plain object (not array, Date, etc.)
44-
*
45-
* @param value - value to check
46-
*/
47-
function isPlainObject(value: unknown): value is Record<string, unknown> {
48-
return Object.prototype.toString.call(value) === '[object Object]';
49-
}
50-
5148
/**
5249
* Runtime check for required EventData fields.
5350
* Per @hawk.so/types EventData, `title` is the only non-optional field.
54-
* Additionally validates `backtrace` shape if present (must be an array).
51+
* Additionally, validates `backtrace` shape if present (must be an array).
5552
*
5653
* @param payload - value to validate
5754
*/
58-
export function isValidEventPayload(payload: unknown): payload is EventData<JavaScriptAddons> {
55+
export function isValidEventPayload(payload: unknown): payload is EventData<EventAddons> {
5956
if (!isPlainObject(payload)) {
6057
return false;
6158
}
@@ -64,11 +61,7 @@ export function isValidEventPayload(payload: unknown): payload is EventData<Java
6461
return false;
6562
}
6663

67-
if (payload.backtrace !== undefined && !Array.isArray(payload.backtrace)) {
68-
return false;
69-
}
70-
71-
return true;
64+
return !(payload.backtrace !== undefined && !Array.isArray(payload.backtrace));
7265
}
7366

7467
/**
@@ -86,9 +79,5 @@ export function isValidBreadcrumb(breadcrumb: unknown): breadcrumb is Breadcrumb
8679
return false;
8780
}
8881

89-
if (breadcrumb.timestamp !== undefined && typeof breadcrumb.timestamp !== 'number') {
90-
return false;
91-
}
92-
93-
return true;
82+
return !(breadcrumb.timestamp !== undefined && typeof breadcrumb.timestamp !== 'number');
9483
}

packages/javascript/tests/utils/validation.test.ts renamed to packages/core/tests/utils/validation.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect, vi } from 'vitest';
2-
import { validateUser, validateContext, isValidEventPayload, isValidBreadcrumb } from '../../src/utils/validation';
2+
import { validateUser, validateContext, isValidEventPayload, isValidBreadcrumb } from '@hawk.so/core';
33

44
// Suppress log output produced by log() calls inside validation failures.
55
vi.mock('@hawk.so/core', () => ({ log: vi.fn(), isLoggerSet: vi.fn(() => true), setLogger: vi.fn() }));

0 commit comments

Comments
 (0)