diff --git a/web/.prettierignore b/web/.prettierignore new file mode 100644 index 0000000..e5ec622 --- /dev/null +++ b/web/.prettierignore @@ -0,0 +1,5 @@ +# Ignore artifacts: +build +coverage +node_modules +public diff --git a/web/.prettierrc b/web/.prettierrc new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/web/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/web/README.md b/web/README.md index da98444..a2980c2 100644 --- a/web/README.md +++ b/web/README.md @@ -24,31 +24,31 @@ export default tseslint.config({ languageOptions: { // other options... parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], + project: ["./tsconfig.node.json", "./tsconfig.app.json"], tsconfigRootDir: import.meta.dirname, }, }, -}) +}); ``` You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: ```js // eslint.config.js -import reactX from 'eslint-plugin-react-x' -import reactDom from 'eslint-plugin-react-dom' +import reactX from "eslint-plugin-react-x"; +import reactDom from "eslint-plugin-react-dom"; export default tseslint.config({ plugins: { // Add the react-x and react-dom plugins - 'react-x': reactX, - 'react-dom': reactDom, + "react-x": reactX, + "react-dom": reactDom, }, rules: { // other rules... // Enable its recommended typescript rules - ...reactX.configs['recommended-typescript'].rules, + ...reactX.configs["recommended-typescript"].rules, ...reactDom.configs.recommended.rules, }, -}) +}); ``` diff --git a/web/eslint.config.js b/web/eslint.config.js index 092408a..79a552e 100644 --- a/web/eslint.config.js +++ b/web/eslint.config.js @@ -1,28 +1,28 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; export default tseslint.config( - { ignores: ['dist'] }, + { ignores: ["dist"] }, { extends: [js.configs.recommended, ...tseslint.configs.recommended], - files: ['**/*.{ts,tsx}'], + files: ["**/*.{ts,tsx}"], languageOptions: { ecmaVersion: 2020, globals: globals.browser, }, plugins: { - 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, + "react-hooks": reactHooks, + "react-refresh": reactRefresh, }, rules: { ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], }, }, -) +); diff --git a/web/index.html b/web/index.html index 7a2f7a0..7ee73e1 100644 --- a/web/index.html +++ b/web/index.html @@ -1,4 +1,4 @@ - + diff --git a/web/package-lock.json b/web/package-lock.json index 2ba5423..9c63c9f 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -34,6 +34,7 @@ "eslint-plugin-react-refresh": "^0.4.19", "globals": "^16.0.0", "path": "^0.12.7", + "prettier": "3.5.3", "sass": "^1.89.0", "typescript": "~5.8.3", "typescript-eslint": "^8.30.1", @@ -4262,6 +4263,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", diff --git a/web/package.json b/web/package.json index b8a41cd..7be89b7 100644 --- a/web/package.json +++ b/web/package.json @@ -36,6 +36,7 @@ "eslint-plugin-react-refresh": "^0.4.19", "globals": "^16.0.0", "path": "^0.12.7", + "prettier": "3.5.3", "sass": "^1.89.0", "typescript": "~5.8.3", "typescript-eslint": "^8.30.1", diff --git a/web/src/api/code.ts b/web/src/api/code.ts index 9251425..19eb222 100644 --- a/web/src/api/code.ts +++ b/web/src/api/code.ts @@ -13,7 +13,7 @@ export const codeApi = async (accessToken: string, nonce: string) => { "Content-Type": "application/json", Authorization: `Bearer ${accessToken}`, }, - } + }, ); if (response.status !== 200 && response.status !== 201) diff --git a/web/src/api/index.ts b/web/src/api/index.ts index 2848e04..28834b8 100644 --- a/web/src/api/index.ts +++ b/web/src/api/index.ts @@ -28,7 +28,7 @@ const processRefreshQueue = async (token: string | null) => { const refreshToken = async ( accountId: string, - refreshToken: string + refreshToken: string, ): Promise<{ access: string; refresh: string }> => { const db = useDbStore.getState().db; const loadAccounts = useAuth.getState().loadAccounts; @@ -79,7 +79,7 @@ axios.interceptors.request.use( try { const { access } = await refreshToken( account!.accountId, - account!.refresh + account!.refresh, ); token = access; } catch (err) { @@ -97,7 +97,7 @@ axios.interceptors.request.use( request.headers["Authorization"] = `Bearer ${token}`; return request; }, - (error) => Promise.reject(error) + (error) => Promise.reject(error), ); export const handleApiError = async (response: AxiosResponse) => { diff --git a/web/src/api/refresh.ts b/web/src/api/refresh.ts index bcf3ecf..01ec670 100644 --- a/web/src/api/refresh.ts +++ b/web/src/api/refresh.ts @@ -15,7 +15,7 @@ export const refreshTokenApi = async (refreshToken: string) => { "Content-Type": "application/json", Authorization: `Bearer ${refreshToken}`, }, - } + }, ); if (response.status !== 200 && response.status !== 201) diff --git a/web/src/context/oauth/provider.tsx b/web/src/context/oauth/provider.tsx index 4cf355d..ba83277 100644 --- a/web/src/context/oauth/provider.tsx +++ b/web/src/context/oauth/provider.tsx @@ -29,7 +29,7 @@ export const OAuthProvider: FC = ({ children }) => { window.location.replace(`${redirectURI}?${params.toString()}`); } }, - [active, nonce, redirectURI, state] + [active, nonce, redirectURI, state], ); return ( diff --git a/web/src/feature/AccountList/index.tsx b/web/src/feature/AccountList/index.tsx index e305d46..9703358 100644 --- a/web/src/feature/AccountList/index.tsx +++ b/web/src/feature/AccountList/index.tsx @@ -14,7 +14,7 @@ const AccountList: FC = () => { (account: LocalAccount) => { updateActiveAccount(account); }, - [updateActiveAccount] + [updateActiveAccount], ); return ( diff --git a/web/src/layout/AuthLayout.tsx b/web/src/layout/AuthLayout.tsx index 19404ba..3b70782 100644 --- a/web/src/layout/AuthLayout.tsx +++ b/web/src/layout/AuthLayout.tsx @@ -73,7 +73,7 @@ const AuthLayout = () => { if (!active) { console.log( "setting search params:", - Object.fromEntries(searchParams.entries()) + Object.fromEntries(searchParams.entries()), ); setActive(true); setClientID(searchParams.get("client_id") ?? ""); diff --git a/web/src/main.tsx b/web/src/main.tsx index c12acfa..51c2c1a 100644 --- a/web/src/main.tsx +++ b/web/src/main.tsx @@ -9,5 +9,5 @@ const root = document.getElementById("root")!; createRoot(root).render( - + , ); diff --git a/web/src/pages/Login/index.tsx b/web/src/pages/Login/index.tsx index 9fb3e6b..ab26ddc 100644 --- a/web/src/pages/Login/index.tsx +++ b/web/src/pages/Login/index.tsx @@ -67,13 +67,13 @@ export default function LoginPage() { console.log(err); setError( "Failed to create account. " + - (err.message ?? "Unexpected error happened") + (err.message ?? "Unexpected error happened"), ); } finally { setLoading(false); } }, - [repo, reset, updateActiveAccount] + [repo, reset, updateActiveAccount], ); return ( diff --git a/web/src/pages/Register/index.tsx b/web/src/pages/Register/index.tsx index 0cfce62..cdbd569 100644 --- a/web/src/pages/Register/index.tsx +++ b/web/src/pages/Register/index.tsx @@ -56,11 +56,11 @@ export default function RegisterPage() { setError( `Failed to create an account. ${ text[0].toUpperCase() + text.slice(1) - }` + }`, ); } else { setSuccess( - "Account has been created. You can now log into your new account" + "Account has been created. You can now log into your new account", ); reset(); } @@ -71,7 +71,7 @@ export default function RegisterPage() { setLoading(false); } }, - [reset] + [reset], ); return ( diff --git a/web/src/repository/account.ts b/web/src/repository/account.ts index c936e7e..4f571ec 100644 --- a/web/src/repository/account.ts +++ b/web/src/repository/account.ts @@ -61,7 +61,7 @@ export const encryptToken = async (token: string) => { const cipherText = await crypto.subtle.encrypt( { name: "AES-GCM", iv }, deviceKey, - encoder.encode(token) + encoder.encode(token), ); return { @@ -84,7 +84,7 @@ export const decryptToken = async (encrypted: { iv: new Uint8Array(encrypted.iv), }, deviceKey, - new Uint8Array(encrypted.data) + new Uint8Array(encrypted.data), ); return decoder.decode(decrypted); @@ -92,7 +92,7 @@ export const decryptToken = async (encrypted: { export const saveAccount = async ( db: IDBPDatabase, - req: CreateAccountRequest + req: CreateAccountRequest, ): Promise => { const access = await encryptToken(req.access); const refresh = await encryptToken(req.refresh); @@ -141,7 +141,7 @@ export const getAllAccounts = async (db: IDBPDatabase) => { } catch (err) { console.warn(`Failed to decrypt account ${account.label}:`, err); } - }) + }), ) ).filter((acc) => acc !== undefined); @@ -186,7 +186,7 @@ export const getAccountRaw = async (db: IDBPDatabase, accountId: string) => { export const updateAccountTokens = async ( db: IDBPDatabase, - req: UpdateAccountTokensRequest + req: UpdateAccountTokensRequest, ) => { const account = await getAccountRaw(db, req.accountId); @@ -204,7 +204,7 @@ export const updateAccountTokens = async ( export const updateAccountInfo = async ( db: IDBPDatabase, - req: UpdateAccountInfoRequest + req: UpdateAccountInfoRequest, ) => { const account = await getAccountRaw(db, req.accountId); await db?.put?.("accounts", { @@ -229,7 +229,7 @@ export const useAccountRepo = () => { return saveAccount(db, req); }, - [db] + [db], ); const loadAll = useCallback(async () => { @@ -244,7 +244,7 @@ export const useAccountRepo = () => { return getAccount(db, accountId); }, - [db] + [db], ); return { save, loadAll, getOne }; diff --git a/web/src/util/deviceId.ts b/web/src/util/deviceId.ts index 9ac9233..a961a67 100644 --- a/web/src/util/deviceId.ts +++ b/web/src/util/deviceId.ts @@ -37,7 +37,7 @@ export const deriveDeviceKey = async (deviceKeyId: string) => { encoder.encode(deviceKeyId), { name: "PBKDF2" }, false, - ["deriveKey"] + ["deriveKey"], ); return crypto.subtle.deriveKey( { @@ -49,6 +49,6 @@ export const deriveDeviceKey = async (deviceKeyId: string) => { baseKey, { name: "AES-GCM", length: 256 }, false, - ["encrypt", "decrypt"] + ["encrypt", "decrypt"], ); }; diff --git a/web/src/util/token.ts b/web/src/util/token.ts index 4ad9b29..313e777 100644 --- a/web/src/util/token.ts +++ b/web/src/util/token.ts @@ -5,14 +5,14 @@ export type EncryptedToken = { export const encryptToken = async ( token: string, - key: CryptoKey + key: CryptoKey, ): Promise => { const encoder = new TextEncoder(); const iv = crypto.getRandomValues(new Uint8Array(12)); const cipherText = await crypto.subtle.encrypt( { name: "AES-GCM", iv }, key, - encoder.encode(token) + encoder.encode(token), ); return { cipherText, iv }; };