diff --git a/packages/loot-core/.babelrc b/packages/loot-core/.babelrc index 0b04f17c1d..3313ff9ef0 100644 --- a/packages/loot-core/.babelrc +++ b/packages/loot-core/.babelrc @@ -1,4 +1,3 @@ { - "presets": ["@babel/preset-env", "@babel/preset-typescript"], - "ignore": ["**/__mocks__"] + "presets": ["@babel/preset-env", "@babel/preset-typescript"] } diff --git a/packages/loot-core/package.json b/packages/loot-core/package.json index 9ef7ac840b..baa90b2708 100644 --- a/packages/loot-core/package.json +++ b/packages/loot-core/package.json @@ -42,9 +42,11 @@ "@babel/core": "~7.14.3", "@babel/preset-env": "^7.20.2", "@babel/preset-typescript": "^7.20.2", + "@types/better-sqlite3": "^7.6.4", "@types/jest": "^27.5.0", "@types/jlongster__sql.js": "npm:@types/sql.js@latest", "@types/node-ipc": "^9.2.0", + "@types/pegjs": "^0.10.3", "@types/webpack": "^5.28.0", "adm-zip": "^0.5.9", "babel-loader": "^8.0.6", @@ -67,6 +69,7 @@ "throttleit": "^1.0.0", "ts-jest": "^27.0.0", "ts-node": "^10.7.0", + "ts-protoc-gen": "^0.15.0", "typescript": "^4.6.4", "uuid": "3.3.2", "webpack": "^4.41.2", diff --git a/packages/loot-core/src/client/actions/account.js b/packages/loot-core/src/client/actions/account.ts similarity index 96% rename from packages/loot-core/src/client/actions/account.js rename to packages/loot-core/src/client/actions/account.ts index 55686982b6..6d9ef4350f 100644 --- a/packages/loot-core/src/client/actions/account.js +++ b/packages/loot-core/src/client/actions/account.ts @@ -227,12 +227,3 @@ export function markAccountRead(accountId) { accountId: accountId, }; } - -export function getBanks() { - return async dispatch => { - dispatch({ - type: constants.LOAD_BANKS, - banks: await send('banks'), - }); - }; -} diff --git a/packages/loot-core/src/client/actions/app.js b/packages/loot-core/src/client/actions/app.ts similarity index 100% rename from packages/loot-core/src/client/actions/app.js rename to packages/loot-core/src/client/actions/app.ts diff --git a/packages/loot-core/src/client/actions/backups.js b/packages/loot-core/src/client/actions/backups.ts similarity index 100% rename from packages/loot-core/src/client/actions/backups.js rename to packages/loot-core/src/client/actions/backups.ts diff --git a/packages/loot-core/src/client/actions/budgets.js b/packages/loot-core/src/client/actions/budgets.ts similarity index 97% rename from packages/loot-core/src/client/actions/budgets.js rename to packages/loot-core/src/client/actions/budgets.ts index 8684e3cf42..aea8292dfb 100644 --- a/packages/loot-core/src/client/actions/budgets.js +++ b/packages/loot-core/src/client/actions/budgets.ts @@ -130,7 +130,7 @@ export function deleteBudget(id, cloudFileId) { }; } -export function createBudget({ testMode, demoMode } = {}) { +export function createBudget({ testMode = false, demoMode = false } = {}) { return async (dispatch, getState) => { dispatch( setAppState({ @@ -165,6 +165,7 @@ export function importBudget(filepath, type) { dispatch(closeModal()); await dispatch(loadPrefs()); + // @ts-expect-error __history needs refinement window.__history.push('/budget'); }; } @@ -193,7 +194,7 @@ export function closeAndDownloadBudget(cloudFileId) { }; } -export function downloadBudget(cloudFileId, { replace } = {}) { +export function downloadBudget(cloudFileId, { replace = false } = {}) { return async dispatch => { dispatch(setAppState({ loadingText: 'Downloading...' })); diff --git a/packages/loot-core/src/client/actions/debug.js b/packages/loot-core/src/client/actions/debug.ts similarity index 100% rename from packages/loot-core/src/client/actions/debug.js rename to packages/loot-core/src/client/actions/debug.ts diff --git a/packages/loot-core/src/client/actions/index.js b/packages/loot-core/src/client/actions/index.ts similarity index 100% rename from packages/loot-core/src/client/actions/index.js rename to packages/loot-core/src/client/actions/index.ts diff --git a/packages/loot-core/src/client/actions/modals.js b/packages/loot-core/src/client/actions/modals.ts similarity index 100% rename from packages/loot-core/src/client/actions/modals.js rename to packages/loot-core/src/client/actions/modals.ts diff --git a/packages/loot-core/src/client/actions/notifications.js b/packages/loot-core/src/client/actions/notifications.ts similarity index 100% rename from packages/loot-core/src/client/actions/notifications.js rename to packages/loot-core/src/client/actions/notifications.ts diff --git a/packages/loot-core/src/client/actions/prefs.js b/packages/loot-core/src/client/actions/prefs.ts similarity index 100% rename from packages/loot-core/src/client/actions/prefs.js rename to packages/loot-core/src/client/actions/prefs.ts diff --git a/packages/loot-core/src/client/actions/queries.js b/packages/loot-core/src/client/actions/queries.ts similarity index 100% rename from packages/loot-core/src/client/actions/queries.js rename to packages/loot-core/src/client/actions/queries.ts diff --git a/packages/loot-core/src/client/actions/sync.js b/packages/loot-core/src/client/actions/sync.ts similarity index 90% rename from packages/loot-core/src/client/actions/sync.js rename to packages/loot-core/src/client/actions/sync.ts index 0c4a7b02fa..81ec013d32 100644 --- a/packages/loot-core/src/client/actions/sync.js +++ b/packages/loot-core/src/client/actions/sync.ts @@ -1,21 +1,10 @@ import { send } from '../../platform/client/fetch'; import { getUploadError } from '../../shared/errors'; -import * as constants from '../constants'; import { syncAccounts } from './account'; import { pushModal } from './modals'; import { loadPrefs } from './prefs'; -export function unregister() { - return async dispatch => { - const profile = await send('unregister'); - dispatch({ - type: constants.SET_PROFILE, - profile, - }); - }; -} - export function resetSync() { return async (dispatch, getState) => { let { error } = await send('sync-reset'); diff --git a/packages/loot-core/src/client/actions/transactions.js b/packages/loot-core/src/client/actions/transactions.js deleted file mode 100644 index e1cd719925..0000000000 --- a/packages/loot-core/src/client/actions/transactions.js +++ /dev/null @@ -1,12 +0,0 @@ -import { send } from '../../platform/client/fetch'; - -import { filterTransactions } from './queries'; - -export function categorize(accountId) { - return async dispatch => { - const res = await send('transactions-categorize', { accountId }); - if (res === 'ok') { - dispatch(filterTransactions(null, accountId)); - } - }; -} diff --git a/packages/loot-core/src/client/actions/user.js b/packages/loot-core/src/client/actions/user.ts similarity index 100% rename from packages/loot-core/src/client/actions/user.js rename to packages/loot-core/src/client/actions/user.ts diff --git a/packages/loot-core/src/client/data-hooks/accounts.js b/packages/loot-core/src/client/data-hooks/accounts.tsx similarity index 93% rename from packages/loot-core/src/client/data-hooks/accounts.js rename to packages/loot-core/src/client/data-hooks/accounts.tsx index 405ad6b3ba..d478bc519c 100644 --- a/packages/loot-core/src/client/data-hooks/accounts.js +++ b/packages/loot-core/src/client/data-hooks/accounts.tsx @@ -34,7 +34,7 @@ export function CachedAccounts({ children, idKey }) { return children(data); } -export function useCachedAccounts({ idKey } = {}) { +export function useCachedAccounts({ idKey }: { idKey? } = {}) { let data = useContext(AccountsContext); return idKey && data ? getAccountsById(data) : data; } diff --git a/packages/loot-core/src/client/data-hooks/payees.js b/packages/loot-core/src/client/data-hooks/payees.tsx similarity index 93% rename from packages/loot-core/src/client/data-hooks/payees.js rename to packages/loot-core/src/client/data-hooks/payees.tsx index af440ea06c..cf7b647855 100644 --- a/packages/loot-core/src/client/data-hooks/payees.js +++ b/packages/loot-core/src/client/data-hooks/payees.tsx @@ -34,7 +34,7 @@ export function CachedPayees({ children, idKey }) { return children(data); } -export function useCachedPayees({ idKey } = {}) { +export function useCachedPayees({ idKey }: { idKey? } = {}) { let data = useContext(PayeesContext); return idKey && data ? getPayeesById(data) : data; } diff --git a/packages/loot-core/src/client/data-hooks/schedules.js b/packages/loot-core/src/client/data-hooks/schedules.tsx similarity index 92% rename from packages/loot-core/src/client/data-hooks/schedules.js rename to packages/loot-core/src/client/data-hooks/schedules.tsx index 879f85b53f..4be18b40b0 100644 --- a/packages/loot-core/src/client/data-hooks/schedules.js +++ b/packages/loot-core/src/client/data-hooks/schedules.tsx @@ -21,7 +21,8 @@ function loadStatuses(schedules, onData) { }); } -export function useSchedules({ transform } = {}) { +type UseSchedulesArgs = { transform?: (v: T) => T }; +export function useSchedules({ transform }: UseSchedulesArgs = {}) { let [data, setData] = useState(null); useEffect(() => { diff --git a/packages/loot-core/src/client/reducers/account.js b/packages/loot-core/src/client/reducers/account.ts similarity index 100% rename from packages/loot-core/src/client/reducers/account.js rename to packages/loot-core/src/client/reducers/account.ts diff --git a/packages/loot-core/src/client/reducers/app.js b/packages/loot-core/src/client/reducers/app.ts similarity index 100% rename from packages/loot-core/src/client/reducers/app.js rename to packages/loot-core/src/client/reducers/app.ts diff --git a/packages/loot-core/src/client/reducers/budgets.js b/packages/loot-core/src/client/reducers/budgets.ts similarity index 100% rename from packages/loot-core/src/client/reducers/budgets.js rename to packages/loot-core/src/client/reducers/budgets.ts diff --git a/packages/loot-core/src/client/reducers/debug.js b/packages/loot-core/src/client/reducers/debug.ts similarity index 100% rename from packages/loot-core/src/client/reducers/debug.js rename to packages/loot-core/src/client/reducers/debug.ts diff --git a/packages/loot-core/src/client/reducers/index.js b/packages/loot-core/src/client/reducers/index.ts similarity index 100% rename from packages/loot-core/src/client/reducers/index.js rename to packages/loot-core/src/client/reducers/index.ts diff --git a/packages/loot-core/src/client/reducers/modals.js b/packages/loot-core/src/client/reducers/modals.ts similarity index 100% rename from packages/loot-core/src/client/reducers/modals.js rename to packages/loot-core/src/client/reducers/modals.ts diff --git a/packages/loot-core/src/client/reducers/notifications.js b/packages/loot-core/src/client/reducers/notifications.ts similarity index 100% rename from packages/loot-core/src/client/reducers/notifications.js rename to packages/loot-core/src/client/reducers/notifications.ts diff --git a/packages/loot-core/src/client/reducers/prefs.js b/packages/loot-core/src/client/reducers/prefs.ts similarity index 100% rename from packages/loot-core/src/client/reducers/prefs.js rename to packages/loot-core/src/client/reducers/prefs.ts diff --git a/packages/loot-core/src/client/reducers/profile.js b/packages/loot-core/src/client/reducers/profile.ts similarity index 100% rename from packages/loot-core/src/client/reducers/profile.js rename to packages/loot-core/src/client/reducers/profile.ts diff --git a/packages/loot-core/src/client/reducers/queries.js b/packages/loot-core/src/client/reducers/queries.ts similarity index 100% rename from packages/loot-core/src/client/reducers/queries.js rename to packages/loot-core/src/client/reducers/queries.ts diff --git a/packages/loot-core/src/client/reducers/tutorial.js b/packages/loot-core/src/client/reducers/tutorial.ts similarity index 100% rename from packages/loot-core/src/client/reducers/tutorial.js rename to packages/loot-core/src/client/reducers/tutorial.ts diff --git a/packages/loot-core/src/client/reducers/user.js b/packages/loot-core/src/client/reducers/user.ts similarity index 100% rename from packages/loot-core/src/client/reducers/user.js rename to packages/loot-core/src/client/reducers/user.ts diff --git a/packages/loot-core/src/platform/client/fetch/index.browser.ts b/packages/loot-core/src/platform/client/fetch/index.browser.ts index 1c2602cee0..9f4c3a89c2 100644 --- a/packages/loot-core/src/platform/client/fetch/index.browser.ts +++ b/packages/loot-core/src/platform/client/fetch/index.browser.ts @@ -2,6 +2,8 @@ import { captureException, captureBreadcrumb } from '../../exceptions'; import * as uuid from '../../uuid'; import * as undo from '../undo'; +import type * as T from '.'; + let replyHandlers = new Map(); let listeners = new Map(); let messageQueue = []; @@ -133,13 +135,17 @@ function connectWorker(worker, onOpen, onError) { } } -export const init = async function (worker) { +export const init: T.Init = async function (worker) { return new Promise((resolve, reject) => connectWorker(worker, resolve, reject), ); }; -export const send = function (name, args, { catchErrors = false } = {}) { +export const send: T.Send = function ( + name, + args, + { catchErrors = false } = {}, +) { return new Promise((resolve, reject) => { uuid.v4().then(id => { replyHandlers.set(id, { resolve, reject }); @@ -156,14 +162,15 @@ export const send = function (name, args, { catchErrors = false } = {}) { globalWorker.postMessage(message); } }); - }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }) as any; }; -export const sendCatch = function (name, args) { +export const sendCatch: T.SendCatch = function (name, args) { return send(name, args, { catchErrors: true }); }; -export const listen = function (name, cb) { +export const listen: T.Listen = function (name, cb) { if (!listeners.get(name)) { listeners.set(name, []); } @@ -178,6 +185,6 @@ export const listen = function (name, cb) { }; }; -export const unlisten = function (name) { +export const unlisten: T.Unlisten = function (name) { listeners.set(name, []); }; diff --git a/packages/loot-core/src/platform/client/fetch/index.d.ts b/packages/loot-core/src/platform/client/fetch/index.d.ts new file mode 100644 index 0000000000..646890e9b0 --- /dev/null +++ b/packages/loot-core/src/platform/client/fetch/index.d.ts @@ -0,0 +1,23 @@ +import type { Handlers } from '../../../types/handlers'; + +export function init(socketName: string): Promise; +export type Init = typeof init; + +export function send( + name: K, + args?: Parameters[0], + options?: { catchErrors?: boolean }, +): ReturnType; +export type Send = typeof send; + +export function sendCatch( + name: K, + args?: Parameters[0], +): ReturnType; +export type SendCatch = typeof sendCatch; + +export function listen(name: string, cb: () => void): () => void; +export type Listen = typeof listen; + +export function unlisten(name: string): void; +export type Unlisten = typeof unlisten; diff --git a/packages/loot-core/src/platform/client/fetch/index.web.ts b/packages/loot-core/src/platform/client/fetch/index.web.ts index 50b02f12e1..db5948454a 100644 --- a/packages/loot-core/src/platform/client/fetch/index.web.ts +++ b/packages/loot-core/src/platform/client/fetch/index.web.ts @@ -1,6 +1,8 @@ import * as uuid from '../../uuid'; import * as undo from '../undo'; +import type * as T from '.'; + let replyHandlers = new Map(); let listeners = new Map(); let messageQueue = []; @@ -75,11 +77,15 @@ function connectSocket(name, onOpen) { }); } -export const init = async function (socketName) { +export const init: T.Init = async function (socketName) { return new Promise(resolve => connectSocket(socketName, resolve)); }; -export const send = function (name, args, { catchErrors = false } = {}) { +export const send: T.Send = function ( + name, + args, + { catchErrors = false } = {}, +) { return new Promise((resolve, reject) => { uuid.v4().then(id => { replyHandlers.set(id, { resolve, reject }); @@ -102,14 +108,15 @@ export const send = function (name, args, { catchErrors = false } = {}) { }); } }); - }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }) as any; }; -export const sendCatch = function (name, args) { +export const sendCatch: T.SendCatch = function (name, args) { return send(name, args, { catchErrors: true }); }; -export const listen = function (name, cb) { +export const listen: T.Listen = function (name, cb) { if (!listeners.get(name)) { listeners.set(name, []); } @@ -126,6 +133,6 @@ export const listen = function (name, cb) { }; }; -export const unlisten = function (name) { +export const unlisten: T.Unlisten = function (name) { listeners.set(name, []); }; diff --git a/packages/loot-core/src/platform/server/asyncStorage/index.d.ts b/packages/loot-core/src/platform/server/asyncStorage/index.d.ts new file mode 100644 index 0000000000..6995ad9f9e --- /dev/null +++ b/packages/loot-core/src/platform/server/asyncStorage/index.d.ts @@ -0,0 +1,20 @@ +export function init(): void; +export type Init = typeof init; + +export function getItem(key: string): Promise; +export type GetItem = typeof getItem; + +export function setItem(key: string, value: unknown): void; +export type SetItem = typeof setItem; + +export function removeItem(key: string): void; +export type RemoveItem = typeof removeItem; + +export function multiGet(keys: string[]): Promise<[string, unknown][]>; +export type MultiGet = typeof multiGet; + +export function multiSet(keyValues: [string, unknown][]): void; +export type MultiSet = typeof multiSet; + +export function multiRemove(keys: string[]): void; +export type MultiRemove = typeof multiRemove; diff --git a/packages/loot-core/src/platform/server/asyncStorage/index.electron.ts b/packages/loot-core/src/platform/server/asyncStorage/index.electron.ts index bbd325b2d4..7551acb07f 100644 --- a/packages/loot-core/src/platform/server/asyncStorage/index.electron.ts +++ b/packages/loot-core/src/platform/server/asyncStorage/index.electron.ts @@ -3,11 +3,13 @@ import { join } from 'path'; import * as lootFs from '../fs'; +import * as T from '.'; + let getStorePath = () => join(lootFs.getDataDir(), 'global-store.json'); let store; let persisted = true; -export const init = function ({ persist = true } = {}) { +export const init: T.Init = function ({ persist = true } = {}) { if (persist) { try { store = JSON.parse(fs.readFileSync(getStorePath(), 'utf8')); @@ -36,23 +38,23 @@ function _saveStore() { } } -export const getItem = function (key) { +export const getItem: T.GetItem = function (key) { return new Promise(function (resolve) { return resolve(store[key]); }); }; -export const setItem = function (key, value) { +export const setItem: T.SetItem = function (key, value) { store[key] = value; return _saveStore(); }; -export const removeItem = function (key) { +export const removeItem: T.RemoveItem = function (key) { delete store[key]; return _saveStore(); }; -export const multiGet = function (keys) { +export const multiGet: T.MultiGet = function (keys) { return new Promise(function (resolve) { return resolve( keys.map(function (key) { @@ -62,14 +64,14 @@ export const multiGet = function (keys) { }); }; -export const multiSet = function (keyValues) { +export const multiSet: T.MultiSet = function (keyValues) { keyValues.forEach(function ([key, value]) { store[key] = value; }); return _saveStore(); }; -export const multiRemove = function (keys) { +export const multiRemove: T.MultiRemove = function (keys) { keys.forEach(function (key) { delete store[key]; }); diff --git a/packages/loot-core/src/platform/server/asyncStorage/index.testing.ts b/packages/loot-core/src/platform/server/asyncStorage/index.testing.ts index 34f5182fa0..38fdc09180 100644 --- a/packages/loot-core/src/platform/server/asyncStorage/index.testing.ts +++ b/packages/loot-core/src/platform/server/asyncStorage/index.testing.ts @@ -1,22 +1,24 @@ +import * as T from '.'; + const store = {}; -export const init = function () {}; +export const init: T.Init = function () {}; -export const getItem = function (key) { +export const getItem: T.GetItem = function (key) { return new Promise(function (resolve) { return resolve(store[key]); }); }; -export const setItem = function (key, value) { +export const setItem: T.SetItem = function (key, value) { store[key] = value; }; -export const removeItem = function (key) { +export const removeItem: T.RemoveItem = function (key) { delete store[key]; }; -export const multiGet = function (keys) { +export const multiGet: T.MultiGet = function (keys) { return new Promise(function (resolve) { return resolve( keys.map(function (key) { @@ -26,13 +28,13 @@ export const multiGet = function (keys) { }); }; -export const multiSet = function (keyValues) { +export const multiSet: T.MultiSet = function (keyValues) { keyValues.forEach(function ([key, value]) { store[key] = value; }); }; -export const multiRemove = function (keys) { +export const multiRemove: T.MultiRemove = function (keys) { keys.forEach(function (key) { delete store[key]; }); diff --git a/packages/loot-core/src/platform/server/asyncStorage/index.web.ts b/packages/loot-core/src/platform/server/asyncStorage/index.web.ts index 0cab70039c..e0bde31202 100644 --- a/packages/loot-core/src/platform/server/asyncStorage/index.web.ts +++ b/packages/loot-core/src/platform/server/asyncStorage/index.web.ts @@ -1,6 +1,8 @@ import { getDatabase } from '../indexeddb'; -export const init = function () {}; +import * as T from '.'; + +export const init: T.Init = function () {}; function commit(trans) { if (trans.commit) { @@ -8,7 +10,7 @@ function commit(trans) { } } -export const getItem = async function (key) { +export const getItem: T.GetItem = async function (key) { let db = await getDatabase(); let transaction = db.transaction(['asyncStorage'], 'readonly'); @@ -22,7 +24,7 @@ export const getItem = async function (key) { }); }; -export const setItem = async function (key, value) { +export const setItem: T.SetItem = async function (key, value) { let db = await getDatabase(); let transaction = db.transaction(['asyncStorage'], 'readwrite'); @@ -36,7 +38,7 @@ export const setItem = async function (key, value) { }); }; -export const removeItem = async function (key) { +export const removeItem: T.RemoveItem = async function (key) { let db = await getDatabase(); let transaction = db.transaction(['asyncStorage'], 'readwrite'); @@ -50,7 +52,7 @@ export const removeItem = async function (key) { }); }; -export const multiGet = async function (keys) { +export const multiGet: T.MultiGet = async function (keys) { let db = await getDatabase(); let transaction = db.transaction(['asyncStorage'], 'readonly'); @@ -58,7 +60,7 @@ export const multiGet = async function (keys) { let promise = Promise.all( keys.map(key => { - return new Promise((resolve, reject) => { + return new Promise<[string, unknown]>((resolve, reject) => { let req = objectStore.get(key); req.onerror = e => reject(e); req.onsuccess = e => resolve([key, e.target.result]); @@ -70,7 +72,7 @@ export const multiGet = async function (keys) { return promise; }; -export const multiSet = async function (keyValues) { +export const multiSet: T.MultiSet = async function (keyValues) { let db = await getDatabase(); let transaction = db.transaction(['asyncStorage'], 'readwrite'); @@ -90,7 +92,7 @@ export const multiSet = async function (keyValues) { return promise; }; -export const multiRemove = async function (keys) { +export const multiRemove: T.MultiRemove = async function (keys) { let db = await getDatabase(); let transaction = db.transaction(['asyncStorage'], 'readwrite'); diff --git a/packages/loot-core/src/platform/server/fetch/index.d.ts b/packages/loot-core/src/platform/server/fetch/index.d.ts index 5d610e61bd..67e3001dc4 100644 --- a/packages/loot-core/src/platform/server/fetch/index.d.ts +++ b/packages/loot-core/src/platform/server/fetch/index.d.ts @@ -1,4 +1,7 @@ -export function fetch(input: RequestInfo | URL): Promise; +export function fetch( + input: RequestInfo | URL, + options?: unknown, +): Promise; export function fetchBinary( input: RequestInfo | URL, filepath: string, diff --git a/packages/loot-core/src/platform/server/log/index.api.ts b/packages/loot-core/src/platform/server/log/index.api.ts index 15f9a99164..cd368c8453 100644 --- a/packages/loot-core/src/platform/server/log/index.api.ts +++ b/packages/loot-core/src/platform/server/log/index.api.ts @@ -1,6 +1,6 @@ -import type Logger from '.'; +import type * as T from '.'; -const logger: Logger = { +const logger: T.Logger = { info: (...args) => { console.log(...args); }, diff --git a/packages/loot-core/src/platform/server/log/index.d.ts b/packages/loot-core/src/platform/server/log/index.d.ts index 6a08454159..00b23ca4db 100644 --- a/packages/loot-core/src/platform/server/log/index.d.ts +++ b/packages/loot-core/src/platform/server/log/index.d.ts @@ -1,9 +1,10 @@ import { type Transports } from 'electron-log'; -interface Logger { +export interface Logger { info(...args: unknown[]): void; warn(...args: unknown[]): void; transports?: Transports; } -export default Logger; +const logger: Logger; +export default logger; diff --git a/packages/loot-core/src/platform/server/log/index.electron.ts b/packages/loot-core/src/platform/server/log/index.electron.ts index 7232b970bd..9aba4ec63f 100644 --- a/packages/loot-core/src/platform/server/log/index.electron.ts +++ b/packages/loot-core/src/platform/server/log/index.electron.ts @@ -1,6 +1,6 @@ import logger from 'electron-log'; -import type Logger from '.'; +import type * as T from '.'; if (logger.transports) { logger.transports.file.appName = 'Actual'; @@ -9,4 +9,4 @@ if (logger.transports) { logger.transports.console.level = false; } -export default logger as Logger; +export default logger as T.Logger; diff --git a/packages/loot-core/src/platform/server/log/index.web.ts b/packages/loot-core/src/platform/server/log/index.web.ts index 15f9a99164..cd368c8453 100644 --- a/packages/loot-core/src/platform/server/log/index.web.ts +++ b/packages/loot-core/src/platform/server/log/index.web.ts @@ -1,6 +1,6 @@ -import type Logger from '.'; +import type * as T from '.'; -const logger: Logger = { +const logger: T.Logger = { info: (...args) => { console.log(...args); }, diff --git a/packages/loot-core/src/platform/server/sqlite/index.d.ts b/packages/loot-core/src/platform/server/sqlite/index.d.ts index 0460c0113d..3c5821903a 100644 --- a/packages/loot-core/src/platform/server/sqlite/index.d.ts +++ b/packages/loot-core/src/platform/server/sqlite/index.d.ts @@ -1,3 +1,5 @@ +import { type Database } from 'better-sqlite3'; + export async function init(): unknown; export function _getModule(): SqlJsStatic; @@ -7,15 +9,15 @@ export function prepare(db, sql): unknown; export function runQuery( db: unknown, sql: string, - params?: string[], + params?: (string | number)[], fetchAll?: false, ): { changes: unknown }; -export function runQuery( +export function runQuery( db: unknown, sql: string, - params: string[], + params: (string | number)[], fetchAll: true, -): unknown[]; +): T[]; export function execQuery(db, sql): void; @@ -23,7 +25,7 @@ export function transaction(db, fn): unknown; export async function asyncTransaction(db, fn): unknown; -export async function openDatabase(pathOrBuffer?: string | Buffer): unknown; +export async function openDatabase(pathOrBuffer?: string | Buffer): Database; export function closeDatabase(db): void; diff --git a/packages/loot-core/src/platform/server/sqlite/index.electron.ts b/packages/loot-core/src/platform/server/sqlite/index.electron.ts index ac4224640e..7f03188dc3 100644 --- a/packages/loot-core/src/platform/server/sqlite/index.electron.ts +++ b/packages/loot-core/src/platform/server/sqlite/index.electron.ts @@ -87,10 +87,10 @@ export function openDatabase(pathOrBuffer) { let db = new Database(pathOrBuffer); // Define Unicode-aware LOWER and UPPER implementation. // This is necessary because better-sqlite3 uses SQLite build without ICU support. - db.function('UNICODE_LOWER', { deterministic: true }, arg => + db.function('UNICODE_LOWER', { deterministic: true }, (arg: string | null) => arg?.toLowerCase(), ); - db.function('UNICODE_UPPER', { deterministic: true }, arg => + db.function('UNICODE_UPPER', { deterministic: true }, (arg: string | null) => arg?.toUpperCase(), ); return db; diff --git a/packages/loot-core/src/server/__mocks__/post.js b/packages/loot-core/src/server/__mocks__/post.ts similarity index 100% rename from packages/loot-core/src/server/__mocks__/post.js rename to packages/loot-core/src/server/__mocks__/post.ts diff --git a/packages/loot-core/src/server/accounts/__snapshots__/parse-file.test.js.snap b/packages/loot-core/src/server/accounts/__snapshots__/parse-file.test.ts.snap similarity index 100% rename from packages/loot-core/src/server/accounts/__snapshots__/parse-file.test.js.snap rename to packages/loot-core/src/server/accounts/__snapshots__/parse-file.test.ts.snap diff --git a/packages/loot-core/src/server/accounts/__snapshots__/sync.test.js.snap b/packages/loot-core/src/server/accounts/__snapshots__/sync.test.ts.snap similarity index 100% rename from packages/loot-core/src/server/accounts/__snapshots__/sync.test.js.snap rename to packages/loot-core/src/server/accounts/__snapshots__/sync.test.ts.snap diff --git a/packages/loot-core/src/server/accounts/__snapshots__/transaction-rules.test.js.snap b/packages/loot-core/src/server/accounts/__snapshots__/transaction-rules.test.ts.snap similarity index 100% rename from packages/loot-core/src/server/accounts/__snapshots__/transaction-rules.test.js.snap rename to packages/loot-core/src/server/accounts/__snapshots__/transaction-rules.test.ts.snap diff --git a/packages/loot-core/src/server/accounts/__snapshots__/transfer.test.js.snap b/packages/loot-core/src/server/accounts/__snapshots__/transfer.test.ts.snap similarity index 100% rename from packages/loot-core/src/server/accounts/__snapshots__/transfer.test.js.snap rename to packages/loot-core/src/server/accounts/__snapshots__/transfer.test.ts.snap diff --git a/packages/loot-core/src/server/accounts/export-to-csv.js b/packages/loot-core/src/server/accounts/export-to-csv.ts similarity index 100% rename from packages/loot-core/src/server/accounts/export-to-csv.js rename to packages/loot-core/src/server/accounts/export-to-csv.ts diff --git a/packages/loot-core/src/server/accounts/link.js b/packages/loot-core/src/server/accounts/link.ts similarity index 100% rename from packages/loot-core/src/server/accounts/link.js rename to packages/loot-core/src/server/accounts/link.ts diff --git a/packages/loot-core/src/server/accounts/parse-file.test.js b/packages/loot-core/src/server/accounts/parse-file.test.ts similarity index 97% rename from packages/loot-core/src/server/accounts/parse-file.test.js rename to packages/loot-core/src/server/accounts/parse-file.test.ts index cc86ac696a..a44754a165 100644 --- a/packages/loot-core/src/server/accounts/parse-file.test.js +++ b/packages/loot-core/src/server/accounts/parse-file.test.ts @@ -28,7 +28,11 @@ async function getTransactions(accountId) { ); } -async function importFileWithRealTime(accountId, filepath, dateFormat) { +async function importFileWithRealTime( + accountId, + filepath, + dateFormat?: string, +) { // Emscripten requires a real Date.now! global.restoreDateNow(); let { errors, transactions } = await parseFile(filepath); diff --git a/packages/loot-core/src/server/accounts/parse-file.js b/packages/loot-core/src/server/accounts/parse-file.ts similarity index 94% rename from packages/loot-core/src/server/accounts/parse-file.js rename to packages/loot-core/src/server/accounts/parse-file.ts index 9f2b8211ed..06e2ae8c19 100644 --- a/packages/loot-core/src/server/accounts/parse-file.js +++ b/packages/loot-core/src/server/accounts/parse-file.ts @@ -7,7 +7,7 @@ import { looselyParseAmount } from '../../shared/util'; import qif2json from './qif2json'; -export function parseFile(filepath, options) { +export function parseFile(filepath, options?: unknown) { let errors = []; let m = filepath.match(/\.[^.]*$/); @@ -30,10 +30,10 @@ export function parseFile(filepath, options) { message: 'Invalid file type', internal: '', }); - return { errors }; + return { errors, transactions: undefined }; } -async function parseCSV(filepath, options = {}) { +async function parseCSV(filepath, options: { delimiter?: string } = {}) { let errors = []; let contents = await fs.readFile(filepath); @@ -148,6 +148,7 @@ async function parseOFX(filepath) { ofxParser: true, which: 'both', errors: { + length: oldErrors.length + newErrors.length, oldErrors, newErrors, }, @@ -157,6 +158,7 @@ async function parseOFX(filepath) { ofxParser: true, which: 'old', errors: { + length: oldErrors.length, oldErrors, }, transactions: newTrans, @@ -166,6 +168,7 @@ async function parseOFX(filepath) { ofxParser: true, which: 'new', errors: { + length: newErrors.length, newErrors, }, transactions: oldTrans, @@ -196,7 +199,7 @@ async function parseOfxJavascript(filepath) { // not sure about browser. We want latin1 and not utf8. // For some reason, utf8 does not parse ofx files correctly here. const contents = new TextDecoder('latin1').decode( - await fs.readFile(filepath, 'binary'), + (await fs.readFile(filepath, 'binary')) as Buffer, ); let data; diff --git a/packages/loot-core/src/server/accounts/payees.js b/packages/loot-core/src/server/accounts/payees.ts similarity index 100% rename from packages/loot-core/src/server/accounts/payees.js rename to packages/loot-core/src/server/accounts/payees.ts diff --git a/packages/loot-core/src/server/accounts/qif2json.js b/packages/loot-core/src/server/accounts/qif2json.ts similarity index 66% rename from packages/loot-core/src/server/accounts/qif2json.js rename to packages/loot-core/src/server/accounts/qif2json.ts index 1f0dc8892b..aff8e4c3d6 100644 --- a/packages/loot-core/src/server/accounts/qif2json.js +++ b/packages/loot-core/src/server/accounts/qif2json.ts @@ -1,19 +1,44 @@ -export default function parse(qif, options) { - var lines = qif.split('\n'), - line = lines.shift(), - type = /!Type:([^$]*)$/.exec(line.trim()), - data = {}, - transactions = (data.transactions = []), - transaction = {}; +type Division = { + category?: string; + subcategory?: string; + description?: string; + amount?: number; +}; - options = options || {}; +type QIFTransaction = { + date?: string; + amount?: string; + number?: string; + memo?: string; + address?: string[]; + clearedStatus?: string; + category?: string; + subcategory?: string; + payee?: string; + division?: Division[]; +}; + +export default function parse(qif, options: { dateFormat?: string } = {}) { + let lines = qif.split('\n'); + let line = lines.shift(); + let type = /!Type:([^$]*)$/.exec(line.trim()); + let data: { + dateFormat: string | undefined; + type?; + transactions: QIFTransaction[]; + } = { + dateFormat: options.dateFormat, + transactions: [], + }; + let transactions = data.transactions; + let transaction: QIFTransaction = {}; if (!type || !type.length) { throw new Error('File does not appear to be a valid qif file: ' + line); } data.type = type[1]; - var division = {}; + let division: Division = {}; while ((line = lines.shift())) { line = line.trim(); @@ -44,7 +69,7 @@ export default function parse(qif, options) { transaction.payee = line.substring(1).replace(/&/g, '&'); break; case 'L': - var lArray = line.substring(1).split(':'); + let lArray = line.substring(1).split(':'); transaction.category = lArray[0]; if (lArray[1] !== undefined) { transaction.subcategory = lArray[1]; @@ -54,7 +79,7 @@ export default function parse(qif, options) { transaction.clearedStatus = line.substring(1); break; case 'S': - var sArray = line.substring(1).split(':'); + let sArray = line.substring(1).split(':'); division.category = sArray[0]; if (sArray[1] !== undefined) { division.subcategory = sArray[1]; @@ -81,7 +106,5 @@ export default function parse(qif, options) { if (Object.keys(transaction).length) { transactions.push(transaction); } - - data.dateFormat = options.dateFormat; return data; } diff --git a/packages/loot-core/src/server/accounts/rules.test.js b/packages/loot-core/src/server/accounts/rules.test.ts similarity index 100% rename from packages/loot-core/src/server/accounts/rules.test.js rename to packages/loot-core/src/server/accounts/rules.test.ts diff --git a/packages/loot-core/src/server/accounts/rules.js b/packages/loot-core/src/server/accounts/rules.ts similarity index 97% rename from packages/loot-core/src/server/accounts/rules.js rename to packages/loot-core/src/server/accounts/rules.ts index 580b3e715d..37d547a0d7 100644 --- a/packages/loot-core/src/server/accounts/rules.js +++ b/packages/loot-core/src/server/accounts/rules.ts @@ -199,6 +199,14 @@ let CONDITION_TYPES = { }; export class Condition { + field; + op; + options; + rawValue; + type; + unparsedValue; + value; + constructor(op, field, value, options, fieldTypes) { let typeName = fieldTypes.get(field); assert(typeName, 'internal', 'Invalid condition field: ' + field); @@ -393,6 +401,13 @@ export class Condition { let ACTION_OPS = ['set', 'link-schedule']; export class Action { + field; + op; + options; + rawValue; + type; + value; + constructor(op, field, value, options, fieldTypes) { assert( ACTION_OPS.includes(op), @@ -440,7 +455,27 @@ export class Action { } export class Rule { - constructor({ id, stage, conditionsOp, conditions, actions, fieldTypes }) { + actions; + conditions; + conditionsOp; + id; + stage; + + constructor({ + id, + stage, + conditionsOp, + conditions, + actions, + fieldTypes, + }: { + id?: string; + stage?; + conditionsOp; + conditions; + actions; + fieldTypes; + }) { this.id = id; this.stage = stage; this.conditionsOp = conditionsOp; @@ -498,7 +533,11 @@ export class Rule { } export class RuleIndexer { - constructor({ field, method }) { + field; + method; + rules; + + constructor({ field, method }: { field: string; method?: string }) { this.field = field; this.method = method; this.rules = new Map(); diff --git a/packages/loot-core/src/server/accounts/sync.test.js b/packages/loot-core/src/server/accounts/sync.test.ts similarity index 97% rename from packages/loot-core/src/server/accounts/sync.test.js rename to packages/loot-core/src/server/accounts/sync.test.ts index 56e22d3dfa..aedd21a910 100644 --- a/packages/loot-core/src/server/accounts/sync.test.js +++ b/packages/loot-core/src/server/accounts/sync.test.ts @@ -19,8 +19,17 @@ import * as transfer from './transfer'; const papaJohns = 'Papa Johns east side'; const lowes = 'Lowe’s Store'; +jest.mock('../../shared/months', () => ({ + ...jest.requireActual('../../shared/months'), + currentDay: jest.fn(), + currentMonth: jest.fn(), +})); + beforeEach(async () => { mockSyncServer.reset(); + jest.resetAllMocks(); + (monthUtils.currentDay as jest.Mock).mockReturnValue('2017-10-15'); + (monthUtils.currentMonth as jest.Mock).mockReturnValue('2017-10'); await global.emptyDatabase()(); await loadMappings(); await loadRules(); @@ -103,7 +112,6 @@ async function getAllPayees() { describe('Account sync', () => { test('reconcile creates payees correctly', async () => { - monthUtils.currentDay = () => '2017-10-15'; let { id } = await prepareDatabase(); let payees = await getAllPayees(); @@ -128,7 +136,6 @@ describe('Account sync', () => { }); test('reconcile matches single transaction', async () => { - monthUtils.currentDay = () => '2017-10-15'; let mockTransactions = prepMockTransactions(); const { id, account_id } = await prepareDatabase(); @@ -166,7 +173,6 @@ describe('Account sync', () => { }); test('reconcile matches multiple transactions', async () => { - monthUtils.currentDay = () => '2017-10-15'; let mockTransactions = prepMockTransactions(); const { id, account_id } = await prepareDatabase(); @@ -231,7 +237,6 @@ describe('Account sync', () => { }); test('reconcile matches multiple transactions (imported_id wins)', async () => { - monthUtils.currentDay = () => '2017-10-15'; let mockTransactions = prepMockTransactions(); const { id, account_id } = await prepareDatabase(); @@ -277,7 +282,6 @@ describe('Account sync', () => { }); test('import never matches existing with financial ids', async () => { - monthUtils.currentDay = () => '2017-10-15'; let mockTransactions = prepMockTransactions(); const { id, account_id } = await prepareDatabase(); @@ -312,14 +316,13 @@ describe('Account sync', () => { differ.expectToMatchDiff(await getAllTransactions()); - monthUtils.currentDay = () => '2017-10-17'; + (monthUtils.currentDay as jest.Mock).mockReturnValue('2017-10-17'); await syncAccount('userId', 'userKey', id, account_id, 'bank'); differ.expectToMatchDiff(await getAllTransactions()); }); test('import updates transfers when matched', async () => { - monthUtils.currentDay = () => '2017-10-15'; let mockTransactions = prepMockTransactions(); const { id, account_id } = await prepareDatabase(); await db.insertAccount({ id: 'two', name: 'two' }); @@ -347,7 +350,7 @@ describe('Account sync', () => { differ.expectToMatchDiff(await getAllTransactions()); - monthUtils.currentDay = () => '2017-10-17'; + (monthUtils.currentDay as jest.Mock).mockReturnValue('2017-10-17'); await syncAccount('userId', 'userKey', id, account_id, 'bank'); // Don't use `differ.expectToMatchDiff` because there's too many @@ -607,16 +610,13 @@ describe('Account sync', () => { }); test('imports transactions for current day and adds latest', async () => { - monthUtils.currentDay = () => '2017-10-15'; - monthUtils.currentMonth = () => '2017-10'; - const { id, account_id } = await prepareDatabase(); expect((await getAllTransactions()).length).toBe(0); await syncAccount('userId', 'userKey', id, account_id, 'bank'); expect(await getAllTransactions()).toMatchSnapshot(); - monthUtils.currentDay = () => '2017-10-17'; + (monthUtils.currentDay as jest.Mock).mockReturnValue('2017-10-17'); await syncAccount('userId', 'userKey', id, account_id, 'bank'); expect(await getAllTransactions()).toMatchSnapshot(); diff --git a/packages/loot-core/src/server/accounts/sync.js b/packages/loot-core/src/server/accounts/sync.ts similarity index 99% rename from packages/loot-core/src/server/accounts/sync.js rename to packages/loot-core/src/server/accounts/sync.ts index 1f3b872234..4eaef2aa89 100644 --- a/packages/loot-core/src/server/accounts/sync.js +++ b/packages/loot-core/src/server/accounts/sync.ts @@ -116,7 +116,7 @@ async function downloadTransactions( acctId, bankId, since, - count, + count?: number, ) { let allTransactions = []; let accountBalance = null; @@ -180,7 +180,6 @@ async function downloadNordigenTransactions( acctId, bankId, since, - count, ) { let userToken = await asyncStorage.getItem('user-token'); if (userToken) { @@ -213,7 +212,7 @@ async function downloadNordigenTransactions( return { transactions: booked, - accountBalances: balances, + accountBalance: balances, startingBalance, }; } @@ -243,7 +242,7 @@ async function resolvePayee(trans, payeeName, payeesToCreate) { async function normalizeTransactions( transactions, acctId, - { rawPayeeName } = {}, + { rawPayeeName = false } = {}, ) { let payeesToCreate = new Map(); diff --git a/packages/loot-core/src/server/accounts/transaction-rules.test.js b/packages/loot-core/src/server/accounts/transaction-rules.test.ts similarity index 100% rename from packages/loot-core/src/server/accounts/transaction-rules.test.js rename to packages/loot-core/src/server/accounts/transaction-rules.test.ts diff --git a/packages/loot-core/src/server/accounts/transaction-rules.js b/packages/loot-core/src/server/accounts/transaction-rules.ts similarity index 99% rename from packages/loot-core/src/server/accounts/transaction-rules.js rename to packages/loot-core/src/server/accounts/transaction-rules.ts index a317adc05d..d1f3602cc4 100644 --- a/packages/loot-core/src/server/accounts/transaction-rules.js +++ b/packages/loot-core/src/server/accounts/transaction-rules.ts @@ -78,7 +78,7 @@ function toInternalField(obj) { } export const ruleModel = { - validate(rule, { update } = {}) { + validate(rule, { update }: { update?: boolean } = {}) { requiredFields('rules', rule, ['conditions', 'actions'], update); if (!update || 'stage' in rule) { @@ -513,7 +513,7 @@ function* getIsSetterRules( stage, condField, actionField, - { condValue, actionValue }, + { condValue, actionValue }: { condValue?: string; actionValue?: string }, ) { let rules = getRules(); for (let i = 0; i < rules.length; i++) { @@ -541,7 +541,7 @@ function* getOneOfSetterRules( stage, condField, actionField, - { condValue, actionValue }, + { condValue, actionValue }: { condValue?: string; actionValue: string }, ) { let rules = getRules(); for (let i = 0; i < rules.length; i++) { diff --git a/packages/loot-core/src/server/accounts/transactions.js b/packages/loot-core/src/server/accounts/transactions.ts similarity index 92% rename from packages/loot-core/src/server/accounts/transactions.js rename to packages/loot-core/src/server/accounts/transactions.ts index 348c7342f1..2775825eb0 100644 --- a/packages/loot-core/src/server/accounts/transactions.js +++ b/packages/loot-core/src/server/accounts/transactions.ts @@ -6,7 +6,7 @@ import { batchMessages } from '../sync'; import * as rules from './transaction-rules'; import * as transfer from './transfer'; -async function idsWithChildren(ids) { +async function idsWithChildren(ids: string[]) { let whereIds = whereIn(ids, 'parent_id'); let rows = await db.all( `SELECT id FROM v_transactions_internal WHERE ${whereIds}`, @@ -18,7 +18,7 @@ async function idsWithChildren(ids) { return [...set]; } -async function getTransactionsByIds(ids) { +async function getTransactionsByIds(ids: string[]) { // TODO: convert to whereIn // // or better yet, use ActualQL @@ -37,6 +37,17 @@ export async function batchUpdateTransactions({ updated, learnCategories = false, detectOrphanPayees = true, +}: { + added?: Array<{ id: string; payee: unknown; category: unknown }>; + deleted?: Array<{ id: string; payee: unknown }>; + updated?: Array<{ + id: string; + payee: unknown; + account: unknown; + category: unknown; + }>; + learnCategories?: boolean; + detectOrphanPayees?: boolean; }) { // Track the ids of each type of transaction change (see below for why) let addedIds = []; diff --git a/packages/loot-core/src/server/accounts/transfer.test.js b/packages/loot-core/src/server/accounts/transfer.test.ts similarity index 94% rename from packages/loot-core/src/server/accounts/transfer.test.js rename to packages/loot-core/src/server/accounts/transfer.test.ts index 98a0360970..5788a301c7 100644 --- a/packages/loot-core/src/server/accounts/transfer.test.js +++ b/packages/loot-core/src/server/accounts/transfer.test.ts @@ -34,11 +34,22 @@ async function prepareDatabase() { }); } +type Transaction = { + account: string; + amount: number; + category?: string; + date: string; + id?: string; + notes?: string; + payee: string; + transfer_id?: string; +}; + describe('Transfer', () => { test('transfers are properly inserted/updated/deleted', async () => { await prepareDatabase(); - let transaction = { + let transaction: Transaction = { account: 'one', amount: 5000, payee: await db.insertPayee({ name: 'Non-transfer' }), @@ -126,7 +137,7 @@ describe('Transfer', () => { "SELECT * FROM payees WHERE transfer_acct = 'three'", ); - let transaction = { + let transaction: Transaction = { account: 'one', amount: 5000, payee: await db.insertPayee({ name: 'Non-transfer' }), diff --git a/packages/loot-core/src/server/accounts/transfer.js b/packages/loot-core/src/server/accounts/transfer.ts similarity index 100% rename from packages/loot-core/src/server/accounts/transfer.js rename to packages/loot-core/src/server/accounts/transfer.ts diff --git a/packages/loot-core/src/server/aql/compiler.test.js b/packages/loot-core/src/server/aql/compiler.test.ts similarity index 100% rename from packages/loot-core/src/server/aql/compiler.test.js rename to packages/loot-core/src/server/aql/compiler.test.ts diff --git a/packages/loot-core/src/server/aql/compiler.js b/packages/loot-core/src/server/aql/compiler.ts similarity index 97% rename from packages/loot-core/src/server/aql/compiler.js rename to packages/loot-core/src/server/aql/compiler.ts index fce92d9ebe..ddd858b05e 100644 --- a/packages/loot-core/src/server/aql/compiler.js +++ b/packages/loot-core/src/server/aql/compiler.ts @@ -324,7 +324,7 @@ function castInput(state, expr, type) { } // TODO: remove state from these functions -function val(state, expr, type) { +function val(state, expr, type?: string) { let castedExpr = expr; // Cast the type if necessary @@ -347,11 +347,11 @@ function val(state, expr, type) { return castedExpr.value; } -function valArray(state, arr, types) { +function valArray(state, arr: unknown[], types?: string[]) { return arr.map((value, idx) => val(state, value, types ? types[idx] : null)); } -function validateArgLength(arr, min, max) { +function validateArgLength(arr: unknown[], min: number, max?: number) { if (max == null) { max = min; } @@ -931,7 +931,7 @@ function isAggregateFunction(expr) { return true; } - return !!argExprs.find(ex => isAggregateFunction(ex)); + return !!(argExprs as unknown[]).find(ex => isAggregateFunction(ex)); } export function isAggregateQuery(queryState) { @@ -952,7 +952,16 @@ export function isAggregateQuery(queryState) { }); } -export function compileQuery(queryState, schema, schemaConfig = {}) { +type SchemaConfig = { + tableViews?: Record | ((...args: unknown[]) => unknown); + tableFilters?: (name: string) => unknown[]; + customizeQuery?: (queryString: T) => T; +}; +export function compileQuery( + queryState, + schema, + schemaConfig: SchemaConfig = {}, +) { let { withDead, validateRefs = true, tableOptions, rawMode } = queryState; let { @@ -979,7 +988,7 @@ export function compileQuery(queryState, schema, schemaConfig = {}) { return filters; }; - let tableRef = (name, isJoin) => { + let tableRef = (name: string, isJoin?: boolean) => { let view = typeof tableViews === 'function' ? tableViews(name, { withDead, isJoin, tableOptions }) @@ -1101,7 +1110,11 @@ export function defaultConstructQuery(queryState, state, sqlPieces) { `; } -export function generateSQLWithState(queryState, schema, schemaConfig) { +export function generateSQLWithState( + queryState, + schema?: unknown, + schemaConfig?: unknown, +) { let { sqlPieces, state } = compileQuery(queryState, schema, schemaConfig); return { sql: defaultConstructQuery(queryState, state, sqlPieces), state }; } diff --git a/packages/loot-core/src/server/aql/exec.test.js b/packages/loot-core/src/server/aql/exec.test.ts similarity index 99% rename from packages/loot-core/src/server/aql/exec.test.js rename to packages/loot-core/src/server/aql/exec.test.ts index 78cb4730c3..1bcbbbdd03 100644 --- a/packages/loot-core/src/server/aql/exec.test.js +++ b/packages/loot-core/src/server/aql/exec.test.ts @@ -16,7 +16,7 @@ function repeat(arr, times) { return result; } -function runQuery(query, options) { +function runQuery(query, options?: unknown) { return aql.runQuery(schema, schemaConfig, query, options); } diff --git a/packages/loot-core/src/server/aql/exec.js b/packages/loot-core/src/server/aql/exec.ts similarity index 100% rename from packages/loot-core/src/server/aql/exec.js rename to packages/loot-core/src/server/aql/exec.ts diff --git a/packages/loot-core/src/server/aql/index.js b/packages/loot-core/src/server/aql/index.ts similarity index 100% rename from packages/loot-core/src/server/aql/index.js rename to packages/loot-core/src/server/aql/index.ts diff --git a/packages/loot-core/src/server/aql/schema-helpers.test.js b/packages/loot-core/src/server/aql/schema-helpers.test.ts similarity index 100% rename from packages/loot-core/src/server/aql/schema-helpers.test.js rename to packages/loot-core/src/server/aql/schema-helpers.test.ts diff --git a/packages/loot-core/src/server/aql/schema-helpers.js b/packages/loot-core/src/server/aql/schema-helpers.ts similarity index 98% rename from packages/loot-core/src/server/aql/schema-helpers.js rename to packages/loot-core/src/server/aql/schema-helpers.ts index 72737d433c..33dd90f7ed 100644 --- a/packages/loot-core/src/server/aql/schema-helpers.js +++ b/packages/loot-core/src/server/aql/schema-helpers.ts @@ -84,7 +84,13 @@ export function convertOutputType(value, type) { return value; } -export function conform(schema, schemaConfig, table, obj, { skipNull } = {}) { +export function conform( + schema, + schemaConfig, + table, + obj, + { skipNull = false } = {}, +) { let tableSchema = schema[table]; if (tableSchema == null) { throw new Error(`Table “${table}” does not exist`); diff --git a/packages/loot-core/src/server/aql/schema/run-query.ts b/packages/loot-core/src/server/aql/schema/run-query.ts index f8088f56b1..403315bd8f 100644 --- a/packages/loot-core/src/server/aql/schema/run-query.ts +++ b/packages/loot-core/src/server/aql/schema/run-query.ts @@ -8,7 +8,7 @@ import { schemaExecutors } from './executors'; import { schema, schemaConfig } from './index'; -export function runCompiledQuery(query, sqlPieces, state, params) { +export function runCompiledQuery(query, sqlPieces, state, params?: unknown) { return _runCompiledQuery(query, sqlPieces, state, { params, executors: schemaExecutors, diff --git a/packages/loot-core/src/server/aql/views.test.js b/packages/loot-core/src/server/aql/views.test.ts similarity index 100% rename from packages/loot-core/src/server/aql/views.test.js rename to packages/loot-core/src/server/aql/views.test.ts diff --git a/packages/loot-core/src/server/aql/views.js b/packages/loot-core/src/server/aql/views.ts similarity index 100% rename from packages/loot-core/src/server/aql/views.js rename to packages/loot-core/src/server/aql/views.ts diff --git a/packages/loot-core/src/server/budget/actions.js b/packages/loot-core/src/server/budget/actions.ts similarity index 100% rename from packages/loot-core/src/server/budget/actions.js rename to packages/loot-core/src/server/budget/actions.ts diff --git a/packages/loot-core/src/server/budget/app.js b/packages/loot-core/src/server/budget/app.ts similarity index 100% rename from packages/loot-core/src/server/budget/app.js rename to packages/loot-core/src/server/budget/app.ts diff --git a/packages/loot-core/src/server/budget/base.test.js b/packages/loot-core/src/server/budget/base.test.ts similarity index 100% rename from packages/loot-core/src/server/budget/base.test.js rename to packages/loot-core/src/server/budget/base.test.ts diff --git a/packages/loot-core/src/server/budget/base.js b/packages/loot-core/src/server/budget/base.ts similarity index 95% rename from packages/loot-core/src/server/budget/base.js rename to packages/loot-core/src/server/budget/base.ts index 9b5c454289..3489be3f22 100644 --- a/packages/loot-core/src/server/budget/base.js +++ b/packages/loot-core/src/server/budget/base.ts @@ -95,7 +95,7 @@ function handleAccountChange(months, oldValue, newValue) { ); months.forEach(month => { - let sheetName = monthUtils.sheetForMonth(month, getBudgetType()); + let sheetName = monthUtils.sheetForMonth(month); rows.forEach(row => { sheet @@ -118,7 +118,7 @@ function handleTransactionChange(transaction, changedFields) { transaction.category ) { let month = monthUtils.monthFromDate(db.fromDateRepr(transaction.date)); - let sheetName = monthUtils.sheetForMonth(month, getBudgetType()); + let sheetName = monthUtils.sheetForMonth(month); sheet .get() @@ -128,7 +128,7 @@ function handleTransactionChange(transaction, changedFields) { function handleCategoryMappingChange(months, oldValue, newValue) { months.forEach(month => { - let sheetName = monthUtils.sheetForMonth(month, getBudgetType()); + let sheetName = monthUtils.sheetForMonth(month); if (oldValue) { sheet .get() @@ -197,8 +197,8 @@ function handleCategoryChange(months, oldValue, newValue) { months.forEach(month => { let prevMonth = monthUtils.prevMonth(month); - let prevSheetName = monthUtils.sheetForMonth(prevMonth, budgetType); - let sheetName = monthUtils.sheetForMonth(month, budgetType); + let prevSheetName = monthUtils.sheetForMonth(prevMonth); + let sheetName = monthUtils.sheetForMonth(month); let { start, end } = monthUtils.bounds(month); createCategory(newValue, sheetName, prevSheetName, start, end); @@ -222,7 +222,7 @@ function handleCategoryChange(months, oldValue, newValue) { let id = newValue.id; months.forEach(month => { - let sheetName = monthUtils.sheetForMonth(month, budgetType); + let sheetName = monthUtils.sheetForMonth(month); removeDeps(sheetName, oldValue.cat_group, id); addDeps(sheetName, newValue.cat_group, id); }); @@ -271,7 +271,7 @@ function handleCategoryGroupChange(months, oldValue, newValue) { if (newValue.tombstone === 1 && oldValue && oldValue.tombstone === 0) { let id = newValue.id; months.forEach(month => { - let sheetName = monthUtils.sheetForMonth(month, budgetType); + let sheetName = monthUtils.sheetForMonth(month); removeDeps(sheetName, id); }); } else if ( @@ -282,7 +282,7 @@ function handleCategoryGroupChange(months, oldValue, newValue) { if (!group.is_income || budgetType !== 'rollover') { months.forEach(month => { - let sheetName = monthUtils.sheetForMonth(month, budgetType); + let sheetName = monthUtils.sheetForMonth(month); // Dirty, dirty hack. These functions should not be async, but this is // OK because we're leveraging the sync nature of queries. Ideally we @@ -402,8 +402,8 @@ export async function createBudget(months) { if (!meta.createdMonths.has(month)) { let prevMonth = monthUtils.prevMonth(month); let { start, end } = monthUtils.bounds(month); - let sheetName = monthUtils.sheetForMonth(month, budgetType); - let prevSheetName = monthUtils.sheetForMonth(prevMonth, budgetType); + let sheetName = monthUtils.sheetForMonth(month); + let prevSheetName = monthUtils.sheetForMonth(prevMonth); categories.forEach(cat => { createCategory(cat, sheetName, prevSheetName, start, end); diff --git a/packages/loot-core/src/server/budget/goaltemplates.js b/packages/loot-core/src/server/budget/goaltemplates.ts similarity index 100% rename from packages/loot-core/src/server/budget/goaltemplates.js rename to packages/loot-core/src/server/budget/goaltemplates.ts diff --git a/packages/loot-core/src/server/budget/report.js b/packages/loot-core/src/server/budget/report.ts similarity index 100% rename from packages/loot-core/src/server/budget/report.js rename to packages/loot-core/src/server/budget/report.ts diff --git a/packages/loot-core/src/server/budget/rollover.js b/packages/loot-core/src/server/budget/rollover.ts similarity index 98% rename from packages/loot-core/src/server/budget/rollover.js rename to packages/loot-core/src/server/budget/rollover.ts index 1c8080a0a9..20b3c60faa 100644 --- a/packages/loot-core/src/server/budget/rollover.js +++ b/packages/loot-core/src/server/budget/rollover.ts @@ -7,7 +7,7 @@ import { number, sumAmounts, flatten2, unflatten2 } from './util'; function getBlankSheet(months) { let blankMonth = monthUtils.prevMonth(months[0]); - return monthUtils.sheetForMonth(blankMonth, 'rollover'); + return monthUtils.sheetForMonth(blankMonth); } export function createBlankCategory(cat, months) { diff --git a/packages/loot-core/src/server/budget/types/handlers.d.ts b/packages/loot-core/src/server/budget/types/handlers.d.ts new file mode 100644 index 0000000000..c41294e4f6 --- /dev/null +++ b/packages/loot-core/src/server/budget/types/handlers.d.ts @@ -0,0 +1,25 @@ +export interface BudgetHandlers { + 'budget/budget-amount': (...args: unknown[]) => Promise; + + 'budget/copy-previous-month': (...args: unknown[]) => Promise; + + 'budget/set-zero': (...args: unknown[]) => Promise; + + 'budget/set-3month-avg': (...args: unknown[]) => Promise; + + 'budget/apply-goal-template': (...args: unknown[]) => Promise; + + 'budget/overwrite-goal-template': (...args: unknown[]) => Promise; + + 'budget/hold-for-next-month': (...args: unknown[]) => Promise; + + 'budget/reset-hold': (...args: unknown[]) => Promise; + + 'budget/cover-overspending': (...args: unknown[]) => Promise; + + 'budget/transfer-available': (...args: unknown[]) => Promise; + + 'budget/transfer-category': (...args: unknown[]) => Promise; + + 'budget/set-carryover': (...args: unknown[]) => Promise; +} diff --git a/packages/loot-core/src/server/budget/util.js b/packages/loot-core/src/server/budget/util.ts similarity index 100% rename from packages/loot-core/src/server/budget/util.js rename to packages/loot-core/src/server/budget/util.ts diff --git a/packages/loot-core/src/server/crdt/__snapshots__/merkle.test.js.snap b/packages/loot-core/src/server/crdt/__snapshots__/merkle.test.ts.snap similarity index 100% rename from packages/loot-core/src/server/crdt/__snapshots__/merkle.test.js.snap rename to packages/loot-core/src/server/crdt/__snapshots__/merkle.test.ts.snap diff --git a/packages/loot-core/src/server/crdt/index.js b/packages/loot-core/src/server/crdt/index.ts similarity index 100% rename from packages/loot-core/src/server/crdt/index.js rename to packages/loot-core/src/server/crdt/index.ts diff --git a/packages/loot-core/src/server/crdt/merkle.test.js b/packages/loot-core/src/server/crdt/merkle.test.ts similarity index 98% rename from packages/loot-core/src/server/crdt/merkle.test.js rename to packages/loot-core/src/server/crdt/merkle.test.ts index 91b383cddc..cf7828a1b7 100644 --- a/packages/loot-core/src/server/crdt/merkle.test.js +++ b/packages/loot-core/src/server/crdt/merkle.test.ts @@ -28,8 +28,8 @@ describe('merkle trie', () => { }); test('diff returns the correct time difference', () => { - let trie1 = {}; - let trie2 = {}; + let trie1: { hash?: unknown } = {}; + let trie2: { hash?: unknown } = {}; const messages = [ // First client messages @@ -90,7 +90,7 @@ describe('merkle trie', () => { message('2018-11-01T02:37:00.000Z-0000-0123456789ABCDEF', 2100), ]; - let trie = {}; + let trie: { hash?: unknown } = {}; messages.forEach(msg => { trie = merkle.insert(trie, msg.timestamp); }); diff --git a/packages/loot-core/src/server/crdt/merkle.js b/packages/loot-core/src/server/crdt/merkle.ts similarity index 100% rename from packages/loot-core/src/server/crdt/merkle.js rename to packages/loot-core/src/server/crdt/merkle.ts diff --git a/packages/loot-core/src/server/crdt/timestamp.test.js b/packages/loot-core/src/server/crdt/timestamp.test.ts similarity index 96% rename from packages/loot-core/src/server/crdt/timestamp.test.js rename to packages/loot-core/src/server/crdt/timestamp.test.ts index 2ed80bf6e2..5dde3525ef 100644 --- a/packages/loot-core/src/server/crdt/timestamp.test.js +++ b/packages/loot-core/src/server/crdt/timestamp.test.ts @@ -2,15 +2,16 @@ import { Timestamp } from './timestamp'; describe('Timestamp', function () { let now = 0; + let prevNow; beforeEach(function () { - Date.prevNow = Date.now; + prevNow = Date.now; Date.now = () => now; Timestamp.init({ node: '1' }); }); afterEach(() => { - Date.now = Date.prevNow; + Date.now = prevNow; }); describe('comparison', function () { @@ -24,7 +25,7 @@ describe('Timestamp', function () { describe('parsing', function () { it('should not parse', function () { - var invalidInputs = [ + let invalidInputs = [ null, undefined, {}, @@ -40,19 +41,19 @@ describe('Timestamp', function () { '9999-12-31T23:59:59.999Z-10000-FFFFFFFFFFFFFFFF', '9999-12-31T23:59:59.999Z-FFFF-10000000000000000', ]; - for (var invalidInput of invalidInputs) { + for (let invalidInput of invalidInputs) { expect(Timestamp.parse(invalidInput)).toBe(null); } }); it('should parse', function () { - var validInputs = [ + let validInputs = [ '1970-01-01T00:00:00.000Z-0000-0000000000000000', '2015-04-24T22:23:42.123Z-1000-0123456789ABCDEF', '9999-12-31T23:59:59.999Z-FFFF-FFFFFFFFFFFFFFFF', ]; - for (var validInput of validInputs) { - var parsed = Timestamp.parse(validInput); + for (let validInput of validInputs) { + let parsed = Timestamp.parse(validInput); expect(typeof parsed).toBe('object'); expect(parsed.millis() >= 0).toBeTruthy(); expect(parsed.millis() < 253402300800000).toBeTruthy(); @@ -117,7 +118,7 @@ describe('Timestamp', function () { it('should fail with counter overflow', function () { now = 40; - for (var i = 0; i < 65536; i++) Timestamp.send(); + for (let i = 0; i < 65536; i++) Timestamp.send(); expect(Timestamp.send).toThrow(Timestamp.OverflowError); }); diff --git a/packages/loot-core/src/server/crdt/timestamp.js b/packages/loot-core/src/server/crdt/timestamp.ts similarity index 83% rename from packages/loot-core/src/server/crdt/timestamp.js rename to packages/loot-core/src/server/crdt/timestamp.ts index ddcbaaf5fc..5b25656095 100644 --- a/packages/loot-core/src/server/crdt/timestamp.js +++ b/packages/loot-core/src/server/crdt/timestamp.ts @@ -68,7 +68,7 @@ export function makeClientId() { return uuid.v4Sync().replace(/-/g, '').slice(-16); } -var config = { +let config = { // Allow 5 minutes of clock drift maxDrift: 5 * 60 * 1000, }; @@ -80,7 +80,20 @@ const MAX_NODE_LENGTH = 16; * timestamp instance class */ export class Timestamp { - constructor(millis, counter, node) { + static init; + static max; + static parse; + static recv; + static send; + static since; + static zero; + static ClockDriftError; + static DuplicateNodeError; + static OverflowError; + + _state; + + constructor(millis: number, counter: number, node: string) { this._state = { millis: millis, counter: counter, @@ -118,6 +131,8 @@ export class Timestamp { } class MutableTimestamp extends Timestamp { + static from; + setMillis(n) { this._state.millis = n; } @@ -142,7 +157,7 @@ MutableTimestamp.from = timestamp => { // Timestamp generator initialization // * sets the node ID to an arbitrary value // * useful for mocking/unit testing -Timestamp.init = function (options = {}) { +Timestamp.init = function (options: { maxDrift?: number; node?: string } = {}) { if (options.maxDrift) { config.maxDrift = options.maxDrift; } @@ -157,7 +172,6 @@ Timestamp.init = function (options = {}) { : '', ), ), - null, ); }; @@ -171,17 +185,17 @@ Timestamp.send = function () { } // retrieve the local wall time - var phys = Date.now(); + let phys = Date.now(); // unpack the clock.timestamp logical time and counter - var lOld = clock.timestamp.millis(); - var cOld = clock.timestamp.counter(); + let lOld = clock.timestamp.millis(); + let cOld = clock.timestamp.counter(); // calculate the next logical time and counter // * ensure that the logical time never goes backward // * increment the counter if phys time does not advance - var lNew = Math.max(lOld, phys); - var cNew = lOld === lNew ? cOld + 1 : 0; + let lNew = Math.max(lOld, phys); + let cNew = lOld === lNew ? cOld + 1 : 0; // check the result for drift and counter overflow if (lNew - phys > config.maxDrift) { @@ -211,11 +225,11 @@ Timestamp.recv = function (msg) { } // retrieve the local wall time - var phys = Date.now(); + let phys = Date.now(); // unpack the message wall time/counter - var lMsg = msg.millis(); - var cMsg = msg.counter(); + let lMsg = msg.millis(); + let cMsg = msg.counter(); // assert the node id and remote clock drift // if (msg.node() === clock.timestamp.node()) { @@ -226,8 +240,8 @@ Timestamp.recv = function (msg) { } // unpack the clock.timestamp logical time and counter - var lOld = clock.timestamp.millis(); - var cOld = clock.timestamp.counter(); + let lOld = clock.timestamp.millis(); + let cOld = clock.timestamp.counter(); // calculate the next logical time and counter // . ensure that the logical time never goes backward @@ -235,8 +249,8 @@ Timestamp.recv = function (msg) { // . if max = old > message, increment local counter // . if max = messsage > old, increment message counter // . otherwise, clocks are monotonic, reset counter - var lNew = Math.max(Math.max(lOld, phys), lMsg); - var cNew = + let lNew = Math.max(Math.max(lOld, phys), lMsg); + let cNew = lNew === lOld && lNew === lMsg ? Math.max(cOld, cMsg) + 1 : lNew === lOld @@ -270,11 +284,11 @@ Timestamp.recv = function (msg) { */ Timestamp.parse = function (timestamp) { if (typeof timestamp === 'string') { - var parts = timestamp.split('-'); + let parts = timestamp.split('-'); if (parts && parts.length === 5) { - var millis = Date.parse(parts.slice(0, 3).join('-')).valueOf(); - var counter = parseInt(parts[3], 16); - var node = parts[4]; + let millis = Date.parse(parts.slice(0, 3).join('-')).valueOf(); + let counter = parseInt(parts[3], 16); + let node = parts[4]; if ( !isNaN(millis) && millis >= 0 && @@ -293,7 +307,7 @@ Timestamp.parse = function (timestamp) { /** * zero/minimum timestamp */ -var zero = Timestamp.parse('1970-01-01T00:00:00.000Z-0000-0000000000000000'); +let zero = Timestamp.parse('1970-01-01T00:00:00.000Z-0000-0000000000000000'); Timestamp.zero = function () { return zero; }; @@ -301,7 +315,7 @@ Timestamp.zero = function () { /** * maximum timestamp */ -var max = Timestamp.parse('9999-12-31T23:59:59.999Z-FFFF-FFFFFFFFFFFFFFFF'); +let max = Timestamp.parse('9999-12-31T23:59:59.999Z-FFFF-FFFFFFFFFFFFFFFF'); Timestamp.max = function () { return max; }; @@ -315,24 +329,21 @@ Timestamp.since = isoString => { */ Timestamp.DuplicateNodeError = class extends Error { constructor(node) { - super(); - this.type = 'DuplicateNodeError'; - this.message = 'duplicate node identifier ' + node; + super('duplicate node identifier ' + node); + this.name = 'DuplicateNodeError'; } }; Timestamp.ClockDriftError = class extends Error { constructor(...args) { - super(); - this.type = 'ClockDriftError'; - this.message = ['maximum clock drift exceeded'].concat(args).join(' '); + super(['maximum clock drift exceeded'].concat(args).join(' ')); + this.name = 'ClockDriftError'; } }; Timestamp.OverflowError = class extends Error { constructor() { - super(); - this.type = 'OverflowError'; - this.message = 'timestamp counter overflow'; + super('timestamp counter overflow'); + this.name = 'OverflowError'; } }; diff --git a/packages/loot-core/src/server/db/__snapshots__/index.test.js.snap b/packages/loot-core/src/server/db/__snapshots__/index.test.ts.snap similarity index 100% rename from packages/loot-core/src/server/db/__snapshots__/index.test.js.snap rename to packages/loot-core/src/server/db/__snapshots__/index.test.ts.snap diff --git a/packages/loot-core/src/server/db/index.test.js b/packages/loot-core/src/server/db/index.test.ts similarity index 100% rename from packages/loot-core/src/server/db/index.test.js rename to packages/loot-core/src/server/db/index.test.ts diff --git a/packages/loot-core/src/server/db/index.ts b/packages/loot-core/src/server/db/index.ts index e67eedfcba..46eef23109 100644 --- a/packages/loot-core/src/server/db/index.ts +++ b/packages/loot-core/src/server/db/index.ts @@ -143,18 +143,18 @@ export function asyncTransaction(fn) { // This function is marked as async because `runQuery` is no longer // async. We return a promise here until we've audited all the code to // make sure nothing calls `.then` on this. -export async function all(sql, params?: string[]) { +export async function all(sql, params?: (string | number)[]) { return runQuery(sql, params, true); } -export async function first(sql, params?: string[]) { +export async function first(sql, params?: (string | number)[]) { const arr = await runQuery(sql, params, true); return arr.length === 0 ? null : arr[0]; } // The underlying sql system is now sync, but we can't update `first` yet // without auditing all uses of it -export function firstSync(sql, params?: string[]) { +export function firstSync(sql, params?: (string | number)[]) { const arr = runQuery(sql, params, true); return arr.length === 0 ? null : arr[0]; } @@ -162,7 +162,7 @@ export function firstSync(sql, params?: string[]) { // This function is marked as async because `runQuery` is no longer // async. We return a promise here until we've audited all the code to // make sure nothing calls `.then` on this. -export async function run(sql, params?: string[]) { +export async function run(sql, params?: (string | number)[]) { return runQuery(sql, params); } @@ -328,7 +328,7 @@ export async function moveCategoryGroup(id, targetId) { await update('category_groups', { id, sort_order }); } -export async function deleteCategoryGroup(group, transferId) { +export async function deleteCategoryGroup(group, transferId?: string) { const categories = await all('SELECT * FROM categories WHERE cat_group = ?', [ group.id, ]); @@ -387,7 +387,7 @@ export function updateCategory(category) { return update('categories', category); } -export async function moveCategory(id, groupId, targetId) { +export async function moveCategory(id, groupId, targetId?: string) { if (!groupId) { throw new Error('moveCategory: groupId is required'); } @@ -404,7 +404,7 @@ export async function moveCategory(id, groupId, targetId) { await update('categories', { id, sort_order, cat_group: groupId }); } -export async function deleteCategory(category, transferId) { +export async function deleteCategory(category, transferId?: string) { if (transferId) { // We need to update all the deleted categories that currently // point to the one we're about to delete so they all are @@ -634,7 +634,7 @@ export async function getTransactionsByDate( throw new Error('`getTransactionsByDate` is deprecated'); } -export async function getTransactions(accountId, arg2) { +export async function getTransactions(accountId, arg2?: unknown) { if (arg2 !== undefined) { throw new Error( '`getTransactions` was given a second argument, it now only takes a single argument `accountId`', diff --git a/packages/loot-core/src/server/db/mappings.js b/packages/loot-core/src/server/db/mappings.ts similarity index 100% rename from packages/loot-core/src/server/db/mappings.js rename to packages/loot-core/src/server/db/mappings.ts diff --git a/packages/loot-core/src/server/db/sort.js b/packages/loot-core/src/server/db/sort.ts similarity index 96% rename from packages/loot-core/src/server/db/sort.js rename to packages/loot-core/src/server/db/sort.ts index 7106b74a57..c033c1f17c 100644 --- a/packages/loot-core/src/server/db/sort.js +++ b/packages/loot-core/src/server/db/sort.ts @@ -13,7 +13,7 @@ function midpoint(items, to) { } } -export function shoveSortOrders(items, targetId) { +export function shoveSortOrders(items, targetId?: string) { const to = items.findIndex(item => item.id === targetId); const target = items[to]; const before = items[to - 1]; diff --git a/packages/loot-core/src/server/db/util.js b/packages/loot-core/src/server/db/util.ts similarity index 93% rename from packages/loot-core/src/server/db/util.js rename to packages/loot-core/src/server/db/util.ts index 5cae87332f..72e6b704f7 100644 --- a/packages/loot-core/src/server/db/util.js +++ b/packages/loot-core/src/server/db/util.ts @@ -25,7 +25,7 @@ export async function incrFetch( return results; } -export function whereIn(ids, field) { +export function whereIn(ids: string[], field: string) { let ids2 = [...new Set(ids)]; // eslint-disable-next-line rulesdir/typography let filter = `${field} IN (` + ids2.map(id => `'${id}'`).join(',') + ')'; diff --git a/packages/loot-core/src/server/migrate/__snapshots__/migrations.test.js.snap b/packages/loot-core/src/server/migrate/__snapshots__/migrations.test.ts.snap similarity index 100% rename from packages/loot-core/src/server/migrate/__snapshots__/migrations.test.js.snap rename to packages/loot-core/src/server/migrate/__snapshots__/migrations.test.ts.snap diff --git a/packages/loot-core/src/server/migrate/cli.js b/packages/loot-core/src/server/migrate/cli.ts similarity index 95% rename from packages/loot-core/src/server/migrate/cli.js rename to packages/loot-core/src/server/migrate/cli.ts index 5f67f773c9..75cdf303c6 100755 --- a/packages/loot-core/src/server/migrate/cli.js +++ b/packages/loot-core/src/server/migrate/cli.ts @@ -33,7 +33,7 @@ const argv = require('yargs').options({ }, }).argv; -function getDatabase(shouldReset) { +function getDatabase() { return sqlite.openDatabase(argv.db); } @@ -47,7 +47,7 @@ function create(migrationName) { async function list(db) { const migrationsDir = getMigrationsDir(); - const applied = await getAppliedMigrations(getDatabase(), migrationsDir); + const applied = await getAppliedMigrations(getDatabase()); const all = await getMigrationList(migrationsDir); const pending = getPending(applied, all); diff --git a/packages/loot-core/src/server/migrate/migrations.test.js b/packages/loot-core/src/server/migrate/migrations.test.ts similarity index 100% rename from packages/loot-core/src/server/migrate/migrations.test.js rename to packages/loot-core/src/server/migrate/migrations.test.ts diff --git a/packages/loot-core/src/server/migrate/migrations.js b/packages/loot-core/src/server/migrate/migrations.ts similarity index 98% rename from packages/loot-core/src/server/migrate/migrations.js rename to packages/loot-core/src/server/migrate/migrations.ts index 231b8be28b..898ff0fd6e 100644 --- a/packages/loot-core/src/server/migrate/migrations.js +++ b/packages/loot-core/src/server/migrate/migrations.ts @@ -36,7 +36,7 @@ export function getUpMigration(id, names) { } export async function getAppliedMigrations(db) { - const rows = await sqlite.runQuery( + const rows = await sqlite.runQuery<{ id: number }>( db, 'SELECT * FROM __migrations__ ORDER BY id ASC', [], diff --git a/packages/loot-core/src/server/notes/app.js b/packages/loot-core/src/server/notes/app.ts similarity index 100% rename from packages/loot-core/src/server/notes/app.js rename to packages/loot-core/src/server/notes/app.ts diff --git a/packages/loot-core/src/server/notes/types/handlers.d.ts b/packages/loot-core/src/server/notes/types/handlers.d.ts new file mode 100644 index 0000000000..873a7fb12b --- /dev/null +++ b/packages/loot-core/src/server/notes/types/handlers.d.ts @@ -0,0 +1,3 @@ +export interface NoteHandlers { + 'notes-save': (arg: { id; note }) => Promise; +} diff --git a/packages/loot-core/src/server/post.js b/packages/loot-core/src/server/post.ts similarity index 100% rename from packages/loot-core/src/server/post.js rename to packages/loot-core/src/server/post.ts diff --git a/packages/loot-core/src/server/schedules/app.test.js b/packages/loot-core/src/server/schedules/app.test.ts similarity index 100% rename from packages/loot-core/src/server/schedules/app.test.js rename to packages/loot-core/src/server/schedules/app.test.ts diff --git a/packages/loot-core/src/server/schedules/app.js b/packages/loot-core/src/server/schedules/app.ts similarity index 97% rename from packages/loot-core/src/server/schedules/app.js rename to packages/loot-core/src/server/schedules/app.ts index dce9170f84..962a9be5fa 100644 --- a/packages/loot-core/src/server/schedules/app.js +++ b/packages/loot-core/src/server/schedules/app.ts @@ -126,7 +126,17 @@ export async function fixRuleForSchedule(id) { return getRules().find(rule => rule.id === newId); } -export async function setNextDate({ id, start, conditions, reset }) { +export async function setNextDate({ + id, + start, + conditions, + reset, +}: { + id: string; + start?; + conditions?; + reset?: boolean; +}) { if (conditions == null) { let rule = await getRuleForSchedule(id); if (rule == null) { @@ -190,7 +200,10 @@ export async function checkIfScheduleExists(name, scheduleId) { return true; } -export async function createSchedule({ schedule, conditions = [] } = {}) { +export async function createSchedule({ + schedule = null, + conditions = [], +} = {}) { let scheduleId = (schedule && schedule.id) || uuid.v4Sync(); let { date: dateCond } = extractScheduleConds(conditions); @@ -242,7 +255,15 @@ export async function createSchedule({ schedule, conditions = [] } = {}) { // TODO: don't allow deleting rules that link schedules -export async function updateSchedule({ schedule, conditions, resetNextDate }) { +export async function updateSchedule({ + schedule, + conditions, + resetNextDate, +}: { + schedule; + conditions?; + resetNextDate?: boolean; +}) { if (schedule.rule) { throw new Error('You cannot change the rule of a schedule'); } diff --git a/packages/loot-core/src/server/schedules/find-schedules.js b/packages/loot-core/src/server/schedules/find-schedules.ts similarity index 99% rename from packages/loot-core/src/server/schedules/find-schedules.js rename to packages/loot-core/src/server/schedules/find-schedules.ts index 656579e974..d812cf2457 100644 --- a/packages/loot-core/src/server/schedules/find-schedules.js +++ b/packages/loot-core/src/server/schedules/find-schedules.ts @@ -107,7 +107,7 @@ async function schedulesForPattern( numDays, baseConfig, accountId, - partialMatchRank, + partialMatchRank?: number, ) { let schedules = []; diff --git a/packages/loot-core/src/server/schedules/types/handlers.ts b/packages/loot-core/src/server/schedules/types/handlers.ts new file mode 100644 index 0000000000..7f7ec60d6f --- /dev/null +++ b/packages/loot-core/src/server/schedules/types/handlers.ts @@ -0,0 +1,19 @@ +export interface SchedulesHandlers { + 'schedule/create': () => Promise; + + 'schedule/update': () => Promise; + + 'schedule/delete': () => Promise; + + 'schedule/skip-next-date': () => Promise; + + 'schedule/post-transaction': () => Promise; + + 'schedule/force-run-service': () => Promise; + + 'schedule/get-possible-transactions': () => Promise; + + 'schedule/discover': () => Promise; + + 'schedule/get-upcoming-dates': () => Promise; +} diff --git a/packages/loot-core/src/server/spreadsheet/__snapshots__/spreadsheet.test.js.snap b/packages/loot-core/src/server/spreadsheet/__snapshots__/spreadsheet.test.ts.snap similarity index 100% rename from packages/loot-core/src/server/spreadsheet/__snapshots__/spreadsheet.test.js.snap rename to packages/loot-core/src/server/spreadsheet/__snapshots__/spreadsheet.test.ts.snap diff --git a/packages/loot-core/src/server/spreadsheet/globals.js b/packages/loot-core/src/server/spreadsheet/globals.ts similarity index 100% rename from packages/loot-core/src/server/spreadsheet/globals.js rename to packages/loot-core/src/server/spreadsheet/globals.ts diff --git a/packages/loot-core/src/server/spreadsheet/graph-data-structure.js b/packages/loot-core/src/server/spreadsheet/graph-data-structure.ts similarity index 96% rename from packages/loot-core/src/server/spreadsheet/graph-data-structure.js rename to packages/loot-core/src/server/spreadsheet/graph-data-structure.ts index 8f45dfdc2c..b4e4ee9a1a 100644 --- a/packages/loot-core/src/server/spreadsheet/graph-data-structure.js +++ b/packages/loot-core/src/server/spreadsheet/graph-data-structure.ts @@ -1,5 +1,5 @@ -function Graph(serialized) { - var graph = { +function Graph() { + let graph = { addNode, removeNode, adjacent, @@ -12,8 +12,8 @@ function Graph(serialized) { getEdges, }; - var edges = new Map(); - var incomingEdges = new Map(); + let edges = new Map(); + let incomingEdges = new Map(); function getEdges() { return { edges, incomingEdges }; diff --git a/packages/loot-core/src/server/spreadsheet/spreadsheet.test.js b/packages/loot-core/src/server/spreadsheet/spreadsheet.test.ts similarity index 98% rename from packages/loot-core/src/server/spreadsheet/spreadsheet.test.js rename to packages/loot-core/src/server/spreadsheet/spreadsheet.test.ts index 33ed0b70bf..381444eb0c 100644 --- a/packages/loot-core/src/server/spreadsheet/spreadsheet.test.js +++ b/packages/loot-core/src/server/spreadsheet/spreadsheet.test.ts @@ -119,7 +119,7 @@ describe('Spreadsheet', () => { return new Promise(resolve => { spreadsheet.onFinish(() => { expect(spreadsheet.getValue('g!foo')).toMatchSnapshot(); - resolve(); + resolve(undefined); }); }); }); @@ -138,7 +138,7 @@ describe('Spreadsheet', () => { return new Promise(resolve => { spreadsheet.onFinish(() => { expect(spreadsheet.getValue('g!foo')).toMatchSnapshot(); - resolve(); + resolve(undefined); }); }); }); diff --git a/packages/loot-core/src/server/spreadsheet/spreadsheet.js b/packages/loot-core/src/server/spreadsheet/spreadsheet.ts similarity index 96% rename from packages/loot-core/src/server/spreadsheet/spreadsheet.js rename to packages/loot-core/src/server/spreadsheet/spreadsheet.ts index 9538f4a5e8..cab8611065 100644 --- a/packages/loot-core/src/server/spreadsheet/spreadsheet.js +++ b/packages/loot-core/src/server/spreadsheet/spreadsheet.ts @@ -6,7 +6,20 @@ import Graph from './graph-data-structure'; import { unresolveName, resolveName } from './util'; export default class Spreadsheet { - constructor(saveCache, setCacheStatus) { + _meta; + cacheBarrier; + computeQueue; + dirtyCells; + events; + graph; + nodes; + running; + saveCache; + setCacheStatus; + transactionDepth; + + constructor(saveCache?: unknown, setCacheStatus?: unknown) { + // @ts-expect-error Graph should be converted to class this.graph = new Graph(); this.nodes = new Map(); this.transactionDepth = 0; @@ -338,7 +351,7 @@ export default class Spreadsheet { createDynamic( sheetName, cellName, - { dependencies = [], run, initialValue, refresh }, + { dependencies = [], run, initialValue, refresh = false }, ) { let name = resolveName(sheetName, cellName); let node = this._getNode(name); diff --git a/packages/loot-core/src/server/spreadsheet/sqlinterp.test.js b/packages/loot-core/src/server/spreadsheet/sqlinterp.test.ts similarity index 100% rename from packages/loot-core/src/server/spreadsheet/sqlinterp.test.js rename to packages/loot-core/src/server/spreadsheet/sqlinterp.test.ts diff --git a/packages/loot-core/src/server/spreadsheet/sqlinterp.js b/packages/loot-core/src/server/spreadsheet/sqlinterp.ts similarity index 99% rename from packages/loot-core/src/server/spreadsheet/sqlinterp.js rename to packages/loot-core/src/server/spreadsheet/sqlinterp.ts index d1ac4d5ca2..b30fa98475 100644 --- a/packages/loot-core/src/server/spreadsheet/sqlinterp.js +++ b/packages/loot-core/src/server/spreadsheet/sqlinterp.ts @@ -12,7 +12,7 @@ function bind(args, func) { // if (shouldLog) { // console.log(args, func.toString()); // } - for (var i = 0; i < args.length; i++) { + for (let i = 0; i < args.length; i++) { if (args[i] === AlwaysTrue) { return AlwaysTrue; } diff --git a/packages/loot-core/src/server/spreadsheet/util.js b/packages/loot-core/src/server/spreadsheet/util.ts similarity index 100% rename from packages/loot-core/src/server/spreadsheet/util.js rename to packages/loot-core/src/server/spreadsheet/util.ts diff --git a/packages/loot-core/src/server/sync/README.md b/packages/loot-core/src/server/sync/README.md index 5df92100ca..d9f4c17be7 100644 --- a/packages/loot-core/src/server/sync/README.md +++ b/packages/loot-core/src/server/sync/README.md @@ -11,9 +11,14 @@ This can be installed by downloading one of the [pre-built binaries](https://git Once installed, the protobuf can be generated by running the below commands. ```bash -cd packages/loot-core/src/server/sync +cd packages/loot-core -protoc sync.proto --js_out=import_style=commonjs,binary:proto +protoc --plugin="protoc-gen-ts=../../node_modules/.bin/protoc-gen-ts" \ + --ts_opt=esModuleInterop=true \ + --ts_out="src/server/sync/proto" \ + --js_out=import_style=commonjs,binary:src/server/sync/proto \ + --proto_path=src/server/sync \ + sync.proto ``` However there is one very important thing to remember! The default output includes this near the top: diff --git a/packages/loot-core/src/server/sync/__snapshots__/sync.test.js.snap b/packages/loot-core/src/server/sync/__snapshots__/sync.test.ts.snap similarity index 100% rename from packages/loot-core/src/server/sync/__snapshots__/sync.test.js.snap rename to packages/loot-core/src/server/sync/__snapshots__/sync.test.ts.snap diff --git a/packages/loot-core/src/server/sync/encoder.js b/packages/loot-core/src/server/sync/encoder.ts similarity index 95% rename from packages/loot-core/src/server/sync/encoder.js rename to packages/loot-core/src/server/sync/encoder.ts index b64aa4d800..46e933e9b3 100644 --- a/packages/loot-core/src/server/sync/encoder.js +++ b/packages/loot-core/src/server/sync/encoder.ts @@ -80,7 +80,7 @@ export async function decode(data) { if (encrypted) { let binary = SyncPb.EncryptedData.deserializeBinary( - envelopePb.getContent(), + envelopePb.getContent() as Uint8Array, ); let decrypted; @@ -100,7 +100,9 @@ export async function decode(data) { msg = SyncPb.Message.deserializeBinary(decrypted); } else { - msg = SyncPb.Message.deserializeBinary(envelopePb.getContent()); + msg = SyncPb.Message.deserializeBinary( + envelopePb.getContent() as Uint8Array, + ); } messages.push({ diff --git a/packages/loot-core/src/server/sync/index.js b/packages/loot-core/src/server/sync/index.ts similarity index 98% rename from packages/loot-core/src/server/sync/index.js rename to packages/loot-core/src/server/sync/index.ts index 70023e05f9..104a6b97e6 100644 --- a/packages/loot-core/src/server/sync/index.js +++ b/packages/loot-core/src/server/sync/index.ts @@ -68,7 +68,7 @@ export function checkSyncingMode(mode) { } } -function apply(msg, prev) { +function apply(msg, prev?: unknown) { let { dataset, row, column, value } = msg; if (dataset === 'prefs') { @@ -238,7 +238,16 @@ function applyMessagesForImport(messages) { }); } -export const applyMessages = sequential(async messages => { +type Message = { + column: string; + dataset: string; + old?: unknown; + row: string; + timestamp: number; + value: unknown; +}; + +export const applyMessages = sequential(async (messages: Message[]) => { if (checkSyncingMode('import')) { return applyMessagesForImport(messages); } else if (checkSyncingMode('enabled')) { @@ -287,7 +296,7 @@ export const applyMessages = sequential(async messages => { return data; } - let prefsToSet = {}; + let prefsToSet: Record = {}; let oldData = await fetchData(); undo.appendMessages(messages, oldData); @@ -404,7 +413,7 @@ export const applyMessages = sequential(async messages => { return messages; }); -export function receiveMessages(messages) { +export function receiveMessages(messages: Message[]) { messages.forEach(msg => { Timestamp.recv(msg.timestamp); }); @@ -662,7 +671,7 @@ async function _fullSync(sinceTimestamp, count, prevDiffTime) { let localTimeChanged = getClock().timestamp.toString() !== currentTime; // Apply the new messages - let receivedMessages = []; + let receivedMessages: unknown[] = []; if (res.messages.length > 0) { receivedMessages = await receiveMessages( res.messages.map(msg => ({ diff --git a/packages/loot-core/src/server/sync/make-test-message.js b/packages/loot-core/src/server/sync/make-test-message.ts similarity index 100% rename from packages/loot-core/src/server/sync/make-test-message.js rename to packages/loot-core/src/server/sync/make-test-message.ts diff --git a/packages/loot-core/src/server/sync/migrate.test.js b/packages/loot-core/src/server/sync/migrate.test.ts similarity index 97% rename from packages/loot-core/src/server/sync/migrate.test.js rename to packages/loot-core/src/server/sync/migrate.test.ts index 93810f8cba..58a1173804 100644 --- a/packages/loot-core/src/server/sync/migrate.test.js +++ b/packages/loot-core/src/server/sync/migrate.test.ts @@ -103,11 +103,7 @@ describe('sync migrations', () => { await sendMessages(msgs); await tracer.expect('applied'); - let transactions = await db.all( - 'SELECT * FROM transactions', - [], - true, - ); + let transactions = await db.all('SELECT * FROM transactions', []); for (let trans of transactions) { let transMsgs = msgs .filter(msg => msg.row === trans.id) diff --git a/packages/loot-core/src/server/sync/migrate.js b/packages/loot-core/src/server/sync/migrate.ts similarity index 100% rename from packages/loot-core/src/server/sync/migrate.js rename to packages/loot-core/src/server/sync/migrate.ts diff --git a/packages/loot-core/src/server/sync/proto/sync_pb.d.ts b/packages/loot-core/src/server/sync/proto/sync_pb.d.ts new file mode 100644 index 0000000000..5a31387bf1 --- /dev/null +++ b/packages/loot-core/src/server/sync/proto/sync_pb.d.ts @@ -0,0 +1,206 @@ +// package: +// file: sync.proto + +import * as jspb from 'google-protobuf'; + +export class EncryptedData extends jspb.Message { + getIv(): Uint8Array | string; + getIv_asU8(): Uint8Array; + getIv_asB64(): string; + setIv(value: Uint8Array | string): void; + + getAuthtag(): Uint8Array | string; + getAuthtag_asU8(): Uint8Array; + getAuthtag_asB64(): string; + setAuthtag(value: Uint8Array | string): void; + + getData(): Uint8Array | string; + getData_asU8(): Uint8Array; + getData_asB64(): string; + setData(value: Uint8Array | string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): EncryptedDataAsObject; + static toObject( + includeInstance: boolean, + msg: EncryptedData, + ): EncryptedDataAsObject; + static extensions: { [key: number]: jspb.ExtensionFieldInfo }; + static extensionsBinary: { + [key: number]: jspb.ExtensionFieldBinaryInfo; + }; + static serializeBinaryToWriter( + message: EncryptedData, + writer: jspb.BinaryWriter, + ): void; + static deserializeBinary(bytes: Uint8Array): EncryptedData; + static deserializeBinaryFromReader( + message: EncryptedData, + reader: jspb.BinaryReader, + ): EncryptedData; +} + +export type EncryptedDataAsObject = { + iv: Uint8Array | string; + authtag: Uint8Array | string; + data: Uint8Array | string; +}; + +export class Message extends jspb.Message { + getDataset(): string; + setDataset(value: string): void; + + getRow(): string; + setRow(value: string): void; + + getColumn(): string; + setColumn(value: string): void; + + getValue(): string; + setValue(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): MessageAsObject; + static toObject(includeInstance: boolean, msg: Message): MessageAsObject; + static extensions: { [key: number]: jspb.ExtensionFieldInfo }; + static extensionsBinary: { + [key: number]: jspb.ExtensionFieldBinaryInfo; + }; + static serializeBinaryToWriter( + message: Message, + writer: jspb.BinaryWriter, + ): void; + static deserializeBinary(bytes: Uint8Array): Message; + static deserializeBinaryFromReader( + message: Message, + reader: jspb.BinaryReader, + ): Message; +} + +export type MessageAsObject = { + dataset: string; + row: string; + column: string; + value: string; +}; + +export class MessageEnvelope extends jspb.Message { + getTimestamp(): string; + setTimestamp(value: string): void; + + getIsencrypted(): boolean; + setIsencrypted(value: boolean): void; + + getContent(): Uint8Array | string; + getContent_asU8(): Uint8Array; + getContent_asB64(): string; + setContent(value: Uint8Array | string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): MessageEnvelopeAsObject; + static toObject( + includeInstance: boolean, + msg: MessageEnvelope, + ): MessageEnvelopeAsObject; + static extensions: { [key: number]: jspb.ExtensionFieldInfo }; + static extensionsBinary: { + [key: number]: jspb.ExtensionFieldBinaryInfo; + }; + static serializeBinaryToWriter( + message: MessageEnvelope, + writer: jspb.BinaryWriter, + ): void; + static deserializeBinary(bytes: Uint8Array): MessageEnvelope; + static deserializeBinaryFromReader( + message: MessageEnvelope, + reader: jspb.BinaryReader, + ): MessageEnvelope; +} + +export type MessageEnvelopeAsObject = { + timestamp: string; + isencrypted: boolean; + content: Uint8Array | string; +}; + +export class SyncRequest extends jspb.Message { + clearMessagesList(): void; + getMessagesList(): Array; + setMessagesList(value: Array): void; + addMessages(value?: MessageEnvelope, index?: number): MessageEnvelope; + + getFileid(): string; + setFileid(value: string): void; + + getGroupid(): string; + setGroupid(value: string): void; + + getKeyid(): string; + setKeyid(value: string): void; + + getSince(): string; + setSince(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): SyncRequestAsObject; + static toObject( + includeInstance: boolean, + msg: SyncRequest, + ): SyncRequestAsObject; + static extensions: { [key: number]: jspb.ExtensionFieldInfo }; + static extensionsBinary: { + [key: number]: jspb.ExtensionFieldBinaryInfo; + }; + static serializeBinaryToWriter( + message: SyncRequest, + writer: jspb.BinaryWriter, + ): void; + static deserializeBinary(bytes: Uint8Array): SyncRequest; + static deserializeBinaryFromReader( + message: SyncRequest, + reader: jspb.BinaryReader, + ): SyncRequest; +} + +export type SyncRequestAsObject = { + messagesList: Array; + fileid: string; + groupid: string; + keyid: string; + since: string; +}; + +export class SyncResponse extends jspb.Message { + clearMessagesList(): void; + getMessagesList(): Array; + setMessagesList(value: Array): void; + addMessages(value?: MessageEnvelope, index?: number): MessageEnvelope; + + getMerkle(): string; + setMerkle(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): SyncResponseAsObject; + static toObject( + includeInstance: boolean, + msg: SyncResponse, + ): SyncResponseAsObject; + static extensions: { [key: number]: jspb.ExtensionFieldInfo }; + static extensionsBinary: { + [key: number]: jspb.ExtensionFieldBinaryInfo; + }; + static serializeBinaryToWriter( + message: SyncResponse, + writer: jspb.BinaryWriter, + ): void; + static deserializeBinary(bytes: Uint8Array): SyncResponse; + static deserializeBinaryFromReader( + message: SyncResponse, + reader: jspb.BinaryReader, + ): SyncResponse; +} + +export type SyncResponseAsObject = { + messagesList: Array; + merkle: string; +}; diff --git a/packages/loot-core/src/server/sync/proto/sync_pb.ts b/packages/loot-core/src/server/sync/proto/sync_pb.js similarity index 100% rename from packages/loot-core/src/server/sync/proto/sync_pb.ts rename to packages/loot-core/src/server/sync/proto/sync_pb.js diff --git a/packages/loot-core/src/server/sync/repair.js b/packages/loot-core/src/server/sync/repair.ts similarity index 94% rename from packages/loot-core/src/server/sync/repair.js rename to packages/loot-core/src/server/sync/repair.ts index 7de86475eb..96df0b6189 100644 --- a/packages/loot-core/src/server/sync/repair.js +++ b/packages/loot-core/src/server/sync/repair.ts @@ -3,7 +3,7 @@ import * as db from '../db'; export function rebuildMerkleHash() { let rows = db.runQuery('SELECT timestamp FROM messages_crdt', [], true); - let trie = {}; + let trie: Record = {}; for (let i = 0; i < rows.length; i++) { trie = merkle.insert(trie, Timestamp.parse(rows[i].timestamp)); diff --git a/packages/loot-core/src/server/sync/reset.js b/packages/loot-core/src/server/sync/reset.ts similarity index 100% rename from packages/loot-core/src/server/sync/reset.js rename to packages/loot-core/src/server/sync/reset.ts diff --git a/packages/loot-core/src/server/sync/sync.property.test.js b/packages/loot-core/src/server/sync/sync.property.test.ts similarity index 96% rename from packages/loot-core/src/server/sync/sync.property.test.js rename to packages/loot-core/src/server/sync/sync.property.test.ts index 454a1f3d26..0b636cbde8 100644 --- a/packages/loot-core/src/server/sync/sync.property.test.js +++ b/packages/loot-core/src/server/sync/sync.property.test.ts @@ -1,4 +1,4 @@ -import jsc from 'jsverify'; +import jsc, { type Arbitrary } from 'jsverify'; import { merkle, getClock, Timestamp } from '../crdt'; import * as db from '../db'; @@ -10,9 +10,9 @@ import * as encoder from './encoder'; import * as sync from './index'; -const uuidGenerator = jsc.integer({ min: 97, max: 122 }).smap( +const uuidGenerator = jsc.integer(97, 122).smap( x => String.fromCharCode(x), - x => x.charCodeAt(x), + x => x.charCodeAt(Number(x)), ); beforeEach(() => { @@ -98,13 +98,23 @@ let baseTime = 1565374471903; let clientId1 = '80dd7da215247293'; let clientId2 = '90xU1sd5124329ac'; -function makeGen({ table, row, field, value }) { +function makeGen({ + table, + row, + field, + value, +}: { + table: string; + row?: Arbitrary; + field: string; + value: Arbitrary; +}) { return jsc.record({ dataset: jsc.constant(table), row: row || uuidGenerator, column: jsc.constant(field), value, - timestamp: jsc.integer({ min: 1000, max: 10000 }).smap( + timestamp: jsc.integer(1000, 10000).smap( x => { let clientId; switch (jsc.random(0, 1)) { @@ -117,7 +127,7 @@ function makeGen({ table, row, field, value }) { } return new Timestamp(baseTime + x, 0, clientId); }, - x => x.millis - baseTime, + x => x.millis() - baseTime, ), }); } diff --git a/packages/loot-core/src/server/sync/sync.test.js b/packages/loot-core/src/server/sync/sync.test.ts similarity index 99% rename from packages/loot-core/src/server/sync/sync.test.js rename to packages/loot-core/src/server/sync/sync.test.ts index a1fa6e0601..749891b863 100644 --- a/packages/loot-core/src/server/sync/sync.test.js +++ b/packages/loot-core/src/server/sync/sync.test.ts @@ -167,7 +167,7 @@ function expectCellToExist(sheetName, name) { expect(value).not.toBe(null); } -function expectCellNotToExist(sheetName, name, voided) { +function expectCellNotToExist(sheetName, name, voided?: boolean) { let value = sheet.get().getCellValueLoose(sheetName, name); expect(value).toBe(voided ? 0 : null); } diff --git a/packages/loot-core/src/server/tests/mockSyncServer.js b/packages/loot-core/src/server/tests/mockSyncServer.ts similarity index 98% rename from packages/loot-core/src/server/tests/mockSyncServer.js rename to packages/loot-core/src/server/tests/mockSyncServer.ts index f279b068a7..cb4a3f2639 100644 --- a/packages/loot-core/src/server/tests/mockSyncServer.js +++ b/packages/loot-core/src/server/tests/mockSyncServer.ts @@ -1,7 +1,7 @@ import { makeClock, Timestamp, merkle } from '../crdt'; import * as SyncPb from '../sync/proto/sync_pb'; -import { basic as defaultMockData } from './mockData'; +import { basic as defaultMockData } from './mockData.json'; const handlers = {}; let currentMockData = defaultMockData; diff --git a/packages/loot-core/src/server/tools/app.js b/packages/loot-core/src/server/tools/app.ts similarity index 100% rename from packages/loot-core/src/server/tools/app.js rename to packages/loot-core/src/server/tools/app.ts diff --git a/packages/loot-core/src/server/util/budget-name.js b/packages/loot-core/src/server/util/budget-name.ts similarity index 100% rename from packages/loot-core/src/server/util/budget-name.js rename to packages/loot-core/src/server/util/budget-name.ts diff --git a/packages/loot-core/src/server/util/rschedule.js b/packages/loot-core/src/server/util/rschedule.ts similarity index 100% rename from packages/loot-core/src/server/util/rschedule.js rename to packages/loot-core/src/server/util/rschedule.ts diff --git a/packages/loot-core/src/shared/accounts.ts b/packages/loot-core/src/shared/accounts.ts index 223a79a2bb..668ca00c17 100644 --- a/packages/loot-core/src/shared/accounts.ts +++ b/packages/loot-core/src/shared/accounts.ts @@ -1,4 +1,4 @@ -export function fromPlaidAccountType(type, subtype) { +export function fromPlaidAccountType(type, subtype?: string) { switch (type) { case 'brokerage': case 'investment': diff --git a/packages/loot-core/src/shared/async.ts b/packages/loot-core/src/shared/async.ts index eb85c23449..3739a4e3d5 100644 --- a/packages/loot-core/src/shared/async.ts +++ b/packages/loot-core/src/shared/async.ts @@ -1,4 +1,6 @@ -export function sequential(fn) { +export function sequential unknown>( + fn: T, +): (...args: Parameters) => Promise> { let sequenceState = { running: null, queue: [], diff --git a/packages/loot-core/src/shared/rules.ts b/packages/loot-core/src/shared/rules.ts index b4295c6faa..3a58c973f7 100644 --- a/packages/loot-core/src/shared/rules.ts +++ b/packages/loot-core/src/shared/rules.ts @@ -25,6 +25,19 @@ export const TYPE_INFO = { }, }; +export type FieldTypes = { + imported_payee: string; + payee: string; + date: string; + notes: string; + amount: number; + amountInflow: number; + amountOutfow: number; + category: string; + account: string; + cleared: boolean; +}; + export const FIELD_TYPES = new Map( Object.entries({ imported_payee: 'string', diff --git a/packages/loot-core/src/shared/schedules.ts b/packages/loot-core/src/shared/schedules.ts index 0181e6ecd6..3844c098a5 100644 --- a/packages/loot-core/src/shared/schedules.ts +++ b/packages/loot-core/src/shared/schedules.ts @@ -1,3 +1,5 @@ +import type { IRuleOptions } from '@rschedule/core'; + import * as monthUtils from './months'; import q from './query'; @@ -156,15 +158,8 @@ export function getRecurringDescription(config) { } } -type RecurringSchedule = { - start: Date; - frequency: string; - byHourOfDay: number[]; - interval?: unknown; -}; - export function recurConfigToRSchedule(config) { - let base: RecurringSchedule = { + let base: IRuleOptions = { start: monthUtils.parseDate(config.start), frequency: config.frequency.toUpperCase(), byHourOfDay: [12], diff --git a/packages/loot-core/src/shared/test-helpers.ts b/packages/loot-core/src/shared/test-helpers.ts index 36e2ade775..6c3d499f75 100644 --- a/packages/loot-core/src/shared/test-helpers.ts +++ b/packages/loot-core/src/shared/test-helpers.ts @@ -27,7 +27,7 @@ export function execTracer() { let log = false; return { - event(name, data) { + event(name: string, data?: unknown) { if (!hasStarted) { return; } else if (log) { @@ -122,7 +122,7 @@ export function execTracer() { } }, - expect(name, data) { + expect(name: string, data?: unknown) { if (queue.length === 0) { return this.expectWait(name, data); } diff --git a/packages/loot-core/src/shared/util.ts b/packages/loot-core/src/shared/util.ts index b62bf249d7..1cc1cb9321 100644 --- a/packages/loot-core/src/shared/util.ts +++ b/packages/loot-core/src/shared/util.ts @@ -121,7 +121,7 @@ export function partitionByField(data, field) { return res; } -export function groupBy(data, field, mapper) { +export function groupBy(data, field, mapper?: (v: unknown) => unknown) { let res = new Map(); for (let i = 0; i < data.length; i++) { let item = data[i]; diff --git a/packages/loot-core/src/types/api.handlers.d.ts b/packages/loot-core/src/types/api.handlers.d.ts new file mode 100644 index 0000000000..962f081d71 --- /dev/null +++ b/packages/loot-core/src/types/api.handlers.d.ts @@ -0,0 +1,123 @@ +export interface ApiHandlers { + 'api/batch-budget-start': () => Promise; + + 'api/batch-budget-end': () => Promise; + + 'api/load-budget': ( + ...args: Parameters + ) => Promise; + + 'api/download-budget': (arg: { syncId; password }) => Promise; + + 'api/start-import': (arg: { budgetName }) => Promise; + + 'api/finish-import': () => Promise; + + 'api/abort-import': () => Promise; + + 'api/query': (arg: { query }) => Promise; + + 'api/budget-months': () => Promise; + + 'api/budget-month': (arg: { month }) => Promise<{ + month; + incomeAvailable: number; + lastMonthOverspent: number; + forNextMonth: number; + totalBudgeted: number; + toBudget: number; + + fromLastMonth: number; + totalIncome: number; + totalSpent: number; + totalBalance: number; + categoryGroups: Record[]; + }>; + + 'api/budget-set-amount': (arg: { + month; + categoryId; + amount; + }) => Promise; + + 'api/budget-set-carryover': (arg: { + month; + categoryId; + flag; + }) => Promise; + + 'api/transactions-export': (arg: { + transactions; + categoryGroups; + payees; + }) => Promise; + + 'api/transactions-import': (arg: { + accountId; + transactions; + }) => Promise; + + 'api/transactions-add': (arg: { accountId; transactions }) => Promise<'ok'>; + + 'api/transactions-get': (arg: { + accountId; + startDate; + endDate; + }) => Promise; + + /** @deprecated `filterTransactions` is deprecated, use `runQuery` instead' */ + 'api/transactions-filter': (arg: { text; accountId }) => Promise; + + 'api/transaction-update': (arg: { id; fields }) => Promise; + + 'api/transaction-delete': (arg: { id }) => Promise; + + 'api/accounts-get': () => Promise; + + 'api/account-create': (arg: { account; initialBalance }) => Promise; + + 'api/account-update': (arg: { id; fields }) => Promise; + + 'api/account-close': (arg: { + id; + transferAccountId; + transferCategoryId; + }) => Promise; + + 'api/account-reopen': (arg: { id }) => Promise; + + 'api/account-delete': (arg: { id }) => Promise; + + 'api/categories-get': (arg: { grouped }) => Promise; + + 'api/category-group-create': (arg: { group }) => Promise; + + 'api/category-group-update': (arg: { id; fields }) => Promise; + + 'api/category-group-delete': (arg: { + id; + transferCategoryId; + }) => Promise; + + 'api/category-create': (arg: { category }) => Promise; + + 'api/category-update': (arg: { id; fields }) => Promise; + + 'api/category-delete': (arg: { id; transferCategoryId }) => Promise; + + 'api/payees-get': () => Promise; + + 'api/payee-create': (arg: { payee }) => Promise; + + 'api/payee-update': (arg: { id; fields }) => Promise; + + 'api/payee-delete': (arg: { id }) => Promise; + + 'api/payee-rules-get': (arg: { payeeId }) => Promise; + + 'api/payee-rule-create': (arg: { payee_id; rule }) => Promise; + + 'api/payee-rule-update': (arg: { id; fields }) => Promise; + + 'api/payee-rule-delete': (arg: { id }) => Promise; +} diff --git a/packages/loot-core/src/types/handlers.d.ts b/packages/loot-core/src/types/handlers.d.ts new file mode 100644 index 0000000000..e4eb27a098 --- /dev/null +++ b/packages/loot-core/src/types/handlers.d.ts @@ -0,0 +1,12 @@ +import type { BudgetHandlers } from '../server/budget/types/handlers'; +import type { NotesHandlers } from '../server/notes/types/handlers'; +import type { SchedulesHandlers } from '../server/schedules/types/handlers'; + +import type { ApiHandlers } from './api.handlers'; +import type { MainHandlers } from './main.handlers'; + +export type Handlers = MainHandlers & + ApiHandlers & + BudgetHandlers & + NotesHandlers & + SchedulesHandlers; diff --git a/packages/loot-core/src/types/main.handlers.d.ts b/packages/loot-core/src/types/main.handlers.d.ts new file mode 100644 index 0000000000..51d1065faa --- /dev/null +++ b/packages/loot-core/src/types/main.handlers.d.ts @@ -0,0 +1,340 @@ +export interface MainHandlers { + 'transaction-update': ( + transaction: unknown, + ) => Promise>; + + undo: () => Promise; + + redo: () => Promise; + + 'transactions-batch-update': (arg: { + added; + deleted; + updated; + learnCategories; + }) => Promise; + + 'transaction-add': (transaction) => Promise>; + + 'transaction-aupdatedd': (transaction) => Promise>; + + 'transaction-delete': (transaction) => Promise>; + + 'transactions-filter': (arg: { + term; + accountId; + latestDate; + count; + notPaged; + options; + }) => Promise; + + 'transactions-parse-file': (arg: { filepath; options }) => Promise; + + 'transactions-export': (arg: { + transactions; + accounts; + categoryGroups; + payees; + }) => Promise; + + 'transactions-export-query': (arg: { query: queryState }) => Promise; + + 'get-categories': () => Promise<{ + grouped: unknown; + list: unknown; + }>; + + 'get-earliest-transaction': () => Promise; + + 'get-budget-bounds': () => Promise; + + 'rollover-budget-month': (arg: { month }) => Promise; + + 'report-budget-month': (arg: { month }) => Promise; + + 'budget-set-type': (arg: { type }) => Promise; + + 'category-create': (arg: { name; groupId; isIncome }) => Promise; + + 'category-update': (category) => Promise; + + 'category-move': (arg: { id; groupId; targetId }) => Promise; + + 'category-delete': (arg: { id; transferId }) => Promise<{ error }>; + + 'category-group-create': (arg: { + name; + isIncome?: boolean; + }) => Promise; + + 'category-group-update': (group) => Promise; + + 'category-group-move': (arg: { id; targetId }) => Promise; + + 'category-group-delete': (arg: { id; transferId }) => Promise; + + 'must-category-transfer': (arg: { id }) => Promise; + + 'payee-create': (arg: { name }) => Promise; + + 'payees-get': () => Promise; + + 'payees-get-rule-counts': () => Promise; + + 'payees-merge': (arg: { targetId; mergeIds }) => Promise; + + 'payees-batch-change': (arg: { added; deleted; updated }) => Promise; + + 'payees-check-orphaned': (arg: { ids }) => Promise; + + 'payees-get-rules': (arg: { id }) => Promise; + + 'payees-delete-rule': (arg: { id; payee_id }) => Promise; + + 'payees-update-rule': (rule) => Promise; + + 'payees-add-rule': (rule) => Promise; + + 'rule-validate': (rule) => Promise<{ error: unknown }>; + + 'rule-add': (rule) => Promise<{ error: unknown } | { id: string }>; + + 'rule-add': (rule) => Promise<{ error: unknown } | unknown>; + + 'rule-delete': (rule) => Promise; + + 'rule-delete-all': (ids) => Promise; + + 'rule-apply-actions': (arg: { transactionIds; actions }) => Promise; + + 'rule-add-payee-rename': (arg: { fromNames; to }) => Promise; + + 'rules-get': () => Promise; + + 'rule-get': (arg: { id }) => Promise; + + 'rules-run': (arg: { transaction }) => Promise; + + 'rules-migrate': () => Promise; + + 'make-filters-from-conditions': (arg: { conditions }) => Promise; + + getCell: (arg: { sheetName; name }) => Promise; + + getCells: (arg: { names }) => Promise; + + getCellNamesInSheet: (arg: { sheetName }) => Promise; + + debugCell: (arg: { sheetName; name }) => Promise; + + 'create-query': (arg: { sheetName; name; query }) => Promise; + + query: (query) => Promise; + + 'bank-delete': (arg: { id }) => Promise; + + 'account-update': (arg: { id; name }) => Promise; + + 'accounts-get': () => Promise; + + 'account-properties': (arg: { + id; + }) => Promise<{ balance: number; numTransactions: number }>; + + 'accounts-link': (arg: { + institution; + publicToken; + accountId; + upgradingId; + }) => Promise<'ok'>; + + 'nordigen-accounts-link': (arg: { + requisitionId; + account; + upgradingId; + }) => Promise<'ok'>; + + 'accounts-connect': (arg: { + institution; + publicToken; + accountIds; + offbudgetIds; + }) => Promise; + + 'nordigen-accounts-connect': (arg: { + institution; + publicToken; + accountIds; + offbudgetIds; + }) => Promise; + + 'account-create': (arg: { + name; + type; + balance; + offBudget; + closed?; + }) => Promise; + + 'account-close': (arg: { + id; + transferAccountId; + categoryId; + forced; + }) => Promise; + + 'account-reopen': (arg: { id }) => Promise; + + 'account-move': (arg: { id; targetId }) => Promise; + + 'poll-web-token': (arg: { token }) => Promise; + + 'poll-web-token-stop': () => Promise<'ok'>; + + 'accounts-sync': (arg: { id }) => Promise<{ + errors: unknown; + newTransactions: unknown; + matchedTransactions: unknown; + updatedAccounts: unknown; + }>; + + 'nordigen-poll-web-token': (arg: { + upgradingAccountId; + requisitionId; + }) => Promise; + + 'nordigen-status': () => Promise; + + 'nordigen-get-banks': (country) => Promise; + + 'nordigen-poll-web-token-stop': () => Promise<'ok'>; + + 'nordigen-create-web-token': (arg: { + upgradingAccountId; + institutionId; + accessValidForDays; + }) => Promise; + + 'nordigen-accounts-sync': (arg: { id }) => Promise<{ + errors; + newTransactions; + matchedTransactions; + updatedAccounts; + }>; + + 'transactions-import': (arg: { accountId; transactions }) => Promise<{ + errors; + added; + updated; + }>; + + 'account-unlink': (arg: { id }) => Promise<'ok'>; + + 'make-plaid-public-token': (arg: { + bankId; + }) => Promise< + | { error: ''; code: data.error_code; type: data.error_type } + | { linkToken: data.link_token } + >; + + 'save-global-prefs': (prefs) => Promise<'ok'>; + + 'load-global-prefs': () => Promise<{ + floatingSidebar: boolean; + seenTutorial: boolean; + maxMonths: number; + autoUpdate: boolean; + documentDir: string; + keyId: string; + }>; + + 'save-prefs': (prefsToSet) => Promise<'ok'>; + + 'load-prefs': () => Promise | null>; + + 'sync-reset': () => Promise<{ error }>; + + 'sync-repair': () => Promise; + + 'key-make': (arg: { password }) => Promise; + + 'key-test': (arg: { fileId; password }) => Promise; + + 'subscribe-needs-bootstrap': ( + args: { url } = {}, + ) => Promise< + { error: string } | { bootstrapped: unknown; hasServer: boolean } + >; + + 'subscribe-bootstrap': (arg: { password }) => Promise<{ error: string }>; + + 'subscribe-set-user': (arg: { token }) => Promise; + + 'subscribe-get-user': () => Promise<{ offline: boolean } | null>; + + 'subscribe-change-password': (arg: { + password; + }) => Promise<{ error: string }>; + + 'subscribe-sign-in': (arg: { password }) => Promise<{ error: string }>; + + 'subscribe-sign-out': () => Promise<'ok'>; + + 'get-server-version': () => Promise<{ error: string } | { version: string }>; + + 'get-server-url': () => Promise; + + 'set-server-url': (arg: { url; validate }) => Promise; + + sync: () => Promise<{ error: string }>; + + 'get-budgets': () => Promise; + + 'get-remote-files': () => Promise; + + 'reset-budget-cache': () => Promise; + + 'upload-budget': (arg: { id } = {}) => Promise<{ error }>; + + 'download-budget': (arg: { fileId; replace }) => Promise<{ error; id }>; + + 'sync-budget': (arg: { id }) => Promise; + + 'load-budget': (arg: { id }) => Promise<{ error }>; + + 'create-demo-budget': () => Promise; + + 'close-budget': () => Promise<'ok'>; + + 'delete-budget': (arg: { id; cloudFileId }) => Promise<'ok'>; + + 'create-budget': (arg: { + budgetName?; + avoidUpload?; + testMode: boolean; + testBudgetId?; + }) => Promise; + + 'set-tutorial-seen': () => Promise<'ok'>; + + 'import-budget': (arg: { filepath; type }) => Promise<{ error }>; + + 'export-budget': () => Promise; + + 'get-upgrade-notifications': () => Promise; + + 'seen-upgrade-notification': (arg: { type }) => Promise; + + 'upload-file-web': (arg: { filename; contents }) => Promise<'ok'>; + + 'backups-get': (arg: { id }) => Promise; + + 'backup-load': (arg: { id; backupId }) => Promise; + + 'backup-make': (arg: { id }) => Promise; + + 'get-last-opened-backup': () => Promise; + + 'app-focused': () => Promise; +} diff --git a/packages/loot-core/typings.d.ts b/packages/loot-core/typings.d.ts new file mode 100644 index 0000000000..40fd9d9516 --- /dev/null +++ b/packages/loot-core/typings.d.ts @@ -0,0 +1 @@ +declare module '*.pegjs'; diff --git a/upcoming-release-notes/896.md b/upcoming-release-notes/896.md new file mode 100644 index 0000000000..af1da3d8a6 --- /dev/null +++ b/upcoming-release-notes/896.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [albertogasparin] +--- + +Convert few more folders in `loot-core` to Typescript diff --git a/yarn.lock b/yarn.lock index 0815186f7a..f352d13e28 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3577,6 +3577,15 @@ __metadata: languageName: node linkType: hard +"@types/better-sqlite3@npm:^7.6.4": + version: 7.6.4 + resolution: "@types/better-sqlite3@npm:7.6.4" + dependencies: + "@types/node": "*" + checksum: 75ab00d31b56437cc65fe15ff673cf8d1609edca52628083921bcbab1cbd828d135a2859fb4e68af8ef5a4801705ba99d54b96499f997bce65dd306ade3dbe58 + languageName: node + linkType: hard + "@types/body-parser@npm:*": version: 1.19.2 resolution: "@types/body-parser@npm:1.19.2" @@ -3956,6 +3965,13 @@ __metadata: languageName: node linkType: hard +"@types/pegjs@npm:^0.10.3": + version: 0.10.3 + resolution: "@types/pegjs@npm:0.10.3" + checksum: b0301595d7d7da4de9f8edcc8a97ee55c959e74645dd49626bbaf344dd383e1491b43082d34c1f55048566fef21bf219a4b4743f770afbe80a390a3a33926b8a + languageName: node + linkType: hard + "@types/plist@npm:^3.0.1": version: 3.0.2 resolution: "@types/plist@npm:3.0.2" @@ -10489,6 +10505,13 @@ __metadata: languageName: node linkType: hard +"google-protobuf@npm:^3.15.5": + version: 3.21.2 + resolution: "google-protobuf@npm:3.21.2" + checksum: 3caa2e1e2654714cc1a201ac5e5faabcaa48f5fba3d8ff9b64ca66fe19e4e0506b94053f32eddc77bf3a7a47ac1660315c6744185c1e2bb27654fd76fe91b988 + languageName: node + linkType: hard + "gopd@npm:^1.0.1": version: 1.0.1 resolution: "gopd@npm:1.0.1" @@ -13406,9 +13429,11 @@ __metadata: "@rschedule/ical-tools": ^1.2.0 "@rschedule/json-tools": ^1.2.0 "@rschedule/standard-date-adapter": ^1.2.0 + "@types/better-sqlite3": ^7.6.4 "@types/jest": ^27.5.0 "@types/jlongster__sql.js": "npm:@types/sql.js@latest" "@types/node-ipc": ^9.2.0 + "@types/pegjs": ^0.10.3 "@types/webpack": ^5.28.0 "@wademason/ofx": 1.0.1 absurd-sql: 0.0.53 @@ -13443,6 +13468,7 @@ __metadata: throttleit: ^1.0.0 ts-jest: ^27.0.0 ts-node: ^10.7.0 + ts-protoc-gen: ^0.15.0 typescript: ^4.6.4 uuid: 3.3.2 webpack: ^4.41.2 @@ -19562,6 +19588,17 @@ __metadata: languageName: node linkType: hard +"ts-protoc-gen@npm:^0.15.0": + version: 0.15.0 + resolution: "ts-protoc-gen@npm:0.15.0" + dependencies: + google-protobuf: ^3.15.5 + bin: + protoc-gen-ts: bin/protoc-gen-ts + checksum: 612799180832786ee28dfb3510015f840ff8acd480ec1159599535878cc77a4f6ad4d6993d1fa546bbb4523de5fb60f4987e1ea810c2e56d91be0448d30f29ad + languageName: node + linkType: hard + "tsconfig-paths@npm:^3.14.1": version: 3.14.1 resolution: "tsconfig-paths@npm:3.14.1"