mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-09 06:03:01 -05:00
Bundle loot-core types into the API (#2053)
* Bundle loot-core types into the API So we can have loot-core be the source of truth for some types that get passed through - Improves downstream development with API by including types - Use path aliases for dist vs dev tsconfigs - Convert api index to typescript as example - Permit ts-ignore for issues with our version of typescript --------- Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
packages/api/app/bundle.api.js
|
||||
packages/api/dist
|
||||
packages/api/@types
|
||||
packages/api/migrations
|
||||
|
||||
packages/crdt/dist
|
||||
|
||||
@@ -165,6 +165,11 @@ module.exports = {
|
||||
{ patterns: [...restrictedImportPatterns, ...restrictedImportColors] },
|
||||
],
|
||||
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'error',
|
||||
{ 'ts-ignore': 'allow-with-description' },
|
||||
],
|
||||
|
||||
// Rules disable during TS migration
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'prefer-const': 'warn',
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
!data/.gitkeep
|
||||
/data2
|
||||
packages/api/dist
|
||||
packages/api/@types
|
||||
packages/crdt/dist
|
||||
packages/desktop-electron/client-build
|
||||
packages/desktop-electron/.electron-symbols
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
// eslint-disable-next-line import/extensions
|
||||
import * as bundle from './app/bundle.api.js';
|
||||
import * as injected from './injected';
|
||||
import { validateNodeVersion } from './validateNodeVersion';
|
||||
|
||||
let actualApp;
|
||||
export const internal = bundle.lib;
|
||||
|
||||
// DEPRECATED: remove the next line in @actual-app/api v7
|
||||
export * as methods from './methods';
|
||||
|
||||
export * from './methods';
|
||||
export * as utils from './utils';
|
||||
|
||||
export async function init(config = {}) {
|
||||
if (actualApp) {
|
||||
return;
|
||||
}
|
||||
|
||||
validateNodeVersion();
|
||||
|
||||
global.fetch = (...args) =>
|
||||
import('node-fetch').then(({ default: fetch }) => fetch(...args));
|
||||
|
||||
await bundle.init(config);
|
||||
actualApp = bundle.lib;
|
||||
|
||||
injected.override(bundle.lib.send);
|
||||
return bundle.lib;
|
||||
}
|
||||
|
||||
export async function shutdown() {
|
||||
if (actualApp) {
|
||||
await actualApp.send('sync');
|
||||
await actualApp.send('close-budget');
|
||||
actualApp = null;
|
||||
}
|
||||
}
|
||||
53
packages/api/index.ts
Normal file
53
packages/api/index.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import type {
|
||||
RequestInfo as FetchInfo,
|
||||
RequestInit as FetchInit,
|
||||
// @ts-ignore: false-positive commonjs module error on build until typescript 5.3
|
||||
} from 'node-fetch'; // with { 'resolution-mode': 'import' };
|
||||
|
||||
// loot-core types
|
||||
import type { InitConfig } from 'loot-core/server/main';
|
||||
|
||||
// @ts-ignore: bundle not available until we build it
|
||||
// eslint-disable-next-line import/extensions
|
||||
import * as bundle from './app/bundle.api.js';
|
||||
import * as injected from './injected';
|
||||
import { validateNodeVersion } from './validateNodeVersion';
|
||||
|
||||
let actualApp: null | typeof bundle.lib;
|
||||
export const internal = bundle.lib;
|
||||
|
||||
// DEPRECATED: remove the next line in @actual-app/api v7
|
||||
export * as methods from './methods';
|
||||
|
||||
export * from './methods';
|
||||
export * as utils from './utils';
|
||||
|
||||
export async function init(config: InitConfig = {}) {
|
||||
if (actualApp) {
|
||||
return;
|
||||
}
|
||||
|
||||
validateNodeVersion();
|
||||
|
||||
if (!globalThis.fetch) {
|
||||
globalThis.fetch = (url: URL | RequestInfo, init?: RequestInit) => {
|
||||
return import('node-fetch').then(({ default: fetch }) =>
|
||||
fetch(url as unknown as FetchInfo, init as unknown as FetchInit),
|
||||
) as unknown as Promise<Response>;
|
||||
};
|
||||
}
|
||||
|
||||
await bundle.init(config);
|
||||
actualApp = bundle.lib;
|
||||
|
||||
injected.override(bundle.lib.send);
|
||||
return bundle.lib;
|
||||
}
|
||||
|
||||
export async function shutdown() {
|
||||
if (actualApp) {
|
||||
await actualApp.send('sync');
|
||||
await actualApp.send('close-budget');
|
||||
actualApp = null;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
// @ts-strict-ignore
|
||||
import type { Handlers } from 'loot-core/src/types/handlers';
|
||||
|
||||
import * as injected from './injected';
|
||||
|
||||
export { q } from './app/query';
|
||||
|
||||
function send(name, args) {
|
||||
function send<K extends keyof Handlers, T extends Handlers[K]>(
|
||||
name: K,
|
||||
args?: Parameters<T>[0],
|
||||
): Promise<Awaited<ReturnType<T>>> {
|
||||
return injected.send(name, args);
|
||||
}
|
||||
|
||||
@@ -21,7 +27,7 @@ export async function loadBudget(budgetId) {
|
||||
return send('api/load-budget', { id: budgetId });
|
||||
}
|
||||
|
||||
export async function downloadBudget(syncId, { password } = {}) {
|
||||
export async function downloadBudget(syncId, { password }: { password? } = {}) {
|
||||
return send('api/download-budget', { syncId, password });
|
||||
}
|
||||
|
||||
@@ -91,7 +97,7 @@ export function getAccounts() {
|
||||
return send('api/accounts-get');
|
||||
}
|
||||
|
||||
export function createAccount(account, initialBalance) {
|
||||
export function createAccount(account, initialBalance?) {
|
||||
return send('api/account-create', { account, initialBalance });
|
||||
}
|
||||
|
||||
@@ -99,7 +105,7 @@ export function updateAccount(id, fields) {
|
||||
return send('api/account-update', { id, fields });
|
||||
}
|
||||
|
||||
export function closeAccount(id, transferAccountId, transferCategoryId) {
|
||||
export function closeAccount(id, transferAccountId?, transferCategoryId?) {
|
||||
return send('api/account-close', {
|
||||
id,
|
||||
transferAccountId,
|
||||
@@ -123,7 +129,7 @@ export function updateCategoryGroup(id, fields) {
|
||||
return send('api/category-group-update', { id, fields });
|
||||
}
|
||||
|
||||
export function deleteCategoryGroup(id, transferCategoryId) {
|
||||
export function deleteCategoryGroup(id, transferCategoryId?) {
|
||||
return send('api/category-group-delete', { id, transferCategoryId });
|
||||
}
|
||||
|
||||
@@ -139,7 +145,7 @@ export function updateCategory(id, fields) {
|
||||
return send('api/category-update', { id, fields });
|
||||
}
|
||||
|
||||
export function deleteCategory(id, transferCategoryId) {
|
||||
export function deleteCategory(id, transferCategoryId?) {
|
||||
return send('api/category-delete', { id, transferCategoryId });
|
||||
}
|
||||
|
||||
@@ -7,17 +7,18 @@
|
||||
"node": ">=18.12.0"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"types": "@types/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build:app": "yarn workspace loot-core build:api",
|
||||
"build:node": "tsc --p tsconfig.dist.json",
|
||||
"build:node": "tsc --p tsconfig.dist.json && tsc-alias -p tsconfig.dist.json",
|
||||
"build:migrations": "cp migrations/*.sql dist/migrations",
|
||||
"build:default-db": "cp default-db.sqlite dist/",
|
||||
"build": "rm -rf dist && yarn run build:app && yarn run build:node && yarn run build:migrations && yarn run build:default-db",
|
||||
"test": "yarn run build:app && jest -c jest.config.js"
|
||||
"build": "yarn run clean && yarn run build:app && yarn run build:node && yarn run build:migrations && yarn run build:default-db",
|
||||
"test": "yarn run build:app && jest -c jest.config.js",
|
||||
"clean": "rm -rf dist @types"
|
||||
},
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^9.2.2",
|
||||
@@ -31,6 +32,7 @@
|
||||
"@types/jest": "^27.5.0",
|
||||
"@types/uuid": "^9.0.2",
|
||||
"jest": "^27.0.0",
|
||||
"tsc-alias": "^1.8.8",
|
||||
"typescript": "^5.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,12 @@
|
||||
"moduleResolution": "Node16",
|
||||
"noEmit": false,
|
||||
"declaration": true,
|
||||
"outDir": "dist"
|
||||
"outDir": "dist",
|
||||
"declarationDir": "@types",
|
||||
"paths": {
|
||||
"loot-core/*": ["./@types/loot-core/*"],
|
||||
}
|
||||
},
|
||||
"include": ["."],
|
||||
"exclude": ["dist"]
|
||||
"exclude": ["**/node_modules/*", "dist", "@types"]
|
||||
}
|
||||
|
||||
21
packages/loot-core/bin/build-api
Executable file
21
packages/loot-core/bin/build-api
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.." || exit 1
|
||||
ROOT="$(pwd -P)"
|
||||
|
||||
yarn tsc -p tsconfig.api.json --outDir ../api/@types/loot-core/
|
||||
# Copy existing handwritten .d.ts files, as tsc doesn't move them for us
|
||||
dest="../../api/@types/loot-core"
|
||||
cd src
|
||||
find . -type f -name "*.d.ts" | while read -r f
|
||||
do
|
||||
d=$(dirname "${f}")
|
||||
d="${dest}/${d}"
|
||||
mkdir -p "${d}"
|
||||
cp "${f}" "${d}"
|
||||
done
|
||||
cd "$ROOT"
|
||||
yarn webpack --config ./webpack/webpack.api.config.js;
|
||||
./bin/copy-migrations ../api
|
||||
@@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"build:node": "cross-env NODE_ENV=production webpack --config ./webpack/webpack.desktop.config.js",
|
||||
"watch:node": "cross-env NODE_ENV=development webpack --config ./webpack/webpack.desktop.config.js --watch",
|
||||
"build:api": "cross-env NODE_ENV=development webpack --config ./webpack/webpack.api.config.js; ./bin/copy-migrations ../api",
|
||||
"build:api": "cross-env NODE_ENV=development ./bin/build-api",
|
||||
"build:browser": "cross-env NODE_ENV=production ./bin/build-browser",
|
||||
"watch:browser": "cross-env NODE_ENV=development ./bin/build-browser",
|
||||
"test": "npm-run-all -cp 'test:*'",
|
||||
|
||||
@@ -146,8 +146,8 @@ async function importTransactions(
|
||||
);
|
||||
|
||||
const payeesByTransferAcct = payees
|
||||
.filter((payee: YNAB5.Payee) => payee?.transfer_acct)
|
||||
.map((payee: YNAB5.Payee) => [payee.transfer_acct, payee]);
|
||||
.filter(payee => payee?.transfer_acct)
|
||||
.map(payee => [payee.transfer_acct, payee] as [string, YNAB5.Payee]);
|
||||
const payeeTransferAcctHashMap = new Map<string, YNAB5.Payee>(
|
||||
payeesByTransferAcct,
|
||||
);
|
||||
|
||||
@@ -2260,8 +2260,14 @@ export async function initApp(isDev, socketName) {
|
||||
}
|
||||
}
|
||||
|
||||
export type InitConfig = {
|
||||
dataDir?: string;
|
||||
serverURL?: string;
|
||||
password?: string;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-unused-modules
|
||||
export async function init(config) {
|
||||
export async function init(config: InitConfig) {
|
||||
// Get from build
|
||||
|
||||
let dataDir, serverURL;
|
||||
|
||||
@@ -51,7 +51,9 @@ export async function runHandler<T extends Handlers[keyof Handlers]>(
|
||||
}
|
||||
|
||||
if (mutatingMethods.has(handler)) {
|
||||
return runMutator(() => handler(args), { undoTag });
|
||||
return runMutator(() => handler(args), { undoTag }) as Promise<
|
||||
ReturnType<T>
|
||||
>;
|
||||
}
|
||||
|
||||
// When closing a file, it clears out all global state for the file. That
|
||||
@@ -67,7 +69,7 @@ export async function runHandler<T extends Handlers[keyof Handlers]>(
|
||||
promise.then(() => {
|
||||
runningMethods.delete(promise);
|
||||
});
|
||||
return promise;
|
||||
return promise as Promise<ReturnType<T>>;
|
||||
}
|
||||
|
||||
// These are useful for tests. Only use them in tests.
|
||||
|
||||
@@ -189,11 +189,13 @@ export function getRecurringDescription(config, dateFormat) {
|
||||
export function recurConfigToRSchedule(config) {
|
||||
const base: IRuleOptions = {
|
||||
start: monthUtils.parseDate(config.start),
|
||||
// @ts-ignore: issues with https://gitlab.com/john.carroll.p/rschedule/-/issues/86
|
||||
frequency: config.frequency.toUpperCase(),
|
||||
byHourOfDay: [12],
|
||||
};
|
||||
|
||||
if (config.interval) {
|
||||
// @ts-ignore: issues with https://gitlab.com/john.carroll.p/rschedule/-/issues/86
|
||||
base.interval = config.interval;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,10 +54,11 @@ export interface ApiHandlers {
|
||||
payees;
|
||||
}) => Promise<unknown>;
|
||||
|
||||
'api/transactions-import': (arg: {
|
||||
accountId;
|
||||
transactions;
|
||||
}) => Promise<unknown>;
|
||||
'api/transactions-import': (arg: { accountId; transactions }) => Promise<{
|
||||
errors?: { message: string }[];
|
||||
added;
|
||||
updated;
|
||||
}>;
|
||||
|
||||
'api/transactions-add': (arg: {
|
||||
accountId;
|
||||
|
||||
12
packages/loot-core/tsconfig.api.json
Normal file
12
packages/loot-core/tsconfig.api.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"allowJs": false,
|
||||
"noEmit": false,
|
||||
},
|
||||
"include": ["./typings", "./src/server/*"],
|
||||
"exclude": ["**/node_modules/*", "**/build/*", "**/lib-dist/*", "./src/server/bench.ts"],
|
||||
}
|
||||
@@ -29,6 +29,10 @@
|
||||
"module": "ES2022",
|
||||
// Until/if we build using tsc
|
||||
"noEmit": true,
|
||||
"paths": {
|
||||
// until we turn on composite/references
|
||||
"loot-core/*": ["./packages/loot-core/src/*"],
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "typescript-strict-plugin",
|
||||
|
||||
6
upcoming-release-notes/2053.md
Normal file
6
upcoming-release-notes/2053.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [twk3]
|
||||
---
|
||||
|
||||
Bundle loot-core types into the API
|
||||
44
yarn.lock
44
yarn.lock
@@ -31,6 +31,7 @@ __metadata:
|
||||
compare-versions: "npm:^6.1.0"
|
||||
jest: "npm:^27.0.0"
|
||||
node-fetch: "npm:^3.3.2"
|
||||
tsc-alias: "npm:^1.8.8"
|
||||
typescript: "npm:^5.0.2"
|
||||
uuid: "npm:^9.0.0"
|
||||
languageName: unknown
|
||||
@@ -6336,7 +6337,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"commander@npm:^9.4.1":
|
||||
"commander@npm:^9.0.0, commander@npm:^9.4.1":
|
||||
version: 9.5.0
|
||||
resolution: "commander@npm:9.5.0"
|
||||
checksum: 41c49b3d0f94a1fbeb0463c85b13f15aa15a9e0b4d5e10a49c0a1d58d4489b549d62262b052ae0aa6cfda53299bee487bfe337825df15e342114dde543f82906
|
||||
@@ -9216,7 +9217,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"globby@npm:^11.1.0":
|
||||
"globby@npm:^11.0.4, globby@npm:^11.1.0":
|
||||
version: 11.1.0
|
||||
resolution: "globby@npm:11.1.0"
|
||||
dependencies:
|
||||
@@ -12639,6 +12640,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mylas@npm:^2.1.9":
|
||||
version: 2.1.13
|
||||
resolution: "mylas@npm:2.1.13"
|
||||
checksum: 37f335424463c422f48d50317aa0a34fe410fabb146cbf27b453a0aa743732b5626f56deaa190bca2ce29836f809d88759007976dc78d5d22b75918a00586577
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nanoid@npm:^3.3.7":
|
||||
version: 3.3.7
|
||||
resolution: "nanoid@npm:3.3.7"
|
||||
@@ -13593,6 +13601,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"plimit-lit@npm:^1.2.6":
|
||||
version: 1.6.1
|
||||
resolution: "plimit-lit@npm:1.6.1"
|
||||
dependencies:
|
||||
queue-lit: "npm:^1.5.1"
|
||||
checksum: e4eaf018dc311fd4d452954c10992cd8a9eb72d168ec2274bb831d86558422703e1405a8978ffdd5c418654e6a25e10a0765a39bf3ce3a84dc799fe6268e0ea4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"plist@npm:^3.0.4, plist@npm:^3.0.5":
|
||||
version: 3.1.0
|
||||
resolution: "plist@npm:3.1.0"
|
||||
@@ -13841,6 +13858,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"queue-lit@npm:^1.5.1":
|
||||
version: 1.5.2
|
||||
resolution: "queue-lit@npm:1.5.2"
|
||||
checksum: 8dd45c79bd25b33b0c7d587391eb0b4acc4deb797bf92fef62b2d8e7c03b64083f5304f09d52a18267d34d020cc67ccde97a88185b67590eeccb194938ff1f98
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"queue-microtask@npm:^1.2.2":
|
||||
version: 1.2.3
|
||||
resolution: "queue-microtask@npm:1.2.3"
|
||||
@@ -16108,6 +16132,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tsc-alias@npm:^1.8.8":
|
||||
version: 1.8.8
|
||||
resolution: "tsc-alias@npm:1.8.8"
|
||||
dependencies:
|
||||
chokidar: "npm:^3.5.3"
|
||||
commander: "npm:^9.0.0"
|
||||
globby: "npm:^11.0.4"
|
||||
mylas: "npm:^2.1.9"
|
||||
normalize-path: "npm:^3.0.0"
|
||||
plimit-lit: "npm:^1.2.6"
|
||||
bin:
|
||||
tsc-alias: dist/bin/index.js
|
||||
checksum: 145d7bb23a618e1136c8addd4b4ed23a1d503a37d3fc5b3698a993fea9331180a68853b0e78ff50fb3fb7ed95d4996a2d82f77395814bbd1c40adee8a9151d90
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tsconfck@npm:^2.1.0":
|
||||
version: 2.1.2
|
||||
resolution: "tsconfck@npm:2.1.2"
|
||||
|
||||
Reference in New Issue
Block a user