@actual-app/core 26.3.0

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.
Files changed (358) hide show
  1. package/.swcrc +11 -0
  2. package/bin/build-browser +40 -0
  3. package/bin/copy-migrations +9 -0
  4. package/db.sqlite +0 -0
  5. package/default-db.sqlite +0 -0
  6. package/migrations/.force-copy-windows +0 -0
  7. package/migrations/1548957970627_remove-db-version.sql +5 -0
  8. package/migrations/1550601598648_payees.sql +23 -0
  9. package/migrations/1555786194328_remove_category_group_unique.sql +25 -0
  10. package/migrations/1561751833510_indexes.sql +7 -0
  11. package/migrations/1567699552727_budget.sql +38 -0
  12. package/migrations/1582384163573_cleared.sql +6 -0
  13. package/migrations/1597756566448_rules.sql +10 -0
  14. package/migrations/1608652596043_parent_field.sql +13 -0
  15. package/migrations/1608652596044_trans_views.sql +56 -0
  16. package/migrations/1612625548236_optimize.sql +7 -0
  17. package/migrations/1614782639336_trans_views2.sql +33 -0
  18. package/migrations/1615745967948_meta.sql +10 -0
  19. package/migrations/1616167010796_accounts_order.sql +5 -0
  20. package/migrations/1618975177358_schedules.sql +28 -0
  21. package/migrations/1632571489012_remove_cache.js +136 -0
  22. package/migrations/1679728867040_rules_conditions.sql +5 -0
  23. package/migrations/1681115033845_add_schedule_name.sql +5 -0
  24. package/migrations/1682974838138_remove_payee_rules.sql +5 -0
  25. package/migrations/1685007876842_add_category_hidden.sql +6 -0
  26. package/migrations/1686139660866_remove_account_type.sql +5 -0
  27. package/migrations/1688749527273_transaction_filters.sql +10 -0
  28. package/migrations/1688841238000_add_account_type.sql +5 -0
  29. package/migrations/1691233396000_add_schedule_next_date_tombstone.sql +5 -0
  30. package/migrations/1694438752000_add_goal_targets.sql +7 -0
  31. package/migrations/1697046240000_add_reconciled.sql +5 -0
  32. package/migrations/1704572023730_add_account_sync_source.sql +5 -0
  33. package/migrations/1704572023731_add_missing_goCardless_sync_source.sql +9 -0
  34. package/migrations/1707267033000_reports.sql +28 -0
  35. package/migrations/1712784523000_unhide_input_group.sql +8 -0
  36. package/migrations/1716359441000_include_current.sql +5 -0
  37. package/migrations/1720310586000_link_transfer_schedules.sql +19 -0
  38. package/migrations/1720664867241_add_payee_favorite.sql +5 -0
  39. package/migrations/1720665000000_goal_context.sql +6 -0
  40. package/migrations/1722717601000_reports_move_selected_categories.js +55 -0
  41. package/migrations/1722804019000_create_dashboard_table.js +69 -0
  42. package/migrations/1723665565000_prefs.js +59 -0
  43. package/migrations/1730744182000_fix_dashboard_table.sql +7 -0
  44. package/migrations/1736640000000_custom_report_sorting.sql +7 -0
  45. package/migrations/1737158400000_add_learn_categories_to_payees.sql +5 -0
  46. package/migrations/1738491452000_sorting_rename.sql +13 -0
  47. package/migrations/1739139550000_bank_sync_page.sql +7 -0
  48. package/migrations/1740506588539_add_last_reconciled_at.sql +5 -0
  49. package/migrations/1745425408000_update_budgetType_pref.sql +7 -0
  50. package/migrations/1749799110000_add_tags.sql +10 -0
  51. package/migrations/1749799110001_tags_tombstone.sql +5 -0
  52. package/migrations/1754611200000_add_category_template_settings.sql +5 -0
  53. package/migrations/1759260219000_add_trim_interval_report_setting.sql +6 -0
  54. package/migrations/1759842823172_add_isGlobal_to_preferences.sql +1 -0
  55. package/migrations/1762178745667_rename_csv_skip_lines_pref.sql +8 -0
  56. package/migrations/1765518577215_multiple_dashboards.js +30 -0
  57. package/migrations/1768872504000_add_payee_locations.sql +21 -0
  58. package/package.json +128 -0
  59. package/src/mocks/arbitrary-schema.ts +162 -0
  60. package/src/mocks/budget.ts +901 -0
  61. package/src/mocks/files/8859-1.qfx +63 -0
  62. package/src/mocks/files/best.data-ever$.QFX +124 -0
  63. package/src/mocks/files/big.data.QiF +91 -0
  64. package/src/mocks/files/budgets/.commit-to-git +0 -0
  65. package/src/mocks/files/camt/camt.053.payee-memo.xml +127 -0
  66. package/src/mocks/files/camt/camt.053.xml +463 -0
  67. package/src/mocks/files/credit-card.ofx +11 -0
  68. package/src/mocks/files/data-multi-decimal.ofx +64 -0
  69. package/src/mocks/files/data-payee-memo.ofx +75 -0
  70. package/src/mocks/files/data-payee-memo.qif +17 -0
  71. package/src/mocks/files/data.ofx +124 -0
  72. package/src/mocks/files/data.qfx +124 -0
  73. package/src/mocks/files/data.qif +91 -0
  74. package/src/mocks/files/default-budget-template/db.sqlite +0 -0
  75. package/src/mocks/files/default-budget-template/metadata.json +6 -0
  76. package/src/mocks/files/html-vals.qfx +17 -0
  77. package/src/mocks/index.ts +221 -0
  78. package/src/mocks/migrations/1508717984291_up_add-poop.sql +13 -0
  79. package/src/mocks/migrations/1508718036311_up_modify-poop.sql +2 -0
  80. package/src/mocks/migrations/1508727787513_remove-is_income.sql +15 -0
  81. package/src/mocks/random.ts +16 -0
  82. package/src/mocks/setup.ts +180 -0
  83. package/src/mocks/spreadsheet.ts +101 -0
  84. package/src/mocks/util.ts +82 -0
  85. package/src/platform/client/connection/README.md +3 -0
  86. package/src/platform/client/connection/__mocks__/index.ts +67 -0
  87. package/src/platform/client/connection/index-types.ts +95 -0
  88. package/src/platform/client/connection/index.browser.ts +213 -0
  89. package/src/platform/client/connection/index.ts +155 -0
  90. package/src/platform/client/undo/index.ts +59 -0
  91. package/src/platform/exceptions/__mocks__/index.ts +7 -0
  92. package/src/platform/exceptions/index.ts +9 -0
  93. package/src/platform/server/asyncStorage/__mocks__/index.ts +50 -0
  94. package/src/platform/server/asyncStorage/index-types.ts +35 -0
  95. package/src/platform/server/asyncStorage/index.api.ts +2 -0
  96. package/src/platform/server/asyncStorage/index.electron.ts +88 -0
  97. package/src/platform/server/asyncStorage/index.ts +126 -0
  98. package/src/platform/server/connection/README.md +3 -0
  99. package/src/platform/server/connection/__mocks__/index.ts +15 -0
  100. package/src/platform/server/connection/index-types.ts +20 -0
  101. package/src/platform/server/connection/index.api.ts +13 -0
  102. package/src/platform/server/connection/index.electron.ts +102 -0
  103. package/src/platform/server/connection/index.ts +154 -0
  104. package/src/platform/server/fetch/__mocks__/index.ts +3 -0
  105. package/src/platform/server/fetch/index.api.ts +1 -0
  106. package/src/platform/server/fetch/index.electron.ts +18 -0
  107. package/src/platform/server/fetch/index.ts +20 -0
  108. package/src/platform/server/fs/index.api.ts +198 -0
  109. package/src/platform/server/fs/index.electron.ts +208 -0
  110. package/src/platform/server/fs/index.test.ts +117 -0
  111. package/src/platform/server/fs/index.ts +416 -0
  112. package/src/platform/server/fs/path-join.api.ts +1 -0
  113. package/src/platform/server/fs/path-join.electron.ts +1 -0
  114. package/src/platform/server/fs/path-join.ts +97 -0
  115. package/src/platform/server/fs/shared.ts +33 -0
  116. package/src/platform/server/indexeddb/index.ts +115 -0
  117. package/src/platform/server/log/index.ts +43 -0
  118. package/src/platform/server/sqlite/index.api.ts +2 -0
  119. package/src/platform/server/sqlite/index.electron.ts +134 -0
  120. package/src/platform/server/sqlite/index.test.ts +108 -0
  121. package/src/platform/server/sqlite/index.ts +241 -0
  122. package/src/platform/server/sqlite/normalise.ts +9 -0
  123. package/src/platform/server/sqlite/unicodeLike.test.ts +58 -0
  124. package/src/platform/server/sqlite/unicodeLike.ts +31 -0
  125. package/src/server/__mocks__/post.ts +9 -0
  126. package/src/server/__snapshots__/main.test.ts.snap +199 -0
  127. package/src/server/__snapshots__/sheet.test.ts.snap +9 -0
  128. package/src/server/accounts/__snapshots__/sync.test.ts.snap +136 -0
  129. package/src/server/accounts/app-bank-sync.test.ts +136 -0
  130. package/src/server/accounts/app.ts +1294 -0
  131. package/src/server/accounts/link.ts +25 -0
  132. package/src/server/accounts/payees.ts +36 -0
  133. package/src/server/accounts/sync.test.ts +679 -0
  134. package/src/server/accounts/sync.ts +1168 -0
  135. package/src/server/accounts/title/index.ts +60 -0
  136. package/src/server/accounts/title/lower-case.ts +93 -0
  137. package/src/server/accounts/title/specials.ts +21 -0
  138. package/src/server/admin/app.ts +241 -0
  139. package/src/server/api-models.ts +244 -0
  140. package/src/server/api.test.ts +36 -0
  141. package/src/server/api.ts +1030 -0
  142. package/src/server/app.ts +91 -0
  143. package/src/server/aql/compiler.test.ts +966 -0
  144. package/src/server/aql/compiler.ts +1222 -0
  145. package/src/server/aql/exec.test.ts +289 -0
  146. package/src/server/aql/exec.ts +128 -0
  147. package/src/server/aql/index.ts +41 -0
  148. package/src/server/aql/schema/executors.test.ts +420 -0
  149. package/src/server/aql/schema/executors.ts +345 -0
  150. package/src/server/aql/schema/index.test.ts +67 -0
  151. package/src/server/aql/schema/index.ts +409 -0
  152. package/src/server/aql/schema-helpers.test.ts +242 -0
  153. package/src/server/aql/schema-helpers.ts +208 -0
  154. package/src/server/aql/views.test.ts +62 -0
  155. package/src/server/aql/views.ts +57 -0
  156. package/src/server/auth/app.ts +387 -0
  157. package/src/server/bench.ts +29 -0
  158. package/src/server/budget/actions.ts +686 -0
  159. package/src/server/budget/app.ts +469 -0
  160. package/src/server/budget/base.test.ts +340 -0
  161. package/src/server/budget/base.ts +339 -0
  162. package/src/server/budget/category-template-context.test.ts +1658 -0
  163. package/src/server/budget/category-template-context.ts +862 -0
  164. package/src/server/budget/cleanup-template.pegjs +27 -0
  165. package/src/server/budget/cleanup-template.ts +408 -0
  166. package/src/server/budget/envelope.ts +403 -0
  167. package/src/server/budget/goal-template.pegjs +110 -0
  168. package/src/server/budget/goal-template.ts +309 -0
  169. package/src/server/budget/report.ts +308 -0
  170. package/src/server/budget/schedule-template.test.ts +184 -0
  171. package/src/server/budget/schedule-template.ts +351 -0
  172. package/src/server/budget/statements.ts +60 -0
  173. package/src/server/budget/template-notes.test.ts +393 -0
  174. package/src/server/budget/template-notes.ts +323 -0
  175. package/src/server/budget/util.ts +25 -0
  176. package/src/server/budgetfiles/__snapshots__/backups.test.ts.snap +101 -0
  177. package/src/server/budgetfiles/app.ts +672 -0
  178. package/src/server/budgetfiles/backups.test.ts +79 -0
  179. package/src/server/budgetfiles/backups.ts +251 -0
  180. package/src/server/cloud-storage.ts +467 -0
  181. package/src/server/dashboard/app.ts +373 -0
  182. package/src/server/db/__snapshots__/index.test.ts.snap +271 -0
  183. package/src/server/db/index.test.ts +300 -0
  184. package/src/server/db/index.ts +855 -0
  185. package/src/server/db/mappings.ts +59 -0
  186. package/src/server/db/sort.ts +58 -0
  187. package/src/server/db/types/index.ts +342 -0
  188. package/src/server/db/util.ts +36 -0
  189. package/src/server/encryption/app.ts +133 -0
  190. package/src/server/encryption/encryption-internals.api.ts +2 -0
  191. package/src/server/encryption/encryption-internals.electron.ts +89 -0
  192. package/src/server/encryption/encryption-internals.ts +109 -0
  193. package/src/server/encryption/encryption.test.ts +19 -0
  194. package/src/server/encryption/index.test.ts +19 -0
  195. package/src/server/encryption/index.ts +89 -0
  196. package/src/server/errors.ts +110 -0
  197. package/src/server/filters/app.ts +191 -0
  198. package/src/server/importers/actual.ts +49 -0
  199. package/src/server/importers/index.ts +58 -0
  200. package/src/server/importers/ynab4-types.ts +163 -0
  201. package/src/server/importers/ynab4.ts +470 -0
  202. package/src/server/importers/ynab5-types.ts +290 -0
  203. package/src/server/importers/ynab5.ts +1193 -0
  204. package/src/server/main-app.ts +25 -0
  205. package/src/server/main.test.ts +392 -0
  206. package/src/server/main.ts +336 -0
  207. package/src/server/migrate/__snapshots__/migrations.test.ts.snap +17 -0
  208. package/src/server/migrate/cli.ts +100 -0
  209. package/src/server/migrate/migrations.test.ts +81 -0
  210. package/src/server/migrate/migrations.ts +192 -0
  211. package/src/server/models.ts +184 -0
  212. package/src/server/mutators.ts +139 -0
  213. package/src/server/notes/app.ts +18 -0
  214. package/src/server/payees/app.ts +351 -0
  215. package/src/server/polyfills.ts +26 -0
  216. package/src/server/post.ts +219 -0
  217. package/src/server/preferences/app.ts +249 -0
  218. package/src/server/prefs.ts +91 -0
  219. package/src/server/reports/app.ts +187 -0
  220. package/src/server/rules/action.ts +344 -0
  221. package/src/server/rules/app.ts +193 -0
  222. package/src/server/rules/condition.ts +436 -0
  223. package/src/server/rules/customFunctions.ts +61 -0
  224. package/src/server/rules/formula-action.test.ts +175 -0
  225. package/src/server/rules/handlebars-helpers.ts +131 -0
  226. package/src/server/rules/index.test.ts +1095 -0
  227. package/src/server/rules/index.ts +22 -0
  228. package/src/server/rules/rule-indexer.ts +89 -0
  229. package/src/server/rules/rule-utils.ts +274 -0
  230. package/src/server/rules/rule.ts +193 -0
  231. package/src/server/schedules/app.test.ts +502 -0
  232. package/src/server/schedules/app.ts +644 -0
  233. package/src/server/schedules/find-schedules.ts +391 -0
  234. package/src/server/server-config.ts +59 -0
  235. package/src/server/sheet.test.ts +101 -0
  236. package/src/server/sheet.ts +280 -0
  237. package/src/server/spreadsheet/__snapshots__/spreadsheet.test.ts.snap +5 -0
  238. package/src/server/spreadsheet/app.ts +54 -0
  239. package/src/server/spreadsheet/globals.ts +13 -0
  240. package/src/server/spreadsheet/graph-data-structure.ts +165 -0
  241. package/src/server/spreadsheet/scratch +60 -0
  242. package/src/server/spreadsheet/spreadsheet.test.ts +191 -0
  243. package/src/server/spreadsheet/spreadsheet.ts +523 -0
  244. package/src/server/spreadsheet/util.ts +15 -0
  245. package/src/server/sql/init.sql +88 -0
  246. package/src/server/sync/__snapshots__/sync.test.ts.snap +31 -0
  247. package/src/server/sync/app.ts +29 -0
  248. package/src/server/sync/encoder.ts +129 -0
  249. package/src/server/sync/index.ts +820 -0
  250. package/src/server/sync/make-test-message.ts +19 -0
  251. package/src/server/sync/migrate.test.ts +169 -0
  252. package/src/server/sync/migrate.ts +48 -0
  253. package/src/server/sync/repair.ts +39 -0
  254. package/src/server/sync/reset.ts +91 -0
  255. package/src/server/sync/sync.property.test.ts +385 -0
  256. package/src/server/sync/sync.test.ts +349 -0
  257. package/src/server/sync/utils.ts +3 -0
  258. package/src/server/tags/app.ts +101 -0
  259. package/src/server/tests/mockData.json +9352 -0
  260. package/src/server/tests/mockSyncServer.ts +119 -0
  261. package/src/server/tools/app.ts +152 -0
  262. package/src/server/transactions/__snapshots__/transaction-rules.test.ts.snap +173 -0
  263. package/src/server/transactions/__snapshots__/transfer.test.ts.snap +655 -0
  264. package/src/server/transactions/app.ts +136 -0
  265. package/src/server/transactions/export/export-to-csv.ts +132 -0
  266. package/src/server/transactions/import/__snapshots__/parse-file.test.ts.snap +1582 -0
  267. package/src/server/transactions/import/ofx2json.test.ts +33 -0
  268. package/src/server/transactions/import/ofx2json.ts +157 -0
  269. package/src/server/transactions/import/parse-file.test.ts +224 -0
  270. package/src/server/transactions/import/parse-file.ts +286 -0
  271. package/src/server/transactions/import/qif2json.ts +110 -0
  272. package/src/server/transactions/import/xmlcamt2json.ts +168 -0
  273. package/src/server/transactions/index.ts +196 -0
  274. package/src/server/transactions/merge.test.ts +370 -0
  275. package/src/server/transactions/merge.ts +139 -0
  276. package/src/server/transactions/transaction-rules.test.ts +994 -0
  277. package/src/server/transactions/transaction-rules.ts +1038 -0
  278. package/src/server/transactions/transfer.test.ts +221 -0
  279. package/src/server/transactions/transfer.ts +173 -0
  280. package/src/server/undo.ts +271 -0
  281. package/src/server/update.ts +37 -0
  282. package/src/server/util/budget-name.ts +61 -0
  283. package/src/server/util/custom-sync-mapping.ts +48 -0
  284. package/src/server/util/rschedule.ts +9 -0
  285. package/src/shared/__mocks__/platform.ts +7 -0
  286. package/src/shared/__snapshots__/months.test.ts.snap +21 -0
  287. package/src/shared/arithmetic.test.ts +112 -0
  288. package/src/shared/arithmetic.ts +170 -0
  289. package/src/shared/async.test.ts +135 -0
  290. package/src/shared/async.ts +76 -0
  291. package/src/shared/constants.ts +5 -0
  292. package/src/shared/currencies.ts +70 -0
  293. package/src/shared/dashboard.ts +260 -0
  294. package/src/shared/environment.ts +18 -0
  295. package/src/shared/errors.ts +195 -0
  296. package/src/shared/locale.ts +27 -0
  297. package/src/shared/location-utils.test.ts +69 -0
  298. package/src/shared/location-utils.ts +49 -0
  299. package/src/shared/months.test.ts +5 -0
  300. package/src/shared/months.ts +485 -0
  301. package/src/shared/normalisation.ts +6 -0
  302. package/src/shared/platform.electron.ts +21 -0
  303. package/src/shared/platform.ts +20 -0
  304. package/src/shared/query.ts +176 -0
  305. package/src/shared/rules.test.ts +56 -0
  306. package/src/shared/rules.ts +371 -0
  307. package/src/shared/schedules.test.ts +570 -0
  308. package/src/shared/schedules.ts +560 -0
  309. package/src/shared/test-helpers.ts +156 -0
  310. package/src/shared/transactions.test.ts +275 -0
  311. package/src/shared/transactions.ts +433 -0
  312. package/src/shared/transfer.test.ts +75 -0
  313. package/src/shared/transfer.ts +16 -0
  314. package/src/shared/user.ts +4 -0
  315. package/src/shared/util.test.ts +240 -0
  316. package/src/shared/util.ts +633 -0
  317. package/src/types/api-handlers.ts +287 -0
  318. package/src/types/budget.ts +8 -0
  319. package/src/types/file.ts +47 -0
  320. package/src/types/handlers.ts +46 -0
  321. package/src/types/models/account.ts +24 -0
  322. package/src/types/models/bank-sync.ts +23 -0
  323. package/src/types/models/bank.ts +6 -0
  324. package/src/types/models/category-group.ts +11 -0
  325. package/src/types/models/category.ts +13 -0
  326. package/src/types/models/dashboard.ts +199 -0
  327. package/src/types/models/gocardless.ts +84 -0
  328. package/src/types/models/import-transaction.ts +56 -0
  329. package/src/types/models/index.ts +23 -0
  330. package/src/types/models/nearby-payee.ts +7 -0
  331. package/src/types/models/note.ts +4 -0
  332. package/src/types/models/openid.ts +8 -0
  333. package/src/types/models/payee-location.ts +8 -0
  334. package/src/types/models/payee.ts +10 -0
  335. package/src/types/models/pluggyai.ts +19 -0
  336. package/src/types/models/reports.ts +144 -0
  337. package/src/types/models/rule.ts +174 -0
  338. package/src/types/models/schedule.ts +49 -0
  339. package/src/types/models/simplefin.ts +28 -0
  340. package/src/types/models/tags.ts +6 -0
  341. package/src/types/models/templates.ts +135 -0
  342. package/src/types/models/transaction-filter.ts +9 -0
  343. package/src/types/models/transaction.ts +39 -0
  344. package/src/types/models/user-access.ts +10 -0
  345. package/src/types/models/user.ts +25 -0
  346. package/src/types/prefs.ts +167 -0
  347. package/src/types/server-events.ts +86 -0
  348. package/src/types/server-handlers.ts +27 -0
  349. package/src/types/util.ts +26 -0
  350. package/tsconfig.json +34 -0
  351. package/typings/pegjs.ts +1 -0
  352. package/typings/process-worker.ts +12 -0
  353. package/typings/vite-plugin-peggy-loader.ts +1 -0
  354. package/typings/window.ts +62 -0
  355. package/vite.config.ts +109 -0
  356. package/vite.desktop.config.ts +59 -0
  357. package/vitest.config.ts +43 -0
  358. package/vitest.web.config.ts +38 -0
@@ -0,0 +1,59 @@
1
+ // @ts-strict-ignore
2
+ import { addSyncListener } from '../sync/index';
3
+
4
+ import * as db from './index';
5
+
6
+ // This file keeps all the mappings in memory so we can access it
7
+ // synchronously. This is primarily used in the rules system, but
8
+ // there may be other uses in the future. You don't need to worry
9
+ // about this generally; if you are querying transactions, ids are
10
+ // transparently mapped for you. But if you are building something
11
+ // that stores ids and later uses them, you need to remember to map
12
+ // the ids.
13
+ //
14
+ // IMPORTANT: `loadMappings` must be called first before other modules
15
+ // that listen for sync changes. This must be the first sync listener
16
+ // to run in case other listeners use this mapping table; otherwise
17
+ // they might see stale mappings.
18
+
19
+ let allMappings;
20
+ let unlistenSync;
21
+
22
+ export async function loadMappings() {
23
+ // The mappings are separated into tables specific to the type of
24
+ // data. But you know, we really could keep a global mapping table.
25
+ const categories = (
26
+ await db.all<db.DbCategoryMapping>('SELECT * FROM category_mapping')
27
+ ).map(r => [r.id, r.transferId] as const);
28
+ const payees = (
29
+ await db.all<db.DbPayeeMapping>('SELECT * FROM payee_mapping')
30
+ ).map(r => [r.id, r.targetId] as const);
31
+
32
+ // All ids are unique, so we can just keep a global table of mappings
33
+ allMappings = new Map(categories.concat(payees));
34
+
35
+ if (unlistenSync) {
36
+ unlistenSync();
37
+ }
38
+ unlistenSync = addSyncListener(onApplySync);
39
+ }
40
+
41
+ function onApplySync(oldValues, newValues) {
42
+ newValues.forEach((items, table) => {
43
+ if (table.indexOf('mapping') !== -1) {
44
+ const field = table === 'category_mapping' ? 'transferId' : 'targetId';
45
+
46
+ items.forEach(newValue => {
47
+ allMappings.set(newValue.id, newValue[field]);
48
+ });
49
+ }
50
+ });
51
+ }
52
+
53
+ export function getMappings() {
54
+ return allMappings;
55
+ }
56
+
57
+ export function getMapping(id) {
58
+ return allMappings.get(id) || null;
59
+ }
@@ -0,0 +1,58 @@
1
+ export const SORT_INCREMENT = 16384;
2
+
3
+ function midpoint<T extends { sort_order: number }>(items: T[], to: number) {
4
+ const below = items[to - 1];
5
+ const above = items[to];
6
+
7
+ if (!below) {
8
+ return above.sort_order / 2;
9
+ } else if (!above) {
10
+ return below.sort_order + SORT_INCREMENT;
11
+ } else {
12
+ return (below.sort_order + above.sort_order) / 2;
13
+ }
14
+ }
15
+
16
+ export function shoveSortOrders<T extends { id: string; sort_order: number }>(
17
+ items: T[],
18
+ targetId: string | null = null,
19
+ ) {
20
+ const to = items.findIndex(item => item.id === targetId);
21
+ const target = items[to];
22
+ const before = items[to - 1];
23
+ const updates: Array<{ id: string; sort_order: number }> = [];
24
+
25
+ // If no target is specified, append at the end
26
+ if (!targetId || to === -1) {
27
+ let order;
28
+ if (items.length > 0) {
29
+ // Add a new increment to whatever is the latest sort order
30
+ order = items[items.length - 1].sort_order + SORT_INCREMENT;
31
+ } else {
32
+ // If no items exist, the default is to use the first increment
33
+ order = SORT_INCREMENT;
34
+ }
35
+
36
+ return { updates, sort_order: order };
37
+ } else {
38
+ if (target.sort_order - (before ? before.sort_order : 0) <= 2) {
39
+ let next = to;
40
+ let order = Math.floor(items[next].sort_order) + SORT_INCREMENT;
41
+ while (next < items.length) {
42
+ // No need to update it if it's already greater than the current
43
+ // order. This can happen because there may already be large
44
+ // gaps
45
+ if (order <= items[next].sort_order) {
46
+ break;
47
+ }
48
+
49
+ updates.push({ id: items[next].id, sort_order: order });
50
+
51
+ next++;
52
+ order += SORT_INCREMENT;
53
+ }
54
+ }
55
+
56
+ return { updates, sort_order: midpoint(items, to) };
57
+ }
58
+ }
@@ -0,0 +1,342 @@
1
+ // These are the types that exactly match the database schema.
2
+ // The `Entity` types e.g. `TransactionEntity`, `AccountEntity`, etc
3
+ // are specific to the AQL query framework and does not necessarily
4
+ // match the actual database schema.
5
+
6
+ type JsonString = string;
7
+
8
+ export type DbAccount = {
9
+ id: string;
10
+ name: string;
11
+ offbudget: 1 | 0;
12
+ closed: 1 | 0;
13
+ tombstone: 1 | 0;
14
+ sort_order: number;
15
+ account_id?: string | null;
16
+ balance_current?: number | null;
17
+ balance_available?: number | null;
18
+ balance_limit?: number | null;
19
+ mask?: string | null;
20
+ official_name?: string | null;
21
+ type?: string | null;
22
+ subtype?: string | null;
23
+ bank?: string | null;
24
+ account_sync_source?: 'simpleFin' | 'goCardless' | null;
25
+ last_reconciled?: string | null;
26
+ last_sync?: string | null;
27
+ };
28
+
29
+ export type DbBank = {
30
+ id: string;
31
+ bank_id: string;
32
+ name: string;
33
+ tombstone: 1 | 0;
34
+ };
35
+
36
+ export type DbCategory = {
37
+ id: string;
38
+ name: string;
39
+ is_income: 1 | 0;
40
+ cat_group: DbCategoryGroup['id'];
41
+ sort_order: number;
42
+ hidden: 1 | 0;
43
+ goal_def?: JsonString | null;
44
+ template_settings?: { source: 'notes' | 'ui' };
45
+ tombstone: 1 | 0;
46
+ };
47
+
48
+ export type DbCategoryGroup = {
49
+ id: string;
50
+ name: string;
51
+ is_income: 1 | 0;
52
+ sort_order: number;
53
+ hidden: 1 | 0;
54
+ tombstone: 1 | 0;
55
+ };
56
+
57
+ export type DbCategoryMapping = {
58
+ id: DbCategory['id'];
59
+ transferId: DbCategory['id'];
60
+ };
61
+
62
+ export type DbKvCache = {
63
+ key: string;
64
+ value: string;
65
+ };
66
+
67
+ export type DbKvCacheKey = {
68
+ id: number;
69
+ key: number;
70
+ };
71
+
72
+ export type DbClockMessage = {
73
+ id: string;
74
+ clock: string;
75
+ };
76
+
77
+ export type DbCrdtMessage = {
78
+ id: string;
79
+ timestamp: string;
80
+ dataset: string;
81
+ row: string;
82
+ column: string;
83
+ value: Uint8Array;
84
+ };
85
+
86
+ export type DbNote = {
87
+ id: string;
88
+ note: string;
89
+ };
90
+
91
+ export type DbPayeeMapping = {
92
+ id: DbPayee['id'];
93
+ targetId: DbPayee['id'];
94
+ };
95
+
96
+ export type DbPayee = {
97
+ id: string;
98
+ name: string;
99
+ transfer_acct?: DbAccount['id'] | null;
100
+ favorite: 1 | 0;
101
+ learn_categories: 1 | 0;
102
+ tombstone: 1 | 0;
103
+ // Unused in the codebase
104
+ category?: string | null;
105
+ };
106
+
107
+ export type DbRule = {
108
+ id: string;
109
+ stage: string;
110
+ conditions: JsonString;
111
+ actions: JsonString;
112
+ tombstone: 1 | 0;
113
+ conditions_op: string;
114
+ };
115
+
116
+ export type DbSchedule = {
117
+ id: string;
118
+ name: string;
119
+ rule: DbRule['id'];
120
+ active: 1 | 0;
121
+ completed: 1 | 0;
122
+ posts_transaction: 1 | 0;
123
+ tombstone: 1 | 0;
124
+ };
125
+
126
+ // type DbScheduleJsonPath = {
127
+ // schedule_id: DbSchedule['id'];
128
+ // payee: string;
129
+ // account: string;
130
+ // amount: string;
131
+ // date: string;
132
+ // };
133
+
134
+ export type DbScheduleNextDate = {
135
+ id: string;
136
+ schedule_id: DbSchedule['id'];
137
+ local_next_date: number;
138
+ local_next_date_ts: number;
139
+ base_next_date: number;
140
+ base_next_date_ts: number;
141
+ };
142
+
143
+ // This is unused in the codebase.
144
+ // type DbPendingTransaction = {
145
+ // id: string;
146
+ // acct: number;
147
+ // amount: number;
148
+ // description: string;
149
+ // date: string;
150
+ // };
151
+
152
+ export type DbTransaction = {
153
+ id: string;
154
+ isParent: 1 | 0;
155
+ isChild: 1 | 0;
156
+ date: number;
157
+ acct: DbAccount['id'];
158
+ amount: number;
159
+ sort_order: number;
160
+ parent_id?: DbTransaction['id'] | null;
161
+ category?: DbCategory['id'] | null;
162
+ description?: string | null;
163
+ notes?: string | null;
164
+ financial_id?: string | null;
165
+ error?: string | null;
166
+ imported_description?: string | null;
167
+ transferred_id?: DbTransaction['id'] | null;
168
+ schedule?: DbSchedule['id'] | null;
169
+ starting_balance_flag: 1 | 0;
170
+ tombstone: 1 | 0;
171
+ cleared: 1 | 0;
172
+ reconciled: 1 | 0;
173
+ // Unused in the codebase
174
+ pending?: 1 | 0 | null;
175
+ location?: string | null;
176
+ type?: string | null;
177
+ };
178
+
179
+ export type DbReflectBudget = {
180
+ id: string;
181
+ month: number;
182
+ category: string;
183
+ amount: number;
184
+ carryover: number;
185
+ goal: number;
186
+ long_goal: number;
187
+ };
188
+
189
+ export type DbZeroBudgetMonth = {
190
+ id: string;
191
+ buffered: number;
192
+ };
193
+
194
+ export type DbZeroBudget = {
195
+ id: string;
196
+ month: number;
197
+ category: string;
198
+ amount: number;
199
+ carryover: number;
200
+ goal: number;
201
+ long_goal: number;
202
+ };
203
+
204
+ export type DbTransactionFilter = {
205
+ id: string;
206
+ name: string;
207
+ conditions: JsonString;
208
+ conditions_op: string;
209
+ tombstone: 1 | 0;
210
+ };
211
+
212
+ export type DbPreference = {
213
+ id: string;
214
+ value: string;
215
+ };
216
+
217
+ export type DbCustomReport = {
218
+ id: string;
219
+ name: string;
220
+ start_date: string;
221
+ end_date: string;
222
+ date_static: number;
223
+ date_range: string;
224
+ mode: string;
225
+ group_by: string;
226
+ balance_type: string;
227
+ show_empty: 1 | 0;
228
+ show_offbudget: 1 | 0;
229
+ show_hidden: 1 | 0;
230
+ show_uncategorized: 1 | 0;
231
+ selected_categories: string;
232
+ graph_type: string;
233
+ conditions: JsonString;
234
+ conditions_op: string;
235
+ metadata: JsonString;
236
+ interval: string;
237
+ color_scheme: string;
238
+ include_current: 1 | 0;
239
+ sort_by: string;
240
+ tombstone: 1 | 0;
241
+ };
242
+
243
+ export type DbDashboardPage = {
244
+ id: string;
245
+ name: string;
246
+ tombstone: 1 | 0;
247
+ };
248
+
249
+ export type DbDashboard = {
250
+ id: string;
251
+ dashboard_page_id: string;
252
+ type: string;
253
+ width: number;
254
+ height: number;
255
+ x: number;
256
+ y: number;
257
+ meta: JsonString;
258
+ tombstone: 1 | 0;
259
+ };
260
+
261
+ export type DbViewTransactionInternal = {
262
+ id: DbTransaction['id'];
263
+ is_parent: DbTransaction['isParent'];
264
+ is_child: DbTransaction['isChild'];
265
+ date: DbTransaction['date'];
266
+ account: DbAccount['id'];
267
+ amount: DbTransaction['amount'];
268
+ parent_id: DbTransaction['parent_id'] | null;
269
+ category: DbCategory['id'] | null;
270
+ payee: DbPayee['id'] | null;
271
+ notes: DbTransaction['notes'] | null;
272
+ imported_id: DbTransaction['financial_id'] | null;
273
+ error: DbTransaction['error'] | null;
274
+ imported_payee: DbTransaction['imported_description'] | null;
275
+ starting_balance_flag: DbTransaction['starting_balance_flag'] | null;
276
+ transfer_id: DbTransaction['transferred_id'] | null;
277
+ schedule: DbSchedule['id'] | null;
278
+ sort_order: DbTransaction['sort_order'];
279
+ cleared: DbTransaction['cleared'];
280
+ tombstone: DbTransaction['tombstone'];
281
+ reconciled: DbTransaction['reconciled'];
282
+ };
283
+
284
+ export type DbViewTransactionInternalAlive = DbViewTransactionInternal;
285
+ export type DbViewTransaction = DbViewTransactionInternalAlive;
286
+
287
+ export type DbViewCategory = {
288
+ id: DbCategory['id'];
289
+ name: DbCategory['name'];
290
+ is_income: DbCategory['is_income'];
291
+ hidden: DbCategory['hidden'];
292
+ group: DbCategoryGroup['id'];
293
+ sort_order: DbCategory['sort_order'];
294
+ tombstone: DbCategory['tombstone'];
295
+ };
296
+
297
+ export type DbViewCategoryWithGroupHidden = {
298
+ id: DbCategory['id'];
299
+ name: DbCategory['name'];
300
+ is_income: DbCategory['is_income'];
301
+ hidden: DbCategory['hidden'];
302
+ group: DbCategoryGroup['id'];
303
+ group_hidden: DbCategoryGroup['hidden'];
304
+ sort_order: DbCategory['sort_order'];
305
+ tombstone: DbCategory['tombstone'];
306
+ };
307
+
308
+ export type DbViewPayee = {
309
+ id: DbPayee['id'];
310
+ name: DbAccount['name'] | DbPayee['name'];
311
+ transfer_acct: DbPayee['transfer_acct'];
312
+ tombstone: DbPayee['tombstone'];
313
+ };
314
+
315
+ export type DbViewSchedule = {
316
+ id: DbSchedule['id'];
317
+ name: DbSchedule['name'];
318
+ rule: DbSchedule['rule'];
319
+ next_date:
320
+ | DbScheduleNextDate['local_next_date_ts']
321
+ | DbScheduleNextDate['local_next_date']
322
+ | DbScheduleNextDate['base_next_date'];
323
+ active: DbSchedule['active'];
324
+ completed: DbSchedule['completed'];
325
+ posts_transaction: DbSchedule['posts_transaction'];
326
+ tombstone: DbSchedule['tombstone'];
327
+ _payee: DbPayeeMapping['targetId'];
328
+ _account: DbAccount['id'];
329
+ _amount: number;
330
+ _amountOp: string;
331
+ _date: JsonString;
332
+ _conditions: JsonString;
333
+ _actions: JsonString;
334
+ };
335
+
336
+ export type DbTag = {
337
+ id: string;
338
+ tag: string;
339
+ color?: string | null;
340
+ description?: string | null;
341
+ tombstone: 1 | 0;
342
+ };
@@ -0,0 +1,36 @@
1
+ // @ts-strict-ignore
2
+ export async function incrFetch(
3
+ runQuery,
4
+ terms,
5
+ compare,
6
+ makeQuery,
7
+ params = [],
8
+ ) {
9
+ const pageCount = 500;
10
+ let results = [];
11
+
12
+ let fetchedIds = new Set();
13
+
14
+ for (let i = 0; i < terms.length; i += pageCount) {
15
+ const slice = terms
16
+ .slice(i, i + pageCount)
17
+ .filter(id => !fetchedIds.has(id));
18
+ if (slice.length > 0) {
19
+ const filter = slice.map(id => compare(id)).join(' OR ');
20
+ const query = makeQuery('(' + filter + ')');
21
+
22
+ const rows = await runQuery(query, params, true);
23
+ fetchedIds = new Set([...fetchedIds, ...slice]);
24
+ results = results.concat(rows);
25
+ }
26
+ }
27
+
28
+ return results;
29
+ }
30
+
31
+ export function whereIn(ids: string[], field: string) {
32
+ const ids2 = [...new Set(ids)];
33
+
34
+ const filter = `${field} IN (` + ids2.map(id => `'${id}'`).join(',') + ')';
35
+ return filter;
36
+ }
@@ -0,0 +1,133 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+
3
+ import * as asyncStorage from '../../platform/server/asyncStorage';
4
+ import { logger } from '../../platform/server/log';
5
+ import type { Budget } from '../../types/budget';
6
+ import { createApp } from '../app';
7
+ import { post } from '../post';
8
+ import * as prefs from '../prefs';
9
+ import { getServer } from '../server-config';
10
+ import { makeTestMessage, resetSync } from '../sync';
11
+
12
+ import * as encryption from '.';
13
+
14
+ export type EncryptionHandlers = {
15
+ 'key-make': typeof keyMake;
16
+ 'key-test': typeof keyTest;
17
+ };
18
+
19
+ export const app = createApp<EncryptionHandlers>();
20
+ app.method('key-make', keyMake);
21
+ app.method('key-test', keyTest);
22
+
23
+ // A user can only enable/change their key with the file loaded. This
24
+ // will change in the future: during onboarding the user should be
25
+ // able to enable encryption. (Imagine if they are importing data from
26
+ // another source, they should be able to encrypt first)
27
+ async function keyMake({ password }: { password: string }) {
28
+ if (!prefs.getPrefs()) {
29
+ throw new Error('key-make must be called with file loaded');
30
+ }
31
+
32
+ const salt = encryption.randomBytes(32).toString('base64');
33
+ const id = uuidv4();
34
+ const key = await encryption.createKey({ id, password, salt });
35
+
36
+ // Load the key
37
+ await encryption.loadKey(key);
38
+
39
+ // Make some test data to use if the key is valid or not
40
+ const testContent = await makeTestMessage(key.getId());
41
+
42
+ // Changing your key necessitates a sync reset as well. This will
43
+ // clear all existing encrypted data from the server so you won't
44
+ // have a mix of data encrypted with different keys.
45
+ return await resetSync({
46
+ key,
47
+ salt,
48
+ testContent: JSON.stringify({
49
+ ...testContent,
50
+ value: testContent.value.toString('base64'),
51
+ }),
52
+ });
53
+ }
54
+
55
+ // This can be called both while a file is already loaded or not. This
56
+ // will see if a key is valid and if so save it off.
57
+ async function keyTest({
58
+ cloudFileId,
59
+ password,
60
+ }: {
61
+ cloudFileId?: Budget['cloudFileId'];
62
+ password: string;
63
+ }) {
64
+ const userToken = await asyncStorage.getItem('user-token');
65
+
66
+ if (cloudFileId == null) {
67
+ cloudFileId = prefs.getPrefs().cloudFileId;
68
+ }
69
+
70
+ let validCloudFileId: NonNullable<Budget['cloudFileId']>;
71
+ let res: {
72
+ id: string;
73
+ salt: string;
74
+ test: string | null;
75
+ };
76
+ try {
77
+ const serverConfig = getServer();
78
+ if (!serverConfig) {
79
+ throw new Error('No sync server configured.');
80
+ }
81
+ res = await post(serverConfig.SYNC_SERVER + '/user-get-key', {
82
+ token: userToken,
83
+ fileId: cloudFileId,
84
+ });
85
+ validCloudFileId = cloudFileId!;
86
+ } catch (e) {
87
+ logger.log(e);
88
+ return { error: { reason: 'network' } };
89
+ }
90
+
91
+ const { id, salt, test: originalTest } = res;
92
+
93
+ if (!originalTest) {
94
+ return { error: { reason: 'old-key-style' } };
95
+ }
96
+
97
+ const test: {
98
+ value: string;
99
+ meta: {
100
+ keyId: string;
101
+ algorithm: string;
102
+ iv: string;
103
+ authTag: string;
104
+ };
105
+ } = JSON.parse(originalTest);
106
+
107
+ const key = await encryption.createKey({ id, password, salt });
108
+ await encryption.loadKey(key);
109
+
110
+ try {
111
+ await encryption.decrypt(Buffer.from(test.value, 'base64'), test.meta);
112
+ } catch (e) {
113
+ logger.log(e);
114
+
115
+ // Unload the key, it's invalid
116
+ encryption.unloadKey(key);
117
+ return { error: { reason: 'decrypt-failure' } };
118
+ }
119
+
120
+ // Persist key in async storage
121
+ const keys = JSON.parse((await asyncStorage.getItem(`encrypt-keys`)) || '{}');
122
+ keys[validCloudFileId] = key.serialize();
123
+ await asyncStorage.setItem('encrypt-keys', JSON.stringify(keys));
124
+
125
+ // Save the key id in prefs if the are loaded. If they aren't, we
126
+ // are testing a key to download a file and when the file is
127
+ // actually downloaded it will update the prefs with the latest key id
128
+ if (prefs.getPrefs()) {
129
+ await prefs.savePrefs({ encryptKeyId: key.getId() });
130
+ }
131
+
132
+ return {};
133
+ }
@@ -0,0 +1,2 @@
1
+ // oxlint-disable-next-line no-restricted-imports
2
+ export * from './encryption-internals.electron';