Skip to content

UserManager abstraction added#158

Open
Reversean wants to merge 1 commit intomasterfrom
refactor/user-manager
Open

UserManager abstraction added#158
Reversean wants to merge 1 commit intomasterfrom
refactor/user-manager

Conversation

@Reversean
Copy link
Member

@Reversean Reversean commented Feb 12, 2026

Related #151

Added UserManager interface which could be used as env-agnositc abstraction to persist AffectedUser's during catching errors.

Also StorageUserManager implementation based on env-agnostic HawkStorage added.

@Reversean Reversean changed the base branch from master to chore/core-workspace February 12, 2026 18:34
@Reversean Reversean changed the title Refactor/user manager UserManager abstraction added Feb 12, 2026
@Reversean Reversean marked this pull request as draft February 12, 2026 18:36
@Reversean Reversean force-pushed the refactor/user-manager branch 2 times, most recently from 37eecbd to 70760fd Compare February 12, 2026 19:44
@Reversean Reversean force-pushed the chore/core-workspace branch 2 times, most recently from 09b7782 to 39d9d88 Compare February 16, 2026 07:34
@Reversean Reversean force-pushed the refactor/user-manager branch from 70760fd to 3f9a33a Compare February 16, 2026 07:35
@Reversean Reversean marked this pull request as ready for review February 16, 2026 07:36
@Reversean Reversean force-pushed the refactor/user-manager branch 4 times, most recently from a0132ff to 9a60f23 Compare February 16, 2026 22:22
@Reversean Reversean changed the base branch from chore/core-workspace to chore/browser-workspace February 16, 2026 22:24
@Reversean Reversean changed the base branch from chore/browser-workspace to refactor/hawk-storage February 16, 2026 22:25
@Reversean Reversean force-pushed the refactor/hawk-storage branch from 9e471fc to 2d655c1 Compare February 16, 2026 22:26
@Reversean Reversean force-pushed the refactor/user-manager branch from 9a60f23 to eacefe1 Compare February 16, 2026 22:27
@Reversean Reversean force-pushed the refactor/hawk-storage branch 2 times, most recently from c3013b6 to 223001d Compare February 17, 2026 13:27
@Reversean Reversean force-pushed the refactor/user-manager branch from eacefe1 to 7d5e8e4 Compare February 17, 2026 13:40
@neSpecc neSpecc requested a review from Copilot February 17, 2026 18:13
* {@link UserManager} implementation that persists the affected user
* in the browser's {@linkcode localStorage}.
*/
export class LocalStorageUserManager extends UserManager {

This comment was marked as outdated.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

* {@link UserManager} implementation that persists the affected user
* via an injected {@link HawkStorage} backend.
*/
export class HawkStorageUserManager implements UserManager {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its not clear for me why do we need this class? Maybe UserManager can do the same (accept HawkStorage as an argument and implement getUser/setUser etc)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I apparently misunderstood the user concept: I thought the user meant the initiator of the action that caused the error (which is usually the user of the application), but here the user means the application instance using Hawk Catcher.

If that's the case, you're right.

Yet we have only HawkUserManager.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a UserManager abstraction in @hawk.so/core to enable environment-agnostic user persistence, as part of the larger effort to extract shared logic from @hawk.so/javascript into a core package (related to issue #151).

Changes:

  • Added UserManager interface and HawkStorageUserManager implementation in @hawk.so/core for environment-agnostic user management
  • Refactored @hawk.so/javascript to use the new UserManager abstraction instead of directly accessing localStorage
  • Modified user initialization to be lazy (generated on first access) rather than eager (generated in constructor)

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
packages/core/src/users/user-manager.ts New interface defining the user management contract
packages/core/src/users/hawk-storage-user-manager.ts Implementation of UserManager using HawkStorage for persistence
packages/core/src/index.ts Exports new UserManager interface and implementation
packages/core/tests/users/hawk-storage-user-manager.test.ts Unit tests for HawkStorageUserManager
packages/core/package.json Added test scripts and dev dependencies for vitest
packages/core/vitest.config.ts New vitest configuration for core package
packages/core/tsconfig.test.json TypeScript config for test files
packages/javascript/src/catcher.ts Refactored to use UserManager, removed getGeneratedUser, changed user initialization to lazy
packages/javascript/src/storages/hawk-local-storage.ts Browser-specific implementation of HawkStorage
packages/javascript/package.json Added @hawk.so/core as dependency
packages/javascript/vite.config.ts Marked @hawk.so/core as external dependency
packages/javascript/vitest.config.ts Added source condition for workspace dependency resolution
yarn.lock Updated workspace dependency configurations
package.json Added test:all and test:modified scripts for workspace testing
.github/workflows/main.yml Updated test workflow to run tests on modified packages with fetch-depth: 0

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +29 to +42
const storedId = this.storage.getItem(HAWK_USER_STORAGE_KEY);

if (storedId) {
return {
id: storedId,
};
}

return null;
}

/** @inheritDoc */
public setUser(user: AffectedUser): void {
this.storage.setItem(HAWK_USER_STORAGE_KEY, user.id);
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HawkStorageUserManager only persists the user.id field and loses any other AffectedUser properties (such as name, email, url, image, or any custom fields that AffectedUser might support). When getUser() is called, it only reconstructs the id field. This means if a developer calls setUser with a full user object containing additional properties, those properties will be lost. Consider serializing the entire AffectedUser object as JSON, or document this limitation clearly if it's intentional.

Suggested change
const storedId = this.storage.getItem(HAWK_USER_STORAGE_KEY);
if (storedId) {
return {
id: storedId,
};
}
return null;
}
/** @inheritDoc */
public setUser(user: AffectedUser): void {
this.storage.setItem(HAWK_USER_STORAGE_KEY, user.id);
const storedValue = this.storage.getItem(HAWK_USER_STORAGE_KEY);
if (!storedValue) {
return null;
}
// Try to parse the stored value as a full AffectedUser object.
try {
const parsed = JSON.parse(storedValue) as AffectedUser | null;
if (parsed && typeof parsed === 'object' && 'id' in parsed) {
return parsed;
}
} catch {
// Ignore JSON parsing errors and fall back to legacy behavior below.
}
// Backward compatibility: prior versions stored only the user id as a string.
return {
id: storedValue,
};
}
/** @inheritDoc */
public setUser(user: AffectedUser): void {
// Persist the full AffectedUser object so that all properties are retained.
this.storage.setItem(HAWK_USER_STORAGE_KEY, JSON.stringify(user));

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we should serialize AffectedUser and store it.

@neSpecc what's your opinion?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getUser is not a public interface, so developer can't actually get previously set user. We store only "id" as a default identifier, which is used only when setUser was not called.

So I don't see any reason to store serialized user.

@Reversean Reversean force-pushed the refactor/hawk-storage branch 2 times, most recently from 8cbdac2 to cd47f1e Compare February 17, 2026 19:27
@Reversean Reversean force-pushed the refactor/user-manager branch 2 times, most recently from fb57118 to 77fb17d Compare February 18, 2026 17:54
@Reversean Reversean force-pushed the refactor/hawk-storage branch from cd47f1e to 7c1ad96 Compare February 22, 2026 14:47
@Reversean Reversean force-pushed the refactor/user-manager branch from 77fb17d to 7c596c5 Compare February 22, 2026 14:48
@Reversean Reversean force-pushed the refactor/hawk-storage branch from 7c1ad96 to 591724a Compare February 25, 2026 17:23
@Reversean Reversean force-pushed the refactor/user-manager branch from 7c596c5 to d0c4bff Compare February 25, 2026 17:24
@Reversean Reversean force-pushed the refactor/hawk-storage branch from 591724a to a2abcb9 Compare March 5, 2026 08:41
Base automatically changed from refactor/hawk-storage to master March 5, 2026 08:48
@Reversean Reversean force-pushed the refactor/user-manager branch from 798f237 to 88436ca Compare March 5, 2026 08:49
/**
* Storage key used to persist the auto-generated user ID.
*/
export const SESSION_STORAGE_KEY = 'hawk-user-id';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why "session"?

Copy link
Member Author

@Reversean Reversean Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally there was HAWK_USER_ID_KEY.

@FeironoX5, you've changed the name? Any comments?

Comment on lines +553 to 561
private getUser(): AffectedUser {
const user = this.userManager.getUser();
if (user) {
return user;
}
const generatedId = id();
this.userManager.persistGeneratedId(generatedId);
return { id: generatedId };
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest to get rid of getUser method. Use userManager.getUser() instead

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants