[AI] Enable subpath imports across all packages (#7462)

* [AI] Enable subpath imports across all packages

Generalize the prefer-subpath-imports ESLint rule to work with any
package (not just loot-core) and enable it globally. Add subpath import
mappings to cli, component-library, and sync-server package.json files.
Auto-fix all backtracked relative imports to use #-prefixed subpath
imports.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* [AI] Add release notes for #7462

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

* [AI] Fix mock specifiers in accounts.test.ts to use aliased imports

Co-authored-by: Matiss Janis Aboltins <[email protected]>

* [AI] Fix mock specifiers in query.test.ts to use aliased imports

Co-authored-by: Matiss Janis Aboltins <[email protected]>

* [AI] Fix ESLint rule to properly validate src/ directory paths

Co-authored-by: Matiss Janis Aboltins <[email protected]>

* [AI] Add publishConfig.imports for sync-server to remap aliases to build directory

Co-authored-by: Matiss Janis Aboltins <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Matiss Janis Aboltins <[email protected]>
This commit is contained in:
Matiss Janis Aboltins
2026-04-11 08:56:49 +00:00
committed by GitHub
co-authored by Claude Opus 4.6 Matiss Janis Aboltins Cursor Agent
parent 4aa6ac76b4
commit 65b154151f
92 changed files with 271 additions and 194 deletions
+1 -9
View File
@@ -36,6 +36,7 @@
"actual/prefer-const": "error",
"actual/no-anchor-tag": "error",
"actual/no-react-default-import": "error",
"actual/prefer-subpath-imports": "error",
// JSX A11y rules
"jsx-a11y/no-autofocus": [
@@ -396,15 +397,6 @@
"actual/no-anchor-tag": "off"
}
},
{
"files": [
"packages/loot-core/src/**/*.{ts,tsx}",
"packages/desktop-client/src/**/*.{ts,tsx}"
],
"rules": {
"actual/prefer-subpath-imports": "error"
}
},
{
"files": ["packages/desktop-client/**/*.{js,ts,jsx,tsx}"],
"rules": {
+8
View File
@@ -11,6 +11,14 @@
"dist"
],
"type": "module",
"imports": {
"#commands/*": "./src/commands/*.ts",
"#config": "./src/config.ts",
"#connection": "./src/connection.ts",
"#input": "./src/input.ts",
"#output": "./src/output.ts",
"#utils": "./src/utils.ts"
},
"scripts": {
"build": "vite build",
"test": "vitest --run",
+3 -3
View File
@@ -1,7 +1,7 @@
import * as api from '@actual-app/api';
import { Command } from 'commander';
import { printOutput } from '../output';
import { printOutput } from '#output';
import { registerAccountsCommand } from './accounts';
@@ -15,11 +15,11 @@ vi.mock('@actual-app/api', () => ({
getAccountBalance: vi.fn().mockResolvedValue(10000),
}));
vi.mock('../connection', () => ({
vi.mock('#connection', () => ({
withConnection: vi.fn((_opts, fn) => fn()),
}));
vi.mock('../output', () => ({
vi.mock('#output', () => ({
printOutput: vi.fn(),
}));
+3 -3
View File
@@ -1,9 +1,9 @@
import * as api from '@actual-app/api';
import type { Command } from 'commander';
import { withConnection } from '../connection';
import { printOutput } from '../output';
import { parseBoolFlag, parseIntFlag } from '../utils';
import { withConnection } from '#connection';
import { printOutput } from '#output';
import { parseBoolFlag, parseIntFlag } from '#utils';
export function registerAccountsCommand(program: Command) {
const accounts = program.command('accounts').description('Manage accounts');
+4 -4
View File
@@ -1,10 +1,10 @@
import * as api from '@actual-app/api';
import type { Command } from 'commander';
import { resolveConfig } from '../config';
import { withConnection } from '../connection';
import { printOutput } from '../output';
import { parseBoolFlag, parseIntFlag } from '../utils';
import { resolveConfig } from '#config';
import { withConnection } from '#connection';
import { printOutput } from '#output';
import { parseBoolFlag, parseIntFlag } from '#utils';
export function registerBudgetsCommand(program: Command) {
const budgets = program.command('budgets').description('Manage budgets');
+3 -3
View File
@@ -1,9 +1,9 @@
import * as api from '@actual-app/api';
import type { Command } from 'commander';
import { withConnection } from '../connection';
import { printOutput } from '../output';
import { parseBoolFlag } from '../utils';
import { withConnection } from '#connection';
import { printOutput } from '#output';
import { parseBoolFlag } from '#utils';
export function registerCategoriesCommand(program: Command) {
const categories = program
+3 -3
View File
@@ -1,9 +1,9 @@
import * as api from '@actual-app/api';
import type { Command } from 'commander';
import { withConnection } from '../connection';
import { printOutput } from '../output';
import { parseBoolFlag } from '../utils';
import { withConnection } from '#connection';
import { printOutput } from '#output';
import { parseBoolFlag } from '#utils';
export function registerCategoryGroupsCommand(program: Command) {
const groups = program
+2 -2
View File
@@ -1,8 +1,8 @@
import * as api from '@actual-app/api';
import type { Command } from 'commander';
import { withConnection } from '../connection';
import { printOutput } from '../output';
import { withConnection } from '#connection';
import { printOutput } from '#output';
export function registerPayeesCommand(program: Command) {
const payees = program.command('payees').description('Manage payees');
+3 -3
View File
@@ -1,7 +1,7 @@
import * as api from '@actual-app/api';
import { Command } from 'commander';
import { printOutput } from '../output';
import { printOutput } from '#output';
import { parseOrderBy, registerQueryCommand } from './query';
@@ -21,11 +21,11 @@ vi.mock('@actual-app/api', () => {
};
});
vi.mock('../connection', () => ({
vi.mock('#connection', () => ({
withConnection: vi.fn((_opts, fn) => fn()),
}));
vi.mock('../output', () => ({
vi.mock('#output', () => ({
printOutput: vi.fn(),
}));
+4 -4
View File
@@ -1,10 +1,10 @@
import * as api from '@actual-app/api';
import type { Command } from 'commander';
import { withConnection } from '../connection';
import { readJsonInput } from '../input';
import { printOutput } from '../output';
import { isRecord, parseIntFlag } from '../utils';
import { withConnection } from '#connection';
import { readJsonInput } from '#input';
import { printOutput } from '#output';
import { isRecord, parseIntFlag } from '#utils';
/**
* Parse order-by strings like "date:desc,amount:asc,id" into
+3 -3
View File
@@ -1,9 +1,9 @@
import * as api from '@actual-app/api';
import type { Command } from 'commander';
import { withConnection } from '../connection';
import { readJsonInput } from '../input';
import { printOutput } from '../output';
import { withConnection } from '#connection';
import { readJsonInput } from '#input';
import { printOutput } from '#output';
export function registerRulesCommand(program: Command) {
const rules = program
+3 -3
View File
@@ -1,9 +1,9 @@
import * as api from '@actual-app/api';
import type { Command } from 'commander';
import { withConnection } from '../connection';
import { readJsonInput } from '../input';
import { printOutput } from '../output';
import { withConnection } from '#connection';
import { readJsonInput } from '#input';
import { printOutput } from '#output';
export function registerSchedulesCommand(program: Command) {
const schedules = program
+2 -2
View File
@@ -2,8 +2,8 @@ import * as api from '@actual-app/api';
import { Option } from 'commander';
import type { Command } from 'commander';
import { withConnection } from '../connection';
import { printOutput } from '../output';
import { withConnection } from '#connection';
import { printOutput } from '#output';
export function registerServerCommand(program: Command) {
const server = program.command('server').description('Server utilities');
+2 -2
View File
@@ -1,8 +1,8 @@
import * as api from '@actual-app/api';
import type { Command } from 'commander';
import { withConnection } from '../connection';
import { printOutput } from '../output';
import { withConnection } from '#connection';
import { printOutput } from '#output';
export function registerTagsCommand(program: Command) {
const tags = program.command('tags').description('Manage tags');
+3 -3
View File
@@ -1,9 +1,9 @@
import * as api from '@actual-app/api';
import type { Command } from 'commander';
import { withConnection } from '../connection';
import { readJsonInput } from '../input';
import { printOutput } from '../output';
import { withConnection } from '#connection';
import { readJsonInput } from '#input';
import { printOutput } from '#output';
export function registerTransactionsCommand(program: Command) {
const transactions = program
+3
View File
@@ -2,6 +2,9 @@
"name": "@actual-app/components",
"version": "0.0.1",
"license": "MIT",
"imports": {
"#tokens": "./src/tokens.ts"
},
"exports": {
"./hooks/*": "./src/hooks/*.ts",
"./icons/logo": "./src/icons/logo/index.ts",
@@ -1,6 +1,6 @@
import { useWindowSize } from 'usehooks-ts';
import { breakpoints } from '../tokens';
import { breakpoints } from '#tokens';
export function useResponsive() {
const { height, width } = useWindowSize({
+3
View File
@@ -27,7 +27,9 @@
"#mocks": "./src/mocks.tsx",
"#polyfills": "./src/polyfills.ts",
"#components/forms": "./src/components/forms/index.tsx",
"#components/banksync": "./src/components/banksync/index.tsx",
"#components/banksync/useBankSyncAccountSettings": "./src/components/banksync/useBankSyncAccountSettings.ts",
"#components/budget": "./src/components/budget/index.tsx",
"#components/budget/goals/actions": "./src/components/budget/goals/actions.ts",
"#components/budget/goals/constants": "./src/components/budget/goals/constants.ts",
"#components/budget/goals/reducer": "./src/components/budget/goals/reducer.ts",
@@ -50,6 +52,7 @@
"#components/reports/useDashboardWidgetCopyMenu": "./src/components/reports/useDashboardWidgetCopyMenu.ts",
"#components/reports/useReport": "./src/components/reports/useReport.ts",
"#components/reports/util": "./src/components/reports/util.ts",
"#components/schedules": "./src/components/schedules/index.tsx",
"#components/schedules/schedule-edit-utils": "./src/components/schedules/schedule-edit-utils.ts",
"#components/util/accountValidation": "./src/components/util/accountValidation.ts",
"#components/util/countries": "./src/components/util/countries.ts",
@@ -1,17 +1,17 @@
export { BudgetPage as Budget } from '../mobile/budget/BudgetPage';
export { BudgetPage as Budget } from '#components/mobile/budget/BudgetPage';
export { AccountsPage as Accounts } from '../mobile/accounts/AccountsPage';
export { AccountPage as Account } from '../mobile/accounts/AccountPage';
export { AccountsPage as Accounts } from '#components/mobile/accounts/AccountsPage';
export { AccountPage as Account } from '#components/mobile/accounts/AccountPage';
export { MobileRulesPage as Rules } from '../mobile/rules/MobileRulesPage';
export { MobileRuleEditPage as RuleEdit } from '../mobile/rules/MobileRuleEditPage';
export { MobileRulesPage as Rules } from '#components/mobile/rules/MobileRulesPage';
export { MobileRuleEditPage as RuleEdit } from '#components/mobile/rules/MobileRuleEditPage';
export { MobileSchedulesPage as Schedules } from '../mobile/schedules/MobileSchedulesPage';
export { MobileScheduleEditPage as ScheduleEdit } from '../mobile/schedules/MobileScheduleEditPage';
export { MobileSchedulesPage as Schedules } from '#components/mobile/schedules/MobileSchedulesPage';
export { MobileScheduleEditPage as ScheduleEdit } from '#components/mobile/schedules/MobileScheduleEditPage';
export { CategoryPage as Category } from '../mobile/budget/CategoryPage';
export { MobilePayeesPage as Payees } from '../mobile/payees/MobilePayeesPage';
export { MobilePayeeEditPage as PayeeEdit } from '../mobile/payees/MobilePayeeEditPage';
export { CategoryPage as Category } from '#components/mobile/budget/CategoryPage';
export { MobilePayeesPage as Payees } from '#components/mobile/payees/MobilePayeesPage';
export { MobilePayeeEditPage as PayeeEdit } from '#components/mobile/payees/MobilePayeeEditPage';
export { MobileBankSyncPage as BankSync } from '../mobile/banksync/MobileBankSyncPage';
export { MobileBankSyncAccountEditPage as BankSyncAccountEdit } from '../mobile/banksync/MobileBankSyncAccountEditPage';
export { MobileBankSyncPage as BankSync } from '#components/mobile/banksync/MobileBankSyncPage';
export { MobileBankSyncAccountEditPage as BankSyncAccountEdit } from '#components/mobile/banksync/MobileBankSyncAccountEditPage';
@@ -1,22 +1,22 @@
export { Budget } from '../budget';
export { Budget } from '#components/budget';
export { Schedules } from '../schedules';
export { Schedules as ScheduleEdit } from '../schedules';
export { Schedules } from '#components/schedules';
export { Schedules as ScheduleEdit } from '#components/schedules';
export { GoCardlessLink } from '../gocardless/GoCardlessLink';
export { GoCardlessLink } from '#components/gocardless/GoCardlessLink';
export { Account as Accounts } from '../accounts/Account';
export { Account } from '../accounts/Account';
export { Account as Accounts } from '#components/accounts/Account';
export { Account } from '#components/accounts/Account';
export { ManageRulesPage as Rules } from '../ManageRulesPage';
export { ManageRulesPage as RuleEdit } from '../ManageRulesPage';
export { ManagePayeesPage as Payees } from '../payees/ManagePayeesPage';
export { ManagePayeesPage as PayeeEdit } from '../payees/ManagePayeesPage';
export { ManageRulesPage as Rules } from '#components/ManageRulesPage';
export { ManageRulesPage as RuleEdit } from '#components/ManageRulesPage';
export { ManagePayeesPage as Payees } from '#components/payees/ManagePayeesPage';
export { ManagePayeesPage as PayeeEdit } from '#components/payees/ManagePayeesPage';
export { BankSync } from '../banksync';
export { BankSync } from '#components/banksync';
export { UserDirectoryPage } from '../admin/UserDirectory/UserDirectoryPage';
export { UserDirectoryPage } from '#components/admin/UserDirectory/UserDirectoryPage';
// Account component is currently used for uncategorized transactions view.
// Need to separate this to it's own component in the future.
export { Account as Category } from '../accounts/Account';
export { Account as Category } from '#components/accounts/Account';
@@ -6,7 +6,7 @@ module.exports = {
type: 'suggestion',
docs: {
description:
'Prefer subpath imports (#path/to/module) over relative backtracked imports (../path/to/module) in loot-core',
'Prefer subpath imports (#path/to/module) over relative backtracked imports (../path/to/module)',
},
fixable: 'code',
schema: [],
@@ -20,19 +20,17 @@ module.exports = {
const filenameRaw = context.getFilename();
const normalizedFilename = filenameRaw.replace(/\\/g, '/');
// Only apply to files inside packages/loot-core/src/
const lootCoreMatch = normalizedFilename.match(
/packages\/loot-core\/src\//,
);
if (!lootCoreMatch) {
// Only apply to files inside packages/*/src/
const pkgMatch = normalizedFilename.match(/packages\/[^/]+\/src\//);
if (!pkgMatch) {
return {};
}
// Get the absolute path to the src/ directory
const srcIndex =
normalizedFilename.indexOf('packages/loot-core/src/') +
'packages/loot-core/src/'.length;
const srcDir = normalizedFilename.slice(0, srcIndex);
const srcDir = normalizedFilename.slice(
0,
pkgMatch.index + pkgMatch[0].length,
);
function getSubpathImport(importSource) {
// Only transform backtracked imports
@@ -44,12 +42,18 @@ module.exports = {
const resolved = path.posix.join(fileDir, importSource);
// Check that the resolved path is inside src/
if (!resolved.startsWith(srcDir.slice(0, -1))) {
const srcRoot = srcDir.slice(0, -1);
const relativeToSrc = path.posix.relative(srcRoot, resolved);
if (
relativeToSrc === '' ||
relativeToSrc.startsWith('..') ||
path.posix.isAbsolute(relativeToSrc)
) {
return null;
}
// Get path relative to src/
let subpath = resolved.slice(srcDir.length);
let subpath = relativeToSrc;
// Strip file extensions
subpath = subpath.replace(/\.(ts|tsx|js|jsx)$/, '');
+48
View File
@@ -12,6 +12,54 @@
"README.md"
],
"type": "module",
"imports": {
"#account-db": "./src/account-db.js",
"#load-config": "./src/load-config.js",
"#migrations": "./src/migrations.ts",
"#accounts/*": "./src/accounts/*.js",
"#app-gocardless/banks/bank.interface": "./src/app-gocardless/banks/bank.interface.ts",
"#app-gocardless/banks/*": "./src/app-gocardless/banks/*.js",
"#app-gocardless/errors": "./src/app-gocardless/errors.ts",
"#app-gocardless/gocardless-node.types": "./src/app-gocardless/gocardless-node.types.ts",
"#app-gocardless/gocardless.types": "./src/app-gocardless/gocardless.types.ts",
"#app-gocardless/services/*": "./src/app-gocardless/services/*.ts",
"#app-gocardless/services/tests/*": "./src/app-gocardless/services/tests/*.js",
"#app-gocardless/util/*": "./src/app-gocardless/util/*.js",
"#app-gocardless/*": "./src/app-gocardless/*.js",
"#app-pluggyai/*": "./src/app-pluggyai/*.js",
"#app-simplefin/*": "./src/app-simplefin/*.js",
"#app-sync/services/*": "./src/app-sync/services/*.js",
"#app-sync/*": "./src/app-sync/*.js",
"#scripts/*": "./src/scripts/*.js",
"#services/*": "./src/services/*.js",
"#util/title": "./src/util/title/index.js",
"#util/*": "./src/util/*.ts"
},
"publishConfig": {
"imports": {
"#account-db": "./build/src/account-db.js",
"#load-config": "./build/src/load-config.js",
"#migrations": "./build/src/migrations.js",
"#accounts/*": "./build/src/accounts/*.js",
"#app-gocardless/banks/bank.interface": "./build/src/app-gocardless/banks/bank.interface.js",
"#app-gocardless/banks/*": "./build/src/app-gocardless/banks/*.js",
"#app-gocardless/errors": "./build/src/app-gocardless/errors.js",
"#app-gocardless/gocardless-node.types": "./build/src/app-gocardless/gocardless-node.types.js",
"#app-gocardless/gocardless.types": "./build/src/app-gocardless/gocardless.types.js",
"#app-gocardless/services/*": "./build/src/app-gocardless/services/*.js",
"#app-gocardless/services/tests/*": "./build/src/app-gocardless/services/tests/*.js",
"#app-gocardless/util/*": "./build/src/app-gocardless/util/*.js",
"#app-gocardless/*": "./build/src/app-gocardless/*.js",
"#app-pluggyai/*": "./build/src/app-pluggyai/*.js",
"#app-simplefin/*": "./build/src/app-simplefin/*.js",
"#app-sync/services/*": "./build/src/app-sync/services/*.js",
"#app-sync/*": "./build/src/app-sync/*.js",
"#scripts/*": "./build/src/scripts/*.js",
"#services/*": "./build/src/services/*.js",
"#util/title": "./build/src/util/title/index.js",
"#util/*": "./build/src/util/*.js"
}
},
"scripts": {
"start": "yarn build && node build/app",
"start-monitor": "nodemon --exec 'yarn build && node build/app' --ignore './build/**/*' --ext 'ts,js' build/app",
+4 -4
View File
@@ -5,13 +5,13 @@ import {
clearExpiredSessions,
getAccountDb,
listLoginMethods,
} from '../account-db';
import { config } from '../load-config';
} from '#account-db';
import { config } from '#load-config';
import {
getUserByUsername,
transferAllFilesFromUser,
} from '../services/user-service';
import { TOKEN_EXPIRATION_NEVER } from '../util/validate-user';
} from '#services/user-service';
import { TOKEN_EXPIRATION_NEVER } from '#util/validate-user';
import { checkPassword } from './password';
@@ -1,9 +1,9 @@
import * as bcrypt from 'bcrypt';
import { v4 as uuidv4 } from 'uuid';
import { clearExpiredSessions, getAccountDb } from '../account-db';
import { config } from '../load-config';
import { TOKEN_EXPIRATION_NEVER } from '../util/validate-user';
import { clearExpiredSessions, getAccountDb } from '#account-db';
import { config } from '#load-config';
import { TOKEN_EXPIRATION_NEVER } from '#util/validate-user';
function isValidPassword(password) {
return password != null && password !== '';
@@ -2,11 +2,11 @@ import path from 'path';
import express from 'express';
import { sha256String } from '../util/hash';
import { sha256String } from '#util/hash';
import {
requestLoggerMiddleware,
validateSessionMiddleware,
} from '../util/middlewares';
} from '#util/middlewares';
import {
AccountNotLinkedToRequisition,
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,8 +1,11 @@
import type { Balance, Transaction } from '../gocardless-node.types';
import type {
Balance,
Transaction,
} from '#app-gocardless/gocardless-node.types';
import type {
DetailedAccountWithInstitution,
NormalizedAccountDetails,
} from '../gocardless.types';
} from '#app-gocardless/gocardless.types';
type TransactionExtended = Transaction & {
date?: string;
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,4 +1,4 @@
import { title } from '../../util/title/index';
import { title } from '#util/title';
import Fallback from './integration-bank';
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,5 +1,5 @@
import { formatPayeeName } from '../../util/payee-name';
import { title } from '../../util/title/index';
import { formatPayeeName } from '#util/payee-name';
import { title } from '#util/title';
import Fallback from './integration-bank';
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,11 +1,11 @@
import * as d from 'date-fns';
import { formatPayeeName } from '../../util/payee-name';
import {
amountToInteger,
printIban,
sortByBookingDateOrValueDate,
} from '../utils';
} from '#app-gocardless/utils';
import { formatPayeeName } from '#util/payee-name';
const SORTED_BALANCE_TYPE_LIST = [
'closingBooked',
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,5 +1,5 @@
import { formatPayeeName } from '../../util/payee-name';
import { title } from '../../util/title/index';
import { formatPayeeName } from '#util/payee-name';
import { title } from '#util/title';
import Fallback from './integration-bank';
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,4 +1,4 @@
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
@@ -1,7 +1,7 @@
/**
* Credit for this code goes to Nebukadneza at https://github.com/Nebukadneza
*/
import { amountToInteger } from '../utils';
import { amountToInteger } from '#app-gocardless/utils';
import Fallback from './integration-bank';
/** @type {import('./bank.interface').IBank} */
@@ -1,5 +1,5 @@
import { mockTransactionAmount } from '../../services/tests/fixtures';
import Abanca from '../abanca_caglesmm';
import Abanca from '#app-gocardless/banks/abanca_caglesmm';
import { mockTransactionAmount } from '#app-gocardless/services/tests/fixtures';
describe('Abanca', () => {
describe('#normalizeTransaction', () => {
@@ -1,4 +1,4 @@
import AbnamroAbnanl2a from '../abnamro_abnanl2a';
import AbnamroAbnanl2a from '#app-gocardless/banks/abnamro_abnanl2a';
describe('AbnamroAbnanl2a', () => {
describe('#normalizeTransaction', () => {
@@ -1,4 +1,4 @@
import Sabadell from '../bancsabadell_bsabesbbb';
import Sabadell from '#app-gocardless/banks/bancsabadell_bsabesbbb';
describe('BancSabadell', () => {
describe('#normalizeTransaction', () => {
@@ -1,5 +1,5 @@
import { mockTransactionAmount } from '../../services/tests/fixtures';
import Belfius from '../belfius_gkccbebb';
import Belfius from '#app-gocardless/banks/belfius_gkccbebb';
import { mockTransactionAmount } from '#app-gocardless/services/tests/fixtures';
describe('Belfius', () => {
describe('#normalizeTransaction', () => {
@@ -1,4 +1,4 @@
import BoursoBank from '../boursobank_bousfrppxxx';
import BoursoBank from '#app-gocardless/banks/boursobank_bousfrppxxx';
describe('BoursoBank', () => {
describe('#normalizeTransaction', () => {
@@ -1,4 +1,4 @@
import BperRetail from '../bper_retail_bpmoit22';
import BperRetail from '#app-gocardless/banks/bper_retail_bpmoit22';
const bookingDate = '2025-09-17';
@@ -1,4 +1,4 @@
import CBCcregbebb from '../cbc_cregbebb';
import CBCcregbebb from '#app-gocardless/banks/cbc_cregbebb';
describe('cbc_cregbebb', () => {
describe('#normalizeTransaction', () => {
@@ -1,4 +1,4 @@
import CommerzbankCobadeff from '../commerzbank_cobadeff';
import CommerzbankCobadeff from '#app-gocardless/banks/commerzbank_cobadeff';
describe('CommerzbankCobadeff', () => {
describe('#normalizeTransaction', () => {
@@ -1,5 +1,5 @@
import { mockTransactionAmount } from '../../services/tests/fixtures';
import EasybankBawaatww from '../easybank_bawaatww';
import EasybankBawaatww from '#app-gocardless/banks/easybank_bawaatww';
import { mockTransactionAmount } from '#app-gocardless/services/tests/fixtures';
describe('easybank', () => {
describe('#normalizeTransaction', () => {
@@ -1,4 +1,4 @@
import Fortuneo from '../fortuneo_ftnofrp1xxx';
import Fortuneo from '#app-gocardless/banks/fortuneo_ftnofrp1xxx';
describe('Fortuneo', () => {
describe('#normalizeTransaction', () => {
@@ -1,4 +1,4 @@
import IngIngddeff from '../ing_ingddeff';
import IngIngddeff from '#app-gocardless/banks/ing_ingddeff';
describe('IngIngddeff', () => {
describe('#normalizeAccount', () => {
@@ -1,5 +1,5 @@
import { mockTransactionAmount } from '../../services/tests/fixtures';
import IngPlIngbplpw from '../ing_pl_ingbplpw';
import IngPlIngbplpw from '#app-gocardless/banks/ing_pl_ingbplpw';
import { mockTransactionAmount } from '#app-gocardless/services/tests/fixtures';
describe('IngPlIngbplpw', () => {
describe('#normalizeAccount', () => {
@@ -1,8 +1,8 @@
import IntegrationBank from '#app-gocardless/banks/integration-bank';
import {
mockExtendAccountsAboutInstitutions,
mockInstitution,
} from '../../services/tests/fixtures';
import IntegrationBank from '../integration-bank';
} from '#app-gocardless/services/tests/fixtures';
describe('IntegrationBank', () => {
describe('normalizeAccount', () => {
@@ -1,4 +1,4 @@
import KBCkredbebb from '../kbc_kredbebb';
import KBCkredbebb from '#app-gocardless/banks/kbc_kredbebb';
describe('kbc_kredbebb', () => {
describe('#normalizeTransaction', () => {
@@ -1,4 +1,4 @@
import LhvLhvbee22 from '../lhv_lhvbee22';
import LhvLhvbee22 from '#app-gocardless/banks/lhv_lhvbee22';
describe('#normalizeTransaction', () => {
const bookedCardTransaction = {
@@ -1,4 +1,4 @@
import MbankRetailBrexplpw from '../mbank_retail_brexplpw';
import MbankRetailBrexplpw from '#app-gocardless/banks/mbank_retail_brexplpw';
describe('MbankRetailBrexplpw', () => {
describe('#normalizeAccount', () => {
@@ -1,5 +1,5 @@
import { mockTransactionAmount } from '../../services/tests/fixtures';
import Nationwide from '../nationwide_naiagb21';
import Nationwide from '#app-gocardless/banks/nationwide_naiagb21';
import { mockTransactionAmount } from '#app-gocardless/services/tests/fixtures';
describe('Nationwide', () => {
describe('#normalizeTransaction', () => {
@@ -1,4 +1,4 @@
import NbgEthngraaxxx from '../nbg_ethngraaxxx';
import NbgEthngraaxxx from '#app-gocardless/banks/nbg_ethngraaxxx';
describe('NbgEthngraaxxx', () => {
describe('#normalizeTransaction', () => {
@@ -1,5 +1,5 @@
import { mockTransactionAmount } from '../../services/tests/fixtures';
import RaiffeisenAtRzbaatww from '../raiffeisen_at_rzbaatww';
import RaiffeisenAtRzbaatww from '#app-gocardless/banks/raiffeisen_at_rzbaatww';
import { mockTransactionAmount } from '#app-gocardless/services/tests/fixtures';
describe('raiffeisen_at', () => {
describe('#normalizeTransaction', () => {
@@ -1,4 +1,4 @@
import RevolutRevolt21 from '../revolut_revolt21';
import RevolutRevolt21 from '#app-gocardless/banks/revolut_revolt21';
describe('RevolutRevolt21', () => {
describe('#normalizeTransaction', () => {
@@ -1,4 +1,4 @@
import SandboxfinanceSfin0000 from '../sandboxfinance_sfin0000';
import SandboxfinanceSfin0000 from '#app-gocardless/banks/sandboxfinance_sfin0000';
describe('SandboxfinanceSfin0000', () => {
describe('#normalizeAccount', () => {
@@ -1,4 +1,4 @@
import SpkMarburgBiedenkopfHeladef1mar from '../spk_marburg_biedenkopf_heladef1mar';
import SpkMarburgBiedenkopfHeladef1mar from '#app-gocardless/banks/spk_marburg_biedenkopf_heladef1mar';
describe('SpkMarburgBiedenkopfHeladef1mar', () => {
describe('#normalizeAccount', () => {
@@ -1,4 +1,4 @@
import SskDusseldorfDussdeddxxx from '../ssk_dusseldorf_dussdeddxxx';
import SskDusseldorfDussdeddxxx from '#app-gocardless/banks/ssk_dusseldorf_dussdeddxxx';
describe('ssk_dusseldorf_dussdeddxxx', () => {
let consoleSpy;
@@ -1,4 +1,4 @@
import SwedbankHabaLV22 from '../swedbank_habalv22';
import SwedbankHabaLV22 from '#app-gocardless/banks/swedbank_habalv22';
describe('#normalizeTransaction', () => {
const bookedCardTransaction = {
@@ -1,5 +1,5 @@
import { mockTransactionAmount } from '../../services/tests/fixtures';
import Virgin from '../virgin_nrnbgb22';
import Virgin from '#app-gocardless/banks/virgin_nrnbgb22';
import { mockTransactionAmount } from '#app-gocardless/services/tests/fixtures';
describe('Virgin', () => {
describe('#normalizeTransaction', () => {
@@ -1,4 +1,4 @@
import type { BrandedId } from '../util/types';
import type { BrandedId } from '#util/types';
export type GoCardlessAccountId = BrandedId<'gocardless-account'>;
export type GoCardlessRequisitionId = BrandedId<'gocardless-requisition'>;
@@ -7,8 +7,11 @@ import type {
GoCardlessRequisitionId,
Institution,
Requisition,
} from '../gocardless-node.types';
import type { GetBalances, GetTransactionsResponse } from '../gocardless.types';
} from '#app-gocardless/gocardless-node.types';
import type {
GetBalances,
GetTransactionsResponse,
} from '#app-gocardless/gocardless.types';
const BASE_URL = 'https://bankaccountdata.gocardless.com/api/v2';
const ALLOWED_ORIGIN = new URL(BASE_URL).origin;
@@ -1,8 +1,10 @@
import { v4 as uuidv4 } from 'uuid';
import { SecretName, secretsService } from '../../services/secrets-service';
import { BankFactory, isSpecialContinuousAccessBank } from '../bank-factory';
import type { IBank } from '../banks/bank.interface';
import {
BankFactory,
isSpecialContinuousAccessBank,
} from '#app-gocardless/bank-factory';
import type { IBank } from '#app-gocardless/banks/bank.interface';
import {
AccessDeniedError,
AccountNotLinkedToRequisition,
@@ -15,7 +17,7 @@ import {
ResourceSuspended,
ServiceError,
UnknownError,
} from '../errors';
} from '#app-gocardless/errors';
import type {
Balance,
GoCardlessAccountId,
@@ -25,7 +27,7 @@ import type {
Institution,
Requisition,
Transaction,
} from '../gocardless-node.types';
} from '#app-gocardless/gocardless-node.types';
import type {
CreateRequisitionParams,
DetailedAccount,
@@ -35,7 +37,8 @@ import type {
GetTransactionsResponse,
NormalizedAccountDetails,
TransactionWithBookedStatus,
} from '../gocardless.types';
} from '#app-gocardless/gocardless.types';
import { SecretName, secretsService } from '#services/secrets-service';
import type { AccountDetailsResponse, TokenResponse } from './gocardless-api';
import { GoCardlessApi, GoCardlessApiError } from './gocardless-api';
@@ -10,13 +10,13 @@ import {
ResourceSuspended,
ServiceError,
UnknownError,
} from '../../errors';
import { GoCardlessApiError } from '../gocardless-api';
} from '#app-gocardless/errors';
import { GoCardlessApiError } from '#app-gocardless/services/gocardless-api';
import {
client,
goCardlessService,
handleGoCardlessError,
} from '../gocardless-service';
} from '#app-gocardless/services/gocardless-service';
import {
mockAccountDetails,
@@ -1,5 +1,5 @@
import { BankFactory, banks } from '../bank-factory';
import IntegrationBank from '../banks/integration-bank';
import { BankFactory, banks } from '#app-gocardless/bank-factory';
import IntegrationBank from '#app-gocardless/banks/integration-bank';
describe('BankFactory', () => {
it.each(banks.flatMap(bank => bank.institutionIds))(
@@ -1,5 +1,5 @@
import { mockTransactionAmount } from '../services/tests/fixtures';
import { sortByBookingDateOrValueDate } from '../utils';
import { mockTransactionAmount } from '#app-gocardless/services/tests/fixtures';
import { sortByBookingDateOrValueDate } from '#app-gocardless/utils';
describe('utils', () => {
describe('#sortByBookingDate', () => {
@@ -1,11 +1,11 @@
import express from 'express';
import { handleError } from '../app-gocardless/util/handle-error';
import { SecretName, secretsService } from '../services/secrets-service';
import { handleError } from '#app-gocardless/util/handle-error';
import { SecretName, secretsService } from '#services/secrets-service';
import {
requestLoggerMiddleware,
validateSessionMiddleware,
} from '../util/middlewares';
} from '#util/middlewares';
import { pluggyaiService } from './pluggyai-service';
@@ -1,6 +1,6 @@
import { PluggyClient } from 'pluggy-sdk';
import { SecretName, secretsService } from '../services/secrets-service';
import { SecretName, secretsService } from '#services/secrets-service';
let pluggyClient = null;
@@ -2,12 +2,12 @@ import https from 'https';
import express from 'express';
import { handleError } from '../app-gocardless/util/handle-error';
import { SecretName, secretsService } from '../services/secrets-service';
import { handleError } from '#app-gocardless/util/handle-error';
import { SecretName, secretsService } from '#services/secrets-service';
import {
requestLoggerMiddleware,
validateSessionMiddleware,
} from '../util/middlewares';
} from '#util/middlewares';
const app = express();
export { app as handlers };
@@ -1,5 +1,5 @@
import { getAccountDb, isAdmin } from '../../account-db';
import { FileNotFound, GenericFileError } from '../errors';
import { getAccountDb, isAdmin } from '#account-db';
import { FileNotFound, GenericFileError } from '#app-sync/errors';
class FileBase {
constructor(
@@ -1,8 +1,12 @@
import crypto from 'node:crypto';
import { getAccountDb } from '../../../account-db';
import { FileNotFound } from '../../errors';
import { File, FilesService, FileUpdate } from '../../services/files-service'; // Adjust the path as necessary
import { getAccountDb } from '#account-db';
import { FileNotFound } from '#app-sync/errors';
import {
File,
FilesService,
FileUpdate,
} from '#app-sync/services/files-service'; // Adjust the path as necessary
describe('FilesService', () => {
let filesService;
let accountDb;
@@ -2,8 +2,8 @@ import {
disableOpenID,
getActiveLoginMethod,
needsBootstrap,
} from '../account-db';
import { promptPassword } from '../util/prompt';
} from '#account-db';
import { promptPassword } from '#util/prompt';
if (needsBootstrap()) {
console.log('System needs to be bootstrapped first. OpenID is not enabled.');
@@ -2,8 +2,8 @@ import {
enableOpenID,
getActiveLoginMethod,
needsBootstrap,
} from '../account-db';
import { config } from '../load-config';
} from '#account-db';
import { config } from '#load-config';
if (needsBootstrap()) {
console.log(
@@ -1,4 +1,4 @@
import { config } from '../load-config';
import { config } from '#load-config';
const protocol =
config.get('https.key') && config.get('https.cert') ? 'https' : 'http';
@@ -1,6 +1,6 @@
import { bootstrap, needsBootstrap } from '../account-db';
import { changePassword } from '../accounts/password';
import { promptPassword } from '../util/prompt';
import { bootstrap, needsBootstrap } from '#account-db';
import { changePassword } from '#accounts/password';
import { promptPassword } from '#util/prompt';
if (needsBootstrap()) {
console.log(
@@ -1,4 +1,4 @@
import { run } from '../migrations';
import { run } from '#migrations';
const direction = process.argv[2] || 'up';
@@ -1,6 +1,6 @@
import createDebug from 'debug';
import { getAccountDb } from '../account-db';
import { getAccountDb } from '#account-db';
/**
* An enum of valid secret names.
@@ -1,4 +1,4 @@
import { getAccountDb } from '../account-db';
import { getAccountDb } from '#account-db';
export function getUserByUsername(userName) {
if (!userName || typeof userName !== 'string') {
+1 -1
View File
@@ -1,6 +1,6 @@
import { join, resolve } from 'node:path';
import { config } from '../load-config';
import { config } from '#load-config';
import type { BrandedId } from './types';
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Transaction } from '../app-gocardless/gocardless-node.types';
import type { Transaction } from '#app-gocardless/gocardless-node.types';
import { title } from './title/index';
@@ -1,8 +1,8 @@
import type { Request, Response } from 'express';
import ipaddr from 'ipaddr.js';
import { getSession } from '../account-db';
import { config } from '../load-config';
import { getSession } from '#account-db';
import { config } from '#load-config';
export const TOKEN_EXPIRATION_NEVER = -1;
const MS_PER_SECOND = 1000;
+6
View File
@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [MatissJanis]
---
Standardise module imports across all packages