actual-mcp-server 0.6.40 → 0.6.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/package.json +2 -2
- package/dist/src/lib/actual-adapter.js +42 -0
- package/dist/src/tools/category_groups_delete.js +3 -4
- package/dist/src/tools/payees_delete.js +7 -9
- package/dist/src/tools/rules_delete.js +3 -4
- package/dist/src/tools/schedules_delete.js +6 -8
- package/dist/src/tools/transactions_delete.js +2 -3
- package/dist/src/tools/transactions_update_batch.js +3 -3
- package/package.json +2 -2
package/README.md
CHANGED
package/dist/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "actual-mcp-server",
|
|
3
3
|
"displayName": "Actual MCP Server",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.42",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0",
|
|
7
7
|
"npm": ">=10.0.0"
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"verify-tools": "npm run build && node scripts/verify-tools.js",
|
|
31
31
|
"check:coverage": "node scripts/list-actual-api-methods.mjs",
|
|
32
32
|
"direct-sync": "node scripts/direct-sync/bank-sync-direct.mjs",
|
|
33
|
-
"test:unit-js": "node tests/unit/transactions_create.test.js && node tests/unit/generated_tools.smoke.test.js && node tests/unit/schema_validation.test.js && node tests/unit/config_https_validation.test.js && node tests/unit/config_insecure_upstream.test.js && node tests/unit/auth-acl.test.js && node tests/unit/bug76.test.js && node tests/unit/budgets_setAmount.test.js && node tests/unit/budgets_transfer.test.js && node tests/unit/transactions_uncategorized.test.js && node tests/unit/httpServer_session_init.test.js && node tests/unit/httpServer_session_not_found.test.js && node tests/unit/manual_mcp_client_retry.test.js && node tests/unit/manual_mcp_client_session.test.js && node tests/unit/manual_mcp_client_circuit.test.js && node tests/unit/manual_runner_killswitch.test.js && node tests/unit/adapter_auth_rate_limit.test.js && node tests/unit/adapter_session_reuse.test.js && node tests/unit/retry_classifier.test.js && node tests/unit/adapter_module_surface.test.js && node tests/unit/adapter_nonidempotent_no_retry.test.js && node tests/unit/pool_liveness.test.js && node tests/unit/pool_shutdown_all.test.js && node tests/unit/query_where_operators.test.js && node tests/unit/query_run_validation.test.js && node tests/unit/adapter_with_write_session.test.js && node tests/unit/category_groups_delete.test.js && node tests/unit/rules_delete.test.js && node tests/unit/schedules_delete.test.js && node tests/unit/payees_delete.test.js && node tests/unit/rules_create_or_update.test.js && node tests/unit/unhandled-rejection.test.js && node tests/unit/rejection-allowlist-purity.test.js && node tests/unit/httpServer_bearer_auth.test.js && node tests/unit/httpServer_oidc_audience.test.js && node tests/unit/httpServer_oidc_auth_verification.test.js && node tests/unit/httpServer_body_limit.test.js && node tests/unit/adapter_write_pool_cooperation.test.js && node tests/unit/budget_acl_enforcement.test.js && node tests/unit/budget_preference_store.test.js && node tests/unit/budget_preference_restore.test.js && node tests/unit/tags_list.test.js && node tests/unit/tags_create.test.js && node tests/unit/tags_update.test.js && node tests/unit/tags_delete.test.js && node tests/unit/notes_get.test.js && node tests/unit/notes_update.test.js && node tests/unit/payees_common_list.test.js && node tests/unit/check_coverage.test.js",
|
|
33
|
+
"test:unit-js": "node tests/unit/transactions_create.test.js && node tests/unit/generated_tools.smoke.test.js && node tests/unit/schema_validation.test.js && node tests/unit/config_https_validation.test.js && node tests/unit/config_insecure_upstream.test.js && node tests/unit/auth-acl.test.js && node tests/unit/bug76.test.js && node tests/unit/budgets_setAmount.test.js && node tests/unit/budgets_transfer.test.js && node tests/unit/transactions_uncategorized.test.js && node tests/unit/httpServer_session_init.test.js && node tests/unit/httpServer_session_not_found.test.js && node tests/unit/manual_mcp_client_retry.test.js && node tests/unit/manual_mcp_client_session.test.js && node tests/unit/manual_mcp_client_circuit.test.js && node tests/unit/manual_runner_killswitch.test.js && node tests/unit/adapter_auth_rate_limit.test.js && node tests/unit/adapter_session_reuse.test.js && node tests/unit/retry_classifier.test.js && node tests/unit/adapter_module_surface.test.js && node tests/unit/adapter_nonidempotent_no_retry.test.js && node tests/unit/pool_liveness.test.js && node tests/unit/pool_shutdown_all.test.js && node tests/unit/query_where_operators.test.js && node tests/unit/query_run_validation.test.js && node tests/unit/adapter_with_write_session.test.js && node tests/unit/category_groups_delete.test.js && node tests/unit/rules_delete.test.js && node tests/unit/schedules_delete.test.js && node tests/unit/payees_delete.test.js && node tests/unit/rules_create_or_update.test.js && node tests/unit/unhandled-rejection.test.js && node tests/unit/rejection-allowlist-purity.test.js && node tests/unit/httpServer_bearer_auth.test.js && node tests/unit/httpServer_oidc_audience.test.js && node tests/unit/httpServer_oidc_auth_verification.test.js && node tests/unit/httpServer_body_limit.test.js && node tests/unit/adapter_write_pool_cooperation.test.js && node tests/unit/budget_acl_enforcement.test.js && node tests/unit/budget_preference_store.test.js && node tests/unit/budget_preference_restore.test.js && node tests/unit/tags_list.test.js && node tests/unit/tags_create.test.js && node tests/unit/tags_update.test.js && node tests/unit/tags_delete.test.js && node tests/unit/notes_get.test.js && node tests/unit/notes_update.test.js && node tests/unit/payees_common_list.test.js && node tests/unit/transactions_update_guard.test.js && node tests/unit/check_coverage.test.js",
|
|
34
34
|
"test:adapter": "npm run build && node dist/src/tests_adapter_runner.js",
|
|
35
35
|
"test:e2e": "npx playwright test",
|
|
36
36
|
"test:e2e:docker": "./tests/e2e/run-docker-e2e.sh",
|
|
@@ -1010,6 +1010,18 @@ export async function updateTransaction(id, fields) {
|
|
|
1010
1010
|
observability.incrementToolCall('actual.transactions.update').catch(() => { });
|
|
1011
1011
|
// Use write queue to batch concurrent updates in a single budget session
|
|
1012
1012
|
return queueWriteOperation(async () => {
|
|
1013
|
+
// Pre-flight existence check (#212): the raw API silently no-ops on a missing id,
|
|
1014
|
+
// so an update that changed nothing would otherwise be reported as success. A
|
|
1015
|
+
// targeted ActualQL query by id keeps this cheap (indexed lookup).
|
|
1016
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1017
|
+
const { q } = (await import('@actual-app/api'));
|
|
1018
|
+
const found = await withConcurrency(() => retry(async () => {
|
|
1019
|
+
const res = (await rawRunQuery(q('transactions').filter({ id }).select(['id'])));
|
|
1020
|
+
return Array.isArray(res?.data) && res.data.length > 0;
|
|
1021
|
+
}, { retries: 2, backoffMs: 200 }));
|
|
1022
|
+
if (!found) {
|
|
1023
|
+
throw new Error(`Transaction "${id}" not found. Use actual_transactions_get to list transactions.`);
|
|
1024
|
+
}
|
|
1013
1025
|
await withConcurrency(() => retry(() => rawUpdateTransaction(id, fields), { retries: 0, backoffMs: 200, isRetryable: isRetryableError }));
|
|
1014
1026
|
});
|
|
1015
1027
|
}
|
|
@@ -1019,9 +1031,27 @@ export async function updateTransactionBatch(updates) {
|
|
|
1019
1031
|
// Sequential loop (not Promise.all) is intentional: concurrent rawUpdateTransaction calls
|
|
1020
1032
|
// within one session can interleave withMutation CRDT messages unpredictably.
|
|
1021
1033
|
return queueWriteOperation(async () => {
|
|
1034
|
+
// Nothing to do for an empty batch. The tool enforces min 1, but the adapter is a
|
|
1035
|
+
// public export, so guard here and avoid an empty-$oneof existence query.
|
|
1036
|
+
if (updates.length === 0)
|
|
1037
|
+
return { succeeded: [], failed: [] };
|
|
1038
|
+
// Pre-flight existence check (#212): ONE query for all ids (not one per item),
|
|
1039
|
+
// since the raw API silently no-ops on a missing id and would otherwise report
|
|
1040
|
+
// a no-op as success. Missing ids are routed to failed[] without an update call.
|
|
1041
|
+
const ids = updates.map((u) => u.id);
|
|
1042
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1043
|
+
const { q } = (await import('@actual-app/api'));
|
|
1044
|
+
const existing = await withConcurrency(() => retry(async () => {
|
|
1045
|
+
const res = (await rawRunQuery(q('transactions').filter({ id: { $oneof: ids } }).select(['id'])));
|
|
1046
|
+
return new Set(Array.isArray(res?.data) ? res.data.map((r) => r.id) : []);
|
|
1047
|
+
}, { retries: 2, backoffMs: 200 }));
|
|
1022
1048
|
const succeeded = [];
|
|
1023
1049
|
const failed = [];
|
|
1024
1050
|
for (const { id, fields } of updates) {
|
|
1051
|
+
if (!existing.has(id)) {
|
|
1052
|
+
failed.push({ id, error: `Transaction "${id}" not found. Use actual_transactions_get to list transactions.` });
|
|
1053
|
+
continue;
|
|
1054
|
+
}
|
|
1025
1055
|
try {
|
|
1026
1056
|
await withConcurrency(() => retry(() => rawUpdateTransaction(id, fields), { retries: 0, backoffMs: 200, isRetryable: isRetryableError }));
|
|
1027
1057
|
succeeded.push({ id });
|
|
@@ -1037,6 +1067,18 @@ export async function updateTransactionBatch(updates) {
|
|
|
1037
1067
|
export async function deleteTransaction(id) {
|
|
1038
1068
|
observability.incrementToolCall('actual.transactions.delete').catch(() => { });
|
|
1039
1069
|
return queueWriteOperation(async () => {
|
|
1070
|
+
// Pre-flight existence check: the raw API silently no-ops on a missing id and would
|
|
1071
|
+
// otherwise report success for a delete that removed nothing. A targeted ActualQL
|
|
1072
|
+
// query by id keeps this cheap (indexed lookup, not a full-table scan).
|
|
1073
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1074
|
+
const { q } = (await import('@actual-app/api'));
|
|
1075
|
+
const found = await withConcurrency(() => retry(async () => {
|
|
1076
|
+
const res = (await rawRunQuery(q('transactions').filter({ id }).select(['id'])));
|
|
1077
|
+
return Array.isArray(res?.data) && res.data.length > 0;
|
|
1078
|
+
}, { retries: 2, backoffMs: 200 }));
|
|
1079
|
+
if (!found) {
|
|
1080
|
+
throw new Error(`Transaction "${id}" not found. Use actual_transactions_get to list transactions.`);
|
|
1081
|
+
}
|
|
1040
1082
|
// Non-idempotent: do not retry (#165). A retry after a lost-response would
|
|
1041
1083
|
// re-issue the delete against an already-removed record and surface a
|
|
1042
1084
|
// confusing "not found" even though the first attempt succeeded.
|
|
@@ -20,10 +20,9 @@ const tool = {
|
|
|
20
20
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
21
|
const groupExists = groups.some((g) => g.id === input.id);
|
|
22
22
|
if (!groupExists) {
|
|
23
|
-
return {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
};
|
|
23
|
+
// Throw (not return {success:false}) so a non-existent id surfaces as an MCP
|
|
24
|
+
// error, consistent with every other delete tool's not-found behavior.
|
|
25
|
+
throw new Error(notFoundMsg('Category group', input.id, 'actual_category_groups_get'));
|
|
27
26
|
}
|
|
28
27
|
await rawDeleteCategoryGroup(input.id);
|
|
29
28
|
return { success: true };
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import adapter from '../lib/actual-adapter.js';
|
|
3
|
-
import api from '@actual-app/api';
|
|
4
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
|
-
const { deletePayee: rawDeletePayee } = api;
|
|
6
3
|
const InputSchema = z.object({
|
|
7
4
|
id: z.string().describe('Payee ID to delete'),
|
|
8
5
|
});
|
|
@@ -12,12 +9,13 @@ const tool = {
|
|
|
12
9
|
inputSchema: InputSchema,
|
|
13
10
|
call: async (args, _meta) => {
|
|
14
11
|
const input = InputSchema.parse(args || {});
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
// Route through the guarded adapter method (pre-flight existence check), not the
|
|
13
|
+
// raw api.deletePayee. The raw call throws a cryptic "Cannot destructure property
|
|
14
|
+
// 'transfer_acct' of null" on a non-existent id; adapter.deletePayee returns an
|
|
15
|
+
// actionable "Payee not found" instead. adapter.deletePayee already runs inside a
|
|
16
|
+
// single write-queue cycle, preserving the #142 lock invariant.
|
|
17
|
+
await adapter.deletePayee(input.id);
|
|
18
|
+
return { success: true };
|
|
21
19
|
},
|
|
22
20
|
};
|
|
23
21
|
export default tool;
|
|
@@ -20,10 +20,9 @@ const tool = {
|
|
|
20
20
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
21
|
const ruleExists = allRules.some((r) => r.id === input.id);
|
|
22
22
|
if (!ruleExists) {
|
|
23
|
-
return {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
};
|
|
23
|
+
// Throw (not return {success:false}) so a non-existent id surfaces as an MCP
|
|
24
|
+
// error, consistent with every other delete tool's not-found behavior.
|
|
25
|
+
throw new Error(notFoundMsg('Rule', input.id, 'actual_rules_get'));
|
|
27
26
|
}
|
|
28
27
|
await rawDeleteRule(input.id);
|
|
29
28
|
return { success: true };
|
|
@@ -23,10 +23,9 @@ const tool = {
|
|
|
23
23
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
24
|
const scheduleExists = schedules.some((s) => s.id === input.id);
|
|
25
25
|
if (!scheduleExists) {
|
|
26
|
-
return {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
};
|
|
26
|
+
// Throw (not return {success:false}) so a non-existent id surfaces as an MCP
|
|
27
|
+
// error, consistent with every other delete tool's not-found behavior.
|
|
28
|
+
throw new Error(notFoundMsg('Schedule', input.id, 'actual_schedules_get'));
|
|
30
29
|
}
|
|
31
30
|
try {
|
|
32
31
|
await rawDeleteSchedule(input.id);
|
|
@@ -35,10 +34,9 @@ const tool = {
|
|
|
35
34
|
catch (err) {
|
|
36
35
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37
36
|
if (msg.includes('NOT NULL constraint') || msg.includes('messages_crdt')) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
};
|
|
37
|
+
// Translate the raw SQLite constraint error into an actionable message, thrown
|
|
38
|
+
// for the same consistency reason as the not-found case above.
|
|
39
|
+
throw new Error(constraintErrorMsg('Schedule', input.id, 'actual_schedules_get'));
|
|
42
40
|
}
|
|
43
41
|
throw err;
|
|
44
42
|
}
|
|
@@ -6,9 +6,8 @@ const InputSchema = z.object({
|
|
|
6
6
|
const tool = {
|
|
7
7
|
name: 'actual_transactions_delete',
|
|
8
8
|
description: 'Delete a transaction from Actual Budget by its ID. ' +
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'Use actual_transactions_filter to verify the transaction exists before deleting. ' +
|
|
9
|
+
'If the transaction does not exist, the call returns an actionable "not found" error ' +
|
|
10
|
+
'(the adapter verifies existence first, since the raw Actual Budget API silently no-ops on a missing id). ' +
|
|
12
11
|
'This permanently removes the transaction and updates account balances accordingly. This operation cannot be undone.',
|
|
13
12
|
inputSchema: InputSchema,
|
|
14
13
|
call: async (args, _meta) => {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Credit: ZanzyTHEbar (https://github.com/ZanzyTHEbar)
|
|
9
9
|
*
|
|
10
10
|
* Adapted for this project's conventions:
|
|
11
|
-
* - No wrapToolCall
|
|
11
|
+
* - No wrapToolCall: uses direct call() pattern
|
|
12
12
|
* - Returns a plain BatchResult object (no { result } wrapper)
|
|
13
13
|
*/
|
|
14
14
|
import { z } from 'zod';
|
|
@@ -36,7 +36,7 @@ const InputSchema = z.object({
|
|
|
36
36
|
});
|
|
37
37
|
const tool = {
|
|
38
38
|
name: 'actual_transactions_update_batch',
|
|
39
|
-
description: `Update multiple transactions in a single call. Accepts up to
|
|
39
|
+
description: `Update multiple transactions in a single call. Accepts up to 50 {id, fields} pairs. Each update is applied independently: partial failures are reported per-item so you know exactly which succeeded and which failed.
|
|
40
40
|
|
|
41
41
|
Returns: { succeeded: [{id}], failed: [{id, error}], total, successCount, failureCount }
|
|
42
42
|
|
|
@@ -45,7 +45,7 @@ Example: { updates: [{ id: "txn-uuid-1", fields: { category: "cat-uuid" } }, { i
|
|
|
45
45
|
call: async (args, _meta) => {
|
|
46
46
|
try {
|
|
47
47
|
const input = InputSchema.parse(args || {});
|
|
48
|
-
// Single adapter call
|
|
48
|
+
// Single adapter call: all updates share one init/sync/shutdown cycle (fixes issue #79).
|
|
49
49
|
// Calling adapter.updateTransaction() in a loop would trigger N separate budget sessions.
|
|
50
50
|
const { succeeded, failed } = await adapter.updateTransactionBatch(input.updates);
|
|
51
51
|
const result = {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "actual-mcp-server",
|
|
3
3
|
"displayName": "Actual MCP Server",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.42",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0",
|
|
7
7
|
"npm": ">=10.0.0"
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"verify-tools": "npm run build && node scripts/verify-tools.js",
|
|
31
31
|
"check:coverage": "node scripts/list-actual-api-methods.mjs",
|
|
32
32
|
"direct-sync": "node scripts/direct-sync/bank-sync-direct.mjs",
|
|
33
|
-
"test:unit-js": "node tests/unit/transactions_create.test.js && node tests/unit/generated_tools.smoke.test.js && node tests/unit/schema_validation.test.js && node tests/unit/config_https_validation.test.js && node tests/unit/config_insecure_upstream.test.js && node tests/unit/auth-acl.test.js && node tests/unit/bug76.test.js && node tests/unit/budgets_setAmount.test.js && node tests/unit/budgets_transfer.test.js && node tests/unit/transactions_uncategorized.test.js && node tests/unit/httpServer_session_init.test.js && node tests/unit/httpServer_session_not_found.test.js && node tests/unit/manual_mcp_client_retry.test.js && node tests/unit/manual_mcp_client_session.test.js && node tests/unit/manual_mcp_client_circuit.test.js && node tests/unit/manual_runner_killswitch.test.js && node tests/unit/adapter_auth_rate_limit.test.js && node tests/unit/adapter_session_reuse.test.js && node tests/unit/retry_classifier.test.js && node tests/unit/adapter_module_surface.test.js && node tests/unit/adapter_nonidempotent_no_retry.test.js && node tests/unit/pool_liveness.test.js && node tests/unit/pool_shutdown_all.test.js && node tests/unit/query_where_operators.test.js && node tests/unit/query_run_validation.test.js && node tests/unit/adapter_with_write_session.test.js && node tests/unit/category_groups_delete.test.js && node tests/unit/rules_delete.test.js && node tests/unit/schedules_delete.test.js && node tests/unit/payees_delete.test.js && node tests/unit/rules_create_or_update.test.js && node tests/unit/unhandled-rejection.test.js && node tests/unit/rejection-allowlist-purity.test.js && node tests/unit/httpServer_bearer_auth.test.js && node tests/unit/httpServer_oidc_audience.test.js && node tests/unit/httpServer_oidc_auth_verification.test.js && node tests/unit/httpServer_body_limit.test.js && node tests/unit/adapter_write_pool_cooperation.test.js && node tests/unit/budget_acl_enforcement.test.js && node tests/unit/budget_preference_store.test.js && node tests/unit/budget_preference_restore.test.js && node tests/unit/tags_list.test.js && node tests/unit/tags_create.test.js && node tests/unit/tags_update.test.js && node tests/unit/tags_delete.test.js && node tests/unit/notes_get.test.js && node tests/unit/notes_update.test.js && node tests/unit/payees_common_list.test.js && node tests/unit/check_coverage.test.js",
|
|
33
|
+
"test:unit-js": "node tests/unit/transactions_create.test.js && node tests/unit/generated_tools.smoke.test.js && node tests/unit/schema_validation.test.js && node tests/unit/config_https_validation.test.js && node tests/unit/config_insecure_upstream.test.js && node tests/unit/auth-acl.test.js && node tests/unit/bug76.test.js && node tests/unit/budgets_setAmount.test.js && node tests/unit/budgets_transfer.test.js && node tests/unit/transactions_uncategorized.test.js && node tests/unit/httpServer_session_init.test.js && node tests/unit/httpServer_session_not_found.test.js && node tests/unit/manual_mcp_client_retry.test.js && node tests/unit/manual_mcp_client_session.test.js && node tests/unit/manual_mcp_client_circuit.test.js && node tests/unit/manual_runner_killswitch.test.js && node tests/unit/adapter_auth_rate_limit.test.js && node tests/unit/adapter_session_reuse.test.js && node tests/unit/retry_classifier.test.js && node tests/unit/adapter_module_surface.test.js && node tests/unit/adapter_nonidempotent_no_retry.test.js && node tests/unit/pool_liveness.test.js && node tests/unit/pool_shutdown_all.test.js && node tests/unit/query_where_operators.test.js && node tests/unit/query_run_validation.test.js && node tests/unit/adapter_with_write_session.test.js && node tests/unit/category_groups_delete.test.js && node tests/unit/rules_delete.test.js && node tests/unit/schedules_delete.test.js && node tests/unit/payees_delete.test.js && node tests/unit/rules_create_or_update.test.js && node tests/unit/unhandled-rejection.test.js && node tests/unit/rejection-allowlist-purity.test.js && node tests/unit/httpServer_bearer_auth.test.js && node tests/unit/httpServer_oidc_audience.test.js && node tests/unit/httpServer_oidc_auth_verification.test.js && node tests/unit/httpServer_body_limit.test.js && node tests/unit/adapter_write_pool_cooperation.test.js && node tests/unit/budget_acl_enforcement.test.js && node tests/unit/budget_preference_store.test.js && node tests/unit/budget_preference_restore.test.js && node tests/unit/tags_list.test.js && node tests/unit/tags_create.test.js && node tests/unit/tags_update.test.js && node tests/unit/tags_delete.test.js && node tests/unit/notes_get.test.js && node tests/unit/notes_update.test.js && node tests/unit/payees_common_list.test.js && node tests/unit/transactions_update_guard.test.js && node tests/unit/check_coverage.test.js",
|
|
34
34
|
"test:adapter": "npm run build && node dist/src/tests_adapter_runner.js",
|
|
35
35
|
"test:e2e": "npx playwright test",
|
|
36
36
|
"test:e2e:docker": "./tests/e2e/run-docker-e2e.sh",
|