+
+
+
+
+
+
+
+
+ Test Vue integration: $root
+
-
-
-
-
- Test Vue integration: <test-component>
-
-
-
-
+
+
+ Test Vue integration: <test-component>
+
+
+
+
+
+
+
-
-
-
-
+
diff --git a/example/sample-errors.js b/example/sample-errors.js
index 26032f2..c15c37f 100644
--- a/example/sample-errors.js
+++ b/example/sample-errors.js
@@ -68,8 +68,89 @@ buttonManualSending.addEventListener('click', () => {
window.hawk.send(
new Error('Manual sending example'),
- contextSample.trim().length
- ? { contextSample }
- : undefined
+ contextSample.trim().length ? { contextSample } : undefined
);
});
+
+/**
+ * User Management
+ */
+const buttonSetUser = document.getElementById('btn-set-user');
+const buttonClearUser = document.getElementById('btn-clear-user');
+const buttonGetUser = document.getElementById('btn-get-user');
+
+buttonSetUser.addEventListener('click', () => {
+ const userId = document.getElementById('userId').value;
+ const userName = document.getElementById('userName').value;
+ const userUrl = document.getElementById('userUrl').value;
+
+ if (!userId.trim()) {
+ alert('User ID is required');
+ return;
+ }
+
+ const user = {
+ id: userId,
+ ...(userName.trim() && { name: userName }),
+ ...(userUrl.trim() && { url: userUrl }),
+ };
+
+ window.hawk.setUser(user);
+});
+
+buttonClearUser.addEventListener('click', () => {
+ window.hawk.clearUser();
+});
+
+buttonGetUser.addEventListener('click', () => {
+ const currentUser = window.hawk.getCurrentUser();
+ alert('Current user: ' + (currentUser ? JSON.stringify(currentUser, null, 2) : 'No user set'));
+});
+
+/**
+ * Context Management
+ */
+const buttonSetContext = document.getElementById('btn-set-context');
+const buttonClearContext = document.getElementById('btn-clear-context');
+const buttonGetContext = document.getElementById('btn-get-context');
+
+buttonSetContext.addEventListener('click', () => {
+ const contextKey = document.getElementById('contextKey').value;
+ const contextValue = document.getElementById('contextValue').value;
+
+ if (!contextKey.trim()) {
+ alert('Context key is required');
+ return;
+ }
+
+ const context = {
+ [contextKey]: contextValue,
+ };
+
+ window.hawk.setContext(context);
+});
+
+buttonClearContext.addEventListener('click', () => {
+ window.hawk.clearContext();
+});
+
+buttonGetContext.addEventListener('click', () => {
+ const currentContext = window.hawk.getCurrentContext();
+ alert(
+ 'Current context: ' +
+ (currentContext ? JSON.stringify(currentContext, null, 2) : 'No context set')
+ );
+});
+
+/**
+ * Test without user
+ */
+const buttonTestWithoutUser = document.getElementById('btn-test-without-user');
+
+buttonTestWithoutUser.addEventListener('click', () => {
+ // Clear user first to ensure no user is set
+ window.hawk.clearUser();
+
+ // Send error without user
+ window.hawk.send(new Error('Test error without user'));
+});
diff --git a/src/catcher.ts b/src/catcher.ts
index 18a629f..0b38dd8 100644
--- a/src/catcher.ts
+++ b/src/catcher.ts
@@ -63,12 +63,12 @@ export default class Catcher {
/**
* Current authenticated user
*/
- private readonly user: AffectedUser;
+ private user: AffectedUser | null;
/**
* Any additional data passed by user for sending with all messages
*/
- private readonly context: EventContext | undefined;
+ private context: EventContext | undefined;
/**
* This Method allows developer to filter any data you don't want sending to Hawk
@@ -218,13 +218,63 @@ export default class Catcher {
*/
public connectVue(vue): void {
// eslint-disable-next-line no-new
- this.vue = new VueIntegration(vue, (error: Error, addons: VueIntegrationAddons) => {
- void this.formatAndSend(error, {
- vue: addons,
- });
- }, {
- disableVueErrorHandler: this.disableVueErrorHandler,
- });
+ this.vue = new VueIntegration(
+ vue,
+ (error: Error, addons: VueIntegrationAddons) => {
+ void this.formatAndSend(error, {
+ vue: addons,
+ });
+ },
+ {
+ disableVueErrorHandler: this.disableVueErrorHandler,
+ }
+ );
+ }
+
+ /**
+ * Update the current user information
+ *
+ * @param user - New user information
+ */
+ public setUser(user: AffectedUser): void {
+ this.user = user;
+ }
+
+ /**
+ * Clear current user information
+ */
+ public clearUser(): void {
+ this.user = null;
+ }
+
+ /**
+ * Get current user information
+ */
+ public getCurrentUser(): AffectedUser | null {
+ return this.user;
+ }
+
+ /**
+ * Update the context data that will be sent with all events
+ *
+ * @param context - New context data
+ */
+ public setContext(context: EventContext): void {
+ this.context = context;
+ }
+
+ /**
+ * Clear current context data
+ */
+ public clearContext(): void {
+ this.context = undefined;
+ }
+
+ /**
+ * Get current context data
+ */
+ public getCurrentContext(): EventContext | undefined {
+ return this.context;
}
/**
From 9849afd1f93df56cc7b16784c6401dc6e7a0ee65 Mon Sep 17 00:00:00 2001
From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com>
Date: Tue, 14 Oct 2025 17:09:56 +0300
Subject: [PATCH 02/13] refactor: remove unused user and context retrieval
buttons and clean up related code
---
example/index.html | 2 --
example/sample-errors.js | 19 +++----------------
src/catcher.ts | 18 ++----------------
3 files changed, 5 insertions(+), 34 deletions(-)
diff --git a/example/index.html b/example/index.html
index fd56c79..13319a0 100644
--- a/example/index.html
+++ b/example/index.html
@@ -165,7 +165,6 @@
User Management
-
Context Management
@@ -177,7 +176,6 @@ Context Management
-
diff --git a/example/sample-errors.js b/example/sample-errors.js
index c15c37f..6c1a6fc 100644
--- a/example/sample-errors.js
+++ b/example/sample-errors.js
@@ -28,7 +28,9 @@ const buttonPromiseRejection = document.getElementById('btn-promise-rejection');
buttonPromiseRejection.addEventListener('click', function promiseRejectionSample() {
// Promise.reject('This is a sample rejected promise');
- Promise.resolve().then(realErrorSample).then(() => {});
+ Promise.resolve()
+ .then(realErrorSample)
+ .then(() => {});
});
/**
@@ -77,7 +79,6 @@ buttonManualSending.addEventListener('click', () => {
*/
const buttonSetUser = document.getElementById('btn-set-user');
const buttonClearUser = document.getElementById('btn-clear-user');
-const buttonGetUser = document.getElementById('btn-get-user');
buttonSetUser.addEventListener('click', () => {
const userId = document.getElementById('userId').value;
@@ -102,17 +103,11 @@ buttonClearUser.addEventListener('click', () => {
window.hawk.clearUser();
});
-buttonGetUser.addEventListener('click', () => {
- const currentUser = window.hawk.getCurrentUser();
- alert('Current user: ' + (currentUser ? JSON.stringify(currentUser, null, 2) : 'No user set'));
-});
-
/**
* Context Management
*/
const buttonSetContext = document.getElementById('btn-set-context');
const buttonClearContext = document.getElementById('btn-clear-context');
-const buttonGetContext = document.getElementById('btn-get-context');
buttonSetContext.addEventListener('click', () => {
const contextKey = document.getElementById('contextKey').value;
@@ -134,14 +129,6 @@ buttonClearContext.addEventListener('click', () => {
window.hawk.clearContext();
});
-buttonGetContext.addEventListener('click', () => {
- const currentContext = window.hawk.getCurrentContext();
- alert(
- 'Current context: ' +
- (currentContext ? JSON.stringify(currentContext, null, 2) : 'No context set')
- );
-});
-
/**
* Test without user
*/
diff --git a/src/catcher.ts b/src/catcher.ts
index 0b38dd8..1aac619 100644
--- a/src/catcher.ts
+++ b/src/catcher.ts
@@ -241,17 +241,10 @@ export default class Catcher {
}
/**
- * Clear current user information
+ * Clear current user information (revert to generated user)
*/
public clearUser(): void {
- this.user = null;
- }
-
- /**
- * Get current user information
- */
- public getCurrentUser(): AffectedUser | null {
- return this.user;
+ this.user = Catcher.getGeneratedUser();
}
/**
@@ -270,13 +263,6 @@ export default class Catcher {
this.context = undefined;
}
- /**
- * Get current context data
- */
- public getCurrentContext(): EventContext | undefined {
- return this.context;
- }
-
/**
* Init global errors handler
*/
From a2d70268f1003d3ee9846e6685aa9c1235dadf21 Mon Sep 17 00:00:00 2001
From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com>
Date: Tue, 14 Oct 2025 17:13:22 +0300
Subject: [PATCH 03/13] refactor: change user property type to ensure it is
always defined
---
src/catcher.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/catcher.ts b/src/catcher.ts
index 1aac619..bd4f72a 100644
--- a/src/catcher.ts
+++ b/src/catcher.ts
@@ -63,7 +63,7 @@ export default class Catcher {
/**
* Current authenticated user
*/
- private user: AffectedUser | null;
+ private user: AffectedUser;
/**
* Any additional data passed by user for sending with all messages
From 1984b1c6ef422c34c86df85d6b0319002426c877 Mon Sep 17 00:00:00 2001
From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com>
Date: Tue, 14 Oct 2025 17:27:43 +0300
Subject: [PATCH 04/13] chore: update version to 3.2.9 and enhance README with
user and context management examples
---
README.md | 39 ++++++++++++++++++++++++++++++++++++++-
package.json | 2 +-
2 files changed, 39 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 8f5fc14..1e3aa40 100644
--- a/README.md
+++ b/README.md
@@ -106,10 +106,47 @@ const hawk = new HawkCatcher({token: 'INTEGRATION_TOKEN'});
// somewhere in try-catch block or other custom place
hawk.send(new Error('Something went wrong'), {
- myOwnDebugInfo: '1234'
+ myOwnDebugInfo: '1234',
});
```
+## User Management
+
+You can dynamically manage user information after the catcher is initialized:
+
+```js
+const hawk = new HawkCatcher({ token: 'INTEGRATION_TOKEN' });
+
+// Set user information
+hawk.setUser({
+ id: 'user123',
+ name: 'John Doe',
+ url: '/users/123',
+ image: 'https://example.com/avatar.jpg',
+});
+
+// Clear user (revert to generated user)
+hawk.clearUser();
+```
+
+## Context Management
+
+You can dynamically update context data that will be sent with all events:
+
+```js
+const hawk = new HawkCatcher({ token: 'INTEGRATION_TOKEN' });
+
+// Set context data
+hawk.setContext({
+ feature: 'user-dashboard',
+ version: '2.1.0',
+ environment: 'production',
+});
+
+// Clear context data
+hawk.clearContext();
+```
+
## Source maps consuming
If your bundle is minified, it is useful to pass source-map files to the Hawk. After that you will see beautiful original source code lines in Hawk Garage instead of minified code.
diff --git a/package.json b/package.json
index c456d6c..14d623a 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "@hawk.so/javascript",
"type": "commonjs",
- "version": "3.2.8",
+ "version": "3.2.9",
"description": "JavaScript errors tracking for Hawk.so",
"files": [
"dist"
From 22f7887e88ebcde32c7e3201c47caae3b7f699b6 Mon Sep 17 00:00:00 2001
From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com>
Date: Tue, 14 Oct 2025 18:03:34 +0300
Subject: [PATCH 05/13] feat: add user and context validation in setUser and
setContext methods
---
src/catcher.ts | 19 +++++++-
src/utils/validation.ts | 96 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 113 insertions(+), 2 deletions(-)
create mode 100644 src/utils/validation.ts
diff --git a/src/catcher.ts b/src/catcher.ts
index bd4f72a..207a2c2 100644
--- a/src/catcher.ts
+++ b/src/catcher.ts
@@ -17,6 +17,7 @@ import { EventRejectedError } from './errors';
import type { HawkJavaScriptEvent } from './types';
import { isErrorProcessed, markErrorAsProcessed } from './utils/event';
import { addErrorEvent, getConsoleLogStack, initConsoleCatcher } from './addons/consoleCatcher';
+import { validateUser, validateContext, logValidationErrors } from './utils/validation';
/**
* Allow to use global VERSION, that will be overwritten by Webpack
@@ -237,7 +238,14 @@ export default class Catcher {
* @param user - New user information
*/
public setUser(user: AffectedUser): void {
- this.user = user;
+ const validation = validateUser(user);
+
+ if (!validation.isValid) {
+ logValidationErrors('setUser', validation.errors);
+ return;
+ }
+
+ this.user = validation.data!;
}
/**
@@ -253,7 +261,14 @@ export default class Catcher {
* @param context - New context data
*/
public setContext(context: EventContext): void {
- this.context = context;
+ const validation = validateContext(context);
+
+ if (!validation.isValid) {
+ logValidationErrors('setContext', validation.errors);
+ return;
+ }
+
+ this.context = validation.data!;
}
/**
diff --git a/src/utils/validation.ts b/src/utils/validation.ts
new file mode 100644
index 0000000..3dd989c
--- /dev/null
+++ b/src/utils/validation.ts
@@ -0,0 +1,96 @@
+import type { AffectedUser, EventContext } from '@hawk.so/types';
+
+/**
+ * Validation result interface
+ */
+interface ValidationResult {
+ isValid: boolean;
+ data?: T;
+ errors: string[];
+}
+
+/**
+ * Validates user data - basic security checks
+ */
+export function validateUser(user: any): ValidationResult {
+ const errors: string[] = [];
+
+ if (!user || typeof user !== 'object') {
+ errors.push('User must be an object');
+ return { isValid: false, errors };
+ }
+
+ // Validate required ID
+ if (!user.id || typeof user.id !== 'string' || user.id.trim() === '') {
+ errors.push('User ID is required and must be a non-empty string');
+ return { isValid: false, errors };
+ }
+
+ const validatedUser: AffectedUser = {
+ id: user.id.trim(),
+ };
+
+ // Add optional fields if they exist and are strings
+ if (user.name && typeof user.name === 'string') {
+ validatedUser.name = user.name.trim();
+ }
+
+ if (user.url && typeof user.url === 'string') {
+ validatedUser.url = user.url.trim();
+ }
+
+ if (user.image && typeof user.image === 'string') {
+ validatedUser.image = user.image.trim();
+ }
+
+ return {
+ isValid: true,
+ data: validatedUser,
+ errors,
+ };
+}
+
+/**
+ * Validates context data - basic security checks
+ */
+export function validateContext(context: any): ValidationResult {
+ const errors: string[] = [];
+
+ if (!context || typeof context !== 'object') {
+ errors.push('Context must be an object');
+ return { isValid: false, errors };
+ }
+
+ const validatedContext: EventContext = {};
+
+ for (const [key, value] of Object.entries(context)) {
+ // Basic key validation
+ if (typeof key !== 'string' || key.trim() === '') {
+ continue;
+ }
+
+ // Check if value is serializable (prevents injection)
+ try {
+ JSON.stringify(value);
+ } catch (e) {
+ continue; // Skip non-serializable values
+ }
+
+ validatedContext[key.trim()] = value as any;
+ }
+
+ return {
+ isValid: true,
+ data: validatedContext,
+ errors,
+ };
+}
+
+/**
+ * Logs validation errors
+ */
+export function logValidationErrors(prefix: string, errors: string[]): void {
+ errors.forEach((error) => {
+ console.warn(`${prefix}: ${error}`);
+ });
+}
From 59854523f2e4b8d94b4da197c1504a37e75157e7 Mon Sep 17 00:00:00 2001
From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com>
Date: Tue, 14 Oct 2025 19:16:31 +0300
Subject: [PATCH 06/13] chore: update version to 3.2.10
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 66b2bad..ebdf1e5 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "@hawk.so/javascript",
"type": "commonjs",
- "version": "3.2.9",
+ "version": "3.2.10",
"description": "JavaScript errors tracking for Hawk.so",
"files": [
"dist"
From 6d85fa3b2e95980770bca5a0b09d67a542b2eb22 Mon Sep 17 00:00:00 2001
From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com>
Date: Tue, 14 Oct 2025 19:32:36 +0300
Subject: [PATCH 07/13] refactor: change validation functions to use Sanitizer
for object checks
---
src/modules/sanitizer.ts | 2 +-
src/utils/validation.ts | 29 ++++++-----------------------
2 files changed, 7 insertions(+), 24 deletions(-)
diff --git a/src/modules/sanitizer.ts b/src/modules/sanitizer.ts
index 749a2e0..030bc74 100644
--- a/src/modules/sanitizer.ts
+++ b/src/modules/sanitizer.ts
@@ -153,7 +153,7 @@ export default class Sanitizer {
*
* @param target - variable to check
*/
- private static isObject(target: any): boolean {
+ public static isObject(target: any): boolean {
return Sanitizer.typeOf(target) === 'object';
}
diff --git a/src/utils/validation.ts b/src/utils/validation.ts
index 3dd989c..4195261 100644
--- a/src/utils/validation.ts
+++ b/src/utils/validation.ts
@@ -1,4 +1,5 @@
import type { AffectedUser, EventContext } from '@hawk.so/types';
+import Sanitizer from 'src/modules/sanitizer';
/**
* Validation result interface
@@ -12,10 +13,10 @@ interface ValidationResult {
/**
* Validates user data - basic security checks
*/
-export function validateUser(user: any): ValidationResult {
+export function validateUser(user: AffectedUser): ValidationResult {
const errors: string[] = [];
- if (!user || typeof user !== 'object') {
+ if (!user || !Sanitizer.isObject(user)) {
errors.push('User must be an object');
return { isValid: false, errors };
}
@@ -53,35 +54,17 @@ export function validateUser(user: any): ValidationResult {
/**
* Validates context data - basic security checks
*/
-export function validateContext(context: any): ValidationResult {
+export function validateContext(context: EventContext): ValidationResult {
const errors: string[] = [];
- if (!context || typeof context !== 'object') {
+ if (!context || !Sanitizer.isObject(context)) {
errors.push('Context must be an object');
return { isValid: false, errors };
}
- const validatedContext: EventContext = {};
-
- for (const [key, value] of Object.entries(context)) {
- // Basic key validation
- if (typeof key !== 'string' || key.trim() === '') {
- continue;
- }
-
- // Check if value is serializable (prevents injection)
- try {
- JSON.stringify(value);
- } catch (e) {
- continue; // Skip non-serializable values
- }
-
- validatedContext[key.trim()] = value as any;
- }
-
return {
isValid: true,
- data: validatedContext,
+ data: context,
errors,
};
}
From e2eaf5f0b231deea7d88f9cadac307ee523e0f74 Mon Sep 17 00:00:00 2001
From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com>
Date: Tue, 14 Oct 2025 19:36:10 +0300
Subject: [PATCH 08/13] fix: correct import path for Sanitizer in validation
utility
---
src/utils/validation.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/utils/validation.ts b/src/utils/validation.ts
index 4195261..40716a1 100644
--- a/src/utils/validation.ts
+++ b/src/utils/validation.ts
@@ -1,5 +1,5 @@
import type { AffectedUser, EventContext } from '@hawk.so/types';
-import Sanitizer from 'src/modules/sanitizer';
+import Sanitizer from '../modules/sanitizer';
/**
* Validation result interface
From 8152f47f4570bb70802c4a8801f980f031b96b74 Mon Sep 17 00:00:00 2001
From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com>
Date: Tue, 14 Oct 2025 19:38:12 +0300
Subject: [PATCH 09/13] feat: add isObject method to Sanitizer for object type
checking
---
src/modules/sanitizer.ts | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/modules/sanitizer.ts b/src/modules/sanitizer.ts
index 030bc74..afad69b 100644
--- a/src/modules/sanitizer.ts
+++ b/src/modules/sanitizer.ts
@@ -7,6 +7,15 @@
* - represent class as or
*/
export default class Sanitizer {
+ /**
+ * Check if passed variable is an object
+ *
+ * @param target - variable to check
+ */
+ public static isObject(target: any): boolean {
+ return Sanitizer.typeOf(target) === 'object';
+ }
+
/**
* Maximum string length
*/
@@ -148,15 +157,6 @@ export default class Sanitizer {
return result;
}
- /**
- * Check if passed variable is an object
- *
- * @param target - variable to check
- */
- public static isObject(target: any): boolean {
- return Sanitizer.typeOf(target) === 'object';
- }
-
/**
* Check if passed variable is an array
*
From c78cc26ec077ea3c017380cdfbebc71c75b218c6 Mon Sep 17 00:00:00 2001
From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com>
Date: Tue, 14 Oct 2025 19:39:41 +0300
Subject: [PATCH 10/13] refactor: reorganize isObject method in Sanitizer for
improved clarity
---
src/modules/sanitizer.ts | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/modules/sanitizer.ts b/src/modules/sanitizer.ts
index afad69b..a02172d 100644
--- a/src/modules/sanitizer.ts
+++ b/src/modules/sanitizer.ts
@@ -7,15 +7,6 @@
* - represent class as or
*/
export default class Sanitizer {
- /**
- * Check if passed variable is an object
- *
- * @param target - variable to check
- */
- public static isObject(target: any): boolean {
- return Sanitizer.typeOf(target) === 'object';
- }
-
/**
* Maximum string length
*/
@@ -37,6 +28,15 @@ export default class Sanitizer {
*/
private static readonly maxArrayLength: number = 10;
+ /**
+ * Check if passed variable is an object
+ *
+ * @param target - variable to check
+ */
+ public static isObject(target: any): boolean {
+ return Sanitizer.typeOf(target) === 'object';
+ }
+
/**
* Apply sanitizing for array/object/primitives
*
From ab3e8ce0677934c1081ee72f64a510040e4aa5e2 Mon Sep 17 00:00:00 2001
From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com>
Date: Tue, 14 Oct 2025 19:45:03 +0300
Subject: [PATCH 11/13] lint
---
src/catcher.ts | 2 ++
src/utils/validation.ts | 19 ++++++++++++++++---
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/src/catcher.ts b/src/catcher.ts
index 207a2c2..858646e 100644
--- a/src/catcher.ts
+++ b/src/catcher.ts
@@ -242,6 +242,7 @@ export default class Catcher {
if (!validation.isValid) {
logValidationErrors('setUser', validation.errors);
+
return;
}
@@ -265,6 +266,7 @@ export default class Catcher {
if (!validation.isValid) {
logValidationErrors('setContext', validation.errors);
+
return;
}
diff --git a/src/utils/validation.ts b/src/utils/validation.ts
index 40716a1..9536b63 100644
--- a/src/utils/validation.ts
+++ b/src/utils/validation.ts
@@ -12,19 +12,25 @@ interface ValidationResult {
/**
* Validates user data - basic security checks
+ *
+ * @param user
*/
export function validateUser(user: AffectedUser): ValidationResult {
const errors: string[] = [];
if (!user || !Sanitizer.isObject(user)) {
errors.push('User must be an object');
- return { isValid: false, errors };
+
+ return { isValid: false,
+ errors };
}
// Validate required ID
if (!user.id || typeof user.id !== 'string' || user.id.trim() === '') {
errors.push('User ID is required and must be a non-empty string');
- return { isValid: false, errors };
+
+ return { isValid: false,
+ errors };
}
const validatedUser: AffectedUser = {
@@ -53,13 +59,17 @@ export function validateUser(user: AffectedUser): ValidationResult
/**
* Validates context data - basic security checks
+ *
+ * @param context
*/
export function validateContext(context: EventContext): ValidationResult {
const errors: string[] = [];
if (!context || !Sanitizer.isObject(context)) {
errors.push('Context must be an object');
- return { isValid: false, errors };
+
+ return { isValid: false,
+ errors };
}
return {
@@ -71,6 +81,9 @@ export function validateContext(context: EventContext): ValidationResult {
From aa172bb85b990244840faa586fbe80d8b41e52be Mon Sep 17 00:00:00 2001
From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com>
Date: Tue, 14 Oct 2025 19:59:36 +0300
Subject: [PATCH 12/13] refactor
---
README.md | 3 --
example/index.html | 3 --
example/sample-errors.js | 18 ----------
src/catcher.ts | 25 +++-----------
src/utils/validation.ts | 74 ++++++----------------------------------
5 files changed, 16 insertions(+), 107 deletions(-)
diff --git a/README.md b/README.md
index 1e3aa40..44fca79 100644
--- a/README.md
+++ b/README.md
@@ -142,9 +142,6 @@ hawk.setContext({
version: '2.1.0',
environment: 'production',
});
-
-// Clear context data
-hawk.clearContext();
```
## Source maps consuming
diff --git a/example/index.html b/example/index.html
index 13319a0..7b6f125 100644
--- a/example/index.html
+++ b/example/index.html
@@ -100,8 +100,6 @@ Hawk JavaScript Catcher
Send test event
-
-
diff --git a/example/sample-errors.js b/example/sample-errors.js
index 6c1a6fc..6e8ab33 100644
--- a/example/sample-errors.js
+++ b/example/sample-errors.js
@@ -107,7 +107,6 @@ buttonClearUser.addEventListener('click', () => {
* Context Management
*/
const buttonSetContext = document.getElementById('btn-set-context');
-const buttonClearContext = document.getElementById('btn-clear-context');
buttonSetContext.addEventListener('click', () => {
const contextKey = document.getElementById('contextKey').value;
@@ -124,20 +123,3 @@ buttonSetContext.addEventListener('click', () => {
window.hawk.setContext(context);
});
-
-buttonClearContext.addEventListener('click', () => {
- window.hawk.clearContext();
-});
-
-/**
- * Test without user
- */
-const buttonTestWithoutUser = document.getElementById('btn-test-without-user');
-
-buttonTestWithoutUser.addEventListener('click', () => {
- // Clear user first to ensure no user is set
- window.hawk.clearUser();
-
- // Send error without user
- window.hawk.send(new Error('Test error without user'));
-});
diff --git a/src/catcher.ts b/src/catcher.ts
index 858646e..623a593 100644
--- a/src/catcher.ts
+++ b/src/catcher.ts
@@ -17,7 +17,7 @@ import { EventRejectedError } from './errors';
import type { HawkJavaScriptEvent } from './types';
import { isErrorProcessed, markErrorAsProcessed } from './utils/event';
import { addErrorEvent, getConsoleLogStack, initConsoleCatcher } from './addons/consoleCatcher';
-import { validateUser, validateContext, logValidationErrors } from './utils/validation';
+import { validateUser, validateContext } from './utils/validation';
/**
* Allow to use global VERSION, that will be overwritten by Webpack
@@ -238,15 +238,11 @@ export default class Catcher {
* @param user - New user information
*/
public setUser(user: AffectedUser): void {
- const validation = validateUser(user);
-
- if (!validation.isValid) {
- logValidationErrors('setUser', validation.errors);
-
+ if (!validateUser(user)) {
return;
}
- this.user = validation.data!;
+ this.user = user;
}
/**
@@ -262,22 +258,11 @@ export default class Catcher {
* @param context - New context data
*/
public setContext(context: EventContext): void {
- const validation = validateContext(context);
-
- if (!validation.isValid) {
- logValidationErrors('setContext', validation.errors);
-
+ if (!validateContext(context)) {
return;
}
- this.context = validation.data!;
- }
-
- /**
- * Clear current context data
- */
- public clearContext(): void {
- this.context = undefined;
+ this.context = context;
}
/**
diff --git a/src/utils/validation.ts b/src/utils/validation.ts
index 9536b63..e6fff31 100644
--- a/src/utils/validation.ts
+++ b/src/utils/validation.ts
@@ -1,60 +1,27 @@
+import log from './log';
import type { AffectedUser, EventContext } from '@hawk.so/types';
import Sanitizer from '../modules/sanitizer';
-/**
- * Validation result interface
- */
-interface ValidationResult {
- isValid: boolean;
- data?: T;
- errors: string[];
-}
-
/**
* Validates user data - basic security checks
*
* @param user
*/
-export function validateUser(user: AffectedUser): ValidationResult {
- const errors: string[] = [];
-
+export function validateUser(user: AffectedUser): boolean {
if (!user || !Sanitizer.isObject(user)) {
- errors.push('User must be an object');
+ log('validateUser: User must be an object', 'warn');
- return { isValid: false,
- errors };
+ return false;
}
// Validate required ID
if (!user.id || typeof user.id !== 'string' || user.id.trim() === '') {
- errors.push('User ID is required and must be a non-empty string');
-
- return { isValid: false,
- errors };
- }
-
- const validatedUser: AffectedUser = {
- id: user.id.trim(),
- };
-
- // Add optional fields if they exist and are strings
- if (user.name && typeof user.name === 'string') {
- validatedUser.name = user.name.trim();
- }
-
- if (user.url && typeof user.url === 'string') {
- validatedUser.url = user.url.trim();
- }
+ log('validateUser: User ID is required and must be a non-empty string', 'warn');
- if (user.image && typeof user.image === 'string') {
- validatedUser.image = user.image.trim();
+ return false;
}
- return {
- isValid: true,
- data: validatedUser,
- errors,
- };
+ return true;
}
/**
@@ -62,31 +29,12 @@ export function validateUser(user: AffectedUser): ValidationResult
*
* @param context
*/
-export function validateContext(context: EventContext): ValidationResult {
- const errors: string[] = [];
-
+export function validateContext(context: EventContext): boolean {
if (!context || !Sanitizer.isObject(context)) {
- errors.push('Context must be an object');
+ log('validateContext: Context must be an object', 'warn');
- return { isValid: false,
- errors };
+ return false;
}
- return {
- isValid: true,
- data: context,
- errors,
- };
-}
-
-/**
- * Logs validation errors
- *
- * @param prefix
- * @param errors
- */
-export function logValidationErrors(prefix: string, errors: string[]): void {
- errors.forEach((error) => {
- console.warn(`${prefix}: ${error}`);
- });
+ return true;
}
From d15b84bf13b45c68d1235fbabf867c2262ba557c Mon Sep 17 00:00:00 2001
From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com>
Date: Tue, 14 Oct 2025 20:06:09 +0300
Subject: [PATCH 13/13] refactor: update user and context handling in Catcher
class for improved clarity
---
src/catcher.ts | 6 +++---
src/utils/validation.ts | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/catcher.ts b/src/catcher.ts
index 623a593..6b8d9b8 100644
--- a/src/catcher.ts
+++ b/src/catcher.ts
@@ -113,8 +113,8 @@ export default class Catcher {
this.token = settings.token;
this.debug = settings.debug || false;
this.release = settings.release;
- this.user = settings.user || Catcher.getGeneratedUser();
- this.context = settings.context || undefined;
+ this.setUser(settings.user || Catcher.getGeneratedUser());
+ this.setContext(settings.context || undefined);
this.beforeSend = settings.beforeSend;
this.disableVueErrorHandler = settings.disableVueErrorHandler !== null && settings.disableVueErrorHandler !== undefined ? settings.disableVueErrorHandler : false;
this.consoleTracking = settings.consoleTracking !== null && settings.consoleTracking !== undefined ? settings.consoleTracking : true;
@@ -257,7 +257,7 @@ export default class Catcher {
*
* @param context - New context data
*/
- public setContext(context: EventContext): void {
+ public setContext(context: EventContext | undefined): void {
if (!validateContext(context)) {
return;
}
diff --git a/src/utils/validation.ts b/src/utils/validation.ts
index e6fff31..468177f 100644
--- a/src/utils/validation.ts
+++ b/src/utils/validation.ts
@@ -29,7 +29,7 @@ export function validateUser(user: AffectedUser): boolean {
*
* @param context
*/
-export function validateContext(context: EventContext): boolean {
+export function validateContext(context: EventContext | undefined): boolean {
if (!context || !Sanitizer.isObject(context)) {
log('validateContext: Context must be an object', 'warn');