diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 738f86f6943..869a733e325 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -6,3 +6,6 @@ contact_links:
   - name: Support
     url: https://discord.gg/pRYNYr4W5A
     about: Need help with something? Having troubles setting up? Or perhaps issues using the API? Reach out to the community on Discord.
+  - name: Translations
+    url: https://hosted.weblate.org/projects/actualbudget/actual/
+    about: Found a string that needs a better translation? Add your suggestion or upvote an existing one in Weblate.
diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml
index f0ac4b33ce3..eb0cd07a651 100644
--- a/.github/actions/setup/action.yml
+++ b/.github/actions/setup/action.yml
@@ -38,3 +38,7 @@ runs:
         repository: actualbudget/translations
         path: ${{ inputs.working-directory }}/packages/desktop-client/locale
       if: ${{ inputs.download-translations == 'true' }}
+    - name: Remove untranslated languages
+      run: packages/desktop-client/bin/remove-untranslated-languages
+      shell: bash
+      if: ${{ inputs.download-translations == 'true' }}
diff --git a/.github/workflows/i18n-string-extract-master.yml b/.github/workflows/i18n-string-extract-master.yml
index b12a98931b2..484f042d213 100644
--- a/.github/workflows/i18n-string-extract-master.yml
+++ b/.github/workflows/i18n-string-extract-master.yml
@@ -41,7 +41,7 @@ jobs:
           wlc \
             --url https://hosted.weblate.org/api/ \
             --key "${{ secrets.WEBLATE_API_KEY_CI_STRINGS }}" \
-            pull \
+            push \
             actualbudget/actual
       - name: Check out updated translations
         uses: actions/checkout@v4
@@ -69,6 +69,13 @@ jobs:
           else
             echo "No changes to commit"
           fi
+      - name: Update Weblate with latest translations
+        run: |
+          wlc \
+            --url https://hosted.weblate.org/api/ \
+            --key "${{ secrets.WEBLATE_API_KEY_CI_STRINGS }}" \
+            pull \
+            actualbudget/actual
 
       - name: Unlock translations
         if: always()  # Clean up even on failure
diff --git a/.github/workflows/update-vrt.yml b/.github/workflows/update-vrt.yml
index 7fc6e8260a8..6fa61d9a183 100644
--- a/.github/workflows/update-vrt.yml
+++ b/.github/workflows/update-vrt.yml
@@ -79,6 +79,8 @@ jobs:
         with:
           name: patch
       - name: Apply patch and push
+        env:
+          BRANCH_NAME: ${{ steps.comment-branch.outputs.head_ref }}
         run: |
           git config --global user.name "github-actions[bot]"
           git config --global user.email "github-actions[bot]@users.noreply.github.com"
@@ -89,7 +91,7 @@ jobs:
             exit 0
           fi
           git commit -m "Update VRT"
-          git push origin HEAD:${{ steps.comment-branch.outputs.head_ref }}
+          git push origin HEAD:${BRANCH_NAME}
       - name: Add finished reaction
         uses: dkershner6/reaction-action@v2
         with:
diff --git a/bin/package-browser b/bin/package-browser
index 955d98f732e..6565d429956 100755
--- a/bin/package-browser
+++ b/bin/package-browser
@@ -11,6 +11,7 @@ fi
 pushd packages/desktop-client/locale > /dev/null
 git pull
 popd > /dev/null
+packages/desktop-client/bin/remove-untranslated-languages
 
 yarn workspace loot-core build:browser
 yarn workspace @actual-app/web build:browser
diff --git a/packages/api/methods.ts b/packages/api/methods.ts
index 5ed53e9c7ec..426afc2d50e 100644
--- a/packages/api/methods.ts
+++ b/packages/api/methods.ts
@@ -85,10 +85,21 @@ export function addTransactions(
   });
 }
 
-export function importTransactions(accountId, transactions) {
+export interface ImportTransactionsOpts {
+  defaultCleared?: boolean;
+}
+
+export function importTransactions(
+  accountId,
+  transactions,
+  opts: ImportTransactionsOpts = {
+    defaultCleared: true,
+  },
+) {
   return send('api/transactions-import', {
     accountId,
     transactions,
+    opts,
   });
 }
 
diff --git a/packages/desktop-client/bin/remove-untranslated-languages b/packages/desktop-client/bin/remove-untranslated-languages
new file mode 100755
index 00000000000..c3dd6506101
--- /dev/null
+++ b/packages/desktop-client/bin/remove-untranslated-languages
@@ -0,0 +1,48 @@
+#!/usr/bin/env node
+const fs = require('fs');
+const path = require('path');
+
+// Local path to the cloned translations repository
+const localRepoPath = './packages/desktop-client/locale';
+
+// Compare JSON files and delete incomplete ones
+const processTranslations = () => {
+  try {
+    const files = fs.readdirSync(localRepoPath);
+    const enJsonPath = path.join(localRepoPath, 'en.json');
+
+    if (!fs.existsSync(enJsonPath)) {
+      throw new Error('en.json not found in the repository.');
+    }
+
+    const enJson = JSON.parse(fs.readFileSync(enJsonPath, 'utf8'));
+    const enKeysCount = Object.keys(enJson).length;
+
+    console.log(`en.json has ${enKeysCount} keys.`);
+
+    files.forEach((file) => {
+      if (file === 'en.json' || path.extname(file) !== '.json') return;
+
+      const filePath = path.join(localRepoPath, file);
+      const jsonData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
+      const fileKeysCount = Object.keys(jsonData).length;
+
+      // Calculate the percentage of keys present compared to en.json
+      const percentage = (fileKeysCount / enKeysCount) * 100;
+      console.log(`${file} has ${fileKeysCount} keys (${percentage.toFixed(2)}%).`);
+
+      if (percentage < 50) {
+        fs.unlinkSync(filePath);
+        console.log(`Deleted ${file} due to insufficient keys.`);
+      } else {
+        console.log(`Keeping ${file}.`);
+      }
+    });
+
+    console.log('Processing completed.');
+  } catch (error) {
+    console.error(`Error: ${error.message}`);
+  }
+};
+
+processTranslations();
diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Bar-Graph-and-checks-the-visuals-1-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Bar-Graph-and-checks-the-visuals-1-chromium-linux.png
index 9210a0b8dfd..1d7fb76c6b1 100644
Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Bar-Graph-and-checks-the-visuals-1-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Bar-Graph-and-checks-the-visuals-1-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Bar-Graph-and-checks-the-visuals-2-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Bar-Graph-and-checks-the-visuals-2-chromium-linux.png
index 2b9312d930f..eee31376838 100644
Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Bar-Graph-and-checks-the-visuals-2-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Bar-Graph-and-checks-the-visuals-2-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Bar-Graph-and-checks-the-visuals-3-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Bar-Graph-and-checks-the-visuals-3-chromium-linux.png
index bae67361093..9d78fe7cb7c 100644
Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Bar-Graph-and-checks-the-visuals-3-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Bar-Graph-and-checks-the-visuals-3-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Line-Graph-and-checks-the-visuals-1-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Line-Graph-and-checks-the-visuals-1-chromium-linux.png
index 952ce325b76..af6971b2dd6 100644
Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Line-Graph-and-checks-the-visuals-1-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Line-Graph-and-checks-the-visuals-1-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Line-Graph-and-checks-the-visuals-2-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Line-Graph-and-checks-the-visuals-2-chromium-linux.png
index 43ee73625d3..803b27d3890 100644
Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Line-Graph-and-checks-the-visuals-2-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Line-Graph-and-checks-the-visuals-2-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Line-Graph-and-checks-the-visuals-3-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Line-Graph-and-checks-the-visuals-3-chromium-linux.png
index 47841b69da4..88bce2b0974 100644
Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Line-Graph-and-checks-the-visuals-3-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Switches-to-Line-Graph-and-checks-the-visuals-3-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Validates-that-show-legend-button-shows-the-legend-side-bar-1-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Validates-that-show-legend-button-shows-the-legend-side-bar-1-chromium-linux.png
index e35ae86c415..7b7d3213884 100644
Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Validates-that-show-legend-button-shows-the-legend-side-bar-1-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Validates-that-show-legend-button-shows-the-legend-side-bar-1-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Validates-that-show-legend-button-shows-the-legend-side-bar-2-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Validates-that-show-legend-button-shows-the-legend-side-bar-2-chromium-linux.png
index 49a0c2af9d7..7b7c5bd5555 100644
Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Validates-that-show-legend-button-shows-the-legend-side-bar-2-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Validates-that-show-legend-button-shows-the-legend-side-bar-2-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Validates-that-show-legend-button-shows-the-legend-side-bar-3-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Validates-that-show-legend-button-shows-the-legend-side-bar-3-chromium-linux.png
index f7654637a52..cbb2116942c 100644
Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Validates-that-show-legend-button-shows-the-legend-side-bar-3-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-custom-reports-Validates-that-show-legend-button-shows-the-legend-side-bar-3-chromium-linux.png differ
diff --git a/packages/desktop-client/globals.d.ts b/packages/desktop-client/globals.d.ts
index 9f9ea2437e3..07f968a4085 100644
--- a/packages/desktop-client/globals.d.ts
+++ b/packages/desktop-client/globals.d.ts
@@ -8,3 +8,7 @@ declare module 'react' {
   // eslint-disable-next-line @typescript-eslint/consistent-type-definitions, @typescript-eslint/no-empty-object-type
   interface CSSProperties extends CSSObject {}
 }
+
+declare global {
+  function __resetWorld(): void;
+}
diff --git a/packages/desktop-client/package.json b/packages/desktop-client/package.json
index 19b8078aceb..23d298dc3d8 100644
--- a/packages/desktop-client/package.json
+++ b/packages/desktop-client/package.json
@@ -40,8 +40,6 @@
     "i18next-parser": "^9.0.0",
     "i18next-resources-to-backend": "^1.2.1",
     "inter-ui": "^3.19.3",
-    "jest": "^27.5.1",
-    "jest-watch-typeahead": "^2.2.2",
     "lodash": "^4.17.21",
     "mdast-util-newline-to-break": "^2.0.0",
     "memoize-one": "^6.0.0",
diff --git a/packages/desktop-client/src/components/ServerContext.tsx b/packages/desktop-client/src/components/ServerContext.tsx
index c32ed2ac518..8e8d4681f1b 100644
--- a/packages/desktop-client/src/components/ServerContext.tsx
+++ b/packages/desktop-client/src/components/ServerContext.tsx
@@ -93,7 +93,11 @@ export function ServerProvider({ children }: { children: ReactNode }) {
 
   useEffect(() => {
     async function run() {
-      setServerURL(await send('get-server-url'));
+      const serverURL = await send('get-server-url');
+      if (!serverURL) {
+        return;
+      }
+      setServerURL(serverURL);
       setVersion(await getServerVersion());
     }
     run();
@@ -135,7 +139,8 @@ export function ServerProvider({ children }: { children: ReactNode }) {
     async (url: string, opts: { validate?: boolean } = {}) => {
       const { error } = await send('set-server-url', { ...opts, url });
       if (!error) {
-        setServerURL(await send('get-server-url'));
+        const serverURL = await send('get-server-url');
+        setServerURL(serverURL!);
         setVersion(await getServerVersion());
       }
       return { error };
diff --git a/packages/desktop-client/src/components/admin/UserAccess/UserAccess.tsx b/packages/desktop-client/src/components/admin/UserAccess/UserAccess.tsx
index 736daad12e1..4d4aba87896 100644
--- a/packages/desktop-client/src/components/admin/UserAccess/UserAccess.tsx
+++ b/packages/desktop-client/src/components/admin/UserAccess/UserAccess.tsx
@@ -106,8 +106,9 @@ function UserAccessContent({
   }, [cloudFileId, setLoading, t]);
 
   const loadOwner = useCallback(async () => {
-    const file: Awaited<ReturnType<Handlers['get-user-file-info']>> =
-      (await send('get-user-file-info', cloudFileId as string)) ?? {};
+    const file = (await send('get-user-file-info', cloudFileId as string)) ?? {
+      usersWithAccess: [],
+    };
     const owner = file?.usersWithAccess.filter(user => user.owner);
 
     if (owner.length > 0) {
diff --git a/packages/desktop-client/src/components/admin/UserDirectory/UserDirectory.tsx b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectory.tsx
index 2d57fc13deb..328f4ebc407 100644
--- a/packages/desktop-client/src/components/admin/UserDirectory/UserDirectory.tsx
+++ b/packages/desktop-client/src/components/admin/UserDirectory/UserDirectory.tsx
@@ -116,11 +116,24 @@ function UserDirectoryContent({
     setLoading(true);
 
     const loadedUsers = (await send('users-get')) ?? [];
+    if ('error' in loadedUsers) {
+      dispatch(
+        addNotification({
+          type: 'error',
+          id: 'error',
+          title: t('Error getting users'),
+          sticky: true,
+          message: getUserDirectoryErrors(loadedUsers.error),
+        }),
+      );
+      setLoading(false);
+      return;
+    }
 
     setAllUsers(loadedUsers);
     setLoading(false);
     return loadedUsers;
-  }, [setLoading]);
+  }, [dispatch, getUserDirectoryErrors, setLoading, t]);
 
   useEffect(() => {
     async function loadData() {
@@ -141,9 +154,11 @@ function UserDirectoryContent({
 
   const onDeleteSelected = useCallback(async () => {
     setLoading(true);
-    const { error } = await send('user-delete-all', [...selectedInst.items]);
+    const res = await send('user-delete-all', [...selectedInst.items]);
 
-    if (error) {
+    const error = res['error'];
+    const someDeletionsFailed = res['someDeletionsFailed'];
+    if (error || someDeletionsFailed) {
       if (error === 'token-expired') {
         dispatch(
           addNotification({
diff --git a/packages/desktop-client/src/components/filters/SavedFilterMenuButton.tsx b/packages/desktop-client/src/components/filters/SavedFilterMenuButton.tsx
index d6fd2e10021..56b7d0a40f3 100644
--- a/packages/desktop-client/src/components/filters/SavedFilterMenuButton.tsx
+++ b/packages/desktop-client/src/components/filters/SavedFilterMenuButton.tsx
@@ -42,7 +42,7 @@ export function SavedFilterMenuButton({
   const [adding, setAdding] = useState(false);
   const [menuOpen, setMenuOpen] = useState(false);
   const triggerRef = useRef(null);
-  const [err, setErr] = useState(null);
+  const [err, setErr] = useState<string | null>(null);
   const [menuItem, setMenuItem] = useState('');
   const [name, setName] = useState(filterId?.name ?? '');
   const id = filterId?.id;
diff --git a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx
index 64597e2a009..afd8bb790e8 100644
--- a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx
+++ b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx
@@ -88,7 +88,7 @@ export function CreateAccountModal({ upgradingAccountId }: CreateAccountProps) {
         balance: number;
       };
 
-      for (const oldAccount of results.accounts) {
+      for (const oldAccount of results.accounts ?? []) {
         const newAccount: NormalizedAccount = {
           account_id: oldAccount.id,
           name: oldAccount.name,
diff --git a/packages/desktop-client/src/components/modals/EditUser.tsx b/packages/desktop-client/src/components/modals/EditUser.tsx
index 0e59ada14a7..5fdb1d71a30 100644
--- a/packages/desktop-client/src/components/modals/EditUser.tsx
+++ b/packages/desktop-client/src/components/modals/EditUser.tsx
@@ -83,12 +83,14 @@ function useSaveUser() {
     user: User,
     setError: (error: string) => void,
   ): Promise<boolean> {
-    const { error, id: newId } = (await send(method, user)) || {};
-    if (!error) {
+    const res = (await send(method, user)) || {};
+    if (!res['error']) {
+      const newId = res['id'];
       if (newId) {
         user.id = newId;
       }
     } else {
+      const error = res['error'];
       setError(getUserDirectoryErrors(error));
       if (error === 'token-expired') {
         dispatch(
diff --git a/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx b/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx
index 66ce987f31c..2b9703637a0 100644
--- a/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx
+++ b/packages/desktop-client/src/components/payees/ManagePayeesWithData.tsx
@@ -35,9 +35,9 @@ export function ManagePayeesWithData({
   }, []);
 
   const refetchRuleCounts = useCallback(async () => {
-    let counts = await send('payees-get-rule-counts');
-    counts = new Map(Object.entries(counts));
-    setRuleCounts({ value: counts });
+    const counts = await send('payees-get-rule-counts');
+    const countsMap = new Map(Object.entries(counts));
+    setRuleCounts({ value: countsMap });
   }, []);
 
   useEffect(() => {
diff --git a/packages/desktop-client/src/components/reports/spreadsheets/calendar-spreadsheet.ts b/packages/desktop-client/src/components/reports/spreadsheets/calendar-spreadsheet.ts
index eb50c66fdf3..71dee8760dd 100644
--- a/packages/desktop-client/src/components/reports/spreadsheets/calendar-spreadsheet.ts
+++ b/packages/desktop-client/src/components/reports/spreadsheets/calendar-spreadsheet.ts
@@ -34,7 +34,7 @@ export function calendarSpreadsheet(
       }[];
     }) => void,
   ) => {
-    let filters;
+    let filters: unknown[];
 
     try {
       const { filters: filtersLocal } = await send(
diff --git a/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts b/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts
index dfd11e58fa5..fca3740a622 100644
--- a/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts
+++ b/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts
@@ -277,18 +277,18 @@ export function createCustomSpreadsheet({
       filterEmptyRows({ showEmpty, data: i, balanceTypeOp }),
     );
 
+    const sortedCalcDataFiltered = [...calcDataFiltered].sort(
+      sortData({ balanceTypeOp, sortByOp }),
+    );
+
     const legend = calculateLegend(
       intervalData,
-      calcDataFiltered,
+      sortedCalcDataFiltered,
       groupBy,
       graphType,
       balanceTypeOp,
     );
 
-    const sortedCalcDataFiltered = [...calcDataFiltered].sort(
-      sortData({ balanceTypeOp, sortByOp }),
-    );
-
     setData({
       data: sortedCalcDataFiltered,
       intervalData,
diff --git a/packages/desktop-client/src/components/reports/spreadsheets/spending-spreadsheet.ts b/packages/desktop-client/src/components/reports/spreadsheets/spending-spreadsheet.ts
index 997bcd3a5a0..6159a2bbf4a 100644
--- a/packages/desktop-client/src/components/reports/spreadsheets/spending-spreadsheet.ts
+++ b/packages/desktop-client/src/components/reports/spreadsheets/spending-spreadsheet.ts
@@ -109,7 +109,7 @@ export function createSpendingSpreadsheet({
             $and: [{ month: { $eq: budgetMonth } }],
           })
           .filter({
-            [conditionsOpKey]: filters.filter(filter => filter.category),
+            [conditionsOpKey]: filters.filter(filter => filter['category']),
           })
           .groupBy([{ $id: '$category' }])
           .select([
diff --git a/packages/desktop-client/src/components/reports/spreadsheets/summary-spreadsheet.ts b/packages/desktop-client/src/components/reports/spreadsheets/summary-spreadsheet.ts
index a4200d1edee..059ff0191d7 100644
--- a/packages/desktop-client/src/components/reports/spreadsheets/summary-spreadsheet.ts
+++ b/packages/desktop-client/src/components/reports/spreadsheets/summary-spreadsheet.ts
@@ -27,7 +27,7 @@ export function summarySpreadsheet(
       toRange: string;
     }) => void,
   ) => {
-    let filters = [];
+    let filters: unknown[] = [];
     try {
       const response = await send('make-filters-from-conditions', {
         conditions: conditions.filter(cond => !cond.customName),
diff --git a/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx b/packages/desktop-client/src/components/transactions/TransactionsTable.test.tsx
similarity index 88%
rename from packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx
rename to packages/desktop-client/src/components/transactions/TransactionsTable.test.tsx
index 3acca829459..cbf94acfc19 100644
--- a/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx
+++ b/packages/desktop-client/src/components/transactions/TransactionsTable.test.tsx
@@ -20,6 +20,13 @@ import {
   updateTransaction,
 } from 'loot-core/src/shared/transactions';
 import { integerToCurrency } from 'loot-core/src/shared/util';
+import {
+  type AccountEntity,
+  type CategoryEntity,
+  type CategoryGroupEntity,
+  type PayeeEntity,
+  type TransactionEntity,
+} from 'loot-core/types/models';
 
 import { AuthProvider } from '../../auth/AuthProvider';
 import { SelectedProviderWithItems } from '../../hooks/useSelected';
@@ -41,26 +48,20 @@ vi.mock('../../hooks/useFeatureFlag', () => ({
 }));
 
 const accounts = [generateAccount('Bank of America')];
-const payees = [
+const payees: PayeeEntity[] = [
   {
     id: 'bob-id',
     name: 'Bob',
     favorite: 1,
-    transfer_acct: null,
-    category: null,
   },
   {
     id: 'alice-id',
     name: 'Alice',
     favorite: 1,
-    transfer_acct: null,
-    category: null,
   },
   {
     id: 'guy',
     favorite: 0,
-    transfer_acct: null,
-    category: null,
     name: 'This guy on the side of the road',
   },
 ];
@@ -80,8 +81,12 @@ const categoryGroups = generateCategoryGroups([
 ]);
 const usualGroup = categoryGroups[1];
 
-function generateTransactions(count, splitAtIndexes = [], showError = false) {
-  const transactions = [];
+function generateTransactions(
+  count: number,
+  splitAtIndexes: number[] = [],
+  showError: boolean = false,
+) {
+  const transactions: TransactionEntity[] = [];
 
   for (let i = 0; i < count; i++) {
     const isSplit = splitAtIndexes.includes(i);
@@ -94,10 +99,10 @@ function generateTransactions(count, splitAtIndexes = [], showError = false) {
           payee: 'alice-id',
           category:
             i === 0
-              ? null
+              ? undefined
               : i === 1
-                ? usualGroup.categories[1].id
-                : usualGroup.categories[0].id,
+                ? usualGroup.categories?.[1].id
+                : usualGroup.categories?.[0].id,
           amount: isSplit ? 50 : undefined,
           sort_order: i,
         },
@@ -110,31 +115,46 @@ function generateTransactions(count, splitAtIndexes = [], showError = false) {
   return transactions;
 }
 
-function LiveTransactionTable(props) {
+type LiveTransactionTableProps = {
+  transactions: TransactionEntity[];
+  payees: PayeeEntity[];
+  accounts: AccountEntity[];
+  categoryGroups: CategoryGroupEntity[];
+  currentAccountId: string | null;
+  showAccount: boolean;
+  showCategory: boolean;
+  showCleared: boolean;
+  isAdding: boolean;
+  onTransactionsChange?: (newTrans: TransactionEntity[]) => void;
+  onCloseAddTransaction?: () => void;
+};
+
+function LiveTransactionTable(props: LiveTransactionTableProps) {
   const [transactions, setTransactions] = useState(props.transactions);
 
   useEffect(() => {
     if (transactions === props.transactions) return;
     props.onTransactionsChange?.(transactions);
+    // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [transactions]);
 
-  const onSplit = id => {
+  const onSplit = (id: string) => {
     const { data, diff } = splitTransaction(transactions, id);
     setTransactions(data);
     return diff.added[0].id;
   };
 
-  const onSave = transaction => {
+  const onSave = (transaction: TransactionEntity) => {
     const { data } = updateTransaction(transactions, transaction);
     setTransactions(data);
   };
 
-  const onAdd = newTransactions => {
+  const onAdd = (newTransactions: TransactionEntity[]) => {
     newTransactions = realizeTempTransactions(newTransactions);
     setTransactions(trans => [...newTransactions, ...trans]);
   };
 
-  const onAddSplit = id => {
+  const onAddSplit = (id: string) => {
     const { data, diff } = addSplitTransaction(transactions, id);
     setTransactions(data);
     return diff.added[0].id;
@@ -155,16 +175,17 @@ function LiveTransactionTable(props) {
               <SelectedProviderWithItems
                 name="transactions"
                 items={transactions}
-                fetchAllIds={() => transactions.map(t => t.id)}
+                fetchAllIds={() => Promise.resolve(transactions.map(t => t.id))}
               >
                 <SplitsExpandedProvider>
                   <TransactionTable
                     {...props}
+                    // @ts-expect-error this will be auto-patched once TransactionTable is moved to TS
                     transactions={transactions}
                     loadMoreTransactions={() => {}}
                     commonPayees={[]}
                     payees={payees}
-                    addNotification={n => console.log(n)}
+                    addNotification={console.log}
                     onSave={onSave}
                     onSplit={onSplit}
                     onAdd={onAdd}
@@ -215,22 +236,22 @@ function waitForAutocomplete() {
   return new Promise(resolve => setTimeout(resolve, 0));
 }
 
-const categories = categoryGroups.reduce(
-  (all, group) => all.concat(group.categories),
+const categories = categoryGroups.reduce<CategoryEntity[]>(
+  (all, group) => (group.categories ? [...all, ...group.categories] : all),
   [],
 );
 
-function prettyDate(date) {
+function prettyDate(date: string) {
   return formatDate(parseDate(date, 'yyyy-MM-dd', new Date()), 'MM/dd/yyyy');
 }
 
-function renderTransactions(extraProps) {
+function renderTransactions(extraProps?: Partial<LiveTransactionTableProps>) {
   let transactions = generateTransactions(5, [6]);
   // Hardcoding the first value makes it easier for tests to do
   // various this
   transactions[0].amount = -2777;
 
-  const defaultProps = {
+  const defaultProps: LiveTransactionTableProps = {
     transactions,
     payees,
     accounts,
@@ -251,7 +272,7 @@ function renderTransactions(extraProps) {
   return {
     ...result,
     getTransactions: () => transactions,
-    updateProps: props =>
+    updateProps: (props: Partial<LiveTransactionTableProps>) =>
       render(
         <LiveTransactionTable {...defaultProps} {...extraProps} {...props} />,
         { container: result.container },
@@ -259,27 +280,37 @@ function renderTransactions(extraProps) {
   };
 }
 
-function queryNewField(container, name, subSelector = '', idx = 0) {
+function queryNewField(
+  container: HTMLElement,
+  name: string,
+  subSelector: string = '',
+  idx: number = 0,
+): HTMLInputElement {
   const field = container.querySelectorAll(
     `[data-testid="new-transaction"] [data-testid="${name}"]`,
   )[idx];
   if (subSelector !== '') {
-    return field.querySelector(subSelector);
+    return field.querySelector(subSelector)!;
   }
-  return field;
+  return field as HTMLInputElement;
 }
 
-function queryField(container, name, subSelector = '', idx) {
+function queryField(
+  container: HTMLElement,
+  name: string,
+  subSelector: string = '',
+  idx: number,
+) {
   const field = container.querySelectorAll(
     `[data-testid="transaction-table"] [data-testid="${name}"]`,
   )[idx];
   if (subSelector !== '') {
-    return field.querySelector(subSelector);
+    return field.querySelector(subSelector)!;
   }
   return field;
 }
 
-async function _editField(field, container) {
+async function _editField(field: Element, container: HTMLElement) {
   // We only short-circuit this for inputs
   const input = field.querySelector(`input`);
   if (input) {
@@ -287,17 +318,17 @@ async function _editField(field, container) {
     return input;
   }
 
-  let element;
+  let element: HTMLInputElement;
   const buttonQuery = 'button,div[data-testid=cell-button]';
 
   if (field.querySelector(buttonQuery)) {
-    const btn = field.querySelector(buttonQuery);
+    const btn = field.querySelector(buttonQuery)!;
     await userEvent.click(btn);
-    element = field.querySelector(':focus');
+    element = field.querySelector(':focus')!;
     expect(element).toBeTruthy();
   } else {
-    await userEvent.click(field.querySelector('div'));
-    element = field.querySelector('input');
+    await userEvent.click(field.querySelector('div')!);
+    element = field.querySelector('input')!;
     expect(element).toBeTruthy();
     expect(container.ownerDocument.activeElement).toBe(element);
   }
@@ -305,20 +336,23 @@ async function _editField(field, container) {
   return element;
 }
 
-function editNewField(container, name, rowIndex) {
+function editNewField(container: HTMLElement, name: string, rowIndex?: number) {
   const field = queryNewField(container, name, '', rowIndex);
   return _editField(field, container);
 }
 
-function editField(container, name, rowIndex) {
+function editField(container: HTMLElement, name: string, rowIndex: number) {
   const field = queryField(container, name, '', rowIndex);
   return _editField(field, container);
 }
 
 expect.extend({
-  payeesToHaveFavoriteStars(container, validPayeeListWithFavorite) {
-    const incorrectStarList = [];
-    const foundStarList = [];
+  payeesToHaveFavoriteStars(
+    container: Element[],
+    validPayeeListWithFavorite: string[],
+  ) {
+    const incorrectStarList: string[] = [];
+    const foundStarList: string[] = [];
     validPayeeListWithFavorite.forEach(payeeItem => {
       const shouldHaveFavorite = payeeItem != null;
       let found = false;
@@ -350,14 +384,19 @@ expect.extend({
   },
 });
 
-function expectToBeEditingField(container, name, rowIndex, isNew) {
-  let field;
+function expectToBeEditingField(
+  container: HTMLElement,
+  name: string,
+  rowIndex: number,
+  isNew?: boolean,
+) {
+  let field: Element;
   if (isNew) {
     field = queryNewField(container, name, '', rowIndex);
   } else {
     field = queryField(container, name, '', rowIndex);
   }
-  const input = field.querySelector(':focus');
+  const input: HTMLInputElement = field.querySelector(':focus')!;
   expect(input).toBeTruthy();
   expect(container.ownerDocument.activeElement).toBe(input);
   return input;
@@ -372,10 +411,10 @@ describe('Transactions', () => {
         prettyDate(transaction.date),
       );
       expect(queryField(container, 'account', 'div', idx).textContent).toBe(
-        accounts.find(acct => acct.id === transaction.account).name,
+        accounts.find(acct => acct.id === transaction.account)?.name,
       );
       expect(queryField(container, 'payee', 'div', idx).textContent).toBe(
-        payees.find(p => p.id === transaction.payee).name,
+        payees.find(p => p.id === transaction.payee)?.name,
       );
       expect(queryField(container, 'notes', 'div', idx).textContent).toBe(
         transaction.notes,
@@ -383,7 +422,7 @@ describe('Transactions', () => {
       expect(queryField(container, 'category', 'div', idx).textContent).toBe(
         transaction.category
           ? categories.find(category => category.id === transaction.category)
-              .name
+              ?.name
           : 'Categorize',
       );
       if (transaction.amount <= 0) {
@@ -531,6 +570,7 @@ describe('Transactions', () => {
     expect(items.length).toBe(2);
     expect(items[0].textContent).toBe('Usual Expenses');
     expect(items[1].textContent).toBe('General 129.87');
+    // @ts-expect-error fix me
     expect(items[1].dataset['highlighted']).toBeDefined();
 
     // It should not allow filtering on group names
@@ -561,10 +601,10 @@ describe('Transactions', () => {
       .getByTestId('autocomplete')
       .querySelector('[data-highlighted]');
     expect(highlighted).not.toBeNull();
-    expect(highlighted.textContent).toBe('General 129.87');
+    expect(highlighted!.textContent).toBe('General 129.87');
 
     expect(getTransactions()[2].category).toBe(
-      categories.find(category => category.name === 'Food').id,
+      categories.find(category => category.name === 'Food')?.id,
     );
 
     await userEvent.type(input, '[Enter]');
@@ -572,7 +612,7 @@ describe('Transactions', () => {
 
     // The transactions data should be updated with the right category
     expect(getTransactions()[2].category).toBe(
-      categories.find(category => category.name === 'General').id,
+      categories.find(category => category.name === 'General')?.id,
     );
 
     // The category field should still be editing
@@ -607,16 +647,16 @@ describe('Transactions', () => {
       .getByTestId('autocomplete')
       .querySelector('[data-highlighted]');
     expect(highlighted).not.toBeNull();
-    expect(highlighted.textContent).toBe('General 129.87');
+    expect(highlighted!.textContent).toBe('General 129.87');
 
     // Click the item and check the before/after values
     expect(getTransactions()[2].category).toBe(
-      categories.find(c => c.name === 'Food').id,
+      categories.find(c => c.name === 'Food')?.id,
     );
     await userEvent.click(items[2]);
     await waitForAutocomplete();
     expect(getTransactions()[2].category).toBe(
-      categories.find(c => c.name === 'General').id,
+      categories.find(c => c.name === 'General')?.id,
     );
 
     // It should still be editing the category
@@ -651,8 +691,9 @@ describe('Transactions', () => {
     // field was different than the transactions' category
     const currentCategory = getTransactions()[2].category;
     expect(currentCategory).toBe(oldCategory);
+    // @ts-expect-error fix me
     expect(highlighted.textContent).not.toBe(
-      categories.find(c => c.id === currentCategory).name,
+      categories.find(c => c.id === currentCategory)?.name,
     );
   });
 
@@ -678,6 +719,7 @@ describe('Transactions', () => {
       'Bob-payee-item',
       'This guy on the side of the road-payee-item',
     ]);
+    // @ts-expect-error fix me
     expect(renderedPayees).payeesToHaveFavoriteStars([
       'Alice-payee-item',
       'Bob-payee-item',
@@ -807,7 +849,7 @@ describe('Transactions', () => {
     await waitForAutocomplete();
 
     await userEvent.click(
-      container.querySelector('[data-testid="add-split-button"]'),
+      container.querySelector('[data-testid="add-split-button"]')!,
     );
 
     input = await editNewField(container, 'debit', 1);
@@ -825,7 +867,7 @@ describe('Transactions', () => {
       null,
     );
 
-    const addButton = container.querySelector('[data-testid="add-button"]');
+    const addButton = container.querySelector('[data-testid="add-button"]')!;
 
     expect(getTransactions().length).toBe(5);
     await userEvent.click(addButton);
@@ -903,13 +945,13 @@ describe('Transactions', () => {
     transactions[0] = { ...transactions[0], id: uuidv4() };
     updateProps({ transactions });
 
-    function expectErrorToNotExist(transactions) {
+    function expectErrorToNotExist(transactions: TransactionEntity[]) {
       transactions.forEach(transaction => {
         expect(transaction.error).toBeFalsy();
       });
     }
 
-    function expectErrorToExist(transactions) {
+    function expectErrorToExist(transactions: TransactionEntity[]) {
       transactions.forEach((transaction, idx) => {
         if (idx === 0) {
           expect(transaction.error).toBeTruthy();
@@ -951,7 +993,7 @@ describe('Transactions', () => {
     // Add another split transaction and make sure everything is
     // updated properly
     await userEvent.click(
-      toolbar.querySelector('[data-testid="add-split-button"]'),
+      toolbar.querySelector('[data-testid="add-split-button"]')!,
     );
     expect(getTransactions().length).toBe(7);
     expect(getTransactions()[2].amount).toBe(0);
@@ -973,10 +1015,10 @@ describe('Transactions', () => {
       {
         account: accounts[0].id,
         amount: -2777,
-        category: null,
+        category: undefined,
         cleared: false,
         date: '2017-01-01',
-        error: null,
+        error: undefined,
         id: expect.any(String),
         is_parent: true,
         notes: 'Notes',
@@ -986,7 +1028,7 @@ describe('Transactions', () => {
       {
         account: accounts[0].id,
         amount: -1000,
-        category: null,
+        category: undefined,
         cleared: false,
         date: '2017-01-01',
         error: null,
@@ -994,13 +1036,14 @@ describe('Transactions', () => {
         is_child: true,
         parent_id: parentId,
         payee: 'alice-id',
+        reconciled: undefined,
         sort_order: -1,
         starting_balance_flag: null,
       },
       {
         account: accounts[0].id,
         amount: -1777,
-        category: null,
+        category: undefined,
         cleared: false,
         date: '2017-01-01',
         error: null,
@@ -1008,6 +1051,7 @@ describe('Transactions', () => {
         is_child: true,
         parent_id: parentId,
         payee: 'alice-id',
+        reconciled: undefined,
         sort_order: -2,
         starting_balance_flag: null,
       },
diff --git a/packages/desktop-client/src/hooks/useSplitsExpanded.tsx b/packages/desktop-client/src/hooks/useSplitsExpanded.tsx
index 2d178e152d1..69c3675decb 100644
--- a/packages/desktop-client/src/hooks/useSplitsExpanded.tsx
+++ b/packages/desktop-client/src/hooks/useSplitsExpanded.tsx
@@ -87,7 +87,7 @@ export function useSplitsExpanded() {
 
 type SplitsExpandedProviderProps = {
   children?: ReactNode;
-  initialMode: SplitMode;
+  initialMode?: SplitMode;
 };
 
 export function SplitsExpandedProvider({
diff --git a/packages/loot-core/src/client/query-helpers.test.ts b/packages/loot-core/src/client/query-helpers.test.ts
index 20179f39e72..c37c454ee5f 100644
--- a/packages/loot-core/src/client/query-helpers.test.ts
+++ b/packages/loot-core/src/client/query-helpers.test.ts
@@ -122,7 +122,9 @@ function initPagingServer(
 
 describe('query helpers', () => {
   it('runQuery runs a query', async () => {
-    initServer({ query: query => ({ data: query, dependencies: [] }) });
+    initServer({
+      query: query => Promise.resolve({ data: query, dependencies: [] }),
+    });
 
     const query = q('transactions').select('*');
     const { data } = await runQuery(query);
diff --git a/packages/loot-core/src/mocks/index.ts b/packages/loot-core/src/mocks/index.ts
index e04cbb7db51..92a8a4194bd 100644
--- a/packages/loot-core/src/mocks/index.ts
+++ b/packages/loot-core/src/mocks/index.ts
@@ -1,33 +1,54 @@
-// @ts-strict-ignore
 import { v4 as uuidv4 } from 'uuid';
 
 import * as monthUtils from '../shared/months';
 import type {
   _SyncFields,
   AccountEntity,
+  CategoryEntity,
+  CategoryGroupEntity,
+  NewCategoryGroupEntity,
   TransactionEntity,
 } from '../types/models';
 
 import { random } from './random';
 
 export function generateAccount(
-  name,
-  isConnected,
-  offbudget,
-): AccountEntity & { bankId: number; bankName: string } {
-  return {
+  name: AccountEntity['name'],
+  isConnected?: boolean,
+  offbudget?: boolean,
+): AccountEntity & { bankId: number | null; bankName: string | null } {
+  const offlineAccount: AccountEntity & {
+    bankId: number | null;
+    bankName: string | null;
+  } = {
     id: uuidv4(),
     name,
-    balance_current: isConnected ? Math.floor(random() * 100000) : null,
-    bankId: isConnected ? Math.floor(random() * 10000) : null,
-    bankName: isConnected ? 'boa' : null,
-    bank: isConnected ? Math.floor(random() * 10000).toString() : null,
+    bankId: null,
+    bankName: null,
     offbudget: offbudget ? 1 : 0,
     sort_order: 0,
     tombstone: 0,
     closed: 0,
     ...emptySyncFields(),
   };
+
+  if (isConnected) {
+    return {
+      ...offlineAccount,
+      balance_current: Math.floor(random() * 100000),
+      bankId: Math.floor(random() * 10000),
+      bankName: 'boa',
+      bank: Math.floor(random() * 10000).toString(),
+      account_id: 'idx',
+      mask: 'xxx',
+      official_name: 'boa',
+      balance_available: 0,
+      balance_limit: 0,
+      account_sync_source: 'goCardless',
+    };
+  }
+
+  return offlineAccount;
 }
 
 function emptySyncFields(): _SyncFields<false> {
@@ -44,40 +65,51 @@ function emptySyncFields(): _SyncFields<false> {
 }
 
 let sortOrder = 1;
-export function generateCategory(name, group, isIncome = false) {
+export function generateCategory(
+  name: string,
+  group: string,
+  isIncome: boolean = false,
+): CategoryEntity {
   return {
     id: uuidv4(),
     name,
     cat_group: group,
-    is_income: isIncome ? 1 : 0,
+    is_income: isIncome,
     sort_order: sortOrder++,
   };
 }
 
 let groupSortOrder = 1;
-export function generateCategoryGroup(name, isIncome = false) {
+export function generateCategoryGroup(
+  name: string,
+  isIncome: boolean = false,
+): CategoryGroupEntity {
   return {
     id: uuidv4(),
     name,
-    is_income: isIncome ? 1 : 0,
+    is_income: isIncome,
     sort_order: groupSortOrder++,
   };
 }
 
-export function generateCategoryGroups(definition) {
+export function generateCategoryGroups(
+  definition: Partial<NewCategoryGroupEntity>[],
+): CategoryGroupEntity[] {
   return definition.map(group => {
-    const g = generateCategoryGroup(group.name, group.is_income);
+    const g = generateCategoryGroup(group.name ?? '', group.is_income);
 
     return {
       ...g,
-      categories: group.categories.map(cat =>
+      categories: group.categories?.map(cat =>
         generateCategory(cat.name, g.id, cat.is_income),
       ),
     };
   });
 }
 
-function _generateTransaction(data): TransactionEntity {
+function _generateTransaction(
+  data: Partial<TransactionEntity> & Pick<TransactionEntity, 'account'>,
+): TransactionEntity {
   return {
     id: data.id || uuidv4(),
     amount: data.amount || Math.floor(random() * 10000 - 7000),
@@ -91,8 +123,12 @@ function _generateTransaction(data): TransactionEntity {
   };
 }
 
-export function generateTransaction(data, splitAmount?, showError = false) {
-  const result = [];
+export function generateTransaction(
+  data: Partial<TransactionEntity> & Pick<TransactionEntity, 'account'>,
+  splitAmount?: number,
+  showError: boolean = false,
+) {
+  const result: TransactionEntity[] = [];
 
   const trans = _generateTransaction(data);
   result.push(trans);
@@ -107,18 +143,14 @@ export function generateTransaction(data, splitAmount?, showError = false) {
         amount: trans.amount - splitAmount,
         account: parent.account,
         date: parent.date,
-        notes: null,
-        category: null,
-        isChild: true,
+        is_child: true,
       },
       {
         id: parent.id + '/' + uuidv4(),
         amount: splitAmount,
         account: parent.account,
         date: parent.date,
-        notes: null,
-        category: null,
-        isChild: true,
+        is_child: true,
       },
     );
 
@@ -137,13 +169,13 @@ export function generateTransaction(data, splitAmount?, showError = false) {
 }
 
 export function generateTransactions(
-  count,
-  accountId,
-  groupId,
-  splitAtIndexes = [],
-  showError = false,
-) {
-  const transactions = [];
+  count: number,
+  accountId: string,
+  groupId: string,
+  splitAtIndexes: number[] = [],
+  showError: boolean = false,
+): TransactionEntity[] {
+  const transactions: TransactionEntity[] = [];
 
   for (let i = 0; i < count; i++) {
     const isSplit = splitAtIndexes.includes(i);
diff --git a/packages/loot-core/src/platform/client/fetch/index.d.ts b/packages/loot-core/src/platform/client/fetch/index.d.ts
index 7ca6e52bc8b..417f4f917d9 100644
--- a/packages/loot-core/src/platform/client/fetch/index.d.ts
+++ b/packages/loot-core/src/platform/client/fetch/index.d.ts
@@ -1,4 +1,5 @@
 import type { Handlers } from '../../../types/handlers';
+import type { CategoryGroupEntity } from '../../../types/models';
 import type { ServerEvents } from '../../../types/server-events';
 
 export function init(socketName: string): Promise<unknown>;
@@ -38,7 +39,14 @@ export function unlisten(name: string): void;
 export type Unlisten = typeof unlisten;
 
 /** Mock functions */
-export function initServer(handlers: unknown): void;
+export function initServer(handlers: {
+  query: (query: { table: string; selectExpressions: unknown }) => Promise<{
+    data: unknown;
+    dependencies: string[];
+  }>;
+  getCell?: () => { value: number };
+  'get-categories'?: () => { grouped: CategoryGroupEntity[] };
+}): void;
 export type InitServer = typeof initServer;
 
 export function serverPush(name: string, args: unknown): void;
diff --git a/packages/loot-core/src/server/accounts/rules.test.ts b/packages/loot-core/src/server/accounts/rules.test.ts
index 88bf161ac00..996cadc6b2a 100644
--- a/packages/loot-core/src/server/accounts/rules.test.ts
+++ b/packages/loot-core/src/server/accounts/rules.test.ts
@@ -547,7 +547,6 @@ describe('Rule', () => {
       expect(
         fixedAmountRule.exec({ imported_payee: 'James', amount: 200 }),
       ).toMatchObject({
-        error: null,
         subtransactions: [{ amount: 100 }, { amount: 100 }],
       });
     });
@@ -574,7 +573,6 @@ describe('Rule', () => {
 
       expect(rule.exec({ imported_payee: 'James', amount: 200 })).toMatchObject(
         {
-          error: null,
           subtransactions: [{ amount: 100 }, { amount: 100 }],
         },
       );
@@ -600,7 +598,6 @@ describe('Rule', () => {
 
       expect(rule.exec({ imported_payee: 'James', amount: 200 })).toMatchObject(
         {
-          error: null,
           subtransactions: [{ amount: 100 }, { amount: 100 }],
         },
       );
@@ -635,7 +632,6 @@ describe('Rule', () => {
       expect(
         prioritizationRule.exec({ imported_payee: 'James', amount: 200 }),
       ).toMatchObject({
-        error: null,
         subtransactions: [{ amount: 100 }, { amount: 50 }, { amount: 50 }],
       });
     });
@@ -645,7 +641,6 @@ describe('Rule', () => {
       expect(
         prioritizationRule.exec({ imported_payee: 'James', amount: 50 }),
       ).toMatchObject({
-        error: null,
         subtransactions: [{ amount: 100 }, { amount: -25 }, { amount: -25 }],
       });
     });
@@ -677,7 +672,6 @@ describe('Rule', () => {
 
       expect(rule.exec({ imported_payee: 'James', amount: 150 })).toMatchObject(
         {
-          error: null,
           subtransactions: [{ amount: 100 }, { amount: 50 }, { amount: 0 }],
         },
       );
diff --git a/packages/loot-core/src/server/accounts/sync.ts b/packages/loot-core/src/server/accounts/sync.ts
index 62954778ca2..5add734d796 100644
--- a/packages/loot-core/src/server/accounts/sync.ts
+++ b/packages/loot-core/src/server/accounts/sync.ts
@@ -393,6 +393,7 @@ export async function reconcileTransactions(
   isBankSyncAccount = false,
   strictIdChecking = true,
   isPreview = false,
+  defaultCleared = true,
 ) {
   console.log('Performing transaction reconciliation');
 
@@ -436,7 +437,7 @@ export async function reconcileTransactions(
         category: existing.category || trans.category || null,
         imported_payee: trans.imported_payee || null,
         notes: existing.notes || trans.notes || null,
-        cleared: trans.cleared != null ? trans.cleared : true,
+        cleared: trans.cleared ?? existing.cleared,
       };
 
       if (hasFieldsChanged(existing, updates, Object.keys(updates))) {
@@ -468,7 +469,7 @@ export async function reconcileTransactions(
         ...newTrans,
         id: uuidv4(),
         category: trans.category || null,
-        cleared: trans.cleared != null ? trans.cleared : true,
+        cleared: trans.cleared ?? defaultCleared,
       };
 
       if (subtransactions && subtransactions.length > 0) {
diff --git a/packages/loot-core/src/server/main.ts b/packages/loot-core/src/server/main.ts
index 4deee12800f..c4ac7e7ef00 100644
--- a/packages/loot-core/src/server/main.ts
+++ b/packages/loot-core/src/server/main.ts
@@ -1242,6 +1242,7 @@ handlers['transactions-import'] = mutator(function ({
   accountId,
   transactions,
   isPreview,
+  opts,
 }) {
   return withUndo(async () => {
     if (typeof accountId !== 'string') {
@@ -1255,6 +1256,7 @@ handlers['transactions-import'] = mutator(function ({
         false,
         true,
         isPreview,
+        opts?.defaultCleared,
       );
     } catch (err) {
       if (err instanceof TransactionError) {
diff --git a/packages/loot-core/src/server/spreadsheet/spreadsheet.test.ts b/packages/loot-core/src/server/spreadsheet/spreadsheet.test.ts
index 2d96b2d77ad..095252fde14 100644
--- a/packages/loot-core/src/server/spreadsheet/spreadsheet.test.ts
+++ b/packages/loot-core/src/server/spreadsheet/spreadsheet.test.ts
@@ -10,7 +10,7 @@ function wait(n) {
   return new Promise(resolve => setTimeout(resolve, n));
 }
 
-async function insertTransactions(payeeId = null) {
+async function insertTransactions() {
   await db.insertAccount({ id: '1', name: 'checking', offbudget: 0 });
   await db.insertAccount({ id: '2', name: 'checking', offbudget: 1 });
   await db.insertCategoryGroup({ id: 'group1', name: 'group1' });
@@ -23,7 +23,6 @@ async function insertTransactions(payeeId = null) {
       account: '1',
       category: 'cat1',
       date: '2017-01-08',
-      description: payeeId,
     })[0],
   );
   await db.insertTransaction(
@@ -32,7 +31,6 @@ async function insertTransactions(payeeId = null) {
       account: '1',
       category: 'cat2',
       date: '2017-01-10',
-      description: payeeId,
     })[0],
   );
   await db.insertTransaction(
@@ -41,7 +39,6 @@ async function insertTransactions(payeeId = null) {
       account: '1',
       category: 'cat2',
       date: '2017-01-15',
-      description: payeeId,
     })[0],
   );
 }
@@ -128,8 +125,8 @@ describe('Spreadsheet', () => {
   test('querying deep join works', async () => {
     const spreadsheet = new Spreadsheet(db);
     await db.insertPayee({ name: '', transfer_acct: '1' });
-    const payeeId2 = await db.insertPayee({ name: '', transfer_acct: '2' });
-    await insertTransactions(payeeId2);
+    await db.insertPayee({ name: '', transfer_acct: '2' });
+    await insertTransactions();
 
     spreadsheet.set(
       'g!foo',
diff --git a/packages/loot-core/src/shared/transactions.ts b/packages/loot-core/src/shared/transactions.ts
index 5572446d9ed..d09b9459139 100644
--- a/packages/loot-core/src/shared/transactions.ts
+++ b/packages/loot-core/src/shared/transactions.ts
@@ -4,11 +4,6 @@ import { type TransactionEntity } from '../types/models';
 
 import { last, diffItems, applyChanges } from './util';
 
-interface TransactionEntityWithError extends TransactionEntity {
-  error: ReturnType<typeof SplitTransactionError> | null;
-  _deleted?: boolean;
-}
-
 export function isTemporaryId(id: string) {
   return id.indexOf('temp') !== -1;
 }
@@ -26,13 +21,13 @@ function SplitTransactionError(total: number, parent: TransactionEntity) {
   const difference = num(parent.amount) - total;
 
   return {
-    type: 'SplitTransactionError',
-    version: 1,
+    type: 'SplitTransactionError' as const,
+    version: 1 as const,
     difference,
   };
 }
 
-type GenericTransactionEntity = TransactionEntity | TransactionEntityWithError;
+type GenericTransactionEntity = TransactionEntity;
 
 export function makeChild<T extends GenericTransactionEntity>(
   parent: T,
@@ -86,8 +81,10 @@ export function recalculateSplit(trans: TransactionEntity) {
   return {
     ...trans,
     error:
-      total === num(trans.amount) ? null : SplitTransactionError(total, trans),
-  } as TransactionEntityWithError;
+      total === num(trans.amount)
+        ? undefined
+        : SplitTransactionError(total, trans),
+  } satisfies TransactionEntity;
 }
 
 function findParentIndex(transactions: TransactionEntity[], idx: number) {
@@ -129,7 +126,10 @@ export function ungroupTransactions(transactions: TransactionEntity[]) {
 }
 
 export function groupTransaction(split: TransactionEntity[]) {
-  return { ...split[0], subtransactions: split.slice(1) } as TransactionEntity;
+  return {
+    ...split[0],
+    subtransactions: split.slice(1),
+  } satisfies TransactionEntity;
 }
 
 export function ungroupTransaction(split: TransactionEntity | null) {
@@ -154,12 +154,10 @@ export function applyTransactionDiff(
 function replaceTransactions(
   transactions: TransactionEntity[],
   id: string,
-  func: (
-    transaction: TransactionEntity,
-  ) => TransactionEntity | TransactionEntityWithError | null,
+  func: (transaction: TransactionEntity) => TransactionEntity | null,
 ): {
   data: TransactionEntity[];
-  newTransaction: TransactionEntity | TransactionEntityWithError | null;
+  newTransaction: TransactionEntity | null;
   diff: ReturnType<typeof diffItems<TransactionEntity>>;
 } {
   const idx = transactions.findIndex(t => t.id === id);
@@ -282,8 +280,8 @@ export function deleteTransaction(
           ...trans,
           subtransactions: undefined,
           is_parent: false,
-          error: null,
-        } as TransactionEntityWithError;
+          error: undefined,
+        } satisfies TransactionEntity;
       } else {
         const sub = trans.subtransactions?.filter(t => t.id !== id);
         return recalculateSplit({ ...trans, subtransactions: sub });
@@ -313,12 +311,13 @@ export function splitTransaction(
     return {
       ...trans,
       is_parent: true,
-      error: num(trans.amount) === 0 ? null : SplitTransactionError(0, trans),
+      error:
+        num(trans.amount) === 0 ? undefined : SplitTransactionError(0, trans),
       subtransactions: subtransactions.map(t => ({
         ...t,
         sort_order: t.sort_order || -1,
       })),
-    } as TransactionEntityWithError;
+    } satisfies TransactionEntity;
   });
 }
 
@@ -338,7 +337,7 @@ export function realizeTempTransactions(
           ...child,
           id: uuidv4(),
           parent_id: parent.id,
-        }) as TransactionEntity,
+        }) satisfies TransactionEntity,
     ),
   ];
 }
diff --git a/packages/loot-core/src/types/api-handlers.d.ts b/packages/loot-core/src/types/api-handlers.d.ts
index eb294292d57..37ca5336689 100644
--- a/packages/loot-core/src/types/api-handlers.d.ts
+++ b/packages/loot-core/src/types/api-handlers.d.ts
@@ -1,3 +1,5 @@
+import { ImportTransactionsOpts } from '@actual-app/api';
+
 import { type batchUpdateTransactions } from '../server/accounts/transactions';
 import type {
   APIAccountEntity,
@@ -80,6 +82,7 @@ export interface ApiHandlers {
     accountId;
     transactions;
     isPreview?;
+    opts?: ImportTransactionsOpts;
   }) => Promise<{
     errors?: { message: string }[];
     added;
diff --git a/packages/loot-core/src/types/models/transaction.d.ts b/packages/loot-core/src/types/models/transaction.d.ts
index 648eec97495..4c071291c8b 100644
--- a/packages/loot-core/src/types/models/transaction.d.ts
+++ b/packages/loot-core/src/types/models/transaction.d.ts
@@ -26,4 +26,9 @@ export interface TransactionEntity {
   subtransactions?: TransactionEntity[];
   _unmatched?: boolean;
   _deleted?: boolean;
+  error?: {
+    type: 'SplitTransactionError';
+    version: 1;
+    difference: number;
+  };
 }
diff --git a/packages/loot-core/src/types/server-handlers.d.ts b/packages/loot-core/src/types/server-handlers.d.ts
index f86c86a124f..904dd94aa71 100644
--- a/packages/loot-core/src/types/server-handlers.d.ts
+++ b/packages/loot-core/src/types/server-handlers.d.ts
@@ -1,3 +1,5 @@
+import { ImportTransactionsOpts } from '@actual-app/api';
+
 import { ParseFileResult } from '../server/accounts/parse-file';
 import { batchUpdateTransactions } from '../server/accounts/transactions';
 import { Backup } from '../server/backups';
@@ -246,6 +248,7 @@ export interface ServerHandlers {
     accountId;
     transactions;
     isPreview;
+    opts?: ImportTransactionsOpts;
   }) => Promise<{
     errors?: { message: string }[];
     added;
diff --git a/tsconfig.json b/tsconfig.json
index 5c5510f8c7f..d46dd74c7e6 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -19,7 +19,7 @@
     "noFallthroughCasesInSwitch": true,
     "skipLibCheck": true,
     "jsx": "preserve",
-    "types": ["vite/client", "jest"],
+    "types": ["vite/client", "jest", "vitest/globals"],
     // Check JS files too
     "allowJs": true,
     "checkJs": false,
diff --git a/upcoming-release-notes/4108.md b/upcoming-release-notes/4108.md
new file mode 100644
index 00000000000..e75000244bf
--- /dev/null
+++ b/upcoming-release-notes/4108.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [MatissJanis]
+---
+
+TypeScript: ported transactions-table tests to TS.
diff --git a/upcoming-release-notes/4129.md b/upcoming-release-notes/4129.md
new file mode 100644
index 00000000000..4a903f60762
--- /dev/null
+++ b/upcoming-release-notes/4129.md
@@ -0,0 +1,6 @@
+---
+category: Enhancements
+authors: [NikxDa]
+---
+
+Add ability to provide default cleared status in the API and skip updating the cleared status on subsequent imports.
diff --git a/upcoming-release-notes/4144.md b/upcoming-release-notes/4144.md
new file mode 100644
index 00000000000..e516e334bc2
--- /dev/null
+++ b/upcoming-release-notes/4144.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [jfdoming]
+---
+
+Update issue template with translation issue type
diff --git a/upcoming-release-notes/4146.md b/upcoming-release-notes/4146.md
new file mode 100644
index 00000000000..78c785e4cc0
--- /dev/null
+++ b/upcoming-release-notes/4146.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [jfdoming]
+---
+
+Fix `send` types in a number of places (1/2)
diff --git a/upcoming-release-notes/4148.md b/upcoming-release-notes/4148.md
new file mode 100644
index 00000000000..30292735b18
--- /dev/null
+++ b/upcoming-release-notes/4148.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [jfdoming]
+---
+
+Exclude untranslated languages from builds
diff --git a/upcoming-release-notes/4149.md b/upcoming-release-notes/4149.md
new file mode 100644
index 00000000000..678e93bdb46
--- /dev/null
+++ b/upcoming-release-notes/4149.md
@@ -0,0 +1,6 @@
+---
+category: Bugfix
+authors: [jfdoming]
+---
+
+Fix string upload if new changes are present
diff --git a/upcoming-release-notes/4151.md b/upcoming-release-notes/4151.md
new file mode 100644
index 00000000000..e45d84e8113
--- /dev/null
+++ b/upcoming-release-notes/4151.md
@@ -0,0 +1,6 @@
+---
+category: Bugfix
+authors: [UnderKoen]
+---
+
+Remove code injection in /update-vrt workflow
diff --git a/upcoming-release-notes/4162.md b/upcoming-release-notes/4162.md
new file mode 100644
index 00000000000..b464003bfd4
--- /dev/null
+++ b/upcoming-release-notes/4162.md
@@ -0,0 +1,6 @@
+---
+category: Bugfix
+authors: [matt-fidd]
+---
+
+Fix inconsistent legend coloring in custom reports
diff --git a/yarn.lock b/yarn.lock
index 5f60a120657..0722add2e6c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -93,8 +93,6 @@ __metadata:
     i18next-parser: "npm:^9.0.0"
     i18next-resources-to-backend: "npm:^1.2.1"
     inter-ui: "npm:^3.19.3"
-    jest: "npm:^27.5.1"
-    jest-watch-typeahead: "npm:^2.2.2"
     lodash: "npm:^4.17.21"
     mdast-util-newline-to-break: "npm:^2.0.0"
     memoize-one: "npm:^6.0.0"
@@ -2366,20 +2364,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@jest/console@npm:^29.5.0":
-  version: 29.5.0
-  resolution: "@jest/console@npm:29.5.0"
-  dependencies:
-    "@jest/types": "npm:^29.5.0"
-    "@types/node": "npm:*"
-    chalk: "npm:^4.0.0"
-    jest-message-util: "npm:^29.5.0"
-    jest-util: "npm:^29.5.0"
-    slash: "npm:^3.0.0"
-  checksum: 10/0971c3d6abbb6adfa0b4e88c41121bbd45d7df821f7a9f7b3f4fce86d25b237925db526b315f9791a24b29efd0028bb235f68d5b6cc343e83246a6e76b5724dc
-  languageName: node
-  linkType: hard
-
 "@jest/core@npm:^27.5.1":
   version: 27.5.1
   resolution: "@jest/core@npm:27.5.1"
@@ -2546,18 +2530,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@jest/test-result@npm:^29.5.0":
-  version: 29.5.0
-  resolution: "@jest/test-result@npm:29.5.0"
-  dependencies:
-    "@jest/console": "npm:^29.5.0"
-    "@jest/types": "npm:^29.5.0"
-    "@types/istanbul-lib-coverage": "npm:^2.0.0"
-    collect-v8-coverage: "npm:^1.0.0"
-  checksum: 10/e41ab6137b26dba4d08441f3c921c8c9f4543bddd23072e1dbb54770584ac118f957fc6da4bf94bc5127161bee8e1ea6983b4e92249e47604163b10347d373ce
-  languageName: node
-  linkType: hard
-
 "@jest/test-sequencer@npm:^27.5.1":
   version: 27.5.1
   resolution: "@jest/test-sequencer@npm:27.5.1"
@@ -6622,15 +6594,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"ansi-escapes@npm:^6.0.0":
-  version: 6.2.0
-  resolution: "ansi-escapes@npm:6.2.0"
-  dependencies:
-    type-fest: "npm:^3.0.0"
-  checksum: 10/442f91b04650b35bc4815f47c20412d69ddbba5d4bf22f72ec03be352fca2de6819c7e3f4dfd17816ee4e0c6c965fe85e6f1b3f09683996a8d12fd366afd924e
-  languageName: node
-  linkType: hard
-
 "ansi-escapes@npm:^7.0.0":
   version: 7.0.0
   resolution: "ansi-escapes@npm:7.0.0"
@@ -7704,7 +7667,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"chalk@npm:^5.2.0, chalk@npm:~5.3.0":
+"chalk@npm:~5.3.0":
   version: 5.3.0
   resolution: "chalk@npm:5.3.0"
   checksum: 10/6373caaab21bd64c405bfc4bd9672b145647fc9482657b5ea1d549b3b2765054e9d3d928870cdf764fb4aad67555f5061538ff247b8310f110c5c888d92397ea
@@ -7718,13 +7681,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"char-regex@npm:^2.0.0":
-  version: 2.0.1
-  resolution: "char-regex@npm:2.0.1"
-  checksum: 10/fadd100b963c160a70192e47e122c654cadf447c2c8f23b0bda4dc9ef1a02c993abbb0f21f50e2e58f90a8453ca019b3c86f001688cb42fb7b54af4e661b1ada
-  languageName: node
-  linkType: hard
-
 "character-entities@npm:^2.0.0":
   version: 2.0.2
   resolution: "character-entities@npm:2.0.2"
@@ -9269,13 +9225,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"emittery@npm:^0.13.1":
-  version: 0.13.1
-  resolution: "emittery@npm:0.13.1"
-  checksum: 10/fbe214171d878b924eedf1757badf58a5dce071cd1fa7f620fa841a0901a80d6da47ff05929d53163105e621ce11a71b9d8acb1148ffe1745e045145f6e69521
-  languageName: node
-  linkType: hard
-
 "emittery@npm:^0.8.1":
   version: 0.8.1
   resolution: "emittery@npm:0.8.1"
@@ -12997,7 +12946,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"jest-regex-util@npm:^29.0.0, jest-regex-util@npm:^29.4.3":
+"jest-regex-util@npm:^29.4.3":
   version: 29.4.3
   resolution: "jest-regex-util@npm:29.4.3"
   checksum: 10/96fc7fc28cd4dd73a63c13a526202c4bd8b351d4e5b68b1a2a2c88da3308c2a16e26feaa593083eb0bac38cca1aa9dd05025412e7de013ba963fb8e66af22b8a
@@ -13205,23 +13154,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"jest-watch-typeahead@npm:^2.2.2":
-  version: 2.2.2
-  resolution: "jest-watch-typeahead@npm:2.2.2"
-  dependencies:
-    ansi-escapes: "npm:^6.0.0"
-    chalk: "npm:^5.2.0"
-    jest-regex-util: "npm:^29.0.0"
-    jest-watcher: "npm:^29.0.0"
-    slash: "npm:^5.0.0"
-    string-length: "npm:^5.0.1"
-    strip-ansi: "npm:^7.0.1"
-  peerDependencies:
-    jest: ^27.0.0 || ^28.0.0 || ^29.0.0
-  checksum: 10/8685277ce1b96ec775882111ec55ce90a862cc57acb21ce94f8ac44a25f6fb34c7a7ce119e07b2d8ff5353a8d9e4f981cf96fa35532f71ddba6ca8fedc05bd8e
-  languageName: node
-  linkType: hard
-
 "jest-watcher@npm:^27.5.1":
   version: 27.5.1
   resolution: "jest-watcher@npm:27.5.1"
@@ -13237,22 +13169,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"jest-watcher@npm:^29.0.0":
-  version: 29.5.0
-  resolution: "jest-watcher@npm:29.5.0"
-  dependencies:
-    "@jest/test-result": "npm:^29.5.0"
-    "@jest/types": "npm:^29.5.0"
-    "@types/node": "npm:*"
-    ansi-escapes: "npm:^4.2.1"
-    chalk: "npm:^4.0.0"
-    emittery: "npm:^0.13.1"
-    jest-util: "npm:^29.5.0"
-    string-length: "npm:^4.0.1"
-  checksum: 10/accd79e95dbe27106500fcc6814c4690438dda54f3bae2e5373b341e398a7ee3be64c07ff0e1e26c675e699025a4d0dd7822466f0273a17a0613d5157f3941ad
-  languageName: node
-  linkType: hard
-
 "jest-worker@npm:^27.4.5, jest-worker@npm:^27.5.1":
   version: 27.5.1
   resolution: "jest-worker@npm:27.5.1"
@@ -17692,13 +17608,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"slash@npm:^5.0.0":
-  version: 5.0.1
-  resolution: "slash@npm:5.0.1"
-  checksum: 10/9f08524c3cd187b8addd5982b314c56ae49d781454a202622e9ca6e15cc1f6d65d3388be40eb38f203700afcc023ebd2186845cf62266f10ff92a91d09b95d33
-  languageName: node
-  linkType: hard
-
 "slice-ansi@npm:^3.0.0":
   version: 3.0.0
   resolution: "slice-ansi@npm:3.0.0"
@@ -18046,16 +17955,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"string-length@npm:^5.0.1":
-  version: 5.0.1
-  resolution: "string-length@npm:5.0.1"
-  dependencies:
-    char-regex: "npm:^2.0.0"
-    strip-ansi: "npm:^7.0.1"
-  checksum: 10/71f73b8c8a743e01dcd001bcf1b197db78d5e5e53b12bd898cddaf0961be09f947dfd8c429783db3694b55b05cb5a51de6406c5085ff1aaa10c4771440c8396d
-  languageName: node
-  linkType: hard
-
 "string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3":
   version: 4.2.3
   resolution: "string-width@npm:4.2.3"
@@ -18993,15 +18892,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"type-fest@npm:^3.0.0":
-  version: 3.10.0
-  resolution: "type-fest@npm:3.10.0"
-  peerDependencies:
-    typescript: ">=4.7.0"
-  checksum: 10/1cc20d5d258ee44a772dd7412c80e97763d3d5a94452663ae6ebb0a3c4e7be7c0c1134bbe8df79abaf972f31252b5e9e91ba59a08481887fb260a31f90dda59d
-  languageName: node
-  linkType: hard
-
 "typed-array-buffer@npm:^1.0.2":
   version: 1.0.2
   resolution: "typed-array-buffer@npm:1.0.2"