diff --git a/apps/browser-extension/eslint.config.js b/apps/browser-extension/eslint.config.js index 8ccdce139..a75e74892 100644 --- a/apps/browser-extension/eslint.config.js +++ b/apps/browser-extension/eslint.config.js @@ -105,8 +105,51 @@ export default [ ], "react-hooks/exhaustive-deps": "warn", "react/jsx-no-constructed-context-values": "error", - "import/no-unresolved": "error", - }, + "import/no-unresolved": [ + "error", + { + ignore: ['^#imports$'] // Ignore virtual imports from WXT which are not resolved by the typescript compiler + } + ], + "import/order": [ + "error", + { + "groups": [ + "builtin", // Node "fs", "path", etc. + "external", // "react", "lodash", etc. + "internal", // Aliased paths like "@/utils" + "parent", // "../" + "sibling", // "./" + "index", // "./index" + "object", // import 'foo' + "type" // import type ... + ], + "pathGroups": [ + { + pattern: "@/entrypoints/**", + group: "internal", + position: "before" + }, + { + pattern: "@/utils/**", + group: "internal", + position: "before" + }, + { + pattern: "@/hooks/**", + group: "internal", + position: "before" + } + ], + "pathGroupsExcludedImportTypes": ["builtin"], + "newlines-between": "always", + "alphabetize": { + order: "asc", + caseInsensitive: true + } + } + ], + }, settings: { 'import/resolver': { typescript: { diff --git a/apps/browser-extension/src/entrypoints/background.ts b/apps/browser-extension/src/entrypoints/background.ts index f93b39731..c7bef95a0 100644 --- a/apps/browser-extension/src/entrypoints/background.ts +++ b/apps/browser-extension/src/entrypoints/background.ts @@ -1,11 +1,13 @@ -import { defineBackground } from '#imports'; import { onMessage, sendMessage } from "webext-bridge/background"; + import { setupContextMenus } from '@/entrypoints/background/ContextMenu'; -import { handleCheckAuthStatus, handleClearVault, handleCreateIdentity, handleGetCredentials, handleGetDefaultEmailDomain, handleGetDefaultIdentityLanguage, handleGetDerivedKey, handleGetPasswordSettings, handleGetVault, handleStoreVault, handleSyncVault } from '@/entrypoints/background/VaultMessageHandler'; import { handleOpenPopup, handlePopupWithCredential, handleToggleContextMenu } from '@/entrypoints/background/PopupMessageHandler'; -import { storage, browser } from '#imports'; +import { handleCheckAuthStatus, handleClearVault, handleCreateIdentity, handleGetCredentials, handleGetDefaultEmailDomain, handleGetDefaultIdentityLanguage, handleGetDerivedKey, handleGetPasswordSettings, handleGetVault, handleStoreVault, handleSyncVault } from '@/entrypoints/background/VaultMessageHandler'; + import { GLOBAL_CONTEXT_MENU_ENABLED_KEY } from '@/utils/Constants'; +import { defineBackground, storage, browser } from '#imports'; + export default defineBackground({ /** * This is the main entry point for the background script. @@ -32,7 +34,7 @@ export default defineBackground({ if (isContextMenuEnabled) { setupContextMenus(); } - + // Listen for custom commands try { browser.commands.onCommand.addListener(async (command) => { diff --git a/apps/browser-extension/src/entrypoints/background/ContextMenu.ts b/apps/browser-extension/src/entrypoints/background/ContextMenu.ts index 45fc8086e..466b1a73a 100644 --- a/apps/browser-extension/src/entrypoints/background/ContextMenu.ts +++ b/apps/browser-extension/src/entrypoints/background/ContextMenu.ts @@ -1,8 +1,10 @@ -import { sendMessage } from 'webext-bridge/background'; -import { browser } from "#imports"; import { type Browser } from '@wxt-dev/browser'; +import { sendMessage } from 'webext-bridge/background'; + import { PasswordGenerator } from '@/utils/shared/password-generator'; +import { browser } from "#imports"; + /** * Setup the context menus. */ diff --git a/apps/browser-extension/src/entrypoints/background/PopupMessageHandler.ts b/apps/browser-extension/src/entrypoints/background/PopupMessageHandler.ts index e99ffbb7e..a489354f3 100644 --- a/apps/browser-extension/src/entrypoints/background/PopupMessageHandler.ts +++ b/apps/browser-extension/src/entrypoints/background/PopupMessageHandler.ts @@ -1,8 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { browser } from '#imports'; import { BoolResponse } from '@/utils/types/messaging/BoolResponse'; + import { setupContextMenus } from './ContextMenu'; +import { browser } from '#imports'; + /** * Handle opening the popup. */ diff --git a/apps/browser-extension/src/entrypoints/background/VaultMessageHandler.ts b/apps/browser-extension/src/entrypoints/background/VaultMessageHandler.ts index c5a1fa2b6..2e4683d05 100644 --- a/apps/browser-extension/src/entrypoints/background/VaultMessageHandler.ts +++ b/apps/browser-extension/src/entrypoints/background/VaultMessageHandler.ts @@ -1,16 +1,15 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { storage } from 'wxt/utils/storage'; + import { EncryptionUtility } from '@/utils/EncryptionUtility'; +import type { Vault, VaultResponse, VaultPostResponse } from '@/utils/shared/models'; import { SqliteClient } from '@/utils/SqliteClient'; -import { WebApiService } from '@/utils/WebApiService'; -import { Vault } from '@/utils/types/webapi/Vault'; -import { VaultResponse } from '@/utils/types/webapi/VaultResponse'; -import { VaultPostResponse } from '@/utils/types/webapi/VaultPostResponse'; import { BoolResponse as messageBoolResponse } from '@/utils/types/messaging/BoolResponse'; -import { VaultResponse as messageVaultResponse } from '@/utils/types/messaging/VaultResponse'; import { CredentialsResponse as messageCredentialsResponse } from '@/utils/types/messaging/CredentialsResponse'; -import { StringResponse as stringResponse } from '@/utils/types/messaging/StringResponse'; import { PasswordSettingsResponse as messagePasswordSettingsResponse } from '@/utils/types/messaging/PasswordSettingsResponse'; +import { StringResponse as stringResponse } from '@/utils/types/messaging/StringResponse'; +import { VaultResponse as messageVaultResponse } from '@/utils/types/messaging/VaultResponse'; +import { WebApiService } from '@/utils/WebApiService'; /** * Check if the user is logged in and if the vault is locked. diff --git a/apps/browser-extension/src/entrypoints/content.ts b/apps/browser-extension/src/entrypoints/content.ts index f0b985a4d..c19310738 100644 --- a/apps/browser-extension/src/entrypoints/content.ts +++ b/apps/browser-extension/src/entrypoints/content.ts @@ -1,9 +1,12 @@ import '@/entrypoints/contentScript/style.css'; -import { FormDetector } from '@/utils/formDetector/FormDetector'; -import { isAutoShowPopupEnabled, openAutofillPopup, removeExistingPopup } from '@/entrypoints/contentScript/Popup'; -import { injectIcon, popupDebounceTimeHasPassed, validateInputField } from '@/entrypoints/contentScript/Form'; import { onMessage } from "webext-bridge/content-script"; + +import { injectIcon, popupDebounceTimeHasPassed, validateInputField } from '@/entrypoints/contentScript/Form'; +import { isAutoShowPopupEnabled, openAutofillPopup, removeExistingPopup } from '@/entrypoints/contentScript/Popup'; + +import { FormDetector } from '@/utils/formDetector/FormDetector'; import { BoolResponse as messageBoolResponse } from '@/utils/types/messaging/BoolResponse'; + import { defineContentScript } from '#imports'; import { createShadowRootUi } from '#imports'; diff --git a/apps/browser-extension/src/entrypoints/contentScript/Form.ts b/apps/browser-extension/src/entrypoints/contentScript/Form.ts index e40c44d64..92ea6392d 100644 --- a/apps/browser-extension/src/entrypoints/contentScript/Form.ts +++ b/apps/browser-extension/src/entrypoints/contentScript/Form.ts @@ -1,7 +1,8 @@ +import { openAutofillPopup } from '@/entrypoints/contentScript/Popup'; + import { FormDetector } from '@/utils/formDetector/FormDetector'; import { FormFiller } from '@/utils/formDetector/FormFiller'; import { Credential } from '@/utils/types/Credential'; -import { openAutofillPopup } from '@/entrypoints/contentScript/Popup'; /** * Global timestamp to track popup debounce time. diff --git a/apps/browser-extension/src/entrypoints/contentScript/Popup.ts b/apps/browser-extension/src/entrypoints/contentScript/Popup.ts index 2d00f0976..30a4276db 100644 --- a/apps/browser-extension/src/entrypoints/contentScript/Popup.ts +++ b/apps/browser-extension/src/entrypoints/contentScript/Popup.ts @@ -1,16 +1,19 @@ -import { storage } from '#imports'; import { sendMessage } from 'webext-bridge/content-script'; -import { fillCredential } from '@/entrypoints/contentScript/Form'; + import { filterCredentials } from '@/entrypoints/contentScript/Filter'; +import { fillCredential } from '@/entrypoints/contentScript/Form'; + +import { DISABLED_SITES_KEY, TEMPORARY_DISABLED_SITES_KEY, GLOBAL_AUTOFILL_POPUP_ENABLED_KEY, VAULT_LOCKED_DISMISS_UNTIL_KEY, LAST_CUSTOM_EMAIL_KEY, LAST_CUSTOM_USERNAME_KEY } from '@/utils/Constants'; +import { FormDetector } from '@/utils/formDetector/FormDetector'; import { CreateIdentityGenerator } from '@/utils/shared/identity-generator'; import { CreatePasswordGenerator, PasswordGenerator } from '@/utils/shared/password-generator'; +import { SqliteClient } from '@/utils/SqliteClient'; +import { Credential } from '@/utils/types/Credential'; import { CredentialsResponse } from '@/utils/types/messaging/CredentialsResponse'; import { PasswordSettingsResponse } from '@/utils/types/messaging/PasswordSettingsResponse'; -import { SqliteClient } from '@/utils/SqliteClient'; import { StringResponse } from '@/utils/types/messaging/StringResponse'; -import { FormDetector } from '@/utils/formDetector/FormDetector'; -import { Credential } from '@/utils/types/Credential'; -import { DISABLED_SITES_KEY, TEMPORARY_DISABLED_SITES_KEY, GLOBAL_AUTOFILL_POPUP_ENABLED_KEY, VAULT_LOCKED_DISMISS_UNTIL_KEY, LAST_CUSTOM_EMAIL_KEY, LAST_CUSTOM_USERNAME_KEY } from '@/utils/Constants'; + +import { storage } from '#imports'; /** * WeakMap to store event listeners for popup containers diff --git a/apps/browser-extension/src/entrypoints/popup/App.tsx b/apps/browser-extension/src/entrypoints/popup/App.tsx index 8daa8b823..a9b011afa 100644 --- a/apps/browser-extension/src/entrypoints/popup/App.tsx +++ b/apps/browser-extension/src/entrypoints/popup/App.tsx @@ -1,20 +1,22 @@ import React, { useState, useEffect } from 'react'; import { HashRouter as Router, Routes, Route } from 'react-router-dom'; -import { useAuth } from '@/entrypoints/popup/context/AuthContext'; -import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; -import Header from '@/entrypoints/popup/components/Layout/Header'; -import BottomNav from '@/entrypoints/popup/components/Layout/BottomNav'; -import AuthSettings from '@/entrypoints/popup/pages/AuthSettings'; -import CredentialsList from '@/entrypoints/popup/pages/CredentialsList'; -import EmailsList from '@/entrypoints/popup/pages/EmailsList'; -import LoadingSpinner from '@/entrypoints/popup/components/LoadingSpinner'; -import Home from '@/entrypoints/popup/pages/Home'; -import CredentialDetails from '@/entrypoints/popup/pages/CredentialDetails'; -import EmailDetails from '@/entrypoints/popup/pages/EmailDetails'; -import Settings from '@/entrypoints/popup/pages/Settings'; + import GlobalStateChangeHandler from '@/entrypoints/popup/components/GlobalStateChangeHandler'; +import BottomNav from '@/entrypoints/popup/components/Layout/BottomNav'; +import Header from '@/entrypoints/popup/components/Layout/Header'; +import LoadingSpinner from '@/entrypoints/popup/components/LoadingSpinner'; +import { useAuth } from '@/entrypoints/popup/context/AuthContext'; import { useLoading } from '@/entrypoints/popup/context/LoadingContext'; +import AuthSettings from '@/entrypoints/popup/pages/AuthSettings'; +import CredentialDetails from '@/entrypoints/popup/pages/CredentialDetails'; +import CredentialsList from '@/entrypoints/popup/pages/CredentialsList'; +import EmailDetails from '@/entrypoints/popup/pages/EmailDetails'; +import EmailsList from '@/entrypoints/popup/pages/EmailsList'; +import Home from '@/entrypoints/popup/pages/Home'; import Logout from '@/entrypoints/popup/pages/Logout'; +import Settings from '@/entrypoints/popup/pages/Settings'; + +import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; import '@/entrypoints/popup/style.css'; /** diff --git a/apps/browser-extension/src/entrypoints/popup/components/CredentialCard.tsx b/apps/browser-extension/src/entrypoints/popup/components/CredentialCard.tsx index b6ec58a23..df845a5cb 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/CredentialCard.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/CredentialCard.tsx @@ -1,7 +1,8 @@ import React from 'react'; import { useNavigate } from 'react-router-dom'; -import { Credential } from '@/utils/types/Credential'; + import SqliteClient from '@/utils/SqliteClient'; +import { Credential } from '@/utils/types/Credential'; type CredentialCardProps = { credential: Credential; diff --git a/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/AliasBlock.tsx b/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/AliasBlock.tsx index 9319e6f4a..7b9f033c1 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/AliasBlock.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/AliasBlock.tsx @@ -1,7 +1,9 @@ import React from 'react'; -import { Credential } from '@/utils/types/Credential'; + import { FormInputCopyToClipboard } from '@/entrypoints/popup/components/FormInputCopyToClipboard'; + import { IdentityHelperUtils } from '@/utils/shared/identity-generator'; +import { Credential } from '@/utils/types/Credential'; type AliasBlockProps = { credential: Credential; diff --git a/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/EmailBlock.tsx b/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/EmailBlock.tsx index bf21426ca..1652577c6 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/EmailBlock.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/EmailBlock.tsx @@ -1,4 +1,5 @@ import React from 'react'; + import { EmailPreview } from '@/entrypoints/popup/components/EmailPreview'; type EmailBlockProps = { diff --git a/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/HeaderBlock.tsx b/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/HeaderBlock.tsx index f7654e329..e29279e37 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/HeaderBlock.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/HeaderBlock.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import { Credential } from '@/utils/types/Credential'; + import SqliteClient from '@/utils/SqliteClient'; +import { Credential } from '@/utils/types/Credential'; type HeaderBlockProps = { credential: Credential; diff --git a/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/LoginCredentialsBlock.tsx b/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/LoginCredentialsBlock.tsx index 8852492fd..2e471c2f8 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/LoginCredentialsBlock.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/LoginCredentialsBlock.tsx @@ -1,7 +1,9 @@ import React from 'react'; -import { Credential } from '@/utils/types/Credential'; + import { FormInputCopyToClipboard } from '@/entrypoints/popup/components/FormInputCopyToClipboard'; +import { Credential } from '@/utils/types/Credential'; + type LoginCredentialsBlockProps = { credential: Credential; } diff --git a/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/TotpBlock.tsx b/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/TotpBlock.tsx index d9c332aac..9a60f6d5b 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/TotpBlock.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/TotpBlock.tsx @@ -1,7 +1,9 @@ -import React, { useState, useEffect } from 'react'; -import { useDb } from '@/entrypoints/popup/context/DbContext'; -import { TotpCode } from '@/utils/types/TotpCode'; import * as OTPAuth from 'otpauth'; +import React, { useState, useEffect } from 'react'; + +import { useDb } from '@/entrypoints/popup/context/DbContext'; + +import { TotpCode } from '@/utils/types/TotpCode'; type TotpBlockProps = { credentialId: string; diff --git a/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/index.tsx b/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/index.tsx index 32ed2cc8d..1ec32a6fd 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/index.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/CredentialDetails/index.tsx @@ -1,9 +1,9 @@ -import HeaderBlock from './HeaderBlock'; -import EmailBlock from './EmailBlock'; -import TotpBlock from './TotpBlock'; -import LoginCredentialsBlock from './LoginCredentialsBlock'; import AliasBlock from './AliasBlock'; +import EmailBlock from './EmailBlock'; +import HeaderBlock from './HeaderBlock'; +import LoginCredentialsBlock from './LoginCredentialsBlock'; import NotesBlock from './NotesBlock'; +import TotpBlock from './TotpBlock'; export { HeaderBlock, diff --git a/apps/browser-extension/src/entrypoints/popup/components/EmailPreview.tsx b/apps/browser-extension/src/entrypoints/popup/components/EmailPreview.tsx index e70c183f6..9e4d4e120 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/EmailPreview.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/EmailPreview.tsx @@ -1,11 +1,14 @@ import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; -import { storage } from '#imports'; -import { useWebApi } from '@/entrypoints/popup/context/WebApiContext'; + import { useDb } from '@/entrypoints/popup/context/DbContext'; -import { EncryptionUtility } from '@/utils/EncryptionUtility'; -import { MailboxEmail } from '@/utils/types/webapi/MailboxEmail'; +import { useWebApi } from '@/entrypoints/popup/context/WebApiContext'; + import { AppInfo } from '@/utils/AppInfo'; +import { EncryptionUtility } from '@/utils/EncryptionUtility'; +import type { MailboxEmail } from '@/utils/shared/models'; + +import { storage } from '#imports'; type EmailPreviewProps = { email: string; diff --git a/apps/browser-extension/src/entrypoints/popup/components/FormInputCopyToClipboard.tsx b/apps/browser-extension/src/entrypoints/popup/components/FormInputCopyToClipboard.tsx index 66174dc2f..91daaf978 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/FormInputCopyToClipboard.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/FormInputCopyToClipboard.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; + import { ClipboardCopyService } from '@/entrypoints/popup/utils/ClipboardCopyService'; /** diff --git a/apps/browser-extension/src/entrypoints/popup/components/GlobalStateChangeHandler.tsx b/apps/browser-extension/src/entrypoints/popup/components/GlobalStateChangeHandler.tsx index 6f0dd45db..7e1f3af87 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/GlobalStateChangeHandler.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/GlobalStateChangeHandler.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; + import { useAuth } from '@/entrypoints/popup/context/AuthContext'; /** diff --git a/apps/browser-extension/src/entrypoints/popup/components/Layout/BottomNav.tsx b/apps/browser-extension/src/entrypoints/popup/components/Layout/BottomNav.tsx index dc65895e1..4c1d8b22e 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/Layout/BottomNav.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/Layout/BottomNav.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; + import { useAuth } from '@/entrypoints/popup/context/AuthContext'; import { useDb } from '@/entrypoints/popup/context/DbContext'; diff --git a/apps/browser-extension/src/entrypoints/popup/components/Layout/Header.tsx b/apps/browser-extension/src/entrypoints/popup/components/Layout/Header.tsx index ee80dbe30..a9d43170f 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/Layout/Header.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/Layout/Header.tsx @@ -1,10 +1,13 @@ import React from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; -import { storage } from '#imports'; + import { UserMenu } from '@/entrypoints/popup/components/Layout/UserMenu'; import { useAuth } from '@/entrypoints/popup/context/AuthContext'; + import { AppInfo } from '@/utils/AppInfo'; +import { storage } from '#imports'; + /** * Header props. */ diff --git a/apps/browser-extension/src/entrypoints/popup/components/Layout/UserMenu.tsx b/apps/browser-extension/src/entrypoints/popup/components/Layout/UserMenu.tsx index d2c149fd0..e298fb054 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/Layout/UserMenu.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/Layout/UserMenu.tsx @@ -1,5 +1,6 @@ import React, { useState, useRef, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; + import { useAuth } from '@/entrypoints/popup/context/AuthContext'; import { useLoading } from '@/entrypoints/popup/context/LoadingContext'; diff --git a/apps/browser-extension/src/entrypoints/popup/components/LoadingSpinnerFullScreen.tsx b/apps/browser-extension/src/entrypoints/popup/components/LoadingSpinnerFullScreen.tsx index 05bfa9d4c..b1620d3fe 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/LoadingSpinnerFullScreen.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/LoadingSpinnerFullScreen.tsx @@ -1,4 +1,5 @@ import React from 'react'; + import { useLoading } from '@/entrypoints/popup/context/LoadingContext'; /** diff --git a/apps/browser-extension/src/entrypoints/popup/components/LoginServerInfo.tsx b/apps/browser-extension/src/entrypoints/popup/components/LoginServerInfo.tsx index c4dc961a5..edd1bc3c9 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/LoginServerInfo.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/LoginServerInfo.tsx @@ -1,8 +1,10 @@ import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { storage } from '#imports'; + import { AppInfo } from '@/utils/AppInfo'; +import { storage } from '#imports'; + /** * Component for displaying the login server information. */ diff --git a/apps/browser-extension/src/entrypoints/popup/context/AuthContext.tsx b/apps/browser-extension/src/entrypoints/popup/context/AuthContext.tsx index 1d2403609..00dce9236 100644 --- a/apps/browser-extension/src/entrypoints/popup/context/AuthContext.tsx +++ b/apps/browser-extension/src/entrypoints/popup/context/AuthContext.tsx @@ -1,9 +1,12 @@ import React, { createContext, useContext, useState, useEffect, useMemo, useCallback } from 'react'; -import { storage } from '#imports'; import { sendMessage } from 'webext-bridge/popup'; + import { useDb } from '@/entrypoints/popup/context/DbContext'; + import { VAULT_LOCKED_DISMISS_UNTIL_KEY } from '@/utils/Constants'; +import { storage } from '#imports'; + type AuthContextType = { isLoggedIn: boolean; isInitialized: boolean; diff --git a/apps/browser-extension/src/entrypoints/popup/context/DbContext.tsx b/apps/browser-extension/src/entrypoints/popup/context/DbContext.tsx index 6c0fd5874..e8703a1a3 100644 --- a/apps/browser-extension/src/entrypoints/popup/context/DbContext.tsx +++ b/apps/browser-extension/src/entrypoints/popup/context/DbContext.tsx @@ -1,8 +1,9 @@ import React, { createContext, useContext, useState, useEffect, useCallback, useMemo } from 'react'; import { sendMessage } from 'webext-bridge/popup'; -import SqliteClient from '@/utils/SqliteClient'; -import { VaultResponse } from '@/utils/types/webapi/VaultResponse'; + import EncryptionUtility from '@/utils/EncryptionUtility'; +import type { VaultResponse } from '@/utils/shared/models'; +import SqliteClient from '@/utils/SqliteClient'; import { VaultResponse as messageVaultResponse } from '@/utils/types/messaging/VaultResponse'; type DbContextType = { diff --git a/apps/browser-extension/src/entrypoints/popup/context/LoadingContext.tsx b/apps/browser-extension/src/entrypoints/popup/context/LoadingContext.tsx index c2463b5ff..4d0e87359 100644 --- a/apps/browser-extension/src/entrypoints/popup/context/LoadingContext.tsx +++ b/apps/browser-extension/src/entrypoints/popup/context/LoadingContext.tsx @@ -1,4 +1,5 @@ import React, { createContext, useContext, useState, useMemo } from 'react'; + import LoadingSpinnerFullScreen from '@/entrypoints/popup/components/LoadingSpinnerFullScreen'; type LoadingContextType = { diff --git a/apps/browser-extension/src/entrypoints/popup/context/ThemeContext.tsx b/apps/browser-extension/src/entrypoints/popup/context/ThemeContext.tsx index 683943368..470465724 100644 --- a/apps/browser-extension/src/entrypoints/popup/context/ThemeContext.tsx +++ b/apps/browser-extension/src/entrypoints/popup/context/ThemeContext.tsx @@ -1,4 +1,5 @@ import React, { createContext, useContext, useState, useMemo, useEffect, useCallback } from 'react'; + import { storage } from '#imports'; /** diff --git a/apps/browser-extension/src/entrypoints/popup/context/WebApiContext.tsx b/apps/browser-extension/src/entrypoints/popup/context/WebApiContext.tsx index 5302701f8..48a3190b1 100644 --- a/apps/browser-extension/src/entrypoints/popup/context/WebApiContext.tsx +++ b/apps/browser-extension/src/entrypoints/popup/context/WebApiContext.tsx @@ -1,7 +1,9 @@ import React, { createContext, useContext, useEffect, useState } from 'react'; -import { WebApiService } from '@/utils/WebApiService'; + import { useAuth } from '@/entrypoints/popup/context/AuthContext'; +import { WebApiService } from '@/utils/WebApiService'; + const WebApiContext = createContext(null); /** diff --git a/apps/browser-extension/src/entrypoints/popup/hooks/useVaultSync.ts b/apps/browser-extension/src/entrypoints/popup/hooks/useVaultSync.ts new file mode 100644 index 000000000..7ca7f860c --- /dev/null +++ b/apps/browser-extension/src/entrypoints/popup/hooks/useVaultSync.ts @@ -0,0 +1,161 @@ +import { useCallback } from 'react'; + +import { useAuth } from '@/entrypoints/popup/context/AuthContext'; +import { useDb } from '@/entrypoints/popup/context/DbContext'; +import { useWebApi } from '@/entrypoints/popup/context/WebApiContext'; + +import { AppInfo } from '@/utils/AppInfo'; +import type { VaultResponse } from '@/utils/shared/models'; + +/** + * Utility function to ensure a minimum time has elapsed for an operation + */ +const withMinimumDelay = async ( + operation: () => Promise, + minDelayMs: number, + initialSync: boolean +): Promise => { + if (!initialSync) { + return operation(); + } + + const startTime = Date.now(); + const result = await operation(); + const elapsedTime = Date.now() - startTime; + + if (elapsedTime < minDelayMs) { + await new Promise(resolve => setTimeout(resolve, minDelayMs - elapsedTime)); + } + + return result; +}; + +type VaultSyncOptions = { + initialSync?: boolean; + onSuccess?: (hasNewVault: boolean) => void; + onError?: (error: string) => void; + onStatus?: (message: string) => void; + onOffline?: () => void; +} + +/** + * Hook to sync the vault with the server. + */ +export const useVaultSync = () : { + syncVault: (options?: VaultSyncOptions) => Promise; +} => { + const authContext = useAuth(); + const dbContext = useDb(); + const webApi = useWebApi(); + + const syncVault = useCallback(async (options: VaultSyncOptions = {}) => { + const { initialSync = false, onSuccess, onError, onStatus, onOffline } = options; + + try { + const { isLoggedIn } = await authContext.initializeAuth(); + + if (!isLoggedIn) { + // Not authenticated, return false immediately + return false; + } + + // Check app status and vault revision + onStatus?.('Checking vault updates'); + const statusResponse = await withMinimumDelay( + () => webApi.getStatus(), + 300, + initialSync + ); + + if (statusResponse.serverVersion === '0.0.0') { + // Server is not available, go into offline mode + onOffline?.(); + return false; + } + + if (!statusResponse.clientVersionSupported) { + const statusError = 'This version of the AliasVault mobile app is not supported by the server anymore. Please update your app to the latest version.'; + onError?.(statusError); + return false; + } + + if (!AppInfo.isServerVersionSupported(statusResponse.serverVersion)) { + const statusError = 'The AliasVault server needs to be updated to a newer version in order to use this mobile app. Please contact support if you need help.'; + onError?.(statusError); + return false; + } + + /* + * If we get here, it means we have a valid connection to the server. + * TODO: browser extension does not support offline mode yet. + * authContext.setOfflineMode(false); + */ + + // Compare vault revisions + const vaultMetadata = await dbContext.getVaultMetadata(); + const vaultRevisionNumber = vaultMetadata?.vaultRevisionNumber ?? 0; + + if (statusResponse.vaultRevision > vaultRevisionNumber) { + onStatus?.('Syncing updated vault'); + const vaultResponseJson = await withMinimumDelay( + () => webApi.get('Vault'), + 1000, + initialSync + ); + + const vaultError = webApi.validateVaultResponse(vaultResponseJson as VaultResponse); + if (vaultError) { + // Only logout if it's an authentication error, not a network error + if (vaultError.includes('authentication') || vaultError.includes('unauthorized')) { + await webApi.logout(vaultError); + onError?.(vaultError); + return false; + } + + /* + * TODO: browser extension does not support offline mode yet. + * For other errors, go into offline mode + * authContext.setOfflineMode(true); + */ + + return false; + } + + try { + await dbContext.initializeDatabase(vaultResponseJson as VaultResponse); + onSuccess?.(true); + return true; + } catch { + // Vault could not be decrypted, throw an error + throw new Error('Vault could not be decrypted, if problem persists please logout and login again.'); + } + } + + await withMinimumDelay( + () => Promise.resolve(onSuccess?.(false)), + 300, + initialSync + ); + return false; + } catch (err) { + const errorMessage = err instanceof Error ? err.message : 'Unknown error during vault sync'; + console.error('Vault sync error:', err); + + /* + * Check if it's a network error + * TODO: browser extension does not support offline mode yet. + */ + /* + * if (errorMessage.includes('network') || errorMessage.includes('timeout')) { + *authContext.setOfflineMode(true); + *return true; + *} + */ + + onError?.(errorMessage); + return false; + } + }, [authContext, dbContext, webApi]); + + return { syncVault }; +}; \ No newline at end of file diff --git a/apps/browser-extension/src/entrypoints/popup/main.tsx b/apps/browser-extension/src/entrypoints/popup/main.tsx index 2c40e7a08..7bf2aae0e 100644 --- a/apps/browser-extension/src/entrypoints/popup/main.tsx +++ b/apps/browser-extension/src/entrypoints/popup/main.tsx @@ -1,10 +1,12 @@ import ReactDOM from 'react-dom/client'; + import App from '@/entrypoints/popup/App'; import { AuthProvider } from '@/entrypoints/popup/context/AuthContext'; -import { WebApiProvider } from '@/entrypoints/popup/context/WebApiContext'; import { DbProvider } from '@/entrypoints/popup/context/DbContext'; import { LoadingProvider } from '@/entrypoints/popup/context/LoadingContext'; import { ThemeProvider } from '@/entrypoints/popup/context/ThemeContext'; +import { WebApiProvider } from '@/entrypoints/popup/context/WebApiContext'; + import { setupExpandedMode } from '@/utils/ExpandedMode'; // Run before React initializes to ensure the popup is always a fixed width except for when explicitly expanded. diff --git a/apps/browser-extension/src/entrypoints/popup/pages/AuthSettings.tsx b/apps/browser-extension/src/entrypoints/popup/pages/AuthSettings.tsx index 68ba29f39..44340f044 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/AuthSettings.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/AuthSettings.tsx @@ -1,9 +1,11 @@ import React, { useState, useEffect } from 'react'; -import { AppInfo } from '@/utils/AppInfo'; -import { storage } from '#imports'; -import { GLOBAL_AUTOFILL_POPUP_ENABLED_KEY, DISABLED_SITES_KEY, VAULT_LOCKED_DISMISS_UNTIL_KEY } from '@/utils/Constants'; import * as Yup from 'yup'; +import { AppInfo } from '@/utils/AppInfo'; +import { GLOBAL_AUTOFILL_POPUP_ENABLED_KEY, DISABLED_SITES_KEY, VAULT_LOCKED_DISMISS_UNTIL_KEY } from '@/utils/Constants'; + +import { storage } from '#imports'; + type ApiOption = { label: string; value: string; diff --git a/apps/browser-extension/src/entrypoints/popup/pages/CredentialDetails.tsx b/apps/browser-extension/src/entrypoints/popup/pages/CredentialDetails.tsx index cc1d7b1e7..0c8d3719d 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/CredentialDetails.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/CredentialDetails.tsx @@ -1,8 +1,6 @@ import React, { useState, useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; -import { useDb } from '@/entrypoints/popup/context/DbContext'; -import { Credential } from '@/utils/types/Credential'; -import { useLoading } from '@/entrypoints/popup/context/LoadingContext'; + import { HeaderBlock, EmailBlock, @@ -11,6 +9,10 @@ import { AliasBlock, NotesBlock } from '@/entrypoints/popup/components/CredentialDetails'; +import { useDb } from '@/entrypoints/popup/context/DbContext'; +import { useLoading } from '@/entrypoints/popup/context/LoadingContext'; + +import { Credential } from '@/utils/types/Credential'; /** * Credential details page. diff --git a/apps/browser-extension/src/entrypoints/popup/pages/CredentialsList.tsx b/apps/browser-extension/src/entrypoints/popup/pages/CredentialsList.tsx index f34dcabc1..5e7552ec8 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/CredentialsList.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/CredentialsList.tsx @@ -1,14 +1,17 @@ import React, { useState, useEffect, useCallback } from 'react'; import { sendMessage } from 'webext-bridge/popup'; + +import CredentialCard from '@/entrypoints/popup/components/CredentialCard'; +import LoadingSpinner from '@/entrypoints/popup/components/LoadingSpinner'; +import ReloadButton from '@/entrypoints/popup/components/ReloadButton'; import { useDb } from '@/entrypoints/popup/context/DbContext'; -import { Credential } from '@/utils/types/Credential'; import { useLoading } from '@/entrypoints/popup/context/LoadingContext'; import { useWebApi } from '@/entrypoints/popup/context/WebApiContext'; -import { VaultResponse } from '@/utils/types/webapi/VaultResponse'; -import ReloadButton from '@/entrypoints/popup/components/ReloadButton'; -import LoadingSpinner from '@/entrypoints/popup/components/LoadingSpinner'; + +import type { VaultResponse } from '@/utils/shared/models'; +import { Credential } from '@/utils/types/Credential'; + import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; -import CredentialCard from '@/entrypoints/popup/components/CredentialCard'; /** * Credentials list page. diff --git a/apps/browser-extension/src/entrypoints/popup/pages/EmailDetails.tsx b/apps/browser-extension/src/entrypoints/popup/pages/EmailDetails.tsx index f8a7e7a47..e3771634c 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/EmailDetails.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/EmailDetails.tsx @@ -1,15 +1,17 @@ import React, { useEffect, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; -import { Email } from '@/utils/types/webapi/Email'; -import { useDb } from '@/entrypoints/popup/context/DbContext'; -import { useWebApi } from '@/entrypoints/popup/context/WebApiContext'; + import LoadingSpinner from '@/entrypoints/popup/components/LoadingSpinner'; -import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; -import EncryptionUtility from '@/utils/EncryptionUtility'; -import { Attachment } from '@/utils/types/webapi/Attachment'; +import { useDb } from '@/entrypoints/popup/context/DbContext'; import { useLoading } from '@/entrypoints/popup/context/LoadingContext'; +import { useWebApi } from '@/entrypoints/popup/context/WebApiContext'; import ConversionUtility from '@/entrypoints/popup/utils/ConversionUtility'; +import EncryptionUtility from '@/utils/EncryptionUtility'; +import type { Attachment, Email } from '@/utils/shared/models'; + +import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; + /** * Email details page. */ diff --git a/apps/browser-extension/src/entrypoints/popup/pages/EmailsList.tsx b/apps/browser-extension/src/entrypoints/popup/pages/EmailsList.tsx index a38349e03..73acc99c2 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/EmailsList.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/EmailsList.tsx @@ -1,13 +1,15 @@ import React, { useEffect, useState, useCallback } from 'react'; import { Link } from 'react-router-dom'; -import { MailboxBulkRequest, MailboxBulkResponse } from '@/utils/types/webapi/MailboxBulk'; -import { MailboxEmail } from '@/utils/types/webapi/MailboxEmail'; + +import LoadingSpinner from '@/entrypoints/popup/components/LoadingSpinner'; +import ReloadButton from '@/entrypoints/popup/components/ReloadButton'; import { useDb } from '@/entrypoints/popup/context/DbContext'; import { useWebApi } from '@/entrypoints/popup/context/WebApiContext'; -import LoadingSpinner from '@/entrypoints/popup/components/LoadingSpinner'; -import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; + import EncryptionUtility from '@/utils/EncryptionUtility'; -import ReloadButton from '@/entrypoints/popup/components/ReloadButton'; +import type { MailboxBulkRequest, MailboxBulkResponse, MailboxEmail } from '@/utils/shared/models'; + +import { useMinDurationLoading } from '@/hooks/useMinDurationLoading'; /** * Emails list page. diff --git a/apps/browser-extension/src/entrypoints/popup/pages/Home.tsx b/apps/browser-extension/src/entrypoints/popup/pages/Home.tsx index 88e289853..823b4d9ea 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/Home.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/Home.tsx @@ -1,11 +1,12 @@ import React, { useState, useEffect } from 'react'; -import { useAuth } from '@/entrypoints/popup/context/AuthContext'; -import Unlock from '@/entrypoints/popup/pages/Unlock'; -import Login from '@/entrypoints/popup/pages/Login'; -import UnlockSuccess from '@/entrypoints/popup/pages/UnlockSuccess'; import { useNavigate } from 'react-router-dom'; + +import { useAuth } from '@/entrypoints/popup/context/AuthContext'; import { useDb } from '@/entrypoints/popup/context/DbContext'; import { useLoading } from '@/entrypoints/popup/context/LoadingContext'; +import Login from '@/entrypoints/popup/pages/Login'; +import Unlock from '@/entrypoints/popup/pages/Unlock'; +import UnlockSuccess from '@/entrypoints/popup/pages/UnlockSuccess'; /** * Home page that shows the correct page based on the user's authentication state. diff --git a/apps/browser-extension/src/entrypoints/popup/pages/Login.tsx b/apps/browser-extension/src/entrypoints/popup/pages/Login.tsx index 1a48921fb..46149eaad 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/Login.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/Login.tsx @@ -1,20 +1,24 @@ -import React, { useEffect, useState } from 'react'; import { Buffer } from 'buffer'; -import { storage } from '#imports'; + +import React, { useEffect, useState } from 'react'; + +import Button from '@/entrypoints/popup/components/Button'; +import LoginServerInfo from '@/entrypoints/popup/components/LoginServerInfo'; import { useAuth } from '@/entrypoints/popup/context/AuthContext'; import { useDb } from '@/entrypoints/popup/context/DbContext'; -import { useWebApi } from '@/entrypoints/popup/context/WebApiContext'; -import { AppInfo } from '@/utils/AppInfo'; -import Button from '@/entrypoints/popup/components/Button'; -import EncryptionUtility from '@/utils/EncryptionUtility'; -import SrpUtility from '@/entrypoints/popup/utils/SrpUtility'; import { useLoading } from '@/entrypoints/popup/context/LoadingContext'; -import { VaultResponse } from '@/utils/types/webapi/VaultResponse'; -import { LoginResponse } from '@/utils/types/webapi/Login'; -import LoginServerInfo from '@/entrypoints/popup/components/LoginServerInfo'; +import { useWebApi } from '@/entrypoints/popup/context/WebApiContext'; +import SrpUtility from '@/entrypoints/popup/utils/SrpUtility'; + +import { AppInfo } from '@/utils/AppInfo'; +import EncryptionUtility from '@/utils/EncryptionUtility'; +import type { VaultResponse, LoginResponse } from '@/utils/shared/models'; import { ApiAuthError } from '@/utils/types/errors/ApiAuthError'; + import ConversionUtility from '../utils/ConversionUtility'; +import { storage } from '#imports'; + /** * Login page */ diff --git a/apps/browser-extension/src/entrypoints/popup/pages/Logout.tsx b/apps/browser-extension/src/entrypoints/popup/pages/Logout.tsx index 4054115dc..9149f0326 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/Logout.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/Logout.tsx @@ -1,5 +1,6 @@ import React, { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; + import { useAuth } from '@/entrypoints/popup/context/AuthContext'; import { useWebApi } from '@/entrypoints/popup/context/WebApiContext'; diff --git a/apps/browser-extension/src/entrypoints/popup/pages/Settings.tsx b/apps/browser-extension/src/entrypoints/popup/pages/Settings.tsx index fe5e877c1..3f93770b2 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/Settings.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/Settings.tsx @@ -1,9 +1,12 @@ import React, { useEffect, useState, useCallback } from 'react'; -import { storage } from "#imports"; import { sendMessage } from 'webext-bridge/popup'; -import { DISABLED_SITES_KEY, GLOBAL_AUTOFILL_POPUP_ENABLED_KEY, GLOBAL_CONTEXT_MENU_ENABLED_KEY, TEMPORARY_DISABLED_SITES_KEY } from '@/utils/Constants'; -import { AppInfo } from '@/utils/AppInfo'; + import { useTheme } from '@/entrypoints/popup/context/ThemeContext'; + +import { AppInfo } from '@/utils/AppInfo'; +import { DISABLED_SITES_KEY, GLOBAL_AUTOFILL_POPUP_ENABLED_KEY, GLOBAL_CONTEXT_MENU_ENABLED_KEY, TEMPORARY_DISABLED_SITES_KEY } from '@/utils/Constants'; + +import { storage } from "#imports"; import { browser } from "#imports"; /** diff --git a/apps/browser-extension/src/entrypoints/popup/pages/Unlock.tsx b/apps/browser-extension/src/entrypoints/popup/pages/Unlock.tsx index a0605e60e..644b8f69f 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/Unlock.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/Unlock.tsx @@ -1,16 +1,20 @@ +import { Buffer } from 'buffer'; + import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Buffer } from 'buffer'; -import { storage } from '#imports'; -import { useDb } from '@/entrypoints/popup/context/DbContext'; -import { useAuth } from '@/entrypoints/popup/context/AuthContext'; -import { useWebApi } from '@/entrypoints/popup/context/WebApiContext'; + import Button from '@/entrypoints/popup/components/Button'; -import EncryptionUtility from '@/utils/EncryptionUtility'; -import SrpUtility from '@/entrypoints/popup/utils/SrpUtility'; -import { VaultResponse } from '@/utils/types/webapi/VaultResponse'; +import { useAuth } from '@/entrypoints/popup/context/AuthContext'; +import { useDb } from '@/entrypoints/popup/context/DbContext'; import { useLoading } from '@/entrypoints/popup/context/LoadingContext'; +import { useWebApi } from '@/entrypoints/popup/context/WebApiContext'; +import SrpUtility from '@/entrypoints/popup/utils/SrpUtility'; + import { VAULT_LOCKED_DISMISS_UNTIL_KEY } from '@/utils/Constants'; +import EncryptionUtility from '@/utils/EncryptionUtility'; +import type { VaultResponse } from '@/utils/shared/models'; + +import { storage } from '#imports'; /** * Unlock page diff --git a/apps/browser-extension/src/entrypoints/popup/utils/SrpUtility.ts b/apps/browser-extension/src/entrypoints/popup/utils/SrpUtility.ts index 611c6615c..7d169ad30 100644 --- a/apps/browser-extension/src/entrypoints/popup/utils/SrpUtility.ts +++ b/apps/browser-extension/src/entrypoints/popup/utils/SrpUtility.ts @@ -1,9 +1,8 @@ import srp from 'secure-remote-password/client' -import { WebApiService } from '@/utils/WebApiService'; -import { LoginRequest, LoginResponse } from '@/utils/types/webapi/Login'; -import { ValidateLoginRequest, ValidateLoginRequest2Fa, ValidateLoginResponse } from '@/utils/types/webapi/ValidateLogin'; -import BadRequestResponse from '@/utils/types/webapi/BadRequestResponse'; + +import type { LoginRequest, LoginResponse, ValidateLoginRequest, ValidateLoginRequest2Fa, ValidateLoginResponse, BadRequestResponse } from '@/utils/shared/models'; import { ApiAuthError } from '@/utils/types/errors/ApiAuthError'; +import { WebApiService } from '@/utils/WebApiService'; /** * Utility class for SRP authentication operations. diff --git a/apps/browser-extension/src/utils/EncryptionUtility.ts b/apps/browser-extension/src/utils/EncryptionUtility.ts index 3a041446d..8c976aed8 100644 --- a/apps/browser-extension/src/utils/EncryptionUtility.ts +++ b/apps/browser-extension/src/utils/EncryptionUtility.ts @@ -1,9 +1,11 @@ -import argon2 from 'argon2-browser/dist/argon2-bundled.min.js'; -import { Email } from './types/webapi/Email'; -import { EncryptionKey } from './types/EncryptionKey'; -import { MailboxEmail } from './types/webapi/MailboxEmail'; import { Buffer } from 'buffer'; +import argon2 from 'argon2-browser/dist/argon2-bundled.min.js'; + +import type { Email, MailboxEmail } from '@/utils/shared/models'; + +import { EncryptionKey } from './types/EncryptionKey'; + /** * Utility class for encryption operations including: * - Argon2Id key derivation diff --git a/apps/browser-extension/src/utils/SqliteClient.ts b/apps/browser-extension/src/utils/SqliteClient.ts index c9c6c7beb..7b0020988 100644 --- a/apps/browser-extension/src/utils/SqliteClient.ts +++ b/apps/browser-extension/src/utils/SqliteClient.ts @@ -1,8 +1,9 @@ import initSqlJs, { Database } from 'sql.js'; + import { Credential } from './types/Credential'; import { EncryptionKey } from './types/EncryptionKey'; -import { TotpCode } from './types/TotpCode'; import { PasswordSettings } from './types/PasswordSettings'; +import { TotpCode } from './types/TotpCode'; /** * Placeholder base64 image for credentials without a logo. diff --git a/apps/browser-extension/src/utils/WebApiService.ts b/apps/browser-extension/src/utils/WebApiService.ts index 8437e7f96..038c6fe2c 100644 --- a/apps/browser-extension/src/utils/WebApiService.ts +++ b/apps/browser-extension/src/utils/WebApiService.ts @@ -1,6 +1,7 @@ +import type { StatusResponse, VaultResponse } from '@/utils/shared/models'; + import { AppInfo } from "./AppInfo"; -import { StatusResponse } from "./types/webapi/StatusResponse"; -import { VaultResponse } from "./types/webapi/VaultResponse"; + import { storage } from '#imports'; type RequestInit = globalThis.RequestInit; diff --git a/apps/browser-extension/src/utils/__tests__/AppInfo.test.ts b/apps/browser-extension/src/utils/__tests__/AppInfo.test.ts index 6b72cceae..f088ebfab 100644 --- a/apps/browser-extension/src/utils/__tests__/AppInfo.test.ts +++ b/apps/browser-extension/src/utils/__tests__/AppInfo.test.ts @@ -1,6 +1,7 @@ -import { AppInfo } from '../AppInfo'; import { describe, it, expect } from 'vitest'; +import { AppInfo } from '../AppInfo'; + describe('AppInfo', () => { describe('isVersionSupported', () => { it('should support exact version match', () => { diff --git a/apps/browser-extension/src/utils/formDetector/FormDetector.ts b/apps/browser-extension/src/utils/formDetector/FormDetector.ts index 9f4513dd3..d0930a22e 100644 --- a/apps/browser-extension/src/utils/formDetector/FormDetector.ts +++ b/apps/browser-extension/src/utils/formDetector/FormDetector.ts @@ -1,5 +1,5 @@ -import { FormFields } from "./types/FormFields"; import { CombinedFieldPatterns, CombinedGenderOptionPatterns, CombinedStopWords } from "./FieldPatterns"; +import { FormFields } from "./types/FormFields"; /** * Form detector. diff --git a/apps/browser-extension/src/utils/formDetector/FormFiller.ts b/apps/browser-extension/src/utils/formDetector/FormFiller.ts index 2314ade45..68f316513 100644 --- a/apps/browser-extension/src/utils/formDetector/FormFiller.ts +++ b/apps/browser-extension/src/utils/formDetector/FormFiller.ts @@ -1,7 +1,7 @@ -import { Credential } from "@/utils/types/Credential"; -import { FormFields } from "@/utils/formDetector/types/FormFields"; import { CombinedDateOptionPatterns, CombinedGenderOptionPatterns } from "@/utils/formDetector/FieldPatterns"; +import { FormFields } from "@/utils/formDetector/types/FormFields"; import { Gender, IdentityHelperUtils } from "@/utils/shared/identity-generator"; +import { Credential } from "@/utils/types/Credential"; /** * Class to fill the fields of a form with the given credential. */ diff --git a/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.en.test.ts b/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.en.test.ts index f24f90d40..3121ccc76 100644 --- a/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.en.test.ts +++ b/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.en.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; + import { FormField, testField } from './TestUtils'; describe('FormDetector English tests', () => { diff --git a/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.generic.test.ts b/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.generic.test.ts index c38b4496e..a31ae7bca 100644 --- a/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.generic.test.ts +++ b/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.generic.test.ts @@ -1,7 +1,9 @@ import { describe, it, expect } from 'vitest'; -import { createTestDom } from './TestUtils'; + import { FormDetector } from '../FormDetector'; +import { createTestDom } from './TestUtils'; + describe('FormDetector generic tests', () => { describe('Invalid form not detected as login form 1', () => { const htmlFile = 'invalid-form1.html'; diff --git a/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.nl.test.ts b/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.nl.test.ts index 10a99167e..c3ec679a7 100644 --- a/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.nl.test.ts +++ b/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.nl.test.ts @@ -1,4 +1,5 @@ import { describe, it, expect } from 'vitest'; + import { FormField, testField, testBirthdateFormat } from './TestUtils'; describe('FormDetector Dutch tests', () => { diff --git a/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.serviceName.en.test.ts b/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.serviceName.en.test.ts index d0af9fd0c..809268a5a 100644 --- a/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.serviceName.en.test.ts +++ b/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.serviceName.en.test.ts @@ -1,7 +1,9 @@ import { describe, it, expect } from 'vitest'; -import { createTestDocument } from './TestUtils'; + import { FormDetector } from '../FormDetector'; +import { createTestDocument } from './TestUtils'; + describe('FormDetector.getSuggestedServiceName (English)', () => { it('should extract service name from title with divider and include domain', () => { const { document, location } = createTestDocument( diff --git a/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.serviceName.nl.test.ts b/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.serviceName.nl.test.ts index 005d8029d..d32dc7fb7 100644 --- a/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.serviceName.nl.test.ts +++ b/apps/browser-extension/src/utils/formDetector/__tests__/FormDetector.serviceName.nl.test.ts @@ -1,7 +1,9 @@ import { describe, it, expect } from 'vitest'; -import { createTestDocument } from './TestUtils'; + import { FormDetector } from '../FormDetector'; +import { createTestDocument } from './TestUtils'; + describe('FormDetector.getSuggestedServiceName (Dutch)', () => { it('should extract service name from title with divider and include domain', () => { const { document, location } = createTestDocument( diff --git a/apps/browser-extension/src/utils/formDetector/__tests__/FormFiller.en.test.ts b/apps/browser-extension/src/utils/formDetector/__tests__/FormFiller.en.test.ts index e414d1665..87d322bb6 100644 --- a/apps/browser-extension/src/utils/formDetector/__tests__/FormFiller.en.test.ts +++ b/apps/browser-extension/src/utils/formDetector/__tests__/FormFiller.en.test.ts @@ -1,9 +1,11 @@ -import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { FormFiller } from '../FormFiller'; import { JSDOM } from 'jsdom'; -import { setupTestDOM, createMockFormFields, createMockCredential, wasTriggerCalledFor, createDateSelects } from './TestUtils'; -import { FormFields } from '../types/FormFields'; +import { describe, it, expect, beforeEach, vi } from 'vitest'; + import { Credential } from '../../types/Credential'; +import { FormFiller } from '../FormFiller'; +import { FormFields } from '../types/FormFields'; + +import { setupTestDOM, createMockFormFields, createMockCredential, wasTriggerCalledFor, createDateSelects } from './TestUtils'; const { window } = new JSDOM(''); global.HTMLSelectElement = window.HTMLSelectElement; diff --git a/apps/browser-extension/src/utils/formDetector/__tests__/FormFiller.generic.test.ts b/apps/browser-extension/src/utils/formDetector/__tests__/FormFiller.generic.test.ts index 8a603c3a4..c24964e27 100644 --- a/apps/browser-extension/src/utils/formDetector/__tests__/FormFiller.generic.test.ts +++ b/apps/browser-extension/src/utils/formDetector/__tests__/FormFiller.generic.test.ts @@ -1,9 +1,11 @@ -import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { FormFiller } from '../FormFiller'; import { JSDOM } from 'jsdom'; -import { setupTestDOM, createMockFormFields, createMockCredential, wasTriggerCalledFor, createDateSelects } from './TestUtils'; -import { FormFields } from '../types/FormFields'; +import { describe, it, expect, beforeEach, vi } from 'vitest'; + import { Credential } from '../../types/Credential'; +import { FormFiller } from '../FormFiller'; +import { FormFields } from '../types/FormFields'; + +import { setupTestDOM, createMockFormFields, createMockCredential, wasTriggerCalledFor, createDateSelects } from './TestUtils'; const { window } = new JSDOM(''); global.HTMLSelectElement = window.HTMLSelectElement; diff --git a/apps/browser-extension/src/utils/formDetector/__tests__/FormFiller.nl.test.ts b/apps/browser-extension/src/utils/formDetector/__tests__/FormFiller.nl.test.ts index beea1d909..a1e9fea84 100644 --- a/apps/browser-extension/src/utils/formDetector/__tests__/FormFiller.nl.test.ts +++ b/apps/browser-extension/src/utils/formDetector/__tests__/FormFiller.nl.test.ts @@ -1,9 +1,11 @@ -import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { FormFiller } from '../FormFiller'; import { JSDOM } from 'jsdom'; -import { setupTestDOM, createMockFormFields, createMockCredential, wasTriggerCalledFor, createDateSelects } from './TestUtils'; -import { FormFields } from '../types/FormFields'; +import { describe, it, expect, beforeEach, vi } from 'vitest'; + import { Credential } from '../../types/Credential'; +import { FormFiller } from '../FormFiller'; +import { FormFields } from '../types/FormFields'; + +import { setupTestDOM, createMockFormFields, createMockCredential, wasTriggerCalledFor, createDateSelects } from './TestUtils'; const { window } = new JSDOM(''); global.HTMLSelectElement = window.HTMLSelectElement; diff --git a/apps/browser-extension/src/utils/formDetector/__tests__/TestUtils.ts b/apps/browser-extension/src/utils/formDetector/__tests__/TestUtils.ts index 16397b4a3..83d785a71 100644 --- a/apps/browser-extension/src/utils/formDetector/__tests__/TestUtils.ts +++ b/apps/browser-extension/src/utils/formDetector/__tests__/TestUtils.ts @@ -1,11 +1,13 @@ -import { FormDetector } from '@/utils/formDetector/FormDetector'; import { readFileSync } from 'fs'; import { join } from 'path'; -import { it, expect, vi } from 'vitest'; + import { JSDOM, DOMWindow } from 'jsdom'; +import { it, expect, vi } from 'vitest'; + +import { FormDetector } from '@/utils/formDetector/FormDetector'; import { FormFields } from '@/utils/formDetector/types/FormFields'; -import { Credential } from '@/utils/types/Credential'; import { Gender } from '@/utils/shared/identity-generator'; +import { Credential } from '@/utils/types/Credential'; export enum FormField { Username = 'username', diff --git a/apps/browser-extension/src/utils/shared/models/index.d.ts b/apps/browser-extension/src/utils/shared/models/index.d.ts index 92234656a..0065eeafb 100644 --- a/apps/browser-extension/src/utils/shared/models/index.d.ts +++ b/apps/browser-extension/src/utils/shared/models/index.d.ts @@ -299,4 +299,12 @@ type VaultPasswordChangeRequest = Vault & { newPasswordVerifier: string; }; -export type { Attachment, AuthLogModel, DeleteAccountInitiateRequest, DeleteAccountInitiateResponse, DeleteAccountRequest, Email, FaviconExtractModel, LoginRequest, LoginResponse, MailboxBulkRequest, MailboxBulkResponse, MailboxEmail, PasswordChangeInitiateResponse, RefreshToken, StatusResponse, ValidateLoginRequest, ValidateLoginRequest2Fa, ValidateLoginResponse, Vault, VaultPasswordChangeRequest, VaultPostResponse, VaultResponse }; +type BadRequestResponse = { + type: string; + title: string; + status: number; + errors: Record; + traceId: string; +}; + +export type { Attachment, AuthLogModel, BadRequestResponse, DeleteAccountInitiateRequest, DeleteAccountInitiateResponse, DeleteAccountRequest, Email, FaviconExtractModel, LoginRequest, LoginResponse, MailboxBulkRequest, MailboxBulkResponse, MailboxEmail, PasswordChangeInitiateResponse, RefreshToken, StatusResponse, ValidateLoginRequest, ValidateLoginRequest2Fa, ValidateLoginResponse, Vault, VaultPasswordChangeRequest, VaultPostResponse, VaultResponse }; diff --git a/apps/browser-extension/src/utils/types/webapi/Attachment.ts b/apps/browser-extension/src/utils/types/webapi/Attachment.ts deleted file mode 100644 index cd74065fe..000000000 --- a/apps/browser-extension/src/utils/types/webapi/Attachment.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Email attachment type. - */ -export type Attachment = { - /** The ID of the attachment */ - id: number; - - /** The ID of the email the attachment belongs to */ - emailId: number; - - /** The filename of the attachment */ - filename: string; - - /** The MIME type of the attachment */ - mimeType: string; - - /** The size of the attachment in bytes */ - filesize: number; -} \ No newline at end of file diff --git a/apps/browser-extension/src/utils/types/webapi/BadRequestResponse.ts b/apps/browser-extension/src/utils/types/webapi/BadRequestResponse.ts deleted file mode 100644 index cc4598f7d..000000000 --- a/apps/browser-extension/src/utils/types/webapi/BadRequestResponse.ts +++ /dev/null @@ -1,9 +0,0 @@ -type BadRequestResponse = { - type: string; - title: string; - status: number; - errors: Record; - traceId: string; -}; - -export default BadRequestResponse; diff --git a/apps/browser-extension/src/utils/types/webapi/Email.ts b/apps/browser-extension/src/utils/types/webapi/Email.ts deleted file mode 100644 index f88518999..000000000 --- a/apps/browser-extension/src/utils/types/webapi/Email.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Attachment } from "./Attachment"; - -export type Email = { - /** The body of the email message */ - messageHtml: string; - - /** The plain text body of the email message */ - messagePlain: string; - - /** The ID of the email */ - id: number; - - /** The subject of the email */ - subject: string; - - /** The display name of the sender */ - fromDisplay: string; - - /** The domain of the sender's email address */ - fromDomain: string; - - /** The local part of the sender's email address */ - fromLocal: string; - - /** The domain of the recipient's email address */ - toDomain: string; - - /** The local part of the recipient's email address */ - toLocal: string; - - /** The date of the email */ - date: string; - - /** The system date of the email */ - dateSystem: string; - - /** The number of seconds ago the email was received */ - secondsAgo: number; - - /** - * The encrypted symmetric key which was used to encrypt the email message. - * This key is encrypted with the public key of the user. - */ - encryptedSymmetricKey: string; - - /** The public key of the user used to encrypt the symmetric key */ - encryptionKey: string; - - /** The attachments of the email */ - attachments: Attachment[]; -} diff --git a/apps/browser-extension/src/utils/types/webapi/Login.ts b/apps/browser-extension/src/utils/types/webapi/Login.ts deleted file mode 100644 index 40a0822d8..000000000 --- a/apps/browser-extension/src/utils/types/webapi/Login.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Login request type. - */ -export type LoginRequest = { - username: string; -} - -/** - * Login response type. - */ -export type LoginResponse = { - salt: string; - serverEphemeral: string; - encryptionType: string; - encryptionSettings: string; -} diff --git a/apps/browser-extension/src/utils/types/webapi/MailboxBulk.ts b/apps/browser-extension/src/utils/types/webapi/MailboxBulk.ts deleted file mode 100644 index 775b8c56d..000000000 --- a/apps/browser-extension/src/utils/types/webapi/MailboxBulk.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MailboxEmail } from "./MailboxEmail"; - -/** - * Mailbox bulk request type. - */ -export type MailboxBulkRequest = { - addresses: string[]; - page: number; - pageSize: number; -} - -/** - * Mailbox bulk response type. - */ -export type MailboxBulkResponse = { - addresses: string[]; - currentPage: number; - pageSize: number; - totalRecords: number; - mails: MailboxEmail[]; -} \ No newline at end of file diff --git a/apps/browser-extension/src/utils/types/webapi/MailboxEmail.ts b/apps/browser-extension/src/utils/types/webapi/MailboxEmail.ts deleted file mode 100644 index dd15b9ae4..000000000 --- a/apps/browser-extension/src/utils/types/webapi/MailboxEmail.ts +++ /dev/null @@ -1,46 +0,0 @@ -export type MailboxEmail = { - /** The preview of the email message */ - messagePreview: string; - - /** Indicates whether the email has attachments */ - hasAttachments: boolean; - - /** The ID of the email */ - id: number; - - /** The subject of the email */ - subject: string; - - /** The display name of the sender */ - fromDisplay: string; - - /** The domain of the sender's email address */ - fromDomain: string; - - /** The local part of the sender's email address */ - fromLocal: string; - - /** The domain of the recipient's email address */ - toDomain: string; - - /** The local part of the recipient's email address */ - toLocal: string; - - /** The date of the email */ - date: string; - - /** The system date of the email */ - dateSystem: string; - - /** The number of seconds ago the email was received */ - secondsAgo: number; - - /** - * The encrypted symmetric key which was used to encrypt the email message. - * This key is encrypted with the public key of the user. - */ - encryptedSymmetricKey: string; - - /** The public key of the user used to encrypt the symmetric key */ - encryptionKey: string; -} \ No newline at end of file diff --git a/apps/browser-extension/src/utils/types/webapi/StatusResponse.ts b/apps/browser-extension/src/utils/types/webapi/StatusResponse.ts deleted file mode 100644 index 53d4181f0..000000000 --- a/apps/browser-extension/src/utils/types/webapi/StatusResponse.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Status response type. - */ -export type StatusResponse = { - clientVersionSupported: boolean; - serverVersion: string; - vaultRevision: number; -} diff --git a/apps/browser-extension/src/utils/types/webapi/ValidateLogin.ts b/apps/browser-extension/src/utils/types/webapi/ValidateLogin.ts deleted file mode 100644 index e64eaf216..000000000 --- a/apps/browser-extension/src/utils/types/webapi/ValidateLogin.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Validate login request type. - */ -export type ValidateLoginRequest = { - username: string; - rememberMe: boolean; - clientPublicEphemeral: string; - clientSessionProof: string; -} - -/** - * Validate login request type for 2FA. - */ -export type ValidateLoginRequest2Fa = { - username: string; - code2Fa: number; - rememberMe: boolean; - clientPublicEphemeral: string; - clientSessionProof: string; -} - -/** - * Validate login response type. - */ -export type ValidateLoginResponse = { - requiresTwoFactor: boolean; - token?: { - token: string; - refreshToken: string; - }; - serverSessionProof: string; - } \ No newline at end of file diff --git a/apps/browser-extension/src/utils/types/webapi/Vault.ts b/apps/browser-extension/src/utils/types/webapi/Vault.ts deleted file mode 100644 index e318aa196..000000000 --- a/apps/browser-extension/src/utils/types/webapi/Vault.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Vault type. - */ -export type Vault = { - blob: string; - createdAt: string; - credentialsCount: number; - currentRevisionNumber: number; - emailAddressList: string[]; - privateEmailDomainList: string[]; - publicEmailDomainList: string[]; - encryptionPublicKey: string; - updatedAt: string; - username: string; - version: string; - client: string; -} \ No newline at end of file diff --git a/apps/browser-extension/src/utils/types/webapi/VaultPostResponse.ts b/apps/browser-extension/src/utils/types/webapi/VaultPostResponse.ts deleted file mode 100644 index 33de71718..000000000 --- a/apps/browser-extension/src/utils/types/webapi/VaultPostResponse.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Vault post response type returned after uploading a new vault to the server. - */ -export type VaultPostResponse = { - status: number; - newRevisionNumber: number; -} \ No newline at end of file diff --git a/apps/browser-extension/src/utils/types/webapi/VaultResponse.ts b/apps/browser-extension/src/utils/types/webapi/VaultResponse.ts deleted file mode 100644 index 06207affb..000000000 --- a/apps/browser-extension/src/utils/types/webapi/VaultResponse.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Vault } from "./Vault"; - -/** - * Vault response type. - */ -export type VaultResponse = { - status: number; - vault: Vault; -} \ No newline at end of file diff --git a/apps/mobile-app/.eslintrc.js b/apps/mobile-app/.eslintrc.js index 1c2e5269e..875c6f080 100644 --- a/apps/mobile-app/.eslintrc.js +++ b/apps/mobile-app/.eslintrc.js @@ -94,7 +94,44 @@ module.exports = { // Import "import/no-unresolved": "error", - "import/order": ["error", { "newlines-between": "always" }], + "import/order": [ + "error", + { + "groups": [ + "builtin", // Node "fs", "path", etc. + "external", // "react", "lodash", etc. + "internal", // Aliased paths like "@/utils" + "parent", // "../" + "sibling", // "./" + "index", // "./index" + "object", // import 'foo' + "type" // import type ... + ], + "pathGroups": [ + { + pattern: "@/entrypoints/**", + group: "internal", + position: "before" + }, + { + pattern: "@/utils/**", + group: "internal", + position: "before" + }, + { + pattern: "@/hooks/**", + group: "internal", + position: "before" + } + ], + "pathGroupsExcludedImportTypes": ["builtin"], + "newlines-between": "always", + "alphabetize": { + order: "asc", + caseInsensitive: true + } + } + ], // JSDoc "jsdoc/require-jsdoc": [ diff --git a/shared/models/src/index.ts b/shared/models/src/index.ts index 3fcb2b95f..89df3f825 100644 --- a/shared/models/src/index.ts +++ b/shared/models/src/index.ts @@ -16,3 +16,4 @@ export * from './webapi/DeleteAccountInitiate'; export * from './webapi/DeleteAccountRequest'; export * from './webapi/PasswordChangeInitiateResponse'; export * from './webapi/VaultPasswordChangeRequest'; +export * from './webapi/BadRequestResponse'; diff --git a/shared/models/src/webapi/BadRequestResponse.ts b/shared/models/src/webapi/BadRequestResponse.ts index cc4598f7d..f65229a80 100644 --- a/shared/models/src/webapi/BadRequestResponse.ts +++ b/shared/models/src/webapi/BadRequestResponse.ts @@ -1,9 +1,7 @@ -type BadRequestResponse = { +export type BadRequestResponse = { type: string; title: string; status: number; errors: Record; traceId: string; }; - -export default BadRequestResponse;