@emilfrom/n8n-nodes-actual-budget 1.0.2 → 2.0.1

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 CHANGED
@@ -1,8 +1,7 @@
1
1
  # @emilfrom/n8n-nodes-actual-budget
2
2
 
3
- This is an n8n community node for importing income and expenses into
4
- [Actual Budget](https://actualbudget.org/) through
5
- [actual-http-api](https://github.com/jhonderson/actual-http-api).
3
+ This is an n8n community node for working with [Actual Budget](https://actualbudget.org/)
4
+ through [actual-http-api](https://github.com/jhonderson/actual-http-api).
6
5
 
7
6
  [n8n](https://n8n.io/) is a workflow automation platform.
8
7
 
@@ -51,39 +50,60 @@ Create an `Actual HTTP API` credential in n8n with:
51
50
 
52
51
  The credential test calls `GET /budgets/{budgetSyncId}/accounts`.
53
52
 
54
- ## Operations
53
+ ## Node version 2 (breaking change)
55
54
 
56
- ### Transaction: Import Expense/Income
55
+ Version **2** of the **Actual Budget** node removes the old **Import** operation (which used
56
+ `POST .../transactions/import` and `imported_id` dedupe). Workflows must use **Transaction: Create**
57
+ and amounts as **integer minor units**, matching the [actual-http-api](https://github.com/jhonderson/actual-http-api)
58
+ contract (`value * 100` for most currencies).
57
59
 
58
- Imports one transaction per incoming n8n item using Actual's transaction import
59
- endpoint. This operation is idempotent when the same transaction UUID is reused,
60
- because the UUID is sent as `imported_id`.
60
+ ## Actions
61
61
 
62
- Required fields:
62
+ Pick an **Action** on the node:
63
63
 
64
- - `Account`: Actual account ID, selectable from actual-http-api
65
- - `Transaction Type`: `Expense` or `Income`
66
- - `Amount`: Positive decimal amount, for example `12.34`
67
- - `Date`: Transaction date or date-time. Actual stores the date only.
68
- - `Transaction UUID`: Stable unique identifier for dedupe/upsert behavior
69
- - `Payee`: Existing payee from the dropdown or a payee name
70
- - `Category`: Actual category ID, selectable from actual-http-api
64
+ ### Transaction: Create
71
65
 
72
- Optional fields:
66
+ Creates one transaction per input item using the official single-create endpoint:
73
67
 
74
- - `Notes`
75
- - `Cleared`
76
- - `Default Cleared`
77
- - `Dry Run`
78
- - `Reimport Deleted`
68
+ `POST /budgets/{budgetSyncId}/accounts/{accountId}/transactions`
79
69
 
80
- ## Amounts
70
+ Request body matches the API: `learnCategories`, `runTransfers`, and `transaction` with
71
+ `account`, `category`, `amount`, `payee_name`, `date`, `cleared` only.
81
72
 
82
- Enter positive decimal amounts. The node converts them to Actual's integer amount
83
- format:
73
+ - **Account** / **Category**: selectable lists from `GET /accounts` and `GET /categories`
74
+ - **Amount**: integer minor units (for example `-7374` for `-73.74` in a two-decimal currency)
75
+ - **Date**: stored as `YYYY-MM-DD`
76
+ - **Payee Name**: plain text (for example `Amazon`)
77
+ - **Cleared**, **Learn Categories**, **Run Transfers**: booleans
84
78
 
85
- - Expense `12.34` becomes `-1234`
86
- - Income `12.34` becomes `1234`
79
+ ### Budget: Get Month
80
+
81
+ `GET /budgets/{budgetSyncId}/months/{month}` — pass **Month** as `YYYY-MM`. Output includes the API
82
+ `data` payload (totals, category groups, etc.).
83
+
84
+ ### Account: List
85
+
86
+ `GET /budgets/{budgetSyncId}/accounts` — returns the API response (including `data`).
87
+
88
+ ### Category: List
89
+
90
+ `GET /budgets/{budgetSyncId}/categories` — returns the API response (including `data`).
91
+
92
+ ## Debugging (logs)
93
+
94
+ This node uses n8n’s **`LoggerProxy`** from `n8n-workflow` (same approach as [n8n’s logging docs](https://docs.n8n.io/hosting/logging-monitoring/logging/)): each outbound call logs at **info** (method, resource, URL with the sync id redacted), extra hints at **debug**, and **warn** if the request fails (HTTP status when present).
95
+
96
+ **Where to read logs:** server stdout/stderr (Docker/Kubernetes logs) or the log file if you enable file output — not the workflow execution panel in the editor.
97
+
98
+ **Environment variables** (optional; defaults are usually enough until you need more detail):
99
+
100
+ | Variable | Typical value | Purpose |
101
+ |----------|-----------------|--------|
102
+ | `N8N_LOG_LEVEL` | `info` (default) | Use **`debug`** to include `Logger.debug` lines (credential-shape hints). |
103
+ | `N8N_LOG_OUTPUT` | `console` (default) | Use `console,file` and set `N8N_LOG_FILE_LOCATION` if you want a persistent log file. |
104
+ | `N8N_LOG_FILE_LOCATION` | e.g. `/home/node/.n8n/logs/n8n.log` | Path when `N8N_LOG_OUTPUT` includes `file`. |
105
+
106
+ After changing env vars, restart n8n.
87
107
 
88
108
  ## Development
89
109
 
@@ -5,12 +5,10 @@ export declare class ActualBudget implements INodeType {
5
5
  listSearch: {
6
6
  getAccounts: typeof getAccounts;
7
7
  getCategories: typeof getCategories;
8
- getPayees: typeof getPayees;
9
8
  };
10
9
  };
11
10
  execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
12
11
  }
13
12
  declare function getAccounts(this: ILoadOptionsFunctions, filter?: string): Promise<INodeListSearchResult>;
14
13
  declare function getCategories(this: ILoadOptionsFunctions, filter?: string): Promise<INodeListSearchResult>;
15
- declare function getPayees(this: ILoadOptionsFunctions, filter?: string): Promise<INodeListSearchResult>;
16
14
  export {};
@@ -3,9 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ActualBudget = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
5
  const transport_1 = require("./transport");
6
- const showForImportTransaction = {
7
- resource: ['transaction'],
8
- operation: ['import'],
6
+ const showForTransactionCreate = {
7
+ action: ['transactionCreate'],
8
+ };
9
+ const showForBudgetGet = {
10
+ action: ['budgetGet'],
9
11
  };
10
12
  function parseActualDate(value) {
11
13
  const rawDate = value.trim();
@@ -19,12 +21,15 @@ function parseActualDate(value) {
19
21
  }
20
22
  return parsedDate.toISOString().slice(0, 10);
21
23
  }
22
- function decimalToActualAmount(value, transactionType) {
23
- if (!Number.isFinite(value) || value <= 0) {
24
- throw new n8n_workflow_1.ApplicationError('Amount must be a positive number');
24
+ function parseMinorUnitsAmount(value) {
25
+ if (!Number.isFinite(value)) {
26
+ throw new n8n_workflow_1.ApplicationError('Amount must be an integer in minor currency units (e.g. -7374 for -73.74)');
27
+ }
28
+ const rounded = Math.round(value);
29
+ if (Math.abs(value - rounded) > 1e-9) {
30
+ throw new n8n_workflow_1.ApplicationError('Amount must be an integer in minor currency units (e.g. -7374 for -73.74)');
25
31
  }
26
- const unsignedAmount = Math.round(value * 100);
27
- return transactionType === 'expense' ? -unsignedAmount : unsignedAmount;
32
+ return rounded;
28
33
  }
29
34
  function filterResults(items, filter) {
30
35
  if (!filter) {
@@ -40,9 +45,9 @@ class ActualBudget {
40
45
  name: 'actualBudget',
41
46
  icon: { light: 'file:actual-budget.svg', dark: 'file:actual-budget.dark.svg' },
42
47
  group: ['input'],
43
- version: 1,
44
- subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
45
- description: 'Import income and expenses into Actual Budget via actual-http-api',
48
+ version: 2,
49
+ subtitle: '={{$parameter["action"]}}',
50
+ description: 'Read budgets and create transactions in Actual Budget via actual-http-api (official endpoints)',
46
51
  defaults: {
47
52
  name: 'Actual Budget',
48
53
  },
@@ -57,37 +62,37 @@ class ActualBudget {
57
62
  ],
58
63
  properties: [
59
64
  {
60
- displayName: 'Resource',
61
- name: 'resource',
65
+ displayName: 'Action',
66
+ name: 'action',
62
67
  type: 'options',
63
68
  noDataExpression: true,
64
69
  options: [
65
70
  {
66
- name: 'Transaction',
67
- value: 'transaction',
71
+ name: 'Account: List',
72
+ value: 'accountList',
73
+ action: 'List accounts',
74
+ description: 'GET /accounts',
68
75
  },
69
- ],
70
- default: 'transaction',
71
- },
72
- {
73
- displayName: 'Operation',
74
- name: 'operation',
75
- type: 'options',
76
- noDataExpression: true,
77
- displayOptions: {
78
- show: {
79
- resource: ['transaction'],
76
+ {
77
+ name: 'Budget: Get Month',
78
+ value: 'budgetGet',
79
+ action: 'Get budget month',
80
+ description: 'GET /months/{month}',
81
+ },
82
+ {
83
+ name: 'Category: List',
84
+ value: 'categoryList',
85
+ action: 'List categories',
86
+ description: 'GET /categories',
80
87
  },
81
- },
82
- options: [
83
88
  {
84
- name: 'Import Expense/Income',
85
- value: 'import',
86
- action: 'Import an income or expense transaction',
87
- description: 'Import one transaction using Actual import dedupe behavior',
89
+ name: 'Transaction: Create',
90
+ value: 'transactionCreate',
91
+ action: 'Create a transaction',
92
+ description: 'POST /accounts/{ID}/transactions',
88
93
  },
89
94
  ],
90
- default: 'import',
95
+ default: 'transactionCreate',
91
96
  },
92
97
  {
93
98
  displayName: 'Account',
@@ -96,7 +101,7 @@ class ActualBudget {
96
101
  default: { mode: 'list', value: '' },
97
102
  required: true,
98
103
  displayOptions: {
99
- show: showForImportTransaction,
104
+ show: showForTransactionCreate,
100
105
  },
101
106
  modes: [
102
107
  {
@@ -117,27 +122,37 @@ class ActualBudget {
117
122
  placeholder: 'e.g. 729cb492-4eab-468b-9522-75d455cded22',
118
123
  },
119
124
  ],
120
- description: 'Actual account to import the transaction into',
125
+ description: 'Account for the transaction',
121
126
  },
122
127
  {
123
- displayName: 'Transaction Type',
124
- name: 'transactionType',
125
- type: 'options',
128
+ displayName: 'Category',
129
+ name: 'categoryId',
130
+ type: 'resourceLocator',
131
+ default: { mode: 'list', value: '' },
132
+ required: true,
126
133
  displayOptions: {
127
- show: showForImportTransaction,
134
+ show: showForTransactionCreate,
128
135
  },
129
- options: [
136
+ modes: [
130
137
  {
131
- name: 'Expense',
132
- value: 'expense',
138
+ displayName: 'Category',
139
+ name: 'list',
140
+ type: 'list',
141
+ placeholder: 'Select a category...',
142
+ typeOptions: {
143
+ searchListMethod: 'getCategories',
144
+ searchable: true,
145
+ searchFilterRequired: false,
146
+ },
133
147
  },
134
148
  {
135
- name: 'Income',
136
- value: 'income',
149
+ displayName: 'By ID',
150
+ name: 'id',
151
+ type: 'string',
152
+ placeholder: 'e.g. 9fa2550c-c3ff-498b-8df6-e0fbe2a62e0e',
137
153
  },
138
154
  ],
139
- default: 'expense',
140
- description: 'Whether the positive amount should be imported as money out or money in',
155
+ description: 'Category ID for the transaction',
141
156
  },
142
157
  {
143
158
  displayName: 'Amount',
@@ -145,14 +160,13 @@ class ActualBudget {
145
160
  type: 'number',
146
161
  required: true,
147
162
  displayOptions: {
148
- show: showForImportTransaction,
163
+ show: showForTransactionCreate,
149
164
  },
150
165
  typeOptions: {
151
- minValue: 0,
152
- numberPrecision: 2,
166
+ numberPrecision: 0,
153
167
  },
154
168
  default: 0,
155
- description: 'Positive decimal amount, for example 12.34',
169
+ description: 'Integer minor units (see actual-http-api docs). Example: expense -7374 means -73.74.',
156
170
  },
157
171
  {
158
172
  displayName: 'Date',
@@ -160,143 +174,64 @@ class ActualBudget {
160
174
  type: 'dateTime',
161
175
  required: true,
162
176
  displayOptions: {
163
- show: showForImportTransaction,
177
+ show: showForTransactionCreate,
164
178
  },
165
179
  default: '',
166
- description: 'Transaction date. Actual stores transactions by date, so time is ignored.',
180
+ description: 'Transaction date (YYYY-MM-DD); time is ignored',
167
181
  },
168
182
  {
169
- displayName: 'Transaction UUID',
170
- name: 'transactionUuid',
183
+ displayName: 'Payee Name',
184
+ name: 'payeeName',
171
185
  type: 'string',
172
186
  required: true,
173
187
  displayOptions: {
174
- show: showForImportTransaction,
188
+ show: showForTransactionCreate,
175
189
  },
176
190
  default: '',
177
- description: 'Stable transaction identifier to send as imported_id for dedupe/upsert behavior',
191
+ placeholder: 'e.g. Amazon',
192
+ description: 'Payee label for the transaction',
178
193
  },
179
194
  {
180
- displayName: 'Payee',
181
- name: 'payeeName',
182
- type: 'resourceLocator',
183
- default: { mode: 'list', value: '' },
184
- required: true,
195
+ displayName: 'Cleared',
196
+ name: 'cleared',
197
+ type: 'boolean',
185
198
  displayOptions: {
186
- show: showForImportTransaction,
199
+ show: showForTransactionCreate,
187
200
  },
188
- modes: [
189
- {
190
- displayName: 'Payee',
191
- name: 'list',
192
- type: 'list',
193
- placeholder: 'Select a payee...',
194
- typeOptions: {
195
- searchListMethod: 'getPayees',
196
- searchable: true,
197
- searchFilterRequired: false,
198
- },
199
- },
200
- {
201
- displayName: 'By Name',
202
- name: 'name',
203
- type: 'string',
204
- placeholder: 'e.g. Grocery Store',
205
- },
206
- ],
207
- description: 'Existing payee to use, or a payee name for Actual to match/create',
201
+ default: false,
202
+ description: 'Whether the transaction is cleared',
208
203
  },
209
204
  {
210
- displayName: 'Category',
211
- name: 'categoryId',
212
- type: 'resourceLocator',
213
- default: { mode: 'list', value: '' },
214
- required: true,
205
+ displayName: 'Learn Categories',
206
+ name: 'learnCategories',
207
+ type: 'boolean',
215
208
  displayOptions: {
216
- show: showForImportTransaction,
209
+ show: showForTransactionCreate,
217
210
  },
218
- modes: [
219
- {
220
- displayName: 'Category',
221
- name: 'list',
222
- type: 'list',
223
- placeholder: 'Select a category...',
224
- typeOptions: {
225
- searchListMethod: 'getCategories',
226
- searchable: true,
227
- searchFilterRequired: false,
228
- },
229
- },
230
- {
231
- displayName: 'By ID',
232
- name: 'id',
233
- type: 'string',
234
- placeholder: 'e.g. 9fa2550c-c3ff-498b-8df6-e0fbe2a62e0e',
235
- },
236
- ],
237
- description: 'Actual category for the transaction',
211
+ default: false,
212
+ description: 'Whether to pass learnCategories to actual-http-api',
238
213
  },
239
214
  {
240
- displayName: 'Additional Fields',
241
- name: 'additionalFields',
242
- type: 'collection',
243
- placeholder: 'Add Field',
244
- default: {},
215
+ displayName: 'Run Transfers',
216
+ name: 'runTransfers',
217
+ type: 'boolean',
245
218
  displayOptions: {
246
- show: showForImportTransaction,
219
+ show: showForTransactionCreate,
247
220
  },
248
- options: [
249
- {
250
- displayName: 'Cleared',
251
- name: 'cleared',
252
- type: 'boolean',
253
- default: true,
254
- description: 'Whether this transaction should be marked as cleared',
255
- },
256
- {
257
- displayName: 'Notes',
258
- name: 'notes',
259
- type: 'string',
260
- typeOptions: {
261
- rows: 3,
262
- },
263
- default: '',
264
- description: 'Optional transaction notes',
265
- },
266
- ],
221
+ default: false,
222
+ description: 'Whether to pass runTransfers to actual-http-api',
267
223
  },
268
224
  {
269
- displayName: 'Import Options',
270
- name: 'importOptions',
271
- type: 'collection',
272
- placeholder: 'Add Option',
273
- default: {},
225
+ displayName: 'Month',
226
+ name: 'month',
227
+ type: 'string',
228
+ required: true,
274
229
  displayOptions: {
275
- show: showForImportTransaction,
230
+ show: showForBudgetGet,
276
231
  },
277
- options: [
278
- {
279
- displayName: 'Default Cleared',
280
- name: 'defaultCleared',
281
- type: 'boolean',
282
- default: true,
283
- description: 'Whether imported transactions should default to cleared',
284
- },
285
- {
286
- displayName: 'Dry Run',
287
- name: 'dryRun',
288
- type: 'boolean',
289
- default: false,
290
- description: 'Whether to preview import results without changing the budget',
291
- },
292
- {
293
- displayName: 'Reimport Deleted',
294
- name: 'reimportDeleted',
295
- type: 'boolean',
296
- default: false,
297
- description: 'Whether to reimport transactions previously deleted in Actual',
298
- },
299
- ],
232
+ default: '',
233
+ placeholder: '2026-05',
234
+ description: 'Budget month in YYYY-MM format',
300
235
  },
301
236
  ],
302
237
  };
@@ -304,58 +239,80 @@ class ActualBudget {
304
239
  listSearch: {
305
240
  getAccounts,
306
241
  getCategories,
307
- getPayees,
308
242
  },
309
243
  };
310
244
  }
311
245
  async execute() {
312
- var _a, _b, _c;
313
246
  const items = this.getInputData();
314
247
  const returnData = [];
315
248
  for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
316
249
  try {
317
- const accountId = this.getNodeParameter('accountId', itemIndex, '', {
318
- extractValue: true,
319
- });
320
- const categoryId = this.getNodeParameter('categoryId', itemIndex, '', {
321
- extractValue: true,
322
- });
323
- const payeeName = this.getNodeParameter('payeeName', itemIndex, '', {
324
- extractValue: true,
325
- });
326
- const transactionType = this.getNodeParameter('transactionType', itemIndex);
327
- const amount = this.getNodeParameter('amount', itemIndex);
328
- const date = this.getNodeParameter('date', itemIndex);
329
- const transactionUuid = this.getNodeParameter('transactionUuid', itemIndex);
330
- const additionalFields = this.getNodeParameter('additionalFields', itemIndex, {});
331
- const importOptions = this.getNodeParameter('importOptions', itemIndex, {});
332
- const transaction = {
333
- account: accountId,
334
- amount: decimalToActualAmount(amount, transactionType),
335
- category: categoryId,
336
- date: parseActualDate(date),
337
- imported_id: transactionUuid,
338
- payee_name: payeeName,
339
- };
340
- if (additionalFields.notes) {
341
- transaction.notes = additionalFields.notes;
250
+ const action = this.getNodeParameter('action', itemIndex);
251
+ if (action === 'transactionCreate') {
252
+ const accountId = this.getNodeParameter('accountId', itemIndex, '', {
253
+ extractValue: true,
254
+ });
255
+ const categoryId = this.getNodeParameter('categoryId', itemIndex, '', {
256
+ extractValue: true,
257
+ });
258
+ const amount = parseMinorUnitsAmount(this.getNodeParameter('amount', itemIndex));
259
+ const date = this.getNodeParameter('date', itemIndex);
260
+ const payeeName = this.getNodeParameter('payeeName', itemIndex);
261
+ const cleared = this.getNodeParameter('cleared', itemIndex, false);
262
+ const learnCategories = this.getNodeParameter('learnCategories', itemIndex, false);
263
+ const runTransfers = this.getNodeParameter('runTransfers', itemIndex, false);
264
+ const body = {
265
+ learnCategories,
266
+ runTransfers,
267
+ transaction: {
268
+ account: accountId,
269
+ category: categoryId,
270
+ amount,
271
+ payee_name: payeeName,
272
+ date: parseActualDate(date),
273
+ cleared,
274
+ },
275
+ };
276
+ const responseData = (await transport_1.actualApiRequest.call(this, 'POST', `/accounts/${encodeURIComponent(accountId)}/transactions`, body));
277
+ returnData.push({
278
+ json: {
279
+ ...responseData,
280
+ request: body,
281
+ },
282
+ pairedItem: itemIndex,
283
+ });
284
+ continue;
342
285
  }
343
- if (additionalFields.cleared !== undefined) {
344
- transaction.cleared = additionalFields.cleared;
286
+ if (action === 'budgetGet') {
287
+ const month = this.getNodeParameter('month', itemIndex);
288
+ const trimmed = month.trim();
289
+ if (!/^\d{4}-\d{2}$/.test(trimmed)) {
290
+ throw new n8n_workflow_1.ApplicationError('Month must be in YYYY-MM format');
291
+ }
292
+ const response = (await transport_1.actualApiRequest.call(this, 'GET', `/months/${encodeURIComponent(trimmed)}`));
293
+ returnData.push({
294
+ json: response,
295
+ pairedItem: itemIndex,
296
+ });
297
+ continue;
345
298
  }
346
- const responseData = (await transport_1.actualApiRequest.call(this, 'POST', `/accounts/${encodeURIComponent(accountId)}/transactions/import`, {
347
- transactions: [transaction],
348
- defaultCleared: (_a = importOptions.defaultCleared) !== null && _a !== void 0 ? _a : true,
349
- dryRun: (_b = importOptions.dryRun) !== null && _b !== void 0 ? _b : false,
350
- reimportDeleted: (_c = importOptions.reimportDeleted) !== null && _c !== void 0 ? _c : false,
351
- }));
352
- returnData.push({
353
- json: {
354
- ...responseData,
355
- transaction,
356
- },
357
- pairedItem: itemIndex,
358
- });
299
+ if (action === 'accountList') {
300
+ const response = (await transport_1.actualApiRequest.call(this, 'GET', '/accounts'));
301
+ returnData.push({
302
+ json: response,
303
+ pairedItem: itemIndex,
304
+ });
305
+ continue;
306
+ }
307
+ if (action === 'categoryList') {
308
+ const response = (await transport_1.actualApiRequest.call(this, 'GET', '/categories'));
309
+ returnData.push({
310
+ json: response,
311
+ pairedItem: itemIndex,
312
+ });
313
+ continue;
314
+ }
315
+ throw new n8n_workflow_1.ApplicationError(`Unsupported action: ${action}`);
359
316
  }
360
317
  catch (error) {
361
318
  if (this.continueOnFail()) {
@@ -387,33 +344,13 @@ async function getAccounts(filter) {
387
344
  return { results };
388
345
  }
389
346
  async function getCategories(filter) {
390
- var _a, _b;
391
- const response = (await transport_1.actualApiRequest.call(this, 'GET', '/categorygroups'));
392
- const results = [];
393
- for (const group of (_a = response.data) !== null && _a !== void 0 ? _a : []) {
394
- if (group.hidden) {
395
- continue;
396
- }
397
- for (const category of (_b = group.categories) !== null && _b !== void 0 ? _b : []) {
398
- if (category.hidden) {
399
- continue;
400
- }
401
- results.push({
402
- name: `${group.name} / ${category.name}`,
403
- value: category.id,
404
- });
405
- }
406
- }
407
- return { results: filterResults(results, filter) };
408
- }
409
- async function getPayees(filter) {
410
347
  var _a;
411
- const response = (await transport_1.actualApiRequest.call(this, 'GET', '/payees'));
412
- const payees = filterResults((_a = response.data) !== null && _a !== void 0 ? _a : [], filter);
413
- const results = payees.map((payee) => ({
414
- name: payee.name,
415
- value: payee.name,
348
+ const response = (await transport_1.actualApiRequest.call(this, 'GET', '/categories'));
349
+ const visible = ((_a = response.data) !== null && _a !== void 0 ? _a : []).filter((c) => !c.hidden);
350
+ const results = visible.map((category) => ({
351
+ name: category.name,
352
+ value: category.id,
416
353
  }));
417
- return { results };
354
+ return { results: filterResults(results, filter) };
418
355
  }
419
356
  //# sourceMappingURL=ActualBudget.node.js.map